Add a streaming api to KeyRing and SessionKey (#131)

* barebone streaming functionality

* encryption needs to return a writecloser

* added eof check

* workaround for reader problem with copies

* separate mobile wrappers from main api

* add a clone in the read result to avoid memory corruption

* refactor to reuse code, and fix verification

* have to give the verify key at the start of the decryption

* enfore readAll before signature verification

* streaming api for SessionKey

* add split message stream apis

* name interface params

* fix streaming api so it's supported by go-mobile

* hide internal writeCloser

* fix nil access

* added detached sigs methods

* started unit testing

* unit testing and fixed a bug where key and data packets where inverted

* remove unecessary error wrapping

* figured out closing order and error handling

* add GC calls to mobile writer and reader

* remove debugging values and arrays

* writer with builtin sha256

* unit testing the mobile helpers

* comments and linting

* Typo in error

Co-authored-by: wussler <aron@wussler.it>

* Add GetKeyPacket doc

Co-authored-by: wussler <aron@wussler.it>

* Add rfc reference in comments

Co-authored-by: wussler <aron@wussler.it>

* small improvements

* add compatibility tests with normal methods

* remove unecessary copies in the tests

* update go-crypto to the merged changes commit

* update comments of core internal functions

* remove unused nolint comment

* group message metadata in a struct

* fix comments

* change default values for metadata

* change the mobile reader wrapper to fit the behavior of java

* remove gc calls in the wrappers to avoid performance penalties

* bring back the former Go2MobileReader to be used for ios

* Update crypto/keyring_streaming.go

Co-authored-by: wussler <aron@wussler.it>

* return an error when verifying an embedded sig with no keyring

* Update crypto/sessionkey_streaming.go

Co-authored-by: wussler <aron@wussler.it>

* linter error

* update changelog

* update changelog

Co-authored-by: wussler <aron@wussler.it>
This commit is contained in:
marinthiercelin 2021-06-30 16:49:30 +02:00 committed by GitHub
parent 7380f7391f
commit c46ed8ed9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 1718 additions and 97 deletions

View file

@ -119,7 +119,7 @@ func (keyRing *KeyRing) VerifyDetachedEncrypted(message *PlainMessage, encrypted
// ------ INTERNAL FUNCTIONS -------
// Core for encryption+signature functions.
// Core for encryption+signature (non-streaming) functions.
func asymmetricEncrypt(
plainMessage *PlainMessage,
publicKey, privateKey *KeyRing,
@ -127,30 +127,17 @@ func asymmetricEncrypt(
) ([]byte, error) {
var outBuf bytes.Buffer
var encryptWriter io.WriteCloser
var signEntity *openpgp.Entity
var err error
if privateKey != nil && len(privateKey.entities) > 0 {
var err error
signEntity, err = privateKey.getSigningEntity()
if err != nil {
return nil, err
}
}
hints := &openpgp.FileHints{
IsBinary: plainMessage.IsBinary(),
FileName: plainMessage.Filename,
ModTime: plainMessage.getFormattedTime(),
}
if plainMessage.IsBinary() {
encryptWriter, err = openpgp.Encrypt(&outBuf, publicKey.entities, signEntity, hints, config)
} else {
encryptWriter, err = openpgp.EncryptText(&outBuf, publicKey.entities, signEntity, hints, config)
}
encryptWriter, err = asymmetricEncryptStream(hints, &outBuf, &outBuf, publicKey, privateKey, config)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in encrypting asymmetrically")
return nil, err
}
_, err = encryptWriter.Write(plainMessage.GetBinary())
@ -166,26 +153,46 @@ func asymmetricEncrypt(
return outBuf.Bytes(), nil
}
// Core for decryption+verification functions.
// Core for encryption+signature (all) functions.
func asymmetricEncryptStream(
hints *openpgp.FileHints,
keyPacketWriter io.Writer,
dataPacketWriter io.Writer,
publicKey, privateKey *KeyRing,
config *packet.Config,
) (encryptWriter io.WriteCloser, err error) {
var signEntity *openpgp.Entity
if privateKey != nil && len(privateKey.entities) > 0 {
var err error
signEntity, err = privateKey.getSigningEntity()
if err != nil {
return nil, err
}
}
if hints.IsBinary {
encryptWriter, err = openpgp.EncryptSplit(keyPacketWriter, dataPacketWriter, publicKey.entities, signEntity, hints, config)
} else {
encryptWriter, err = openpgp.EncryptTextSplit(keyPacketWriter, dataPacketWriter, publicKey.entities, signEntity, hints, config)
}
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in encrypting asymmetrically")
}
return encryptWriter, nil
}
// Core for decryption+verification (non streaming) functions.
func asymmetricDecrypt(
encryptedIO io.Reader, privateKey *KeyRing, verifyKey *KeyRing, verifyTime int64,
) (message *PlainMessage, err error) {
privKeyEntries := privateKey.entities
var additionalEntries openpgp.EntityList
if verifyKey != nil {
additionalEntries = verifyKey.entities
}
if additionalEntries != nil {
privKeyEntries = append(privKeyEntries, additionalEntries...)
}
config := &packet.Config{Time: getTimeGenerator()}
messageDetails, err := openpgp.ReadMessage(encryptedIO, privKeyEntries, nil, config)
messageDetails, err := asymmetricDecryptStream(
encryptedIO,
privateKey,
verifyKey,
)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in reading message")
return nil, err
}
body, err := ioutil.ReadAll(messageDetails.UnverifiedBody)
@ -205,3 +212,27 @@ func asymmetricDecrypt(
Time: messageDetails.LiteralData.Time,
}, err
}
// Core for decryption+verification (all) functions.
func asymmetricDecryptStream(
encryptedIO io.Reader, privateKey *KeyRing, verifyKey *KeyRing,
) (messageDetails *openpgp.MessageDetails, err error) {
privKeyEntries := privateKey.entities
var additionalEntries openpgp.EntityList
if verifyKey != nil {
additionalEntries = verifyKey.entities
}
if additionalEntries != nil {
privKeyEntries = append(privKeyEntries, additionalEntries...)
}
config := &packet.Config{Time: getTimeGenerator()}
messageDetails, err = openpgp.ReadMessage(encryptedIO, privKeyEntries, nil, config)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in reading message")
}
return messageDetails, err
}

299
crypto/keyring_streaming.go Normal file
View file

@ -0,0 +1,299 @@
package crypto
import (
"bytes"
"crypto"
"io"
"time"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/pkg/errors"
)
type Reader interface {
Read(b []byte) (n int, err error)
}
type Writer interface {
Write(b []byte) (n int, err error)
}
type WriteCloser interface {
Write(b []byte) (n int, err error)
Close() (err error)
}
type PlainMessageMetadata struct {
IsBinary bool
Filename string
ModTime int64
}
func NewPlainMessageMetadata(isBinary bool, filename string, modTime int64) *PlainMessageMetadata {
return &PlainMessageMetadata{IsBinary: isBinary, Filename: filename, ModTime: modTime}
}
// EncryptStream is used to encrypt data as a Writer.
// It takes a writer for the encrypted data and returns a WriteCloser for the plaintext data
// If signKeyRing is not nil, it is used to do an embedded signature.
func (keyRing *KeyRing) EncryptStream(
pgpMessageWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (plainMessageWriter WriteCloser, err error) {
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
if plainMessageMetadata == nil {
// Use sensible default metadata
plainMessageMetadata = &PlainMessageMetadata{
IsBinary: true,
Filename: "",
ModTime: GetUnixTime(),
}
}
hints := &openpgp.FileHints{
FileName: plainMessageMetadata.Filename,
IsBinary: plainMessageMetadata.IsBinary,
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
}
plainMessageWriter, err = asymmetricEncryptStream(hints, pgpMessageWriter, pgpMessageWriter, keyRing, signKeyRing, config)
if err != nil {
return nil, err
}
return plainMessageWriter, nil
}
// EncryptSplitResult is used to wrap the encryption writecloser while storing the key packet.
type EncryptSplitResult struct {
isClosed bool
keyPacketBuf *bytes.Buffer
keyPacket []byte
plainMessageWriter WriteCloser // The writer to writer plaintext data in.
}
func (res *EncryptSplitResult) Write(b []byte) (n int, err error) {
return res.plainMessageWriter.Write(b)
}
func (res *EncryptSplitResult) Close() (err error) {
err = res.plainMessageWriter.Close()
if err != nil {
return err
}
res.isClosed = true
res.keyPacket = res.keyPacketBuf.Bytes()
return nil
}
// GetKeyPacket returns the Public-Key Encrypted Session Key Packets (https://datatracker.ietf.org/doc/html/rfc4880#section-5.1).
// This can be retrieved only after the message has been fully written and the writer is closed.
func (res *EncryptSplitResult) GetKeyPacket() (keyPacket []byte, err error) {
if !res.isClosed {
return nil, errors.New("gopenpgp: can't access key packet until the message writer has been closed")
}
return res.keyPacket, nil
}
// EncryptSplitStream is used to encrypt data as a stream.
// It takes a writer for the Symmetrically Encrypted Data Packet
// (https://datatracker.ietf.org/doc/html/rfc4880#section-5.7)
// and returns a writer for the plaintext data and the key packet.
// If signKeyRing is not nil, it is used to do an embedded signature.
func (keyRing *KeyRing) EncryptSplitStream(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (*EncryptSplitResult, error) {
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
if plainMessageMetadata == nil {
// Use sensible default metadata
plainMessageMetadata = &PlainMessageMetadata{
IsBinary: true,
Filename: "",
ModTime: GetUnixTime(),
}
}
hints := &openpgp.FileHints{
FileName: plainMessageMetadata.Filename,
IsBinary: plainMessageMetadata.IsBinary,
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
}
var keyPacketBuf bytes.Buffer
plainMessageWriter, err := asymmetricEncryptStream(hints, &keyPacketBuf, dataPacketWriter, keyRing, signKeyRing, config)
if err != nil {
return nil, err
}
return &EncryptSplitResult{
keyPacketBuf: &keyPacketBuf,
plainMessageWriter: plainMessageWriter,
}, nil
}
// PlainMessageReader is used to wrap the data of the decrypted plain message.
// It can be used to read the decrypted data and verify the embedded signature.
type PlainMessageReader struct {
details *openpgp.MessageDetails
verifyKeyRing *KeyRing
verifyTime int64
readAll bool
}
// GetMetadata returns the metadata of the decrypted message.
func (msg *PlainMessageReader) GetMetadata() *PlainMessageMetadata {
return &PlainMessageMetadata{
Filename: msg.details.LiteralData.FileName,
IsBinary: msg.details.LiteralData.IsBinary,
ModTime: int64(msg.details.LiteralData.Time),
}
}
// Read is used to access the message decrypted data.
// Makes PlainMessageReader implement the Reader interface.
func (msg *PlainMessageReader) Read(b []byte) (n int, err error) {
n, err = msg.details.UnverifiedBody.Read(b)
if errors.Is(err, io.EOF) {
msg.readAll = true
}
return
}
// VerifySignature is used to verify that the signature is valid.
// This method needs to be called once all the data has been read.
// It will return an error if the signature is invalid
// or if the message hasn't been read entirely.
func (msg *PlainMessageReader) VerifySignature() (err error) {
if !msg.readAll {
return errors.New("gopenpgp: can't verify the signature until the message reader has been read entirely")
}
if msg.verifyKeyRing != nil {
processSignatureExpiration(msg.details, msg.verifyTime)
err = verifyDetailsSignature(msg.details, msg.verifyKeyRing)
} else {
err = errors.New("gopenpgp: no verify keyring was provided before decryption")
}
return
}
// DecryptStream is used to decrypt a pgp message as a Reader.
// It takes a reader for the message data
// and returns a PlainMessageReader for the plaintext data.
// If verifyKeyRing is not nil, PlainMessageReader.VerifySignature() will
// verify the embedded signature with the given key ring and verification time.
func (keyRing *KeyRing) DecryptStream(
message Reader,
verifyKeyRing *KeyRing,
verifyTime int64,
) (plainMessage *PlainMessageReader, err error) {
messageDetails, err := asymmetricDecryptStream(
message,
keyRing,
verifyKeyRing,
)
if err != nil {
return nil, err
}
return &PlainMessageReader{
messageDetails,
verifyKeyRing,
verifyTime,
false,
}, err
}
// DecryptSplitStream is used to decrypt a split pgp message as a Reader.
// It takes a key packet and a reader for the data packet
// and returns a PlainMessageReader for the plaintext data.
// If verifyKeyRing is not nil, PlainMessageReader.VerifySignature() will
// verify the embedded signature with the given key ring and verification time.
func (keyRing *KeyRing) DecryptSplitStream(
keypacket []byte,
dataPacketReader Reader,
verifyKeyRing *KeyRing, verifyTime int64,
) (plainMessage *PlainMessageReader, err error) {
messageReader := io.MultiReader(
bytes.NewReader(keypacket),
dataPacketReader,
)
return keyRing.DecryptStream(
messageReader,
verifyKeyRing,
verifyTime,
)
}
// SignDetachedStream generates and returns a PGPSignature for a given message Reader.
func (keyRing *KeyRing) SignDetachedStream(message Reader) (*PGPSignature, error) {
signEntity, err := keyRing.getSigningEntity()
if err != nil {
return nil, err
}
config := &packet.Config{DefaultHash: crypto.SHA512, Time: getTimeGenerator()}
var outBuf bytes.Buffer
// sign bin
if err := openpgp.DetachSign(&outBuf, signEntity, message, config); err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in signing")
}
return NewPGPSignature(outBuf.Bytes()), nil
}
// VerifyDetachedStream verifies a message reader with a detached PGPSignature
// and returns a SignatureVerificationError if fails.
func (keyRing *KeyRing) VerifyDetachedStream(
message Reader,
signature *PGPSignature,
verifyTime int64,
) error {
return verifySignature(
keyRing.entities,
message,
signature.GetBinary(),
verifyTime,
)
}
// SignDetachedEncryptedStream generates and returns a PGPMessage
// containing an encrypted detached signature for a given message Reader.
func (keyRing *KeyRing) SignDetachedEncryptedStream(
message Reader,
encryptionKeyRing *KeyRing,
) (encryptedSignature *PGPMessage, err error) {
if encryptionKeyRing == nil {
return nil, errors.New("gopenpgp: no encryption key ring provided")
}
signature, err := keyRing.SignDetachedStream(message)
if err != nil {
return nil, err
}
plainMessage := NewPlainMessage(signature.GetBinary())
encryptedSignature, err = encryptionKeyRing.Encrypt(plainMessage, nil)
return
}
// VerifyDetachedEncryptedStream verifies a PlainMessage
// with a PGPMessage containing an encrypted detached signature
// and returns a SignatureVerificationError if fails.
func (keyRing *KeyRing) VerifyDetachedEncryptedStream(
message Reader,
encryptedSignature *PGPMessage,
decryptionKeyRing *KeyRing,
verifyTime int64,
) error {
if decryptionKeyRing == nil {
return errors.New("gopenpgp: no decryption key ring provided")
}
plainMessage, err := decryptionKeyRing.Decrypt(encryptedSignature, nil, 0)
if err != nil {
return err
}
signature := NewPGPSignature(plainMessage.GetBinary())
return keyRing.VerifyDetachedStream(message, signature, verifyTime)
}

View file

@ -0,0 +1,491 @@
package crypto
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/pkg/errors"
)
var testMeta = &PlainMessageMetadata{
IsBinary: true,
Filename: "filename.txt",
ModTime: GetUnixTime(),
}
func TestKeyRing_EncryptDecryptStream(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
var ciphertextBuf bytes.Buffer
messageWriter, err := keyRingTestPublic.EncryptStream(
&ciphertextBuf,
testMeta,
keyRingTestPrivate,
)
if err != nil {
t.Fatal("Expected no error while encrypting stream with key ring, got:", err)
}
reachedEnd := false
bufferSize := 2
buffer := make([]byte, bufferSize)
for !reachedEnd {
n, err := messageReader.Read(buffer)
if err != nil {
if errors.Is(err, io.EOF) {
reachedEnd = true
} else {
t.Fatal("Expected no error while reading data, got:", err)
}
}
writtenTotal := 0
for writtenTotal < n {
written, err := messageWriter.Write(buffer[writtenTotal:n])
if err != nil {
t.Fatal("Expected no error while writing data, got:", err)
}
writtenTotal += written
}
}
err = messageWriter.Close()
if err != nil {
t.Fatal("Expected no error while closing plaintext writer, got:", err)
}
ciphertextBytes := ciphertextBuf.Bytes()
decryptedReader, err := keyRingTestPrivate.DecryptStream(
bytes.NewReader(ciphertextBytes),
keyRingTestPublic,
GetUnixTime(),
)
if err != nil {
t.Fatal("Expected no error while calling decrypting stream with key ring, got:", err)
}
err = decryptedReader.VerifySignature()
if err == nil {
t.Fatal("Expected an error while verifying the signature before reading the data, got nil")
}
decryptedBytes, err := io.ReadAll(decryptedReader)
if err != nil {
t.Fatal("Expected no error while reading the decrypted data, got:", err)
}
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
err = decryptedReader.VerifySignature()
if err != nil {
t.Fatal("Expected no error while verifying the signature, got:", err)
}
decryptedMeta := decryptedReader.GetMetadata()
if !reflect.DeepEqual(testMeta, decryptedMeta) {
t.Fatalf("Expected the decrypted metadata to be %v got %v", testMeta, decryptedMeta)
}
decryptedReaderNoVerify, err := keyRingTestPrivate.DecryptStream(
bytes.NewReader(ciphertextBytes),
nil,
0,
)
if err != nil {
t.Fatal("Expected no error while calling decrypting stream with key ring, got:", err)
}
decryptedBytes, err = io.ReadAll(decryptedReaderNoVerify)
if err != nil {
t.Fatal("Expected no error while reading the decrypted data, got:", err)
}
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
decryptedMeta = decryptedReaderNoVerify.GetMetadata()
if !reflect.DeepEqual(testMeta, decryptedMeta) {
t.Fatalf("Expected the decrypted metadata to be %v got %v", testMeta, decryptedMeta)
}
err = decryptedReaderNoVerify.VerifySignature()
if err == nil {
t.Fatal("Expected an error while verifying the signature with no keyring, got nil")
}
}
func TestKeyRing_EncryptStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
var ciphertextBuf bytes.Buffer
messageWriter, err := keyRingTestPublic.EncryptStream(
&ciphertextBuf,
testMeta,
keyRingTestPrivate,
)
if err != nil {
t.Fatal("Expected no error while encrypting stream with key ring, got:", err)
}
reachedEnd := false
bufferSize := 2
buffer := make([]byte, bufferSize)
for !reachedEnd {
n, err := messageReader.Read(buffer)
if err != nil {
if errors.Is(err, io.EOF) {
reachedEnd = true
} else {
t.Fatal("Expected no error while reading data, got:", err)
}
}
writtenTotal := 0
for writtenTotal < n {
written, err := messageWriter.Write(buffer[writtenTotal:n])
if err != nil {
t.Fatal("Expected no error while writing data, got:", err)
}
writtenTotal += written
}
}
err = messageWriter.Close()
if err != nil {
t.Fatal("Expected no error while closing plaintext writer, got:", err)
}
encryptedData := ciphertextBuf.Bytes()
decryptedMsg, err := keyRingTestPrivate.Decrypt(
NewPGPMessage(encryptedData),
keyRingTestPublic,
GetUnixTime(),
)
if err != nil {
t.Fatal("Expected no error while calling normal decrypt with key ring, got:", err)
}
decryptedBytes := decryptedMsg.GetBinary()
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the normally decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
if testMeta.IsBinary != decryptedMsg.IsBinary() {
t.Fatalf("Expected isBinary to be %t got %t", testMeta.IsBinary, decryptedMsg.IsBinary())
}
if testMeta.Filename != decryptedMsg.GetFilename() {
t.Fatalf("Expected filename to be %s got %s", testMeta.Filename, decryptedMsg.GetFilename())
}
if testMeta.ModTime != int64(decryptedMsg.GetTime()) {
t.Fatalf("Expected modification time to be %d got %d", testMeta.ModTime, int64(decryptedMsg.GetTime()))
}
}
func TestKeyRing_DecryptStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
pgpMessage, err := keyRingTestPublic.Encrypt(
&PlainMessage{
Data: messageBytes,
TextType: !testMeta.IsBinary,
Time: uint32(testMeta.ModTime),
Filename: testMeta.Filename,
},
keyRingTestPrivate,
)
if err != nil {
t.Fatal("Expected no error while encrypting plaintext, got:", err)
}
decryptedReader, err := keyRingTestPrivate.DecryptStream(
bytes.NewReader(pgpMessage.GetBinary()),
keyRingTestPublic,
GetUnixTime(),
)
if err != nil {
t.Fatal("Expected no error while calling decrypting stream with key ring, got:", err)
}
decryptedBytes, err := io.ReadAll(decryptedReader)
if err != nil {
t.Fatal("Expected no error while reading the decrypted data, got:", err)
}
err = decryptedReader.VerifySignature()
if err != nil {
t.Fatal("Expected no error while verifying the signature, got:", err)
}
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
decryptedMeta := decryptedReader.GetMetadata()
if !reflect.DeepEqual(testMeta, decryptedMeta) {
t.Fatalf("Expected the decrypted metadata to be %v got %v", testMeta, decryptedMeta)
}
}
func TestKeyRing_EncryptDecryptSplitStream(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
var dataPacketBuf bytes.Buffer
encryptionResult, err := keyRingTestPublic.EncryptSplitStream(
&dataPacketBuf,
testMeta,
keyRingTestPrivate,
)
if err != nil {
t.Fatal("Expected no error while calling encrypting split stream with key ring, got:", err)
}
messageWriter := encryptionResult
reachedEnd := false
bufferSize := 2
buffer := make([]byte, bufferSize)
for !reachedEnd {
n, err := messageReader.Read(buffer)
if err != nil {
if errors.Is(err, io.EOF) {
reachedEnd = true
} else {
t.Fatal("Expected no error while reading data, got:", err)
}
}
writtenTotal := 0
for writtenTotal < n {
written, err := messageWriter.Write(buffer[writtenTotal:n])
if err != nil {
t.Fatal("Expected no error while writing data, got:", err)
}
writtenTotal += written
}
}
err = messageWriter.Close()
if err != nil {
t.Fatal("Expected no error while closing plaintext writer, got:", err)
}
keyPacket, err := encryptionResult.GetKeyPacket()
if err != nil {
t.Fatal("Expected no error while accessing key packet, got:", err)
}
dataPacket := dataPacketBuf.Bytes()
decryptedReader, err := keyRingTestPrivate.DecryptSplitStream(
keyPacket,
bytes.NewReader(dataPacket),
keyRingTestPublic,
GetUnixTime(),
)
if err != nil {
t.Fatal("Expected no error while decrypting split stream with key ring, got:", err)
}
decryptedBytes, err := io.ReadAll(decryptedReader)
if err != nil {
t.Fatal("Expected no error while reading the decrypted data, got:", err)
}
err = decryptedReader.VerifySignature()
if err != nil {
t.Fatal("Expected no error while verifying the signature, got:", err)
}
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
decryptedMeta := decryptedReader.GetMetadata()
if !reflect.DeepEqual(testMeta, decryptedMeta) {
t.Fatalf("Expected the decrypted metadata to be %v got %v", testMeta, decryptedMeta)
}
}
func TestKeyRing_EncryptSplitStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
var dataPacketBuf bytes.Buffer
encryptionResult, err := keyRingTestPublic.EncryptSplitStream(
&dataPacketBuf,
testMeta,
keyRingTestPrivate,
)
if err != nil {
t.Fatal("Expected no error while calling encrypting split stream with key ring, got:", err)
}
messageWriter := encryptionResult
reachedEnd := false
bufferSize := 2
buffer := make([]byte, bufferSize)
for !reachedEnd {
n, err := messageReader.Read(buffer)
if err != nil {
if errors.Is(err, io.EOF) {
reachedEnd = true
} else {
t.Fatal("Expected no error while reading data, got:", err)
}
}
writtenTotal := 0
for writtenTotal < n {
written, err := messageWriter.Write(buffer[writtenTotal:n])
if err != nil {
t.Fatal("Expected no error while writing data, got:", err)
}
writtenTotal += written
}
}
err = messageWriter.Close()
if err != nil {
t.Fatal("Expected no error while closing plaintext writer, got:", err)
}
keyPacket, err := encryptionResult.GetKeyPacket()
if err != nil {
t.Fatal("Expected no error while accessing key packet, got:", err)
}
dataPacket := dataPacketBuf.Bytes()
decryptedMsg, err := keyRingTestPrivate.Decrypt(
NewPGPSplitMessage(keyPacket, dataPacket).GetPGPMessage(),
keyRingTestPublic,
GetUnixTime(),
)
if err != nil {
t.Fatal("Expected no error while decrypting split stream with key ring, got:", err)
}
decryptedBytes := decryptedMsg.GetBinary()
if err != nil {
t.Fatal("Expected no error while reading the decrypted data, got:", err)
}
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
if testMeta.IsBinary != decryptedMsg.IsBinary() {
t.Fatalf("Expected isBinary to be %t got %t", testMeta.IsBinary, decryptedMsg.IsBinary())
}
if testMeta.Filename != decryptedMsg.GetFilename() {
t.Fatalf("Expected filename to be %s got %s", testMeta.Filename, decryptedMsg.GetFilename())
}
if testMeta.ModTime != int64(decryptedMsg.GetTime()) {
t.Fatalf("Expected modification time to be %d got %d", testMeta.ModTime, int64(decryptedMsg.GetTime()))
}
}
func TestKeyRing_DecryptSplitStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
pgpMessage, err := keyRingTestPublic.Encrypt(
&PlainMessage{
Data: messageBytes,
TextType: !testMeta.IsBinary,
Time: uint32(testMeta.ModTime),
Filename: testMeta.Filename,
},
keyRingTestPrivate,
)
if err != nil {
t.Fatal("Expected no error while encrypting plaintext, got:", err)
}
armored, err := pgpMessage.GetArmored()
if err != nil {
t.Fatal("Expected no error while armoring ciphertext, got:", err)
}
splitMsg, err := NewPGPSplitMessageFromArmored(armored)
if err != nil {
t.Fatal("Expected no error while splitting the ciphertext, got:", err)
}
keyPacket := splitMsg.KeyPacket
if err != nil {
t.Fatal("Expected no error while accessing key packet, got:", err)
}
dataPacket := splitMsg.DataPacket
decryptedReader, err := keyRingTestPrivate.DecryptSplitStream(
keyPacket,
bytes.NewReader(dataPacket),
keyRingTestPublic,
GetUnixTime(),
)
if err != nil {
t.Fatal("Expected no error while decrypting split stream with key ring, got:", err)
}
decryptedBytes, err := io.ReadAll(decryptedReader)
if err != nil {
t.Fatal("Expected no error while reading the decrypted data, got:", err)
}
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
err = decryptedReader.VerifySignature()
if err != nil {
t.Fatal("Expected no error while verifying the signature, got:", err)
}
decryptedMeta := decryptedReader.GetMetadata()
if !reflect.DeepEqual(testMeta, decryptedMeta) {
t.Fatalf("Expected the decrypted metadata to be %v got %v", testMeta, decryptedMeta)
}
}
func TestKeyRing_SignVerifyDetachedStream(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
signature, err := keyRingTestPrivate.SignDetachedStream(messageReader)
if err != nil {
t.Fatal("Expected no error while signing the message, got:", err)
}
_, err = messageReader.Seek(0, 0)
if err != nil {
t.Fatal("Expected no error while rewinding the message reader, got:", err)
}
err = keyRingTestPublic.VerifyDetachedStream(messageReader, signature, GetUnixTime())
if err != nil {
t.Fatal("Expected no error while verifying the detached signature, got:", err)
}
}
func TestKeyRing_SignDetachedStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
signature, err := keyRingTestPrivate.SignDetachedStream(messageReader)
if err != nil {
t.Fatal("Expected no error while signing the message, got:", err)
}
err = keyRingTestPublic.VerifyDetached(NewPlainMessage(messageBytes), signature, GetUnixTime())
if err != nil {
t.Fatal("Expected no error while verifying the detached signature, got:", err)
}
}
func TestKeyRing_VerifyDetachedStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
signature, err := keyRingTestPrivate.SignDetached(NewPlainMessage(messageBytes))
if err != nil {
t.Fatal("Expected no error while signing the message, got:", err)
}
_, err = messageReader.Seek(0, 0)
if err != nil {
t.Fatal("Expected no error while rewinding the message reader, got:", err)
}
err = keyRingTestPublic.VerifyDetachedStream(messageReader, signature, GetUnixTime())
if err != nil {
t.Fatal("Expected no error while verifying the detached signature, got:", err)
}
}
func TestKeyRing_SignVerifyDetachedEncryptedStream(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
encSignature, err := keyRingTestPrivate.SignDetachedEncryptedStream(messageReader, keyRingTestPublic)
if err != nil {
t.Fatal("Expected no error while signing the message, got:", err)
}
_, err = messageReader.Seek(0, 0)
if err != nil {
t.Fatal("Expected no error while rewinding the message reader, got:", err)
}
err = keyRingTestPublic.VerifyDetachedEncryptedStream(messageReader, encSignature, keyRingTestPrivate, GetUnixTime())
if err != nil {
t.Fatal("Expected no error while verifying the detached signature, got:", err)
}
}
func TestKeyRing_SignDetachedEncryptedStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
encSignature, err := keyRingTestPrivate.SignDetachedEncryptedStream(messageReader, keyRingTestPublic)
if err != nil {
t.Fatal("Expected no error while signing the message, got:", err)
}
err = keyRingTestPublic.VerifyDetachedEncrypted(NewPlainMessage(messageBytes), encSignature, keyRingTestPrivate, GetUnixTime())
if err != nil {
t.Fatal("Expected no error while verifying the detached signature, got:", err)
}
}
func TestKeyRing_VerifyDetachedEncryptedStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
encSignature, err := keyRingTestPrivate.SignDetachedEncrypted(NewPlainMessage(messageBytes), keyRingTestPublic)
if err != nil {
t.Fatal("Expected no error while signing the message, got:", err)
}
_, err = messageReader.Seek(0, 0)
if err != nil {
t.Fatal("Expected no error while rewinding the message reader, got:", err)
}
err = keyRingTestPublic.VerifyDetachedEncryptedStream(messageReader, encSignature, keyRingTestPrivate, GetUnixTime())
if err != nil {
t.Fatal("Expected no error while verifying the detached signature, got:", err)
}
}

View file

@ -5,6 +5,7 @@ import (
"encoding/base64"
"fmt"
"io"
"time"
"github.com/ProtonMail/gopenpgp/v2/constants"
"github.com/pkg/errors"
@ -171,67 +172,87 @@ func (sk *SessionKey) EncryptWithCompression(message *PlainMessage) ([]byte, err
func encryptWithSessionKey(message *PlainMessage, sk *SessionKey, signEntity *openpgp.Entity, config *packet.Config) ([]byte, error) {
var encBuf = new(bytes.Buffer)
var encryptWriter, signWriter io.WriteCloser
encryptWriter, err := packet.SerializeSymmetricallyEncrypted(encBuf, config.Cipher(), sk.Key, config)
encryptWriter, signWriter, err := encryptStreamWithSessionKey(
message.IsBinary(),
message.Filename,
message.Time,
encBuf,
sk,
signEntity,
config,
)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt")
return nil, err
}
if algo := config.Compression(); algo != packet.CompressionNone {
encryptWriter, err = packet.SerializeCompressed(encryptWriter, algo, config.CompressionConfig)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in compression")
}
}
if signEntity != nil { // nolint:nestif
hints := &openpgp.FileHints{
IsBinary: message.IsBinary(),
FileName: message.Filename,
ModTime: message.getFormattedTime(),
}
signWriter, err = openpgp.Sign(encryptWriter, signEntity, hints, config)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to sign")
}
if signEntity != nil {
_, err = signWriter.Write(message.GetBinary())
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in writing signed message")
}
err = signWriter.Close()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in closing signing writer")
}
} else {
encryptWriter, err = packet.SerializeLiteral(
encryptWriter,
message.IsBinary(),
message.Filename,
message.Time,
)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to serialize")
}
_, err = encryptWriter.Write(message.GetBinary())
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in writing message")
}
}
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in writing message")
}
err = encryptWriter.Close()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in closing encryption writer")
}
return encBuf.Bytes(), nil
}
func encryptStreamWithSessionKey(
isBinary bool,
filename string,
modTime uint32,
dataPacketWriter io.Writer,
sk *SessionKey,
signEntity *openpgp.Entity,
config *packet.Config,
) (encryptWriter, signWriter io.WriteCloser, err error) {
encryptWriter, err = packet.SerializeSymmetricallyEncrypted(dataPacketWriter, config.Cipher(), sk.Key, config)
if err != nil {
return nil, nil, errors.Wrap(err, "gopenpgp: unable to encrypt")
}
if algo := config.Compression(); algo != packet.CompressionNone {
encryptWriter, err = packet.SerializeCompressed(encryptWriter, algo, config.CompressionConfig)
if err != nil {
return nil, nil, errors.Wrap(err, "gopenpgp: error in compression")
}
}
if signEntity != nil {
hints := &openpgp.FileHints{
IsBinary: isBinary,
FileName: filename,
ModTime: time.Unix(int64(modTime), 0),
}
signWriter, err = openpgp.Sign(encryptWriter, signEntity, hints, config)
if err != nil {
return nil, nil, errors.Wrap(err, "gopenpgp: unable to sign")
}
} else {
encryptWriter, err = packet.SerializeLiteral(
encryptWriter,
isBinary,
filename,
modTime,
)
if err != nil {
return nil, nil, errors.Wrap(err, "gopenpgp: unable to serialize")
}
}
return encryptWriter, signWriter, nil
}
// Decrypt decrypts pgp data packets using directly a session key.
// * encrypted: PGPMessage.
// * output: PlainMessage.
@ -246,8 +267,32 @@ func (sk *SessionKey) Decrypt(dataPacket []byte) (*PlainMessage, error) {
// * output: PlainMessage.
func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing, verifyTime int64) (*PlainMessage, error) {
var messageReader = bytes.NewReader(dataPacket)
md, err := decryptStreamWithSessionKey(sk, messageReader, verifyKeyRing)
if err != nil {
return nil, err
}
messageBuf := new(bytes.Buffer)
_, err = messageBuf.ReadFrom(md.UnverifiedBody)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in reading message body")
}
if verifyKeyRing != nil {
processSignatureExpiration(md, verifyTime)
err = verifyDetailsSignature(md, verifyKeyRing)
}
return &PlainMessage{
Data: messageBuf.Bytes(),
TextType: !md.LiteralData.IsBinary,
Filename: md.LiteralData.FileName,
Time: md.LiteralData.Time,
}, err
}
func decryptStreamWithSessionKey(sk *SessionKey, messageReader io.Reader, verifyKeyRing *KeyRing) (*openpgp.MessageDetails, error) {
var decrypted io.ReadCloser
var decBuf bytes.Buffer
var keyring openpgp.EntityList
// Read symmetrically encrypted data packet
@ -273,45 +318,24 @@ func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing
default:
return nil, errors.New("gopenpgp: invalid packet type")
}
_, err = decBuf.ReadFrom(decrypted)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to read from decrypted symmetric packet")
}
config := &packet.Config{
Time: getTimeGenerator(),
}
// Push decrypted packet as literal packet and use openpgp's reader
if verifyKeyRing != nil {
keyring = verifyKeyRing.entities
} else {
keyring = openpgp.EntityList{}
}
md, err := openpgp.ReadMessage(&decBuf, keyring, nil, config)
md, err := openpgp.ReadMessage(decrypted, keyring, nil, config)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to decode symmetric packet")
}
messageBuf := new(bytes.Buffer)
_, err = messageBuf.ReadFrom(md.UnverifiedBody)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in reading message body")
}
if verifyKeyRing != nil {
processSignatureExpiration(md, verifyTime)
err = verifyDetailsSignature(md, verifyKeyRing)
}
return &PlainMessage{
Data: messageBuf.Bytes(),
TextType: !md.LiteralData.IsBinary,
Filename: md.LiteralData.FileName,
Time: md.LiteralData.Time,
}, err
return md, nil
}
func (sk *SessionKey) checkSize() error {

View file

@ -0,0 +1,105 @@
package crypto
import (
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/pkg/errors"
)
type signAndEncryptWriteCloser struct {
signWriter WriteCloser
encryptWriter WriteCloser
}
func (w *signAndEncryptWriteCloser) Write(b []byte) (int, error) {
return w.signWriter.Write(b)
}
func (w *signAndEncryptWriteCloser) Close() error {
if err := w.signWriter.Close(); err != nil {
return err
}
return w.encryptWriter.Close()
}
// EncryptStream is used to encrypt data as a Writer.
// It takes a writer for the encrypted data packet and returns a writer for the plaintext data.
// If signKeyRing is not nil, it is used to do an embedded signature.
func (sk *SessionKey) EncryptStream(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (plainMessageWriter WriteCloser, err error) {
dc, err := sk.GetCipherFunc()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt with session key")
}
config := &packet.Config{
Time: getTimeGenerator(),
DefaultCipher: dc,
}
var signEntity *openpgp.Entity
if signKeyRing != nil {
signEntity, err = signKeyRing.getSigningEntity()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to sign")
}
}
if plainMessageMetadata == nil {
// Use sensible default metadata
plainMessageMetadata = &PlainMessageMetadata{
IsBinary: true,
Filename: "",
ModTime: GetUnixTime(),
}
}
encryptWriter, signWriter, err := encryptStreamWithSessionKey(
plainMessageMetadata.IsBinary,
plainMessageMetadata.Filename,
uint32(plainMessageMetadata.ModTime),
dataPacketWriter,
sk,
signEntity,
config,
)
if err != nil {
return nil, err
}
if signWriter != nil {
plainMessageWriter = &signAndEncryptWriteCloser{signWriter, encryptWriter}
} else {
plainMessageWriter = encryptWriter
}
return plainMessageWriter, err
}
// DecryptStream is used to decrypt a data packet as a Reader.
// It takes a reader for the data packet
// and returns a PlainMessageReader for the plaintext data.
// If verifyKeyRing is not nil, PlainMessageReader.VerifySignature() will
// verify the embedded signature with the given key ring and verification time.
func (sk *SessionKey) DecryptStream(
dataPacketReader Reader,
verifyKeyRing *KeyRing,
verifyTime int64,
) (plainMessage *PlainMessageReader, err error) {
messageDetails, err := decryptStreamWithSessionKey(
sk,
dataPacketReader,
verifyKeyRing,
)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in reading message")
}
return &PlainMessageReader{
messageDetails,
verifyKeyRing,
verifyTime,
false,
}, err
}

View file

@ -0,0 +1,176 @@
package crypto
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/pkg/errors"
)
func TestSessionKey_EncryptDecryptStream(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
var dataPacketBuf bytes.Buffer
messageWriter, err := testSessionKey.EncryptStream(
&dataPacketBuf,
testMeta,
keyRingTestPrivate,
)
if err != nil {
t.Fatal("Expected no error while encrypting stream with session key, got:", err)
}
bufferSize := 2
buffer := make([]byte, bufferSize)
reachedEnd := false
for !reachedEnd {
n, err := messageReader.Read(buffer)
if err != nil {
if errors.Is(err, io.EOF) {
reachedEnd = true
} else {
t.Fatal("Expected no error while reading data, got:", err)
}
}
writtenTotal := 0
for writtenTotal < n {
written, err := messageWriter.Write(buffer[writtenTotal:n])
if err != nil {
t.Fatal("Expected no error while writing data, got:", err)
}
writtenTotal += written
}
}
err = messageWriter.Close()
if err != nil {
t.Fatal("Expected no error while closing plaintext writer, got:", err)
}
dataPacket := dataPacketBuf.Bytes()
decryptedReader, err := testSessionKey.DecryptStream(
bytes.NewReader(dataPacket),
keyRingTestPublic,
GetUnixTime(),
)
if err != nil {
t.Fatal("Expected no error while calling DecryptStream, got:", err)
}
decryptedBytes, err := io.ReadAll(decryptedReader)
if err != nil {
t.Fatal("Expected no error while reading the decrypted data, got:", err)
}
err = decryptedReader.VerifySignature()
if err != nil {
t.Fatal("Expected no error while verifying the signature, got:", err)
}
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
decryptedMeta := decryptedReader.GetMetadata()
if !reflect.DeepEqual(testMeta, decryptedMeta) {
t.Fatalf("Expected the decrypted metadata to be %v got %v", testMeta, decryptedMeta)
}
}
func TestSessionKey_EncryptStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
var dataPacketBuf bytes.Buffer
messageWriter, err := testSessionKey.EncryptStream(
&dataPacketBuf,
testMeta,
keyRingTestPrivate,
)
if err != nil {
t.Fatal("Expected no error while encrypting stream with session key, got:", err)
}
bufferSize := 2
buffer := make([]byte, bufferSize)
reachedEnd := false
for !reachedEnd {
n, err := messageReader.Read(buffer)
if err != nil {
if errors.Is(err, io.EOF) {
reachedEnd = true
} else {
t.Fatal("Expected no error while reading data, got:", err)
}
}
writtenTotal := 0
for writtenTotal < n {
written, err := messageWriter.Write(buffer[writtenTotal:n])
if err != nil {
t.Fatal("Expected no error while writing data, got:", err)
}
writtenTotal += written
}
}
err = messageWriter.Close()
if err != nil {
t.Fatal("Expected no error while closing plaintext writer, got:", err)
}
dataPacket := dataPacketBuf.Bytes()
decryptedMsg, err := testSessionKey.DecryptAndVerify(
dataPacket,
keyRingTestPublic,
GetUnixTime(),
)
if err != nil {
t.Fatal("Expected no error while calling DecryptAndVerify, got:", err)
}
decryptedBytes := decryptedMsg.Data
if err != nil {
t.Fatal("Expected no error while reading the decrypted data, got:", err)
}
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
if testMeta.IsBinary != decryptedMsg.IsBinary() {
t.Fatalf("Expected isBinary to be %t got %t", testMeta.IsBinary, decryptedMsg.IsBinary())
}
if testMeta.Filename != decryptedMsg.GetFilename() {
t.Fatalf("Expected filename to be %s got %s", testMeta.Filename, decryptedMsg.GetFilename())
}
if testMeta.ModTime != int64(decryptedMsg.GetTime()) {
t.Fatalf("Expected modification time to be %d got %d", testMeta.ModTime, int64(decryptedMsg.GetTime()))
}
}
func TestSessionKey_DecryptStreamCompatible(t *testing.T) {
messageBytes := []byte("Hello World!")
dataPacket, err := testSessionKey.EncryptAndSign(
&PlainMessage{
Data: messageBytes,
TextType: !testMeta.IsBinary,
Time: uint32(testMeta.ModTime),
Filename: testMeta.Filename,
},
keyRingTestPrivate,
)
if err != nil {
t.Fatal("Expected no error while encrypting plaintext, got:", err)
}
decryptedReader, err := testSessionKey.DecryptStream(
bytes.NewReader(dataPacket),
keyRingTestPublic,
GetUnixTime(),
)
if err != nil {
t.Fatal("Expected no error while calling DecryptStream, got:", err)
}
decryptedBytes, err := io.ReadAll(decryptedReader)
if err != nil {
t.Fatal("Expected no error while reading the decrypted data, got:", err)
}
err = decryptedReader.VerifySignature()
if err != nil {
t.Fatal("Expected no error while verifying the signature, got:", err)
}
if !bytes.Equal(decryptedBytes, messageBytes) {
t.Fatalf("Expected the decrypted data to be %s got %s", string(decryptedBytes), string(messageBytes))
}
decryptedMeta := decryptedReader.GetMetadata()
if !reflect.DeepEqual(testMeta, decryptedMeta) {
t.Fatalf("Expected the decrypted metadata to be %v got %v", testMeta, decryptedMeta)
}
}