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
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.46.2
|
||||
version: v1.50.1
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
@ -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{
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
plainMessageWriter, err := keyRing.encryptStreamWithConfig(
|
||||
config,
|
||||
&keyPacketBuf,
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
)
|
||||
if err != nil {
|
||||
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{
|
||||
keyPacketBuf: &keyPacketBuf,
|
||||
plainMessageWriter: plainMessageWriter,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
@ -29,16 +30,51 @@ func (sk *SessionKey) EncryptStream(
|
|||
dataPacketWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
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) {
|
||||
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,
|
||||
}
|
||||
config.DefaultCipher = dc
|
||||
var signEntity *openpgp.Entity
|
||||
if signKeyRing != nil {
|
||||
signEntity, err = signKeyRing.getSigningEntity()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue