Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.46.2
version: v1.50.1
3 changes: 2 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ linters:
- ireturn # Prevents returning interfaces
- forcetypeassert # Forces to assert types in tests
- nonamedreturns # Disallows named returns
- exhaustruct # Forces all structs to be named
- exhaustruct # Forces all structs to be named
- nosnakecase # Disallows snake case
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added
- Streaming API to encrypt with compression:
- `func (keyRing *KeyRing) EncryptStreamWithCompression`
- `func (keyRing *KeyRing) EncryptSplitStreamWithCompression`
- `func (sk *SessionKey) EncryptStreamWithCompression`

## [2.5.0] 2022-12-16
### Changed
- Update `github.com/ProtonMail/go-crypto` to the latest version
Expand Down
97 changes: 84 additions & 13 deletions crypto/keyring_streaming.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/ProtonMail/gopenpgp/v2/constants"
"github.com/pkg/errors"
)

Expand Down Expand Up @@ -44,6 +45,47 @@ func (keyRing *KeyRing) EncryptStream(
) (plainMessageWriter WriteCloser, err error) {
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}

return keyRing.encryptStreamWithConfig(
config,
pgpMessageWriter,
pgpMessageWriter,
plainMessageMetadata,
signKeyRing,
)
}

// EncryptStreamWithCompression is used to encrypt data as a Writer.
// The plaintext data is compressed before being encrypted.
// 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) EncryptStreamWithCompression(
pgpMessageWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (plainMessageWriter WriteCloser, err error) {
config := &packet.Config{
DefaultCipher: packet.CipherAES256,
Time: getTimeGenerator(),
DefaultCompressionAlgo: constants.DefaultCompression,
CompressionConfig: &packet.CompressionConfig{Level: constants.DefaultCompressionLevel},
}

return keyRing.encryptStreamWithConfig(
config,
pgpMessageWriter,
pgpMessageWriter,
plainMessageMetadata,
signKeyRing,
)
}

func (keyRing *KeyRing) encryptStreamWithConfig(
config *packet.Config,
keyPacketWriter Writer,
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (plainMessageWriter WriteCloser, err error) {
if plainMessageMetadata == nil {
// Use sensible default metadata
plainMessageMetadata = &PlainMessageMetadata{
Expand All @@ -59,7 +101,7 @@ func (keyRing *KeyRing) EncryptStream(
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
}

plainMessageWriter, err = asymmetricEncryptStream(hints, pgpMessageWriter, pgpMessageWriter, keyRing, signKeyRing, config)
plainMessageWriter, err = asymmetricEncryptStream(hints, keyPacketWriter, dataPacketWriter, keyRing, signKeyRing, config)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -109,26 +151,55 @@ func (keyRing *KeyRing) EncryptSplitStream(
) (*EncryptSplitResult, error) {
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}

if plainMessageMetadata == nil {
// Use sensible default metadata
plainMessageMetadata = &PlainMessageMetadata{
IsBinary: true,
Filename: "",
ModTime: GetUnixTime(),
}
var keyPacketBuf bytes.Buffer

plainMessageWriter, err := keyRing.encryptStreamWithConfig(
config,
&keyPacketBuf,
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
)
if err != nil {
return nil, err
}

hints := &openpgp.FileHints{
FileName: plainMessageMetadata.Filename,
IsBinary: plainMessageMetadata.IsBinary,
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
return &EncryptSplitResult{
keyPacketBuf: &keyPacketBuf,
plainMessageWriter: plainMessageWriter,
}, nil
}

// EncryptSplitStreamWithCompression 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) EncryptSplitStreamWithCompression(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (*EncryptSplitResult, error) {
config := &packet.Config{
DefaultCipher: packet.CipherAES256,
Time: getTimeGenerator(),
DefaultCompressionAlgo: constants.DefaultCompression,
CompressionConfig: &packet.CompressionConfig{Level: constants.DefaultCompressionLevel},
}

var keyPacketBuf bytes.Buffer
plainMessageWriter, err := asymmetricEncryptStream(hints, &keyPacketBuf, dataPacketWriter, keyRing, signKeyRing, config)

plainMessageWriter, err := keyRing.encryptStreamWithConfig(
config,
&keyPacketBuf,
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
)
if err != nil {
return nil, err
}

return &EncryptSplitResult{
keyPacketBuf: &keyPacketBuf,
plainMessageWriter: plainMessageWriter,
Expand Down
52 changes: 50 additions & 2 deletions crypto/keyring_streaming_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,34 @@ func TestKeyRing_EncryptDecryptStream(t *testing.T) {
}

func TestKeyRing_EncryptStreamCompatible(t *testing.T) {
enc := func(w io.Writer, meta *PlainMessageMetadata, kr *KeyRing) (io.WriteCloser, error) {
return keyRingTestPublic.EncryptStream(
w,
meta,
kr,
)
}
testKeyRing_EncryptStreamCompatible(enc, t)
}

func TestKeyRing_EncryptStreamWithCompressionCompatible(t *testing.T) {
enc := func(w io.Writer, meta *PlainMessageMetadata, kr *KeyRing) (io.WriteCloser, error) {
return keyRingTestPublic.EncryptStreamWithCompression(
w,
meta,
kr,
)
}
testKeyRing_EncryptStreamCompatible(enc, t)
}

type keyringEncryptionFunction = func(io.Writer, *PlainMessageMetadata, *KeyRing) (io.WriteCloser, error)

func testKeyRing_EncryptStreamCompatible(encrypt keyringEncryptionFunction, t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
var ciphertextBuf bytes.Buffer
messageWriter, err := keyRingTestPublic.EncryptStream(
messageWriter, err := encrypt(
&ciphertextBuf,
testMeta,
keyRingTestPrivate,
Expand Down Expand Up @@ -276,10 +300,34 @@ func TestKeyRing_EncryptDecryptSplitStream(t *testing.T) {
}

func TestKeyRing_EncryptSplitStreamCompatible(t *testing.T) {
enc := func(w io.Writer, meta *PlainMessageMetadata, kr *KeyRing) (*EncryptSplitResult, error) {
return keyRingTestPublic.EncryptSplitStream(
w,
meta,
kr,
)
}
testKeyRing_EncryptSplitStreamCompatible(enc, t)
}

func TestKeyRing_EncryptSplitStreamWithCompressionCompatible(t *testing.T) {
enc := func(w io.Writer, meta *PlainMessageMetadata, kr *KeyRing) (*EncryptSplitResult, error) {
return keyRingTestPublic.EncryptSplitStreamWithCompression(
w,
meta,
kr,
)
}
testKeyRing_EncryptSplitStreamCompatible(enc, t)
}

type keyringEncryptionSplitFunction = func(io.Writer, *PlainMessageMetadata, *KeyRing) (*EncryptSplitResult, error)

func testKeyRing_EncryptSplitStreamCompatible(encrypt keyringEncryptionSplitFunction, t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
var dataPacketBuf bytes.Buffer
encryptionResult, err := keyRingTestPublic.EncryptSplitStream(
encryptionResult, err := encrypt(
&dataPacketBuf,
testMeta,
keyRingTestPrivate,
Expand Down
46 changes: 41 additions & 5 deletions crypto/sessionkey_streaming.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package crypto
import (
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/ProtonMail/gopenpgp/v2/constants"
"github.com/pkg/errors"
)

Expand Down Expand Up @@ -30,15 +31,50 @@ func (sk *SessionKey) EncryptStream(
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(),
}
return sk.encryptStreamWithConfig(
config,
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
)
}

// EncryptStreamWithCompression is used to encrypt data as a Writer.
// The plaintext data is compressed before being encrypted.
// 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) EncryptStreamWithCompression(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (plainMessageWriter WriteCloser, err error) {
config := &packet.Config{
Time: getTimeGenerator(),
DefaultCipher: dc,
Time: getTimeGenerator(),
DefaultCompressionAlgo: constants.DefaultCompression,
CompressionConfig: &packet.CompressionConfig{Level: constants.DefaultCompressionLevel},
}
return sk.encryptStreamWithConfig(
config,
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
)
}

func (sk *SessionKey) encryptStreamWithConfig(
config *packet.Config,
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.DefaultCipher = dc
var signEntity *openpgp.Entity
if signKeyRing != nil {
signEntity, err = signKeyRing.getSigningEntity()
Expand Down
18 changes: 17 additions & 1 deletion crypto/sessionkey_streaming_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,26 @@ func TestSessionKey_EncryptDecryptStream(t *testing.T) {
}

func TestSessionKey_EncryptStreamCompatible(t *testing.T) {
enc := func(w io.Writer, meta *PlainMessageMetadata, kr *KeyRing) (io.WriteCloser, error) {
return testSessionKey.EncryptStream(w, meta, kr)
}
testSessionKey_EncryptStreamCompatible(enc, t)
}

func TestSessionKey_EncryptStreamWithCompressionCompatible(t *testing.T) {
enc := func(w io.Writer, meta *PlainMessageMetadata, kr *KeyRing) (io.WriteCloser, error) {
return testSessionKey.EncryptStreamWithCompression(w, meta, kr)
}
testSessionKey_EncryptStreamCompatible(enc, t)
}

type sessionKeyEncryptionFunction = func(io.Writer, *PlainMessageMetadata, *KeyRing) (io.WriteCloser, error)

func testSessionKey_EncryptStreamCompatible(enc sessionKeyEncryptionFunction, t *testing.T) {
messageBytes := []byte("Hello World!")
messageReader := bytes.NewReader(messageBytes)
var dataPacketBuf bytes.Buffer
messageWriter, err := testSessionKey.EncryptStream(
messageWriter, err := enc(
&dataPacketBuf,
testMeta,
keyRingTestPrivate,
Expand Down
4 changes: 2 additions & 2 deletions crypto/signature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ func Test_KeyRing_GetVerifiedSignatureTimestampError(t *testing.T) {
if err != nil {
t.Errorf("Got an error while generating the signature: %v", err)
}
message_corrupted := NewPlainMessageFromString("Ciao world!")
_, err = keyRingTestPublic.GetVerifiedSignatureTimestamp(message_corrupted, signature, 0)
messageCorrupted := NewPlainMessageFromString("Ciao world!")
_, err = keyRingTestPublic.GetVerifiedSignatureTimestamp(messageCorrupted, signature, 0)
if err == nil {
t.Errorf("Expected an error while parsing the creation time of a wrong signature, got nil")
}
Expand Down