From 54eafe1dea80186c74f79090c2a229a73f9108f5 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Mon, 20 Feb 2012 10:48:21 -0500 Subject: [PATCH] openpgp: eliminate implicit rand.Reader and time.Now Fixes golang/go#2501. R=rsc, bradfitz CC=golang-dev https://golang.org/cl/5653067 --- openpgp/clearsign/clearsign.go | 15 ++-- openpgp/keys.go | 32 ++++---- openpgp/packet/encrypted_key.go | 13 +-- openpgp/packet/encrypted_key_test.go | 7 +- openpgp/packet/signature.go | 17 ++-- openpgp/packet/symmetric_key_encrypted.go | 8 +- .../packet/symmetric_key_encrypted_test.go | 11 +-- openpgp/packet/symmetrically_encrypted.go | 5 +- .../packet/symmetrically_encrypted_test.go | 3 +- openpgp/read.go | 5 +- openpgp/read_test.go | 8 +- openpgp/write.go | 79 ++++++++++++------- openpgp/write_test.go | 22 +++--- 13 files changed, 127 insertions(+), 98 deletions(-) diff --git a/openpgp/clearsign/clearsign.go b/openpgp/clearsign/clearsign.go index b5dcaa0..a753bcf 100644 --- a/openpgp/clearsign/clearsign.go +++ b/openpgp/clearsign/clearsign.go @@ -17,7 +17,6 @@ import ( "io" "net/textproto" "strconv" - "time" "code.google.com/p/go.crypto/openpgp/armor" "code.google.com/p/go.crypto/openpgp/errors" @@ -179,9 +178,8 @@ type dashEscaper struct { whitespace []byte byteBuf []byte // a one byte buffer to save allocations - privateKey *packet.PrivateKey - signingTime time.Time - rand io.Reader + privateKey *packet.PrivateKey + config *packet.Config } func (d *dashEscaper) Write(data []byte) (n int, err error) { @@ -261,10 +259,10 @@ func (d *dashEscaper) Close() (err error) { sig.SigType = packet.SigTypeText sig.PubKeyAlgo = d.privateKey.PubKeyAlgo sig.Hash = d.hashType - sig.CreationTime = d.signingTime + sig.CreationTime = d.config.Now() sig.IssuerKeyId = &d.privateKey.KeyId - if err = sig.Sign(d.rand, d.h, d.privateKey); err != nil { + if err = sig.Sign(d.h, d.privateKey, d.config); err != nil { return } @@ -334,9 +332,8 @@ func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) ( byteBuf: make([]byte, 1), - privateKey: privateKey, - signingTime: config.Now(), - rand: config.Random(), + privateKey: privateKey, + config: config, } return diff --git a/openpgp/keys.go b/openpgp/keys.go index 5ae8fbd..6404aff 100644 --- a/openpgp/keys.go +++ b/openpgp/keys.go @@ -8,11 +8,8 @@ import ( "code.google.com/p/go.crypto/openpgp/armor" "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/packet" - "crypto" - "crypto/rand" "crypto/rsa" "io" - "time" ) // PublicKeyType is the armor type for a PGP public key. @@ -383,16 +380,19 @@ const defaultRSAKeyBits = 2048 // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a // single identity composed of the given full name, comment and email, any of // which may be empty but must not contain any of "()<>\x00". -func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email string) (*Entity, error) { +// If config is nil, sensible defaults will be used. +func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) { + currentTime := config.Now() + uid := packet.NewUserId(name, comment, email) if uid == nil { return nil, errors.InvalidArgumentError("user id field contained invalid characters") } - signingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) + signingPriv, err := rsa.GenerateKey(config.Random(), defaultRSAKeyBits) if err != nil { return nil, err } - encryptingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) + encryptingPriv, err := rsa.GenerateKey(config.Random(), defaultRSAKeyBits) if err != nil { return nil, err } @@ -410,7 +410,7 @@ func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email strin CreationTime: currentTime, SigType: packet.SigTypePositiveCert, PubKeyAlgo: packet.PubKeyAlgoRSA, - Hash: crypto.SHA256, + Hash: config.Hash(), IsPrimaryId: &isPrimaryId, FlagsValid: true, FlagSign: true, @@ -427,7 +427,7 @@ func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email strin CreationTime: currentTime, SigType: packet.SigTypeSubkeyBinding, PubKeyAlgo: packet.PubKeyAlgoRSA, - Hash: crypto.SHA256, + Hash: config.Hash(), FlagsValid: true, FlagEncryptStorage: true, FlagEncryptCommunications: true, @@ -443,7 +443,8 @@ func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email strin // SerializePrivate serializes an Entity, including private key material, to // the given Writer. For now, it must only be used on an Entity returned from // NewEntity. -func (e *Entity) SerializePrivate(w io.Writer) (err error) { +// If config is nil, sensible defaults will be used. +func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) { err = e.PrivateKey.Serialize(w) if err != nil { return @@ -453,7 +454,7 @@ func (e *Entity) SerializePrivate(w io.Writer) (err error) { if err != nil { return } - err = ident.SelfSignature.SignUserId(rand.Reader, ident.UserId.Id, e.PrimaryKey, e.PrivateKey) + err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config) if err != nil { return } @@ -467,7 +468,7 @@ func (e *Entity) SerializePrivate(w io.Writer) (err error) { if err != nil { return } - err = subkey.Sig.SignKey(rand.Reader, subkey.PublicKey, e.PrivateKey) + err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config) if err != nil { return } @@ -519,7 +520,8 @@ func (e *Entity) Serialize(w io.Writer) error { // associated with e. The provided identity must already be an element of // e.Identities and the private key of signer must have been decrypted if // necessary. -func (e *Entity) SignIdentity(identity string, signer *Entity) error { +// If config is nil, sensible defaults will be used. +func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error { if signer.PrivateKey == nil { return errors.InvalidArgumentError("signing Entity must have a private key") } @@ -534,11 +536,11 @@ func (e *Entity) SignIdentity(identity string, signer *Entity) error { sig := &packet.Signature{ SigType: packet.SigTypeGenericCert, PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, - Hash: crypto.SHA256, - CreationTime: time.Now(), + Hash: config.Hash(), + CreationTime: config.Now(), IssuerKeyId: &signer.PrivateKey.KeyId, } - if err := sig.SignKey(rand.Reader, e.PrimaryKey, signer.PrivateKey); err != nil { + if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey, config); err != nil { return err } ident.Signatures = append(ident.Signatures, sig) diff --git a/openpgp/packet/encrypted_key.go b/openpgp/packet/encrypted_key.go index b4a4eff..e32d522 100644 --- a/openpgp/packet/encrypted_key.go +++ b/openpgp/packet/encrypted_key.go @@ -7,7 +7,6 @@ package packet import ( "code.google.com/p/go.crypto/openpgp/elgamal" "code.google.com/p/go.crypto/openpgp/errors" - "crypto/rand" "crypto/rsa" "encoding/binary" "io" @@ -63,7 +62,8 @@ func checksumKeyMaterial(key []byte) uint16 { // Decrypt decrypts an encrypted session key with the given private key. The // private key must have been decrypted first. -func (e *EncryptedKey) Decrypt(priv *PrivateKey) error { +// If config is nil, sensible defaults will be used. +func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { var err error var b []byte @@ -71,7 +71,7 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey) error { // padding oracle attacks. switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - b, err = rsa.DecryptPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1) + b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1) case PubKeyAlgoElGamal: c1 := new(big.Int).SetBytes(e.encryptedMPI1) c2 := new(big.Int).SetBytes(e.encryptedMPI2) @@ -97,7 +97,8 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey) error { // SerializeEncryptedKey serializes an encrypted key packet to w that contains // key, encrypted to pub. -func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFunc CipherFunction, key []byte) error { +// If config is nil, sensible defaults will be used. +func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { var buf [10]byte buf[0] = encryptedKeyVersion binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) @@ -112,9 +113,9 @@ func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFu switch pub.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - return serializeEncryptedKeyRSA(w, rand, buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) + return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) case PubKeyAlgoElGamal: - return serializeEncryptedKeyElGamal(w, rand, buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) + return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) } diff --git a/openpgp/packet/encrypted_key_test.go b/openpgp/packet/encrypted_key_test.go index 2b8eff7..0a8dcc6 100644 --- a/openpgp/packet/encrypted_key_test.go +++ b/openpgp/packet/encrypted_key_test.go @@ -6,7 +6,6 @@ package packet import ( "bytes" - "crypto/rand" "crypto/rsa" "fmt" "math/big" @@ -58,7 +57,7 @@ func TestDecryptingEncryptedKey(t *testing.T) { return } - err = ek.Decrypt(encryptedKeyPriv) + err = ek.Decrypt(encryptedKeyPriv, nil) if err != nil { t.Errorf("error from Decrypt: %s", err) return @@ -87,7 +86,7 @@ func TestEncryptingEncryptedKey(t *testing.T) { } buf := new(bytes.Buffer) - err := SerializeEncryptedKey(buf, rand.Reader, pub, CipherAES128, key) + err := SerializeEncryptedKey(buf, pub, CipherAES128, key, nil) if err != nil { t.Errorf("error writing encrypted key packet: %s", err) } @@ -108,7 +107,7 @@ func TestEncryptingEncryptedKey(t *testing.T) { return } - err = ek.Decrypt(encryptedKeyPriv) + err = ek.Decrypt(encryptedKeyPriv, nil) if err != nil { t.Errorf("error from Decrypt: %s", err) return diff --git a/openpgp/packet/signature.go b/openpgp/packet/signature.go index 7f2fdab..7440462 100644 --- a/openpgp/packet/signature.go +++ b/openpgp/packet/signature.go @@ -426,7 +426,8 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { // Sign signs a message with a private key. The hash, h, must contain // the hash of the message to be signed and will be mutated by this function. // On success, the signature is stored in sig. Call Serialize to write it out. -func (sig *Signature) Sign(rand io.Reader, h hash.Hash, priv *PrivateKey) (err error) { +// If config is nil, sensible defaults will be used. +func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { sig.outSubpackets = sig.buildSubpackets() digest, err := sig.signPrepareHash(h) if err != nil { @@ -435,7 +436,7 @@ func (sig *Signature) Sign(rand io.Reader, h hash.Hash, priv *PrivateKey) (err e switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature.bytes, err = rsa.SignPKCS1v15(rand, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) + sig.RSASignature.bytes, err = rsa.SignPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) case PubKeyAlgoDSA: dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) @@ -445,7 +446,7 @@ func (sig *Signature) Sign(rand io.Reader, h hash.Hash, priv *PrivateKey) (err e if len(digest) > subgroupSize { digest = digest[:subgroupSize] } - r, s, err := dsa.Sign(rand, dsaPriv, digest) + r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) if err == nil { sig.DSASigR.bytes = r.Bytes() sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) @@ -462,22 +463,24 @@ func (sig *Signature) Sign(rand io.Reader, h hash.Hash, priv *PrivateKey) (err e // SignUserId computes a signature from priv, asserting that pub is a valid // key for the identity id. On success, the signature is stored in sig. Call // Serialize to write it out. -func (sig *Signature) SignUserId(rand io.Reader, id string, pub *PublicKey, priv *PrivateKey) error { +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error { h, err := userIdSignatureHash(id, pub, sig) if err != nil { return nil } - return sig.Sign(rand, h, priv) + return sig.Sign(h, priv, config) } // SignKey computes a signature from priv, asserting that pub is a subkey. On // success, the signature is stored in sig. Call Serialize to write it out. -func (sig *Signature) SignKey(rand io.Reader, pub *PublicKey, priv *PrivateKey) error { +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error { h, err := keySignatureHash(&priv.PublicKey, pub, sig) if err != nil { return err } - return sig.Sign(rand, h, priv) + return sig.Sign(h, priv, config) } // Serialize marshals sig to w. SignRSA or SignDSA must have been called first. diff --git a/openpgp/packet/symmetric_key_encrypted.go b/openpgp/packet/symmetric_key_encrypted.go index 574badd..a78344f 100644 --- a/openpgp/packet/symmetric_key_encrypted.go +++ b/openpgp/packet/symmetric_key_encrypted.go @@ -107,7 +107,9 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) error { // packet contains a random session key, encrypted by a key derived from the // given passphrase. The session key is returned and must be passed to // SerializeSymmetricallyEncrypted. -func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []byte, cipherFunc CipherFunction) (key []byte, err error) { +// If config is nil, sensible defaults will be used. +func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) { + cipherFunc := config.Cipher() keySize := cipherFunc.KeySize() if keySize == 0 { return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) @@ -117,7 +119,7 @@ func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []by keyEncryptingKey := make([]byte, keySize) // s2k.Serialize salts and stretches the passphrase, and writes the // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. - err = s2k.Serialize(s2kBuf, keyEncryptingKey, rand, passphrase) + err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase) if err != nil { return } @@ -142,7 +144,7 @@ func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []by } sessionKey := make([]byte, keySize) - _, err = io.ReadFull(rand, sessionKey) + _, err = io.ReadFull(config.Random(), sessionKey) if err != nil { return } diff --git a/openpgp/packet/symmetric_key_encrypted_test.go b/openpgp/packet/symmetric_key_encrypted_test.go index 87690f0..dd983cb 100644 --- a/openpgp/packet/symmetric_key_encrypted_test.go +++ b/openpgp/packet/symmetric_key_encrypted_test.go @@ -6,7 +6,6 @@ package packet import ( "bytes" - "crypto/rand" "encoding/hex" "io" "io/ioutil" @@ -65,9 +64,11 @@ const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a" func TestSerializeSymmetricKeyEncrypted(t *testing.T) { buf := bytes.NewBuffer(nil) passphrase := []byte("testing") - cipherFunc := CipherAES128 + config := &Config{ + DefaultCipher: CipherAES128, + } - key, err := SerializeSymmetricKeyEncrypted(buf, rand.Reader, passphrase, cipherFunc) + key, err := SerializeSymmetricKeyEncrypted(buf, passphrase, config) if err != nil { t.Errorf("failed to serialize: %s", err) return @@ -87,8 +88,8 @@ func TestSerializeSymmetricKeyEncrypted(t *testing.T) { if !ske.Encrypted { t.Errorf("SKE not encrypted but should be") } - if ske.CipherFunc != cipherFunc { - t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, cipherFunc) + if ske.CipherFunc != config.DefaultCipher { + t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, config.DefaultCipher) } err = ske.Decrypt(passphrase) if err != nil { diff --git a/openpgp/packet/symmetrically_encrypted.go b/openpgp/packet/symmetrically_encrypted.go index 5154aba..04dd670 100644 --- a/openpgp/packet/symmetrically_encrypted.go +++ b/openpgp/packet/symmetrically_encrypted.go @@ -252,7 +252,8 @@ func (c noOpCloser) Close() error { // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet // to w and returns a WriteCloser to which the to-be-encrypted packets can be // written. -func SerializeSymmetricallyEncrypted(w io.Writer, rand io.Reader, c CipherFunction, key []byte) (contents io.WriteCloser, err error) { +// If config is nil, sensible defaults will be used. +func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (contents io.WriteCloser, err error) { if c.KeySize() != len(key) { return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") } @@ -270,7 +271,7 @@ func SerializeSymmetricallyEncrypted(w io.Writer, rand io.Reader, c CipherFuncti block := c.new(key) blockSize := block.BlockSize() iv := make([]byte, blockSize) - _, err = rand.Read(iv) + _, err = config.Random().Read(iv) if err != nil { return } diff --git a/openpgp/packet/symmetrically_encrypted_test.go b/openpgp/packet/symmetrically_encrypted_test.go index 32ccff2..c4845eb 100644 --- a/openpgp/packet/symmetrically_encrypted_test.go +++ b/openpgp/packet/symmetrically_encrypted_test.go @@ -7,7 +7,6 @@ package packet import ( "bytes" "code.google.com/p/go.crypto/openpgp/errors" - "crypto/rand" "crypto/sha1" "encoding/hex" "io" @@ -83,7 +82,7 @@ func TestSerialize(t *testing.T) { c := CipherAES128 key := make([]byte, c.KeySize()) - w, err := SerializeSymmetricallyEncrypted(buf, rand.Reader, c, key) + w, err := SerializeSymmetricallyEncrypted(buf, c, key, nil) if err != nil { t.Errorf("error from SerializeSymmetricallyEncrypted: %s", err) return diff --git a/openpgp/read.go b/openpgp/read.go index 1eec1b1..de0920c 100644 --- a/openpgp/read.go +++ b/openpgp/read.go @@ -80,7 +80,8 @@ type keyEnvelopePair struct { // ReadMessage parses an OpenPGP message that may be signed and/or encrypted. // The given KeyRing should contain both public keys (for signature // verification) and, possibly encrypted, private keys for decrypting. -func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction) (md *MessageDetails, err error) { +// If config is nil, sensible defaults will be used. +func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction, config *packet.Config) (md *MessageDetails, err error) { var p packet.Packet var symKeys []*packet.SymmetricKeyEncrypted @@ -155,7 +156,7 @@ FindKey: } if !pk.key.PrivateKey.Encrypted { if len(pk.encryptedKey.Key) == 0 { - pk.encryptedKey.Decrypt(pk.key.PrivateKey) + pk.encryptedKey.Decrypt(pk.key.PrivateKey, config) } if len(pk.encryptedKey.Key) == 0 { continue diff --git a/openpgp/read_test.go b/openpgp/read_test.go index 18fcfe6..f49c6df 100644 --- a/openpgp/read_test.go +++ b/openpgp/read_test.go @@ -104,7 +104,7 @@ func TestGetKeyById(t *testing.T) { func checkSignedMessage(t *testing.T, signedHex, expected string) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - md, err := ReadMessage(readerFromHex(signedHex), kring, nil) + md, err := ReadMessage(readerFromHex(signedHex), kring, nil, nil) if err != nil { t.Error(err) return @@ -178,7 +178,7 @@ func TestSignedEncryptedMessage(t *testing.T) { return nil, nil } - md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt) + md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt, nil) if err != nil { t.Errorf("#%d: error reading message: %s", i, err) return @@ -206,7 +206,7 @@ func TestUnspecifiedRecipient(t *testing.T) { expected := "Recipient unspecified\n" kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - md, err := ReadMessage(readerFromHex(recipientUnspecifiedHex), kring, nil) + md, err := ReadMessage(readerFromHex(recipientUnspecifiedHex), kring, nil, nil) if err != nil { t.Errorf("error reading message: %s", err) return @@ -236,7 +236,7 @@ func TestSymmetricallyEncrypted(t *testing.T) { return []byte("password"), nil } - md, err := ReadMessage(readerFromHex(symmetricallyEncryptedCompressedHex), nil, prompt) + md, err := ReadMessage(readerFromHex(symmetricallyEncryptedCompressedHex), nil, prompt, nil) if err != nil { t.Errorf("ReadMessage: %s", err) return diff --git a/openpgp/write.go b/openpgp/write.go index c93b26e..38515b9 100644 --- a/openpgp/write.go +++ b/openpgp/write.go @@ -10,8 +10,6 @@ import ( "code.google.com/p/go.crypto/openpgp/packet" "code.google.com/p/go.crypto/openpgp/s2k" "crypto" - "crypto/rand" - _ "crypto/sha256" "hash" "io" "strconv" @@ -20,43 +18,47 @@ import ( // DetachSign signs message with the private key from signer (which must // already have been decrypted) and writes the signature to w. -func DetachSign(w io.Writer, signer *Entity, message io.Reader) error { - return detachSign(w, signer, message, packet.SigTypeBinary) +// If config is nil, sensible defaults will be used. +func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeBinary, config) } // ArmoredDetachSign signs message with the private key from signer (which // must already have been decrypted) and writes an armored signature to w. -func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader) (err error) { - return armoredDetachSign(w, signer, message, packet.SigTypeBinary) +// If config is nil, sensible defaults will be used. +func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) { + return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config) } // DetachSignText signs message (after canonicalising the line endings) with // the private key from signer (which must already have been decrypted) and // writes the signature to w. -func DetachSignText(w io.Writer, signer *Entity, message io.Reader) error { - return detachSign(w, signer, message, packet.SigTypeText) +// If config is nil, sensible defaults will be used. +func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeText, config) } // ArmoredDetachSignText signs message (after canonicalising the line endings) // with the private key from signer (which must already have been decrypted) // and writes an armored signature to w. -func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader) error { - return armoredDetachSign(w, signer, message, packet.SigTypeText) +// If config is nil, sensible defaults will be used. +func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return armoredDetachSign(w, signer, message, packet.SigTypeText, config) } -func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType) (err error) { +func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { out, err := armor.Encode(w, SignatureType, nil) if err != nil { return } - err = detachSign(out, signer, message, sigType) + err = detachSign(out, signer, message, sigType, config) if err != nil { return } return out.Close() } -func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType) (err error) { +func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { if signer.PrivateKey == nil { return errors.InvalidArgumentError("signing key doesn't have a private key") } @@ -67,8 +69,8 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S sig := new(packet.Signature) sig.SigType = sigType sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo - sig.Hash = crypto.SHA256 - sig.CreationTime = time.Now() + sig.Hash = config.Hash() + sig.CreationTime = config.Now() sig.IssuerKeyId = &signer.PrivateKey.KeyId h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) @@ -77,7 +79,7 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S } io.Copy(wrappedHash, message) - err = sig.Sign(rand.Reader, h, signer.PrivateKey) + err = sig.Sign(h, signer.PrivateKey, config) if err != nil { return } @@ -102,16 +104,17 @@ type FileHints struct { // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. // The resulting WriteCloser must be closed after the contents of the file have // been written. -func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints) (plaintext io.WriteCloser, err error) { +// If config is nil, sensible defaults will be used. +func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { if hints == nil { hints = &FileHints{} } - key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, rand.Reader, passphrase, packet.CipherAES128) + key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config) if err != nil { return } - w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, packet.CipherAES128, key) + w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config) if err != nil { return } @@ -151,7 +154,8 @@ func hashToHashId(h crypto.Hash) uint8 { // it. hints contains optional information, that is also encrypted, that aids // the recipients in processing the message. The resulting WriteCloser must // be closed after the contents of the file have been written. -func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints) (plaintext io.WriteCloser, err error) { +// If config is nil, sensible defaults will be used. +func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { var signer *packet.PrivateKey if signed != nil { signer = signed.signingKey().PrivateKey @@ -205,19 +209,39 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint } cipher := packet.CipherFunction(candidateCiphers[0]) - hash, _ := s2k.HashIdToHash(candidateHashes[0]) + // If the cipher specifed by config is a candidate, we'll use that. + configuredCipher := config.Cipher() + for _, c := range candidateCiphers { + cipherFunc := packet.CipherFunction(c) + if cipherFunc == configuredCipher { + cipher = cipherFunc + break + } + } + + hashFunc := candidateHashes[0] + // If the hash specified by config is a candidate, we'll use that. + configuredHash := config.Hash() + for _, h := range candidateHashes { + if h == uint8(configuredHash) { + hashFunc = h + break + } + } + hash, _ := s2k.HashIdToHash(hashFunc) + symKey := make([]byte, cipher.KeySize()) - if _, err := io.ReadFull(rand.Reader, symKey); err != nil { + if _, err := io.ReadFull(config.Random(), symKey); err != nil { return nil, err } for _, key := range encryptKeys { - if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil { + if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil { return nil, err } } - encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, cipher, symKey) + encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config) if err != nil { return } @@ -257,7 +281,7 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint } if signer != nil { - return signatureWriter{encryptedData, literalData, hash, hash.New(), signer}, nil + return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil } return literalData, nil } @@ -271,6 +295,7 @@ type signatureWriter struct { hashType crypto.Hash h hash.Hash signer *packet.PrivateKey + config *packet.Config } func (s signatureWriter) Write(data []byte) (int, error) { @@ -283,11 +308,11 @@ func (s signatureWriter) Close() error { SigType: packet.SigTypeBinary, PubKeyAlgo: s.signer.PubKeyAlgo, Hash: s.hashType, - CreationTime: time.Now(), + CreationTime: s.config.Now(), IssuerKeyId: &s.signer.KeyId, } - if err := sig.Sign(rand.Reader, s.h, s.signer); err != nil { + if err := sig.Sign(s.h, s.signer, s.config); err != nil { return err } if err := s.literalData.Close(); err != nil { diff --git a/openpgp/write_test.go b/openpgp/write_test.go index 7df02e7..3d0be93 100644 --- a/openpgp/write_test.go +++ b/openpgp/write_test.go @@ -6,18 +6,16 @@ package openpgp import ( "bytes" - "crypto/rand" "io" "io/ioutil" "testing" - "time" ) func TestSignDetached(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) out := bytes.NewBuffer(nil) message := bytes.NewBufferString(signedInput) - err := DetachSign(out, kring[0], message) + err := DetachSign(out, kring[0], message, nil) if err != nil { t.Error(err) } @@ -29,7 +27,7 @@ func TestSignTextDetached(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) out := bytes.NewBuffer(nil) message := bytes.NewBufferString(signedInput) - err := DetachSignText(out, kring[0], message) + err := DetachSignText(out, kring[0], message, nil) if err != nil { t.Error(err) } @@ -41,7 +39,7 @@ func TestSignDetachedDSA(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyPrivateHex)) out := bytes.NewBuffer(nil) message := bytes.NewBufferString(signedInput) - err := DetachSign(out, kring[0], message) + err := DetachSign(out, kring[0], message, nil) if err != nil { t.Error(err) } @@ -54,14 +52,14 @@ func TestNewEntity(t *testing.T) { return } - e, err := NewEntity(rand.Reader, time.Now(), "Test User", "test", "test@example.com") + e, err := NewEntity("Test User", "test", "test@example.com", nil) if err != nil { t.Errorf("failed to create entity: %s", err) return } w := bytes.NewBuffer(nil) - if err := e.SerializePrivate(w); err != nil { + if err := e.SerializePrivate(w, nil); err != nil { t.Errorf("failed to serialize entity: %s", err) return } @@ -78,7 +76,7 @@ func TestNewEntity(t *testing.T) { } w = bytes.NewBuffer(nil) - if err := e.SerializePrivate(w); err != nil { + if err := e.SerializePrivate(w, nil); err != nil { t.Errorf("failed to serialize entity second time: %s", err) return } @@ -90,7 +88,7 @@ func TestNewEntity(t *testing.T) { func TestSymmetricEncryption(t *testing.T) { buf := new(bytes.Buffer) - plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil) + plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil, nil) if err != nil { t.Errorf("error writing headers: %s", err) return @@ -107,7 +105,7 @@ func TestSymmetricEncryption(t *testing.T) { md, err := ReadMessage(buf, nil, func(keys []Key, symmetric bool) ([]byte, error) { return []byte("testing"), nil - }) + }, nil) if err != nil { t.Errorf("error rereading message: %s", err) } @@ -171,7 +169,7 @@ func TestEncryption(t *testing.T) { } buf := new(bytes.Buffer) - w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */ ) + w, err := Encrypt(buf, kring[:1], signed, nil, /* no hints */ nil) if err != nil { t.Errorf("#%d: error in Encrypt: %s", i, err) continue @@ -189,7 +187,7 @@ func TestEncryption(t *testing.T) { continue } - md, err := ReadMessage(buf, kring, nil /* no prompt */ ) + md, err := ReadMessage(buf, kring, nil, /* no prompt */ nil) if err != nil { t.Errorf("#%d: error reading message: %s", i, err) continue