Add a streaming api to KeyRing and SessionKey (#131)
* barebone streaming functionality * encryption needs to return a writecloser * added eof check * workaround for reader problem with copies * separate mobile wrappers from main api * add a clone in the read result to avoid memory corruption * refactor to reuse code, and fix verification * have to give the verify key at the start of the decryption * enfore readAll before signature verification * streaming api for SessionKey * add split message stream apis * name interface params * fix streaming api so it's supported by go-mobile * hide internal writeCloser * fix nil access * added detached sigs methods * started unit testing * unit testing and fixed a bug where key and data packets where inverted * remove unecessary error wrapping * figured out closing order and error handling * add GC calls to mobile writer and reader * remove debugging values and arrays * writer with builtin sha256 * unit testing the mobile helpers * comments and linting * Typo in error Co-authored-by: wussler <aron@wussler.it> * Add GetKeyPacket doc Co-authored-by: wussler <aron@wussler.it> * Add rfc reference in comments Co-authored-by: wussler <aron@wussler.it> * small improvements * add compatibility tests with normal methods * remove unecessary copies in the tests * update go-crypto to the merged changes commit * update comments of core internal functions * remove unused nolint comment * group message metadata in a struct * fix comments * change default values for metadata * change the mobile reader wrapper to fit the behavior of java * remove gc calls in the wrappers to avoid performance penalties * bring back the former Go2MobileReader to be used for ios * Update crypto/keyring_streaming.go Co-authored-by: wussler <aron@wussler.it> * return an error when verifying an embedded sig with no keyring * Update crypto/sessionkey_streaming.go Co-authored-by: wussler <aron@wussler.it> * linter error * update changelog * update changelog Co-authored-by: wussler <aron@wussler.it>
This commit is contained in:
parent
7380f7391f
commit
c46ed8ed9e
11 changed files with 1718 additions and 97 deletions
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/gopenpgp/v2/constants"
|
||||
"github.com/pkg/errors"
|
||||
|
|
@ -171,67 +172,87 @@ func (sk *SessionKey) EncryptWithCompression(message *PlainMessage) ([]byte, err
|
|||
|
||||
func encryptWithSessionKey(message *PlainMessage, sk *SessionKey, signEntity *openpgp.Entity, config *packet.Config) ([]byte, error) {
|
||||
var encBuf = new(bytes.Buffer)
|
||||
var encryptWriter, signWriter io.WriteCloser
|
||||
|
||||
encryptWriter, err := packet.SerializeSymmetricallyEncrypted(encBuf, config.Cipher(), sk.Key, config)
|
||||
encryptWriter, signWriter, err := encryptStreamWithSessionKey(
|
||||
message.IsBinary(),
|
||||
message.Filename,
|
||||
message.Time,
|
||||
encBuf,
|
||||
sk,
|
||||
signEntity,
|
||||
config,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if algo := config.Compression(); algo != packet.CompressionNone {
|
||||
encryptWriter, err = packet.SerializeCompressed(encryptWriter, algo, config.CompressionConfig)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in compression")
|
||||
}
|
||||
}
|
||||
|
||||
if signEntity != nil { // nolint:nestif
|
||||
hints := &openpgp.FileHints{
|
||||
IsBinary: message.IsBinary(),
|
||||
FileName: message.Filename,
|
||||
ModTime: message.getFormattedTime(),
|
||||
}
|
||||
|
||||
signWriter, err = openpgp.Sign(encryptWriter, signEntity, hints, config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: unable to sign")
|
||||
}
|
||||
|
||||
if signEntity != nil {
|
||||
_, err = signWriter.Write(message.GetBinary())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in writing signed message")
|
||||
}
|
||||
|
||||
err = signWriter.Close()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in closing signing writer")
|
||||
}
|
||||
} else {
|
||||
encryptWriter, err = packet.SerializeLiteral(
|
||||
encryptWriter,
|
||||
message.IsBinary(),
|
||||
message.Filename,
|
||||
message.Time,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: unable to serialize")
|
||||
}
|
||||
|
||||
_, err = encryptWriter.Write(message.GetBinary())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in writing message")
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in writing message")
|
||||
}
|
||||
err = encryptWriter.Close()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in closing encryption writer")
|
||||
}
|
||||
|
||||
return encBuf.Bytes(), nil
|
||||
}
|
||||
|
||||
func encryptStreamWithSessionKey(
|
||||
isBinary bool,
|
||||
filename string,
|
||||
modTime uint32,
|
||||
dataPacketWriter io.Writer,
|
||||
sk *SessionKey,
|
||||
signEntity *openpgp.Entity,
|
||||
config *packet.Config,
|
||||
) (encryptWriter, signWriter io.WriteCloser, err error) {
|
||||
encryptWriter, err = packet.SerializeSymmetricallyEncrypted(dataPacketWriter, config.Cipher(), sk.Key, config)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "gopenpgp: unable to encrypt")
|
||||
}
|
||||
|
||||
if algo := config.Compression(); algo != packet.CompressionNone {
|
||||
encryptWriter, err = packet.SerializeCompressed(encryptWriter, algo, config.CompressionConfig)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "gopenpgp: error in compression")
|
||||
}
|
||||
}
|
||||
|
||||
if signEntity != nil {
|
||||
hints := &openpgp.FileHints{
|
||||
IsBinary: isBinary,
|
||||
FileName: filename,
|
||||
ModTime: time.Unix(int64(modTime), 0),
|
||||
}
|
||||
|
||||
signWriter, err = openpgp.Sign(encryptWriter, signEntity, hints, config)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "gopenpgp: unable to sign")
|
||||
}
|
||||
} else {
|
||||
encryptWriter, err = packet.SerializeLiteral(
|
||||
encryptWriter,
|
||||
isBinary,
|
||||
filename,
|
||||
modTime,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "gopenpgp: unable to serialize")
|
||||
}
|
||||
}
|
||||
return encryptWriter, signWriter, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts pgp data packets using directly a session key.
|
||||
// * encrypted: PGPMessage.
|
||||
// * output: PlainMessage.
|
||||
|
|
@ -246,8 +267,32 @@ func (sk *SessionKey) Decrypt(dataPacket []byte) (*PlainMessage, error) {
|
|||
// * output: PlainMessage.
|
||||
func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing, verifyTime int64) (*PlainMessage, error) {
|
||||
var messageReader = bytes.NewReader(dataPacket)
|
||||
|
||||
md, err := decryptStreamWithSessionKey(sk, messageReader, verifyKeyRing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
messageBuf := new(bytes.Buffer)
|
||||
_, err = messageBuf.ReadFrom(md.UnverifiedBody)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in reading message body")
|
||||
}
|
||||
|
||||
if verifyKeyRing != nil {
|
||||
processSignatureExpiration(md, verifyTime)
|
||||
err = verifyDetailsSignature(md, verifyKeyRing)
|
||||
}
|
||||
|
||||
return &PlainMessage{
|
||||
Data: messageBuf.Bytes(),
|
||||
TextType: !md.LiteralData.IsBinary,
|
||||
Filename: md.LiteralData.FileName,
|
||||
Time: md.LiteralData.Time,
|
||||
}, err
|
||||
}
|
||||
|
||||
func decryptStreamWithSessionKey(sk *SessionKey, messageReader io.Reader, verifyKeyRing *KeyRing) (*openpgp.MessageDetails, error) {
|
||||
var decrypted io.ReadCloser
|
||||
var decBuf bytes.Buffer
|
||||
var keyring openpgp.EntityList
|
||||
|
||||
// Read symmetrically encrypted data packet
|
||||
|
|
@ -273,45 +318,24 @@ func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing
|
|||
default:
|
||||
return nil, errors.New("gopenpgp: invalid packet type")
|
||||
}
|
||||
_, err = decBuf.ReadFrom(decrypted)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: unable to read from decrypted symmetric packet")
|
||||
}
|
||||
|
||||
config := &packet.Config{
|
||||
Time: getTimeGenerator(),
|
||||
}
|
||||
|
||||
// Push decrypted packet as literal packet and use openpgp's reader
|
||||
|
||||
if verifyKeyRing != nil {
|
||||
keyring = verifyKeyRing.entities
|
||||
} else {
|
||||
keyring = openpgp.EntityList{}
|
||||
}
|
||||
|
||||
md, err := openpgp.ReadMessage(&decBuf, keyring, nil, config)
|
||||
md, err := openpgp.ReadMessage(decrypted, keyring, nil, config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: unable to decode symmetric packet")
|
||||
}
|
||||
|
||||
messageBuf := new(bytes.Buffer)
|
||||
_, err = messageBuf.ReadFrom(md.UnverifiedBody)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "gopenpgp: error in reading message body")
|
||||
}
|
||||
|
||||
if verifyKeyRing != nil {
|
||||
processSignatureExpiration(md, verifyTime)
|
||||
err = verifyDetailsSignature(md, verifyKeyRing)
|
||||
}
|
||||
|
||||
return &PlainMessage{
|
||||
Data: messageBuf.Bytes(),
|
||||
TextType: !md.LiteralData.IsBinary,
|
||||
Filename: md.LiteralData.FileName,
|
||||
Time: md.LiteralData.Time,
|
||||
}, err
|
||||
return md, nil
|
||||
}
|
||||
|
||||
func (sk *SessionKey) checkSize() error {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue