Skip to content

Examples

Andrew Lambert edited this page Nov 26, 2022 · 13 revisions

Examples

Putting it all together

This example is a BinaryStream workalike class that encrypts or decrypts a single file with a password. The initialization vector for the cipher and the salt for the password-based key generator are randomly generated and stored alongside the encrypted stream in a Xojo VirtualVolume. Download the source file here. See farther down the page for a demonstration of this class.

Public Class PasswordProtectedFile
Implements Readable,Writeable

    Private Property mFileSys As VirtualVolume

    Private Property mReadable As Boolean

    Private Property mStream As libsodium.SKI.SecretStream

    Protected Sub Constructor(Key As libsodium.SKI.SecretKey, FileSys As VirtualVolume)
      ' This protected constructor is called by the Create() and Open() shared methods.
      ' Any kind of SecretKey could be used here, not just ones derived from passwords.
      ' A SharedSecret key could also be used if the Key parameter's datatype were changed
      ' accordingly.

      mFileSys = FileSys
      If mFileSys.Root.Child("_iv").Exists Then
        ' decrypt mode
        mReadable = True
        Dim payloadstream As BinaryStream = BinaryStream.Open(mFileSys.Root.Child("_payload"))
        Dim bs As BinaryStream = BinaryStream.Open(mFileSys.Root.Child("_iv"))
        Dim iv As MemoryBlock = bs.Read(bs.Length)
        bs.Close
        mStream = mStream.Open(Key, payloadstream, iv)
        
      Else
        ' encrypt mode
        mReadable = False
        Dim payloadstream As BinaryStream = BinaryStream.Create(mFileSys.Root.Child("_payload"))
        mStream = mStream.Create(Key, payloadstream)
        Dim bs As BinaryStream = BinaryStream.Create(mFileSys.Root.Child("_iv"))
        bs.Write(mStream.DecryptionHeader)
        bs.Close
        
      End If
    End Sub

    Shared Function Create(Destination As FolderItem, Passwd As libsodium.Password) As PasswordProtectedFile
      ' This method derives a key from the password plus a random salt and then
      ' returns a PasswordProtectedFile in encrypt/write-only mode.

      Dim filesys As VirtualVolume = Destination.CreateVirtualVolume()
      Dim key As New libsodium.SKI.SecretKey(Passwd)
      Dim bs As BinaryStream = BinaryStream.Create(filesys.Root.Child("_salt"))
      bs.Write(key.Salt)
      bs.Close
      Return New PasswordProtectedFile(key, filesys)
    End Function

    Sub Write(text As String)
      // Part of the Writeable interface. 
      If Not mReadable And mStream <> Nil Then mStream.Write(text)
    End Sub

    Shared Function Open(Source As FolderItem, Passwd As libsodium.Password) As PasswordProtectedFile
      ' This method derives the key from the password plus the salt stored in the
      ' file and then returns a PasswordProtectedFile in decrypt/read-only mode.

      Dim filesys As VirtualVolume = Source.OpenAsVirtualVolume()
      If filesys = Nil Or Not filesys.Root.Child("_salt").Exists Then Raise New IOException ' not a PasswordProtectedFile!
      Dim bs As BinaryStream = BinaryStream.Open(filesys.Root.Child("_salt"))
      Dim salt As MemoryBlock = bs.Read(bs.Length)
      bs.Close
      Dim key As New libsodium.SKI.SecretKey(Passwd, salt)
      Return New PasswordProtectedFile(key, filesys)
    End Function

    Function Read(Count As Integer, encoding As TextEncoding = Nil) As String
      // Part of the Readable interface.   
      If mReadable And mStream <> Nil Then Return mStream.Read(Count, encoding)
    End Function

    Sub Close()
      If mStream <> Nil Then mStream.Close
      If mFileSys <> Nil Then mFileSys.Flush()
      mStream = Nil
      mFileSys = Nil
    End Sub
   
    Private Sub Destructor()
      Me.Close()
    End Sub

    Function EOF() As Boolean
      // Part of the Readable interface.
      Return mReadable And (mStream <> Nil And mStream.EOF)
    End Function

    Sub Flush()
      // Part of the Writeable interface.
      If Not mReadable And mStream <> Nil Then mStream.Flush()
    End Sub

    Function ReadError() As Boolean
      // Part of the Readable interface.
      Return mReadable And (mStream <> Nil And mStream.ReadError)
    End Function

    Function WriteError() As Boolean
      // Part of the Writeable interface.
      Return Not mReadable And (mStream <> Nil And mStream.WriteError)
    End Function
End Class

Demonstration

This example uses the PasswordProtectedFile class to encrypt a file:

  Dim passwd As libsodium.Password = "seekrit"
  Dim src As FolderItem = GetOpenFolderItem("") ' the file to be protected
  Dim dst As FolderItem = GetSaveFolderItem("", src.Name + ".encrypted") ' the output file
  Dim srcstream As BinaryStream = BinaryStream.Open(src)
  Dim dststream As PasswordProtectedFile = PasswordProtectedFile.Create(dst, passwd)
  Do Until srcstream.EOF
    dststream.Write(srcstream.Read(1024 * 1024 * 32))
  Loop
  srcstream.Close()
  dststream.Close()

This example uses the PasswordProtectedFile class to decrypt the file from the previous example:

  Dim passwd As libsodium.Password = "seekrit"
  Dim src As FolderItem = GetOpenFolderItem("") ' the file to be decrypted
  Dim dst As FolderItem = GetSaveFolderItem("", Replace(src.Name,".encrypted", "")) ' the output file
  Dim srcstream As PasswordProtectedFile = PasswordProtectedFile.Open(src, passwd)
  Dim dststream As BinaryStream = BinaryStream.Create(dst)
  Do Until srcstream.EOF
    dststream.Write(srcstream.Read(1024 * 1024 * 32))
  Loop
  srcstream.Close()
  dststream.Close()
Clone this wiki locally