Bläddra i källkod

go.crypto/openpgp: Allow configuration of s2k hash during encryption.

This patch/CL allows the user to configure the hash function
used in the s2k key-stretching transformation during
encryption. It fixes the current limitation in which SHA1 is
hard-coded for that task.

With this change, packet.Config.DefaultHash is used by default
for the mode 3 s2k key-stretching that is currently used for
encryption, but the user may choose any other valid openpgp
hash by passing a new (possibly nil) pointer argument to
s2k.Serialize().

Note that this change introduces an API change to
s2k.Serialize() in the form of an additional
argument. However, the s2k package is a low-level facility
that (as far as I could tell) is used directly by
go.crypto/openpgp only.

The CL comes with an modified test to check the use and
decoding of all the valid hash functions with regard to
s2k.Serialize().

This patch should be followed by one that allows the user to
configure the iteration count in mode 3 s2k, and it is a
prerequisite for that forthcoming change.

R=golang-codereviews, agl
CC=agl, golang-codereviews
https://golang.org/cl/160110045
Brian Gitonga Marete 11 år sedan
förälder
incheckning
c24604da51
3 ändrade filer med 49 tillägg och 10 borttagningar
  1. 4 3
      openpgp/packet/symmetric_key_encrypted.go
  2. 28 6
      openpgp/s2k/s2k.go
  3. 17 1
      openpgp/s2k/s2k_test.go

+ 4 - 3
openpgp/packet/symmetric_key_encrypted.go

@@ -6,11 +6,12 @@ package packet
 
 import (
 	"bytes"
-	"code.google.com/p/go.crypto/openpgp/errors"
-	"code.google.com/p/go.crypto/openpgp/s2k"
 	"crypto/cipher"
 	"io"
 	"strconv"
+
+	"code.google.com/p/go.crypto/openpgp/errors"
+	"code.google.com/p/go.crypto/openpgp/s2k"
 )
 
 // This is the largest session key that we'll support. Since no 512-bit cipher
@@ -119,7 +120,7 @@ func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Conf
 	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, config.Random(), passphrase)
+	err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, &s2k.Config{Hash: config.Hash()})
 	if err != nil {
 		return
 	}

+ 28 - 6
openpgp/s2k/s2k.go

@@ -7,13 +7,33 @@
 package s2k
 
 import (
-	"code.google.com/p/go.crypto/openpgp/errors"
 	"crypto"
 	"hash"
 	"io"
 	"strconv"
+
+	"code.google.com/p/go.crypto/openpgp/errors"
 )
 
+// Config collects configuration parameters for s2k key-stretching
+// transformatioms. A nil *Config is valid and results in all default
+// values. Currently, Config is used only by the Serialize function in
+// this package.
+type Config struct {
+	// Hash is the default hash function to be used. If
+	// nil, SHA1 is used.
+	Hash crypto.Hash
+}
+
+func (c *Config) hash() crypto.Hash {
+	if c == nil || uint(c.Hash) == 0 {
+		// SHA1 is the historical default in this package.
+		return crypto.SHA1
+	}
+
+	return c.Hash
+}
+
 // Simple writes to out the result of computing the Simple S2K function (RFC
 // 4880, section 3.7.1.1) using the given hash and input passphrase.
 func Simple(out []byte, h hash.Hash, in []byte) {
@@ -126,12 +146,14 @@ func Parse(r io.Reader) (f func(out, in []byte), err error) {
 	return nil, errors.UnsupportedError("S2K function")
 }
 
-// Serialize salts and stretches the given passphrase and writes the resulting
-// key into key. It also serializes an S2K descriptor to w.
-func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte) error {
+// Serialize salts and stretches the given passphrase and writes the
+// resulting key into key. It also serializes an S2K descriptor to
+// w. The key stretching can be configured with c, which may be
+// nil. In that case, sensible defaults will be used.
+func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Config) error {
 	var buf [11]byte
 	buf[0] = 3 /* iterated and salted */
-	buf[1], _ = HashToHashId(crypto.SHA1)
+	buf[1], _ = HashToHashId(c.hash())
 	salt := buf[2:10]
 	if _, err := io.ReadFull(rand, salt); err != nil {
 		return err
@@ -142,7 +164,7 @@ func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte) error
 		return err
 	}
 
-	Iterated(key, crypto.SHA1.New(), passphrase, salt, count)
+	Iterated(key, c.hash().New(), passphrase, salt, count)
 	return nil
 }
 

+ 17 - 1
openpgp/s2k/s2k_test.go

@@ -6,10 +6,16 @@ package s2k
 
 import (
 	"bytes"
+	"crypto"
+	_ "crypto/md5"
 	"crypto/rand"
 	"crypto/sha1"
+	_ "crypto/sha256"
+	_ "crypto/sha512"
 	"encoding/hex"
 	"testing"
+
+	_ "code.google.com/p/go.crypto/ripemd160"
 )
 
 var saltedTests = []struct {
@@ -96,10 +102,20 @@ func TestParse(t *testing.T) {
 }
 
 func TestSerialize(t *testing.T) {
+	hashes := []crypto.Hash{crypto.MD5, crypto.SHA1, crypto.RIPEMD160,
+		crypto.SHA256, crypto.SHA384, crypto.SHA512, crypto.SHA224}
+	for _, h := range hashes {
+		testSerializeConfig(t, &Config{Hash: h})
+	}
+}
+
+func testSerializeConfig(t *testing.T, c *Config) {
+	t.Logf("Running testSerializeConfig() with config: %+v", c)
+
 	buf := bytes.NewBuffer(nil)
 	key := make([]byte, 16)
 	passphrase := []byte("testing")
-	err := Serialize(buf, key, rand.Reader, passphrase)
+	err := Serialize(buf, key, rand.Reader, passphrase, c)
 	if err != nil {
 		t.Errorf("failed to serialize: %s", err)
 		return