Add signature context for embedded signatures
This commit is contained in:
parent
49211b24ff
commit
97323a4c2b
11 changed files with 931 additions and 169 deletions
|
|
@ -13,6 +13,7 @@ status `constants.SIGNATURE_BAD_CONTEXT` instead of `constants.SIGNATURE_FAILED`
|
|||
|
||||
## Added
|
||||
- Add api for signature context on streams `SignDetachedStreamWithContext`.
|
||||
- Add API for signature context on embedded signatures.
|
||||
|
||||
## [2.6.1] 2023-03-22
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,16 @@ import (
|
|||
// * message : The plaintext input as a PlainMessage.
|
||||
// * privateKey : (optional) an unlocked private keyring to include signature in the message.
|
||||
func (keyRing *KeyRing) Encrypt(message *PlainMessage, privateKey *KeyRing) (*PGPMessage, error) {
|
||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
|
||||
encrypted, err := asymmetricEncrypt(message, keyRing, privateKey, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return asymmetricEncrypt(message, keyRing, privateKey, false, nil)
|
||||
}
|
||||
|
||||
return NewPGPMessage(encrypted), nil
|
||||
// EncryptWithContext encrypts a PlainMessage, outputs a PGPMessage.
|
||||
// If an unlocked private key is also provided it will also sign the message.
|
||||
// * message : The plaintext input as a PlainMessage.
|
||||
// * privateKey : (optional) an unlocked private keyring to include signature in the message.
|
||||
// * signingContext : (optional) the context for the signature.
|
||||
func (keyRing *KeyRing) EncryptWithContext(message *PlainMessage, privateKey *KeyRing, signingContext *SigningContext) (*PGPMessage, error) {
|
||||
return asymmetricEncrypt(message, keyRing, privateKey, false, signingContext)
|
||||
}
|
||||
|
||||
// EncryptWithCompression encrypts with compression support a PlainMessage to PGPMessage using public/private keys.
|
||||
|
|
@ -31,32 +34,47 @@ func (keyRing *KeyRing) Encrypt(message *PlainMessage, privateKey *KeyRing) (*PG
|
|||
// * privateKey : (optional) an unlocked private keyring to include signature in the message.
|
||||
// * output : The encrypted data as PGPMessage.
|
||||
func (keyRing *KeyRing) EncryptWithCompression(message *PlainMessage, privateKey *KeyRing) (*PGPMessage, error) {
|
||||
config := &packet.Config{
|
||||
DefaultCipher: packet.CipherAES256,
|
||||
Time: getTimeGenerator(),
|
||||
DefaultCompressionAlgo: constants.DefaultCompression,
|
||||
CompressionConfig: &packet.CompressionConfig{Level: constants.DefaultCompressionLevel},
|
||||
}
|
||||
return asymmetricEncrypt(message, keyRing, privateKey, true, nil)
|
||||
}
|
||||
|
||||
encrypted, err := asymmetricEncrypt(message, keyRing, privateKey, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPGPMessage(encrypted), nil
|
||||
// EncryptWithContextAndCompression encrypts with compression support a PlainMessage to PGPMessage using public/private keys.
|
||||
// * message : The plain data as a PlainMessage.
|
||||
// * privateKey : (optional) an unlocked private keyring to include signature in the message.
|
||||
// * signingContext : (optional) the context for the signature.
|
||||
// * output : The encrypted data as PGPMessage.
|
||||
func (keyRing *KeyRing) EncryptWithContextAndCompression(message *PlainMessage, privateKey *KeyRing, signingContext *SigningContext) (*PGPMessage, error) {
|
||||
return asymmetricEncrypt(message, keyRing, privateKey, true, signingContext)
|
||||
}
|
||||
|
||||
// Decrypt decrypts encrypted string using pgp keys, returning a PlainMessage
|
||||
// * message : The encrypted input as a PGPMessage
|
||||
// * verifyKey : Public key for signature verification (optional)
|
||||
// * verifyTime : Time at verification (necessary only if verifyKey is not nil)
|
||||
// * verificationContext : (optional) the context for the signature verification.
|
||||
//
|
||||
// When verifyKey is not provided, then verifyTime should be zero, and
|
||||
// signature verification will be ignored.
|
||||
func (keyRing *KeyRing) Decrypt(
|
||||
message *PGPMessage, verifyKey *KeyRing, verifyTime int64,
|
||||
) (*PlainMessage, error) {
|
||||
return asymmetricDecrypt(message.NewReader(), keyRing, verifyKey, verifyTime)
|
||||
return asymmetricDecrypt(message.NewReader(), keyRing, verifyKey, verifyTime, nil)
|
||||
}
|
||||
|
||||
// DecryptWithContext decrypts encrypted string using pgp keys, returning a PlainMessage
|
||||
// * message : The encrypted input as a PGPMessage
|
||||
// * verifyKey : Public key for signature verification (optional)
|
||||
// * verifyTime : Time at verification (necessary only if verifyKey is not nil)
|
||||
// * verificationContext : (optional) the context for the signature verification.
|
||||
//
|
||||
// When verifyKey is not provided, then verifyTime should be zero, and
|
||||
// signature verification will be ignored.
|
||||
func (keyRing *KeyRing) DecryptWithContext(
|
||||
message *PGPMessage,
|
||||
verifyKey *KeyRing,
|
||||
verifyTime int64,
|
||||
verificationContext *VerificationContext,
|
||||
) (*PlainMessage, error) {
|
||||
return asymmetricDecrypt(message.NewReader(), keyRing, verifyKey, verifyTime, verificationContext)
|
||||
}
|
||||
|
||||
// SignDetached generates and returns a PGPSignature for a given PlainMessage.
|
||||
|
|
@ -181,8 +199,9 @@ func (keyRing *KeyRing) GetVerifiedSignatureTimestampWithContext(
|
|||
func asymmetricEncrypt(
|
||||
plainMessage *PlainMessage,
|
||||
publicKey, privateKey *KeyRing,
|
||||
config *packet.Config,
|
||||
) ([]byte, error) {
|
||||
compress bool,
|
||||
signingContext *SigningContext,
|
||||
) (*PGPMessage, error) {
|
||||
var outBuf bytes.Buffer
|
||||
var encryptWriter io.WriteCloser
|
||||
var err error
|
||||
|
|
@ -193,7 +212,7 @@ func asymmetricEncrypt(
|
|||
ModTime: plainMessage.getFormattedTime(),
|
||||
}
|
||||
|
||||
encryptWriter, err = asymmetricEncryptStream(hints, &outBuf, &outBuf, publicKey, privateKey, config)
|
||||
encryptWriter, err = asymmetricEncryptStream(hints, &outBuf, &outBuf, publicKey, privateKey, compress, signingContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -208,7 +227,7 @@ func asymmetricEncrypt(
|
|||
return nil, errors.Wrap(err, "gopenpgp: error in closing message")
|
||||
}
|
||||
|
||||
return outBuf.Bytes(), nil
|
||||
return &PGPMessage{outBuf.Bytes()}, nil
|
||||
}
|
||||
|
||||
// Core for encryption+signature (all) functions.
|
||||
|
|
@ -217,10 +236,24 @@ func asymmetricEncryptStream(
|
|||
keyPacketWriter io.Writer,
|
||||
dataPacketWriter io.Writer,
|
||||
publicKey, privateKey *KeyRing,
|
||||
config *packet.Config,
|
||||
compress bool,
|
||||
signingContext *SigningContext,
|
||||
) (encryptWriter io.WriteCloser, err error) {
|
||||
var signEntity *openpgp.Entity
|
||||
config := &packet.Config{
|
||||
DefaultCipher: packet.CipherAES256,
|
||||
Time: getTimeGenerator(),
|
||||
}
|
||||
|
||||
if compress {
|
||||
config.DefaultCompressionAlgo = constants.DefaultCompression
|
||||
config.CompressionConfig = &packet.CompressionConfig{Level: constants.DefaultCompressionLevel}
|
||||
}
|
||||
|
||||
if signingContext != nil {
|
||||
config.SignatureNotations = append(config.SignatureNotations, signingContext.getNotation())
|
||||
}
|
||||
|
||||
var signEntity *openpgp.Entity
|
||||
if privateKey != nil && len(privateKey.entities) > 0 {
|
||||
var err error
|
||||
signEntity, err = privateKey.getSigningEntity()
|
||||
|
|
@ -242,13 +275,18 @@ func asymmetricEncryptStream(
|
|||
|
||||
// Core for decryption+verification (non streaming) functions.
|
||||
func asymmetricDecrypt(
|
||||
encryptedIO io.Reader, privateKey *KeyRing, verifyKey *KeyRing, verifyTime int64,
|
||||
encryptedIO io.Reader,
|
||||
privateKey *KeyRing,
|
||||
verifyKey *KeyRing,
|
||||
verifyTime int64,
|
||||
verificationContext *VerificationContext,
|
||||
) (message *PlainMessage, err error) {
|
||||
messageDetails, err := asymmetricDecryptStream(
|
||||
encryptedIO,
|
||||
privateKey,
|
||||
verifyKey,
|
||||
verifyTime,
|
||||
verificationContext,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -261,7 +299,7 @@ func asymmetricDecrypt(
|
|||
|
||||
if verifyKey != nil {
|
||||
processSignatureExpiration(messageDetails, verifyTime)
|
||||
err = verifyDetailsSignature(messageDetails, verifyKey)
|
||||
err = verifyDetailsSignature(messageDetails, verifyKey, verificationContext)
|
||||
}
|
||||
|
||||
return &PlainMessage{
|
||||
|
|
@ -278,6 +316,7 @@ func asymmetricDecryptStream(
|
|||
privateKey *KeyRing,
|
||||
verifyKey *KeyRing,
|
||||
verifyTime int64,
|
||||
verificationContext *VerificationContext,
|
||||
) (messageDetails *openpgp.MessageDetails, err error) {
|
||||
privKeyEntries := privateKey.entities
|
||||
var additionalEntries openpgp.EntityList
|
||||
|
|
@ -304,6 +343,10 @@ func asymmetricDecryptStream(
|
|||
},
|
||||
}
|
||||
|
||||
if verificationContext != nil {
|
||||
config.KnownNotations = map[string]bool{constants.SignatureContextName: true}
|
||||
}
|
||||
|
||||
messageDetails, err = openpgp.ReadMessage(encryptedIO, privKeyEntries, nil, config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in reading message")
|
||||
|
|
|
|||
|
|
@ -36,3 +36,45 @@ func TestAEADKeyRingDecryption(t *testing.T) {
|
|||
|
||||
assert.Exactly(t, "hello world\n", decrypted.GetString())
|
||||
}
|
||||
|
||||
func TestTextMessageEncryptionWithSignatureAndContext(t *testing.T) {
|
||||
var message = NewPlainMessageFromString("plain text")
|
||||
var testContext = "test-context"
|
||||
|
||||
ciphertext, err := keyRingTestPublic.EncryptWithContext(message, keyRingTestPrivate, NewSigningContext(testContext, true))
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error when encrypting, got:", err)
|
||||
}
|
||||
|
||||
decrypted, err := keyRingTestPrivate.DecryptWithContext(
|
||||
ciphertext,
|
||||
keyRingTestPublic,
|
||||
GetUnixTime(),
|
||||
NewVerificationContext(testContext, true, 0),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error when decrypting, got:", err)
|
||||
}
|
||||
assert.Exactly(t, message.GetString(), decrypted.GetString())
|
||||
}
|
||||
|
||||
func TestTextMessageEncryptionWithSignatureAndContextAndCompression(t *testing.T) {
|
||||
var message = NewPlainMessageFromString("plain text")
|
||||
var testContext = "test-context"
|
||||
|
||||
ciphertext, err := keyRingTestPublic.EncryptWithContextAndCompression(message, keyRingTestPrivate, NewSigningContext(testContext, true))
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error when encrypting, got:", err)
|
||||
}
|
||||
|
||||
decrypted, err := keyRingTestPrivate.DecryptWithContext(
|
||||
ciphertext,
|
||||
keyRingTestPublic,
|
||||
GetUnixTime(),
|
||||
NewVerificationContext(testContext, true, 0),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error when decrypting, got:", err)
|
||||
}
|
||||
assert.Exactly(t, message.GetString(), decrypted.GetString())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
"github.com/ProtonMail/gopenpgp/v2/constants"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
|
@ -42,14 +40,35 @@ func (keyRing *KeyRing) EncryptStream(
|
|||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
) (plainMessageWriter WriteCloser, err error) {
|
||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
|
||||
|
||||
return keyRing.encryptStreamWithConfig(
|
||||
config,
|
||||
return encryptStream(
|
||||
keyRing,
|
||||
pgpMessageWriter,
|
||||
pgpMessageWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// EncryptStreamWithContext 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.
|
||||
// * signingContext : (optional) a context for the embedded signature.
|
||||
func (keyRing *KeyRing) EncryptStreamWithContext(
|
||||
pgpMessageWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
signingContext *SigningContext,
|
||||
) (plainMessageWriter WriteCloser, err error) {
|
||||
return encryptStream(
|
||||
keyRing,
|
||||
pgpMessageWriter,
|
||||
pgpMessageWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
false,
|
||||
signingContext,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -62,28 +81,47 @@ func (keyRing *KeyRing) EncryptStreamWithCompression(
|
|||
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,
|
||||
return encryptStream(
|
||||
keyRing,
|
||||
pgpMessageWriter,
|
||||
pgpMessageWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
true,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func (keyRing *KeyRing) encryptStreamWithConfig(
|
||||
config *packet.Config,
|
||||
// EncryptStreamWithContextAndCompression 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.
|
||||
// * signingContext : (optional) a context for the embedded signature.
|
||||
func (keyRing *KeyRing) EncryptStreamWithContextAndCompression(
|
||||
pgpMessageWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
signingContext *SigningContext,
|
||||
) (plainMessageWriter WriteCloser, err error) {
|
||||
return encryptStream(
|
||||
keyRing,
|
||||
pgpMessageWriter,
|
||||
pgpMessageWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
true,
|
||||
signingContext,
|
||||
)
|
||||
}
|
||||
|
||||
func encryptStream(
|
||||
encryptionKeyRing *KeyRing,
|
||||
keyPacketWriter Writer,
|
||||
dataPacketWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
compress bool,
|
||||
signingContext *SigningContext,
|
||||
) (plainMessageWriter WriteCloser, err error) {
|
||||
if plainMessageMetadata == nil {
|
||||
// Use sensible default metadata
|
||||
|
|
@ -100,7 +138,7 @@ func (keyRing *KeyRing) encryptStreamWithConfig(
|
|||
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
|
||||
}
|
||||
|
||||
plainMessageWriter, err = asymmetricEncryptStream(hints, keyPacketWriter, dataPacketWriter, keyRing, signKeyRing, config)
|
||||
plainMessageWriter, err = asymmetricEncryptStream(hints, keyPacketWriter, dataPacketWriter, encryptionKeyRing, signKeyRing, compress, signingContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -148,25 +186,36 @@ func (keyRing *KeyRing) EncryptSplitStream(
|
|||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
) (*EncryptSplitResult, error) {
|
||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
|
||||
|
||||
var keyPacketBuf bytes.Buffer
|
||||
|
||||
plainMessageWriter, err := keyRing.encryptStreamWithConfig(
|
||||
config,
|
||||
&keyPacketBuf,
|
||||
return encryptSplitStream(
|
||||
keyRing,
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &EncryptSplitResult{
|
||||
keyPacketBuf: &keyPacketBuf,
|
||||
plainMessageWriter: plainMessageWriter,
|
||||
}, nil
|
||||
// EncryptSplitStreamWithContext 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.
|
||||
// * signingContext : (optional) a context for the embedded signature.
|
||||
func (keyRing *KeyRing) EncryptSplitStreamWithContext(
|
||||
dataPacketWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
signingContext *SigningContext,
|
||||
) (*EncryptSplitResult, error) {
|
||||
return encryptSplitStream(
|
||||
keyRing,
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
false,
|
||||
signingContext,
|
||||
)
|
||||
}
|
||||
|
||||
// EncryptSplitStreamWithCompression is used to encrypt data as a stream.
|
||||
|
|
@ -179,21 +228,55 @@ func (keyRing *KeyRing) EncryptSplitStreamWithCompression(
|
|||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
) (*EncryptSplitResult, error) {
|
||||
config := &packet.Config{
|
||||
DefaultCipher: packet.CipherAES256,
|
||||
Time: getTimeGenerator(),
|
||||
DefaultCompressionAlgo: constants.DefaultCompression,
|
||||
CompressionConfig: &packet.CompressionConfig{Level: constants.DefaultCompressionLevel},
|
||||
}
|
||||
return encryptSplitStream(
|
||||
keyRing,
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
true,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// EncryptSplitStreamWithContextAndCompression 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.
|
||||
// * signingContext : (optional) a context for the embedded signature.
|
||||
func (keyRing *KeyRing) EncryptSplitStreamWithContextAndCompression(
|
||||
dataPacketWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
signingContext *SigningContext,
|
||||
) (*EncryptSplitResult, error) {
|
||||
return encryptSplitStream(
|
||||
keyRing,
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
true,
|
||||
signingContext,
|
||||
)
|
||||
}
|
||||
|
||||
func encryptSplitStream(
|
||||
encryptionKeyRing *KeyRing,
|
||||
dataPacketWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
compress bool,
|
||||
signingContext *SigningContext,
|
||||
) (*EncryptSplitResult, error) {
|
||||
var keyPacketBuf bytes.Buffer
|
||||
|
||||
plainMessageWriter, err := keyRing.encryptStreamWithConfig(
|
||||
config,
|
||||
plainMessageWriter, err := encryptStream(
|
||||
encryptionKeyRing,
|
||||
&keyPacketBuf,
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
compress,
|
||||
signingContext,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -212,6 +295,7 @@ type PlainMessageReader struct {
|
|||
verifyKeyRing *KeyRing
|
||||
verifyTime int64
|
||||
readAll bool
|
||||
verificationContext *VerificationContext
|
||||
}
|
||||
|
||||
// GetMetadata returns the metadata of the decrypted message.
|
||||
|
|
@ -243,7 +327,7 @@ func (msg *PlainMessageReader) VerifySignature() (err error) {
|
|||
}
|
||||
if msg.verifyKeyRing != nil {
|
||||
processSignatureExpiration(msg.details, msg.verifyTime)
|
||||
err = verifyDetailsSignature(msg.details, msg.verifyKeyRing)
|
||||
err = verifyDetailsSignature(msg.details, msg.verifyKeyRing, msg.verificationContext)
|
||||
} else {
|
||||
err = errors.New("gopenpgp: no verify keyring was provided before decryption")
|
||||
}
|
||||
|
|
@ -260,11 +344,49 @@ func (keyRing *KeyRing) DecryptStream(
|
|||
verifyKeyRing *KeyRing,
|
||||
verifyTime int64,
|
||||
) (plainMessage *PlainMessageReader, err error) {
|
||||
messageDetails, err := asymmetricDecryptStream(
|
||||
message,
|
||||
return decryptStream(
|
||||
keyRing,
|
||||
message,
|
||||
verifyKeyRing,
|
||||
verifyTime,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// DecryptStreamWithContext 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.
|
||||
// * verificationContext (optional): context for the signature verification.
|
||||
func (keyRing *KeyRing) DecryptStreamWithContext(
|
||||
message Reader,
|
||||
verifyKeyRing *KeyRing,
|
||||
verifyTime int64,
|
||||
verificationContext *VerificationContext,
|
||||
) (plainMessage *PlainMessageReader, err error) {
|
||||
return decryptStream(
|
||||
keyRing,
|
||||
message,
|
||||
verifyKeyRing,
|
||||
verifyTime,
|
||||
verificationContext,
|
||||
)
|
||||
}
|
||||
|
||||
func decryptStream(
|
||||
decryptionKeyRing *KeyRing,
|
||||
message Reader,
|
||||
verifyKeyRing *KeyRing,
|
||||
verifyTime int64,
|
||||
verificationContext *VerificationContext,
|
||||
) (plainMessage *PlainMessageReader, err error) {
|
||||
messageDetails, err := asymmetricDecryptStream(
|
||||
message,
|
||||
decryptionKeyRing,
|
||||
verifyKeyRing,
|
||||
verifyTime,
|
||||
verificationContext,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -275,6 +397,7 @@ func (keyRing *KeyRing) DecryptStream(
|
|||
verifyKeyRing,
|
||||
verifyTime,
|
||||
false,
|
||||
verificationContext,
|
||||
}, err
|
||||
}
|
||||
|
||||
|
|
@ -299,6 +422,30 @@ func (keyRing *KeyRing) DecryptSplitStream(
|
|||
)
|
||||
}
|
||||
|
||||
// DecryptSplitStreamWithContext 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.
|
||||
// * verificationContext (optional): context for the signature verification.
|
||||
func (keyRing *KeyRing) DecryptSplitStreamWithContext(
|
||||
keypacket []byte,
|
||||
dataPacketReader Reader,
|
||||
verifyKeyRing *KeyRing, verifyTime int64,
|
||||
verificationContext *VerificationContext,
|
||||
) (plainMessage *PlainMessageReader, err error) {
|
||||
messageReader := io.MultiReader(
|
||||
bytes.NewReader(keypacket),
|
||||
dataPacketReader,
|
||||
)
|
||||
return keyRing.DecryptStreamWithContext(
|
||||
messageReader,
|
||||
verifyKeyRing,
|
||||
verifyTime,
|
||||
verificationContext,
|
||||
)
|
||||
}
|
||||
|
||||
// SignDetachedStream generates and returns a PGPSignature for a given message Reader.
|
||||
func (keyRing *KeyRing) SignDetachedStream(message Reader) (*PGPSignature, error) {
|
||||
return keyRing.SignDetachedStreamWithContext(message, nil)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const testContext = "test-context"
|
||||
|
||||
var testMeta = &PlainMessageMetadata{
|
||||
IsBinary: true,
|
||||
Filename: "filename.txt",
|
||||
|
|
@ -106,6 +108,190 @@ func TestKeyRing_EncryptDecryptStream(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestKeyRing_EncryptDecryptStreamWithContext(t *testing.T) {
|
||||
messageBytes := []byte("Hello World!")
|
||||
messageReader := bytes.NewReader(messageBytes)
|
||||
var ciphertextBuf bytes.Buffer
|
||||
messageWriter, err := keyRingTestPublic.EncryptStreamWithContext(
|
||||
&ciphertextBuf,
|
||||
testMeta,
|
||||
keyRingTestPrivate,
|
||||
NewSigningContext(testContext, true),
|
||||
)
|
||||
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.DecryptStreamWithContext(
|
||||
bytes.NewReader(ciphertextBytes),
|
||||
keyRingTestPublic,
|
||||
GetUnixTime(),
|
||||
NewVerificationContext(testContext, true, 0),
|
||||
)
|
||||
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 := ioutil.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 = ioutil.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_EncryptDecryptStreamWithContextAndCompression(t *testing.T) {
|
||||
messageBytes := []byte("Hello World!")
|
||||
messageReader := bytes.NewReader(messageBytes)
|
||||
var ciphertextBuf bytes.Buffer
|
||||
messageWriter, err := keyRingTestPublic.EncryptStreamWithContextAndCompression(
|
||||
&ciphertextBuf,
|
||||
testMeta,
|
||||
keyRingTestPrivate,
|
||||
NewSigningContext(testContext, true),
|
||||
)
|
||||
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.DecryptStreamWithContext(
|
||||
bytes.NewReader(ciphertextBytes),
|
||||
keyRingTestPublic,
|
||||
GetUnixTime(),
|
||||
NewVerificationContext(testContext, true, 0),
|
||||
)
|
||||
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 := ioutil.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 = ioutil.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) {
|
||||
enc := func(w io.Writer, meta *PlainMessageMetadata, kr *KeyRing) (io.WriteCloser, error) {
|
||||
return keyRingTestPublic.EncryptStream(
|
||||
|
|
@ -299,6 +485,57 @@ func TestKeyRing_EncryptDecryptSplitStream(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestKeyRing_EncryptDecryptSplitStreamWithCont(t *testing.T) {
|
||||
messageBytes := []byte("Hello World!")
|
||||
messageReader := bytes.NewReader(messageBytes)
|
||||
|
||||
var dataPacketBuf bytes.Buffer
|
||||
encryptionResult, err := keyRingTestPublic.EncryptSplitStreamWithContext(
|
||||
&dataPacketBuf,
|
||||
testMeta,
|
||||
keyRingTestPrivate,
|
||||
NewSigningContext(testContext, true),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while calling encrypting split stream with key ring, got:", err)
|
||||
}
|
||||
messageWriter := encryptionResult
|
||||
_, err = io.Copy(messageWriter, messageReader)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while copying plaintext writer, got:", err)
|
||||
}
|
||||
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.DecryptSplitStreamWithContext(
|
||||
keyPacket,
|
||||
bytes.NewReader(dataPacket),
|
||||
keyRingTestPublic,
|
||||
GetUnixTime(),
|
||||
NewVerificationContext(testContext, true, 0),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while decrypting split stream with key ring, got:", err)
|
||||
}
|
||||
decryptedBytes, err := ioutil.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))
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyRing_EncryptSplitStreamCompatible(t *testing.T) {
|
||||
enc := func(w io.Writer, meta *PlainMessageMetadata, kr *KeyRing) (*EncryptSplitResult, error) {
|
||||
return keyRingTestPublic.EncryptSplitStream(
|
||||
|
|
@ -462,6 +699,23 @@ func TestKeyRing_SignVerifyDetachedStream(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestKeyRing_SignVerifyDetachedStreamWithContext(t *testing.T) {
|
||||
messageBytes := []byte("Hello World!")
|
||||
messageReader := bytes.NewReader(messageBytes)
|
||||
signature, err := keyRingTestPrivate.SignDetachedStreamWithContext(messageReader, NewSigningContext(testContext, true))
|
||||
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.VerifyDetachedStreamWithContext(messageReader, signature, GetUnixTime(), NewVerificationContext(testContext, true, 0))
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -138,17 +138,7 @@ func newSessionKeyFromEncrypted(ek *packet.EncryptedKey) (*SessionKey, error) {
|
|||
// * message : The plain data as a PlainMessage.
|
||||
// * output : The encrypted data as PGPMessage.
|
||||
func (sk *SessionKey) Encrypt(message *PlainMessage) ([]byte, 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,
|
||||
}
|
||||
|
||||
return encryptWithSessionKey(message, sk, nil, config)
|
||||
return encryptWithSessionKey(message, sk, nil, false, nil)
|
||||
}
|
||||
|
||||
// EncryptAndSign encrypts a PlainMessage to PGPMessage with a SessionKey and signs it with a Private key.
|
||||
|
|
@ -156,59 +146,50 @@ func (sk *SessionKey) Encrypt(message *PlainMessage) ([]byte, error) {
|
|||
// * signKeyRing: The KeyRing to sign the message
|
||||
// * output : The encrypted data as PGPMessage.
|
||||
func (sk *SessionKey) EncryptAndSign(message *PlainMessage, signKeyRing *KeyRing) ([]byte, error) {
|
||||
dc, err := sk.GetCipherFunc()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt with session key")
|
||||
}
|
||||
return encryptWithSessionKey(message, sk, signKeyRing, false, nil)
|
||||
}
|
||||
|
||||
config := &packet.Config{
|
||||
Time: getTimeGenerator(),
|
||||
DefaultCipher: dc,
|
||||
}
|
||||
|
||||
signEntity, err := signKeyRing.getSigningEntity()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: unable to sign")
|
||||
}
|
||||
|
||||
return encryptWithSessionKey(message, sk, signEntity, config)
|
||||
// EncryptAndSignWithContext encrypts a PlainMessage to PGPMessage with a SessionKey and signs it with a Private key.
|
||||
// * message : The plain data as a PlainMessage.
|
||||
// * signKeyRing: The KeyRing to sign the message
|
||||
// * output : The encrypted data as PGPMessage.
|
||||
// * signingContext : (optional) the context for the signature.
|
||||
func (sk *SessionKey) EncryptAndSignWithContext(message *PlainMessage, signKeyRing *KeyRing, signingContext *SigningContext) ([]byte, error) {
|
||||
return encryptWithSessionKey(message, sk, signKeyRing, false, signingContext)
|
||||
}
|
||||
|
||||
// EncryptWithCompression encrypts with compression support a PlainMessage to PGPMessage with a SessionKey.
|
||||
// * message : The plain data as a PlainMessage.
|
||||
// * output : The encrypted data as PGPMessage.
|
||||
func (sk *SessionKey) EncryptWithCompression(message *PlainMessage) ([]byte, 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,
|
||||
DefaultCompressionAlgo: constants.DefaultCompression,
|
||||
CompressionConfig: &packet.CompressionConfig{Level: constants.DefaultCompressionLevel},
|
||||
}
|
||||
|
||||
return encryptWithSessionKey(message, sk, nil, config)
|
||||
return encryptWithSessionKey(message, sk, nil, true, nil)
|
||||
}
|
||||
|
||||
func encryptWithSessionKey(message *PlainMessage, sk *SessionKey, signEntity *openpgp.Entity, config *packet.Config) ([]byte, error) {
|
||||
func encryptWithSessionKey(
|
||||
message *PlainMessage,
|
||||
sk *SessionKey,
|
||||
signKeyRing *KeyRing,
|
||||
compress bool,
|
||||
signingContext *SigningContext,
|
||||
) ([]byte, error) {
|
||||
var encBuf = new(bytes.Buffer)
|
||||
|
||||
encryptWriter, signWriter, err := encryptStreamWithSessionKey(
|
||||
NewPlainMessageMetadata(
|
||||
message.IsBinary(),
|
||||
message.Filename,
|
||||
message.Time,
|
||||
int64(message.Time),
|
||||
),
|
||||
encBuf,
|
||||
sk,
|
||||
signEntity,
|
||||
config,
|
||||
signKeyRing,
|
||||
compress,
|
||||
signingContext,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if signEntity != nil {
|
||||
if signKeyRing != nil {
|
||||
_, err = signWriter.Write(message.GetBinary())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in writing signed message")
|
||||
|
|
@ -231,6 +212,61 @@ func encryptWithSessionKey(message *PlainMessage, sk *SessionKey, signEntity *op
|
|||
}
|
||||
|
||||
func encryptStreamWithSessionKey(
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
dataPacketWriter io.Writer,
|
||||
sk *SessionKey,
|
||||
signKeyRing *KeyRing,
|
||||
compress bool,
|
||||
signingContext *SigningContext,
|
||||
) (encryptWriter, signWriter io.WriteCloser, err error) {
|
||||
dc, err := sk.GetCipherFunc()
|
||||
if err != nil {
|
||||
return nil, 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, nil, errors.Wrap(err, "gopenpgp: unable to sign")
|
||||
}
|
||||
}
|
||||
|
||||
if compress {
|
||||
config.DefaultCompressionAlgo = constants.DefaultCompression
|
||||
config.CompressionConfig = &packet.CompressionConfig{Level: constants.DefaultCompressionLevel}
|
||||
}
|
||||
|
||||
if signingContext != nil {
|
||||
config.SignatureNotations = append(config.SignatureNotations, signingContext.getNotation())
|
||||
}
|
||||
|
||||
if plainMessageMetadata == nil {
|
||||
// Use sensible default metadata
|
||||
plainMessageMetadata = &PlainMessageMetadata{
|
||||
IsBinary: true,
|
||||
Filename: "",
|
||||
ModTime: GetUnixTime(),
|
||||
}
|
||||
}
|
||||
|
||||
return encryptStreamWithSessionKeyAndConfig(
|
||||
plainMessageMetadata.IsBinary,
|
||||
plainMessageMetadata.Filename,
|
||||
uint32(plainMessageMetadata.ModTime),
|
||||
dataPacketWriter,
|
||||
sk,
|
||||
signEntity,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
func encryptStreamWithSessionKeyAndConfig(
|
||||
isBinary bool,
|
||||
filename string,
|
||||
modTime uint32,
|
||||
|
|
@ -297,9 +333,41 @@ func (sk *SessionKey) Decrypt(dataPacket []byte) (*PlainMessage, error) {
|
|||
// * verifyTime: when should the signature be valid, as timestamp. If 0 time verification is disabled.
|
||||
// * output: PlainMessage.
|
||||
func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing, verifyTime int64) (*PlainMessage, error) {
|
||||
return decryptWithSessionKeyAndContext(
|
||||
sk,
|
||||
dataPacket,
|
||||
verifyKeyRing,
|
||||
verifyTime,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// DecryptAndVerifyWithContext decrypts pgp data packets using directly a session key and verifies embedded signatures.
|
||||
// * encrypted: PGPMessage.
|
||||
// * verifyKeyRing: KeyRing with verification public keys
|
||||
// * verifyTime: when should the signature be valid, as timestamp. If 0 time verification is disabled.
|
||||
// * output: PlainMessage.
|
||||
// * verificationContext (optional): context for the signature verification.
|
||||
func (sk *SessionKey) DecryptAndVerifyWithContext(dataPacket []byte, verifyKeyRing *KeyRing, verifyTime int64, verificationContext *VerificationContext) (*PlainMessage, error) {
|
||||
return decryptWithSessionKeyAndContext(
|
||||
sk,
|
||||
dataPacket,
|
||||
verifyKeyRing,
|
||||
verifyTime,
|
||||
verificationContext,
|
||||
)
|
||||
}
|
||||
|
||||
func decryptWithSessionKeyAndContext(
|
||||
sk *SessionKey,
|
||||
dataPacket []byte,
|
||||
verifyKeyRing *KeyRing,
|
||||
verifyTime int64,
|
||||
verificationContext *VerificationContext,
|
||||
) (*PlainMessage, error) {
|
||||
var messageReader = bytes.NewReader(dataPacket)
|
||||
|
||||
md, err := decryptStreamWithSessionKey(sk, messageReader, verifyKeyRing)
|
||||
md, err := decryptStreamWithSessionKey(sk, messageReader, verifyKeyRing, verificationContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -311,7 +379,7 @@ func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing
|
|||
|
||||
if verifyKeyRing != nil {
|
||||
processSignatureExpiration(md, verifyTime)
|
||||
err = verifyDetailsSignature(md, verifyKeyRing)
|
||||
err = verifyDetailsSignature(md, verifyKeyRing, verificationContext)
|
||||
}
|
||||
|
||||
return &PlainMessage{
|
||||
|
|
@ -322,7 +390,12 @@ func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing
|
|||
}, err
|
||||
}
|
||||
|
||||
func decryptStreamWithSessionKey(sk *SessionKey, messageReader io.Reader, verifyKeyRing *KeyRing) (*openpgp.MessageDetails, error) {
|
||||
func decryptStreamWithSessionKey(
|
||||
sk *SessionKey,
|
||||
messageReader io.Reader,
|
||||
verifyKeyRing *KeyRing,
|
||||
verificationContext *VerificationContext,
|
||||
) (*openpgp.MessageDetails, error) {
|
||||
var decrypted io.ReadCloser
|
||||
var keyring openpgp.EntityList
|
||||
|
||||
|
|
@ -356,6 +429,10 @@ func decryptStreamWithSessionKey(sk *SessionKey, messageReader io.Reader, verify
|
|||
Time: getTimeGenerator(),
|
||||
}
|
||||
|
||||
if verificationContext != nil {
|
||||
config.KnownNotations = map[string]bool{constants.SignatureContextName: true}
|
||||
}
|
||||
|
||||
// Push decrypted packet as literal packet and use openpgp's reader
|
||||
if verifyKeyRing != nil {
|
||||
keyring = verifyKeyRing.entities
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
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"
|
||||
)
|
||||
|
||||
|
|
@ -31,14 +28,31 @@ func (sk *SessionKey) EncryptStream(
|
|||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
) (plainMessageWriter WriteCloser, err error) {
|
||||
config := &packet.Config{
|
||||
Time: getTimeGenerator(),
|
||||
}
|
||||
return sk.encryptStreamWithConfig(
|
||||
config,
|
||||
return sk.encryptStream(
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// EncryptStreamWithContext 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.
|
||||
// * signingContext : (optional) the context for the signature.
|
||||
func (sk *SessionKey) EncryptStreamWithContext(
|
||||
dataPacketWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
signingContext *SigningContext,
|
||||
) (plainMessageWriter WriteCloser, err error) {
|
||||
return sk.encryptStream(
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
false,
|
||||
signingContext,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -46,60 +60,55 @@ func (sk *SessionKey) EncryptStream(
|
|||
// 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.
|
||||
// * signingContext : (optional) the context for the 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,
|
||||
return sk.encryptStream(
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
true,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func (sk *SessionKey) encryptStreamWithConfig(
|
||||
config *packet.Config,
|
||||
// EncryptStreamWithContextAndCompression 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.
|
||||
// * signingContext : (optional) the context for the signature.
|
||||
func (sk *SessionKey) EncryptStreamWithContextAndCompression(
|
||||
dataPacketWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
signingContext *SigningContext,
|
||||
) (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()
|
||||
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(),
|
||||
}
|
||||
}
|
||||
return sk.encryptStream(
|
||||
dataPacketWriter,
|
||||
plainMessageMetadata,
|
||||
signKeyRing,
|
||||
true,
|
||||
signingContext,
|
||||
)
|
||||
}
|
||||
|
||||
func (sk *SessionKey) encryptStream(
|
||||
dataPacketWriter Writer,
|
||||
plainMessageMetadata *PlainMessageMetadata,
|
||||
signKeyRing *KeyRing,
|
||||
compress bool,
|
||||
signingContext *SigningContext,
|
||||
) (plainMessageWriter WriteCloser, err error) {
|
||||
encryptWriter, signWriter, err := encryptStreamWithSessionKey(
|
||||
plainMessageMetadata.IsBinary,
|
||||
plainMessageMetadata.Filename,
|
||||
uint32(plainMessageMetadata.ModTime),
|
||||
plainMessageMetadata,
|
||||
dataPacketWriter,
|
||||
sk,
|
||||
signEntity,
|
||||
config,
|
||||
signKeyRing,
|
||||
compress,
|
||||
signingContext,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -123,10 +132,48 @@ func (sk *SessionKey) DecryptStream(
|
|||
verifyKeyRing *KeyRing,
|
||||
verifyTime int64,
|
||||
) (plainMessage *PlainMessageReader, err error) {
|
||||
messageDetails, err := decryptStreamWithSessionKey(
|
||||
return decryptStreamWithSessionKeyAndContext(
|
||||
sk,
|
||||
dataPacketReader,
|
||||
verifyKeyRing,
|
||||
verifyTime,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// DecryptStreamWithContext 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.
|
||||
// * verificationContext (optional): context for the signature verification.
|
||||
func (sk *SessionKey) DecryptStreamWithContext(
|
||||
dataPacketReader Reader,
|
||||
verifyKeyRing *KeyRing,
|
||||
verifyTime int64,
|
||||
verificationContext *VerificationContext,
|
||||
) (plainMessage *PlainMessageReader, err error) {
|
||||
return decryptStreamWithSessionKeyAndContext(
|
||||
sk,
|
||||
dataPacketReader,
|
||||
verifyKeyRing,
|
||||
verifyTime,
|
||||
verificationContext,
|
||||
)
|
||||
}
|
||||
|
||||
func decryptStreamWithSessionKeyAndContext(
|
||||
sessionKey *SessionKey,
|
||||
dataPacketReader Reader,
|
||||
verifyKeyRing *KeyRing,
|
||||
verifyTime int64,
|
||||
verificationContext *VerificationContext,
|
||||
) (plainMessage *PlainMessageReader, err error) {
|
||||
messageDetails, err := decryptStreamWithSessionKey(
|
||||
sessionKey,
|
||||
dataPacketReader,
|
||||
verifyKeyRing,
|
||||
verificationContext,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in reading message")
|
||||
|
|
@ -137,5 +184,6 @@ func (sk *SessionKey) DecryptStream(
|
|||
verifyKeyRing,
|
||||
verifyTime,
|
||||
false,
|
||||
verificationContext,
|
||||
}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,104 @@ func TestSessionKey_EncryptDecryptStream(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSessionKey_EncryptDecryptStreamWithContext(t *testing.T) {
|
||||
messageBytes := []byte("Hello World!")
|
||||
messageReader := bytes.NewReader(messageBytes)
|
||||
var dataPacketBuf bytes.Buffer
|
||||
testContext := "test-context"
|
||||
messageWriter, err := testSessionKey.EncryptStreamWithContext(
|
||||
&dataPacketBuf,
|
||||
testMeta,
|
||||
keyRingTestPrivate,
|
||||
NewSigningContext(testContext, true),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while encrypting, got:", err)
|
||||
}
|
||||
_, err = io.Copy(messageWriter, messageReader)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while copying plaintext, got:", err)
|
||||
}
|
||||
err = messageWriter.Close()
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while closing plaintext writer, got:", err)
|
||||
}
|
||||
dataPacket := dataPacketBuf.Bytes()
|
||||
decryptedReader, err := testSessionKey.DecryptStreamWithContext(
|
||||
bytes.NewReader(dataPacket),
|
||||
keyRingTestPublic,
|
||||
GetUnixTime(),
|
||||
NewVerificationContext(testContext, true, 0),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while calling DecryptStream, got:", err)
|
||||
}
|
||||
decryptedBytes, err := ioutil.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_EncryptDecryptStreamWithContextAndCompression(t *testing.T) {
|
||||
messageBytes := []byte("Hello World!")
|
||||
messageReader := bytes.NewReader(messageBytes)
|
||||
var dataPacketBuf bytes.Buffer
|
||||
testContext := "test-context"
|
||||
messageWriter, err := testSessionKey.EncryptStreamWithContextAndCompression(
|
||||
&dataPacketBuf,
|
||||
testMeta,
|
||||
keyRingTestPrivate,
|
||||
NewSigningContext(testContext, true),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while encrypting, got:", err)
|
||||
}
|
||||
_, err = io.Copy(messageWriter, messageReader)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while copying plaintext, got:", err)
|
||||
}
|
||||
err = messageWriter.Close()
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while closing plaintext writer, got:", err)
|
||||
}
|
||||
dataPacket := dataPacketBuf.Bytes()
|
||||
decryptedReader, err := testSessionKey.DecryptStreamWithContext(
|
||||
bytes.NewReader(dataPacket),
|
||||
keyRingTestPublic,
|
||||
GetUnixTime(),
|
||||
NewVerificationContext(testContext, true, 0),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while calling DecryptStream, got:", err)
|
||||
}
|
||||
decryptedBytes, err := ioutil.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) {
|
||||
enc := func(w io.Writer, meta *PlainMessageMetadata, kr *KeyRing) (io.WriteCloser, error) {
|
||||
return testSessionKey.EncryptStream(w, meta, kr)
|
||||
|
|
|
|||
|
|
@ -249,6 +249,30 @@ func TestDataPacketEncryptionAndSignature(t *testing.T) {
|
|||
assert.Exactly(t, message.GetString(), finalMessage.GetString())
|
||||
}
|
||||
|
||||
func TestDataPacketEncryptionAndSignatureWithContext(t *testing.T) {
|
||||
var message = NewPlainMessageFromString(
|
||||
"The secret code is... 1, 2, 3, 4, 5. I repeat: the secret code is... 1, 2, 3, 4, 5",
|
||||
)
|
||||
var testContext = "test-context"
|
||||
// Encrypt data with session key
|
||||
dataPacket, err := testSessionKey.EncryptAndSignWithContext(message, keyRingTestPrivate, NewSigningContext(testContext, true))
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error when encrypting and signing, got:", err)
|
||||
}
|
||||
|
||||
// Decrypt & verify data with the good session key and keyring
|
||||
decrypted, err := testSessionKey.DecryptAndVerifyWithContext(
|
||||
dataPacket,
|
||||
keyRingTestPublic,
|
||||
GetUnixTime(),
|
||||
NewVerificationContext(testContext, true, 0),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error when decrypting & verifying, got:", err)
|
||||
}
|
||||
assert.Exactly(t, message.GetString(), decrypted.GetString())
|
||||
}
|
||||
|
||||
func TestDataPacketDecryption(t *testing.T) {
|
||||
pgpMessage, err := NewPGPMessageFromArmored(readTestFile("message_signed", false))
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ func processSignatureExpiration(md *openpgp.MessageDetails, verifyTime int64) {
|
|||
}
|
||||
|
||||
// verifyDetailsSignature verifies signature from message details.
|
||||
func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing) error {
|
||||
func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing, verificationContext *VerificationContext) error {
|
||||
if !md.IsSigned {
|
||||
return newSignatureNotSigned()
|
||||
}
|
||||
|
|
@ -133,6 +133,13 @@ func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing) er
|
|||
md.Signature.Hash > allowedHashes[len(allowedHashes)-1] {
|
||||
return newSignatureInsecure()
|
||||
}
|
||||
if verificationContext != nil {
|
||||
err := verificationContext.verifyContext(md.Signature)
|
||||
if err != nil {
|
||||
return newSignatureBadContext(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -154,6 +154,27 @@ func Test_KeyRing_GetVerifiedSignatureTimestampSuccess(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_KeyRing_GetVerifiedSignatureTimestampWithContext(t *testing.T) {
|
||||
message := NewPlainMessageFromString(testMessage)
|
||||
var time int64 = 1600000000
|
||||
pgp.latestServerTime = time
|
||||
defer func() {
|
||||
pgp.latestServerTime = testTime
|
||||
}()
|
||||
var testContext = "test-context"
|
||||
signature, err := keyRingTestPrivate.SignDetachedWithContext(message, NewSigningContext(testContext, true))
|
||||
if err != nil {
|
||||
t.Errorf("Got an error while generating the signature: %v", err)
|
||||
}
|
||||
actualTime, err := keyRingTestPublic.GetVerifiedSignatureTimestampWithContext(message, signature, 0, NewVerificationContext(testContext, true, 0))
|
||||
if err != nil {
|
||||
t.Errorf("Got an error while parsing the signature creation time: %v", err)
|
||||
}
|
||||
if time != actualTime {
|
||||
t.Errorf("Expected creation time to be %d, got %d", time, actualTime)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KeyRing_GetVerifiedSignatureWithTwoKeysTimestampSuccess(t *testing.T) {
|
||||
publicKey1Armored, err := ioutil.ReadFile("testdata/signature/publicKey1")
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue