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

@ -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)
}
}