Skip to content

Compressed SecretStream Example

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

Remarks

This class is a subclass of the SecretStream class that compresses or decompresses the encrypted data stream with GZip. This class uses the zlib module from the RB-zlib project to perform compression and decompression, so be sure to have imported it into your project before attempting to use this class.

Download the source file for this class here. See farther down the page for a demonstration of this class.

Caution

Combining compression and encryption can potentially leak information if an attacker can control the input. The SecretStream class already mitigates this attack vector by applying padding before encrypting, but for maximum security do not use compression if the input is untrusted.

Class CompressedSecretStream
Inherits libsodium.SKI.SecretStream

    Private Property mCompressor As zlib.Deflater

    Private Property mDecompressor As zlib.Inflater

    Sub Close()
        If mCompressor <> Nil Then
            Dim data As String = mCompressor.Deflate("", zlib.Z_FINISH)
            mWriteBuffer = mWriteBuffer + data
        End If
        Super.Close()
    End Sub

    Protected Sub Constructor(InputStream As Readable, Key As MemoryBlock, Header As MemoryBlock)
        mDecompressor = New zlib.Inflater(zlib.GZIP_ENCODING)
        // Calling the overridden superclass constructor.
        // Constructor(InputStream As Readable, Key As MemoryBlock, Header As MemoryBlock) -- From SecretStream
        Super.Constructor(InputStream, Key, Header)      
    End Sub

    Protected Sub Constructor(OutputStream As Writeable, Key As MemoryBlock)
        mCompressor = New zlib.Deflater(zlib.Z_DEFAULT_COMPRESSION, zlib.Z_DEFAULT_STRATEGY, zlib.GZIP_ENCODING)
        // Calling the overridden superclass constructor.
        // Constructor(OutputStream As Writeable, Key As MemoryBlock) -- From SecretStream
        Super.Constructor(OutputStream, Key)    
    End Sub

    Shared Function Create(Key As libsodium.SKI.KeyContainer, OutputStream As Writeable) As CompressedSecretStream
        If Key IsA libsodium.Password Then Raise New libsodium.SodiumException(libsodium.ERR_KEYTYPE_MISMATCH)
        Return New CompressedSecretStream(OutputStream, Key.Value)
    End Function

    Shared Function Open(Key As libsodium.SKI.KeyContainer, InputStream As Readable, DecryptHeader As FolderItem, HeaderPassword As libsodium.Password = Nil) As CompressedSecretStream
        If Key IsA libsodium.Password Then Raise New libsodium.SodiumException(libsodium.ERR_KEYTYPE_MISMATCH)
        Dim bs As BinaryStream = BinaryStream.Open(DecryptHeader)
        Dim metadata As Dictionary
        Dim header As MemoryBlock = bs.Read(bs.Length)
        If header.StringValue(0, 5) = "-----" Then header = libsodium.Exporting.Import(header, metadata, HeaderPassword)
        Return New CompressedSecretStream(InputStream, Key.Value, header)
    End Function

    Shared Function Open(Key As libsodium.SKI.KeyContainer, InputStream As Readable, DecryptHeader As MemoryBlock, HeaderPassword As libsodium.Password = Nil) As CompressedSecretStream
        If Key IsA libsodium.Password Then Raise New libsodium.SodiumException(libsodium.ERR_KEYTYPE_MISMATCH)
        Dim metadata As Dictionary
        If DecryptHeader.StringValue(0, 5) = "-----" Then DecryptHeader = libsodium.Exporting.Import(DecryptHeader, metadata, HeaderPassword)
        Return New CompressedSecretStream(InputStream, Key.Value, DecryptHeader)
    End Function

    Function Read(Count As Integer, encoding As TextEncoding = Nil) As String
        Dim data As String = Super.Read(Count, encoding)
        If mDecompressor <> Nil Then data = mDecompressor.Inflate(data)
        Return DefineEncoding(data, encoding)
    End Function

    Sub Write(text As String)
        If mCompressor <> Nil Then text = mCompressor.Deflate(text, zlib.Z_NO_FLUSH)
        Super.Write(text)
    End Sub

End Class

Demonstration

This class can be used exactly the same way a plain SecretStream is used. Just replace libsodium.SKI.SecretStream with CompressedSecretStream when creating or opening the stream.

This example encrypts and compresses a file and saves the decryption header/initialization vector to another file:

  Dim mykey As libsodium.SKI.SecretKey
  mykey = mykey.Generate ' random key for example
  
  Dim src As FolderItem = GetOpenFolderItem("")
  Dim state As FolderItem = src.Parent.Child(src.Name + ".state")
  Dim dst As FolderItem = src.Parent.Child(src.Name + ".crypt")
  
  Dim bsin As BinaryStream = BinaryStream.Open(src)
  Dim bsout As BinaryStream = BinaryStream.Create(dst)
  Dim stream As CompressedSecretStream = CompressedSecretStream.Create(mykey, bsout)
  
  Do Until bsin.EOF
    stream.Write(bsin.Read(1024 * 64))
  Loop
  
  Call stream.ExportDecryptionHeader(state)
  stream.Close
  bsout.Close
  bsin.Close

This example decrypts and decompresses the file from the previous example:

  ' continuing from above
  bsin = BinaryStream.Open(dst)
  bsout = BinaryStream.Create(src.Parent.Child(src.Name + ".decrypted"))
  stream = CompressedSecretStream.Open(mykey, bsin, state)
  
  Do Until stream.EOF Or stream.ReadError
    bsout.Write(stream.Read(1024 * 64))
  Loop
  
  stream.Close
  bsout.Close
  bsin.Close

See also

Clone this wiki locally