Browse Source

openpgp: eliminate implicit rand.Reader and time.Now

Fixes golang/go#2501.

R=rsc, bradfitz
CC=golang-dev
https://golang.org/cl/5653067
Adam Langley 13 years ago
parent
commit
54eafe1dea

+ 6 - 9
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

+ 17 - 15
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)

+ 7 - 6
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)))
 	}

+ 3 - 4
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

+ 10 - 7
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.

+ 5 - 3
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
 	}

+ 6 - 5
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 {

+ 3 - 2
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
 	}

+ 1 - 2
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

+ 3 - 2
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

+ 4 - 4
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

+ 52 - 27
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 {

+ 10 - 12
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