Add streaming APIs to encrypt with compression
This commit is contained in:
parent
ffcaa7f87b
commit
eccc1df619
8 changed files with 209 additions and 29 deletions
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
|
|
@ -48,4 +48,4 @@ jobs:
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v3
|
||||||
with:
|
with:
|
||||||
version: v1.46.2
|
version: v1.50.1
|
||||||
|
|
@ -46,4 +46,5 @@ linters:
|
||||||
- ireturn # Prevents returning interfaces
|
- ireturn # Prevents returning interfaces
|
||||||
- forcetypeassert # Forces to assert types in tests
|
- forcetypeassert # Forces to assert types in tests
|
||||||
- nonamedreturns # Disallows named returns
|
- nonamedreturns # Disallows named returns
|
||||||
- exhaustruct # Forces all structs to be named
|
- exhaustruct # Forces all structs to be named
|
||||||
|
- nosnakecase # Disallows snake case
|
||||||
|
|
@ -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/),
|
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).
|
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
|
## [2.5.0] 2022-12-16
|
||||||
### Changed
|
### Changed
|
||||||
- Update `github.com/ProtonMail/go-crypto` to the latest version
|
- Update `github.com/ProtonMail/go-crypto` to the latest version
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/ProtonMail/go-crypto/openpgp"
|
"github.com/ProtonMail/go-crypto/openpgp"
|
||||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||||
|
"github.com/ProtonMail/gopenpgp/v2/constants"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -44,6 +45,47 @@ func (keyRing *KeyRing) EncryptStream(
|
||||||
) (plainMessageWriter WriteCloser, err error) {
|
) (plainMessageWriter WriteCloser, err error) {
|
||||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
|
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 {
|
if plainMessageMetadata == nil {
|
||||||
// Use sensible default metadata
|
// Use sensible default metadata
|
||||||
plainMessageMetadata = &PlainMessageMetadata{
|
plainMessageMetadata = &PlainMessageMetadata{
|
||||||
|
|
@ -59,7 +101,7 @@ func (keyRing *KeyRing) EncryptStream(
|
||||||
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -109,26 +151,55 @@ func (keyRing *KeyRing) EncryptSplitStream(
|
||||||
) (*EncryptSplitResult, error) {
|
) (*EncryptSplitResult, error) {
|
||||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
|
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
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 := keyRing.encryptStreamWithConfig(
|
||||||
|
config,
|
||||||
|
&keyPacketBuf,
|
||||||
|
dataPacketWriter,
|
||||||
|
plainMessageMetadata,
|
||||||
|
signKeyRing,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &EncryptSplitResult{
|
return &EncryptSplitResult{
|
||||||
keyPacketBuf: &keyPacketBuf,
|
keyPacketBuf: &keyPacketBuf,
|
||||||
plainMessageWriter: plainMessageWriter,
|
plainMessageWriter: plainMessageWriter,
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,34 @@ func TestKeyRing_EncryptDecryptStream(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyRing_EncryptStreamCompatible(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!")
|
messageBytes := []byte("Hello World!")
|
||||||
messageReader := bytes.NewReader(messageBytes)
|
messageReader := bytes.NewReader(messageBytes)
|
||||||
var ciphertextBuf bytes.Buffer
|
var ciphertextBuf bytes.Buffer
|
||||||
messageWriter, err := keyRingTestPublic.EncryptStream(
|
messageWriter, err := encrypt(
|
||||||
&ciphertextBuf,
|
&ciphertextBuf,
|
||||||
testMeta,
|
testMeta,
|
||||||
keyRingTestPrivate,
|
keyRingTestPrivate,
|
||||||
|
|
@ -276,10 +300,34 @@ func TestKeyRing_EncryptDecryptSplitStream(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyRing_EncryptSplitStreamCompatible(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!")
|
messageBytes := []byte("Hello World!")
|
||||||
messageReader := bytes.NewReader(messageBytes)
|
messageReader := bytes.NewReader(messageBytes)
|
||||||
var dataPacketBuf bytes.Buffer
|
var dataPacketBuf bytes.Buffer
|
||||||
encryptionResult, err := keyRingTestPublic.EncryptSplitStream(
|
encryptionResult, err := encrypt(
|
||||||
&dataPacketBuf,
|
&dataPacketBuf,
|
||||||
testMeta,
|
testMeta,
|
||||||
keyRingTestPrivate,
|
keyRingTestPrivate,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"github.com/ProtonMail/go-crypto/openpgp"
|
"github.com/ProtonMail/go-crypto/openpgp"
|
||||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||||
|
"github.com/ProtonMail/gopenpgp/v2/constants"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,16 +30,51 @@ func (sk *SessionKey) EncryptStream(
|
||||||
dataPacketWriter Writer,
|
dataPacketWriter Writer,
|
||||||
plainMessageMetadata *PlainMessageMetadata,
|
plainMessageMetadata *PlainMessageMetadata,
|
||||||
signKeyRing *KeyRing,
|
signKeyRing *KeyRing,
|
||||||
|
) (plainMessageWriter WriteCloser, err error) {
|
||||||
|
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(),
|
||||||
|
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) {
|
) (plainMessageWriter WriteCloser, err error) {
|
||||||
dc, err := sk.GetCipherFunc()
|
dc, err := sk.GetCipherFunc()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt with session key")
|
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt with session key")
|
||||||
}
|
}
|
||||||
|
config.DefaultCipher = dc
|
||||||
config := &packet.Config{
|
|
||||||
Time: getTimeGenerator(),
|
|
||||||
DefaultCipher: dc,
|
|
||||||
}
|
|
||||||
var signEntity *openpgp.Entity
|
var signEntity *openpgp.Entity
|
||||||
if signKeyRing != nil {
|
if signKeyRing != nil {
|
||||||
signEntity, err = signKeyRing.getSigningEntity()
|
signEntity, err = signKeyRing.getSigningEntity()
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,26 @@ func TestSessionKey_EncryptDecryptStream(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSessionKey_EncryptStreamCompatible(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!")
|
messageBytes := []byte("Hello World!")
|
||||||
messageReader := bytes.NewReader(messageBytes)
|
messageReader := bytes.NewReader(messageBytes)
|
||||||
var dataPacketBuf bytes.Buffer
|
var dataPacketBuf bytes.Buffer
|
||||||
messageWriter, err := testSessionKey.EncryptStream(
|
messageWriter, err := enc(
|
||||||
&dataPacketBuf,
|
&dataPacketBuf,
|
||||||
testMeta,
|
testMeta,
|
||||||
keyRingTestPrivate,
|
keyRingTestPrivate,
|
||||||
|
|
|
||||||
|
|
@ -228,8 +228,8 @@ func Test_KeyRing_GetVerifiedSignatureTimestampError(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Got an error while generating the signature: %v", err)
|
t.Errorf("Got an error while generating the signature: %v", err)
|
||||||
}
|
}
|
||||||
message_corrupted := NewPlainMessageFromString("Ciao world!")
|
messageCorrupted := NewPlainMessageFromString("Ciao world!")
|
||||||
_, err = keyRingTestPublic.GetVerifiedSignatureTimestamp(message_corrupted, signature, 0)
|
_, err = keyRingTestPublic.GetVerifiedSignatureTimestamp(messageCorrupted, signature, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected an error while parsing the creation time of a wrong signature, got nil")
|
t.Errorf("Expected an error while parsing the creation time of a wrong signature, got nil")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue