274 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			274 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|   | // Copyright 2011 The Go Authors. All rights reserved. | ||
|  | // Use of this source code is governed by a BSD-style | ||
|  | // license that can be found in the LICENSE file. | ||
|  | 
 | ||
|  | // Package s2k implements the various OpenPGP string-to-key transforms as | ||
|  | // specified in RFC 4800 section 3.7.1. | ||
|  | package s2k // import "golang.org/x/crypto/openpgp/s2k" | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"crypto" | ||
|  | 	"hash" | ||
|  | 	"io" | ||
|  | 	"strconv" | ||
|  | 
 | ||
|  | 	"golang.org/x/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 | ||
|  | 	// S2KCount is only used for symmetric encryption. It | ||
|  | 	// determines the strength of the passphrase stretching when | ||
|  | 	// the said passphrase is hashed to produce a key. S2KCount | ||
|  | 	// should be between 1024 and 65011712, inclusive. If Config | ||
|  | 	// is nil or S2KCount is 0, the value 65536 used. Not all | ||
|  | 	// values in the above range can be represented. S2KCount will | ||
|  | 	// be rounded up to the next representable value if it cannot | ||
|  | 	// be encoded exactly. When set, it is strongly encrouraged to | ||
|  | 	// use a value that is at least 65536. See RFC 4880 Section | ||
|  | 	// 3.7.1.3. | ||
|  | 	S2KCount int | ||
|  | } | ||
|  | 
 | ||
|  | 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 | ||
|  | } | ||
|  | 
 | ||
|  | func (c *Config) encodedCount() uint8 { | ||
|  | 	if c == nil || c.S2KCount == 0 { | ||
|  | 		return 96 // The common case. Correspoding to 65536 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	i := c.S2KCount | ||
|  | 	switch { | ||
|  | 	// Behave like GPG. Should we make 65536 the lowest value used? | ||
|  | 	case i < 1024: | ||
|  | 		i = 1024 | ||
|  | 	case i > 65011712: | ||
|  | 		i = 65011712 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return encodeCount(i) | ||
|  | } | ||
|  | 
 | ||
|  | // encodeCount converts an iterative "count" in the range 1024 to | ||
|  | // 65011712, inclusive, to an encoded count. The return value is the | ||
|  | // octet that is actually stored in the GPG file. encodeCount panics | ||
|  | // if i is not in the above range (encodedCount above takes care to | ||
|  | // pass i in the correct range). See RFC 4880 Section 3.7.7.1. | ||
|  | func encodeCount(i int) uint8 { | ||
|  | 	if i < 1024 || i > 65011712 { | ||
|  | 		panic("count arg i outside the required range") | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for encoded := 0; encoded < 256; encoded++ { | ||
|  | 		count := decodeCount(uint8(encoded)) | ||
|  | 		if count >= i { | ||
|  | 			return uint8(encoded) | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return 255 | ||
|  | } | ||
|  | 
 | ||
|  | // decodeCount returns the s2k mode 3 iterative "count" corresponding to | ||
|  | // the encoded octet c. | ||
|  | func decodeCount(c uint8) int { | ||
|  | 	return (16 + int(c&15)) << (uint32(c>>4) + 6) | ||
|  | } | ||
|  | 
 | ||
|  | // 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) { | ||
|  | 	Salted(out, h, in, nil) | ||
|  | } | ||
|  | 
 | ||
|  | var zero [1]byte | ||
|  | 
 | ||
|  | // Salted writes to out the result of computing the Salted S2K function (RFC | ||
|  | // 4880, section 3.7.1.2) using the given hash, input passphrase and salt. | ||
|  | func Salted(out []byte, h hash.Hash, in []byte, salt []byte) { | ||
|  | 	done := 0 | ||
|  | 	var digest []byte | ||
|  | 
 | ||
|  | 	for i := 0; done < len(out); i++ { | ||
|  | 		h.Reset() | ||
|  | 		for j := 0; j < i; j++ { | ||
|  | 			h.Write(zero[:]) | ||
|  | 		} | ||
|  | 		h.Write(salt) | ||
|  | 		h.Write(in) | ||
|  | 		digest = h.Sum(digest[:0]) | ||
|  | 		n := copy(out[done:], digest) | ||
|  | 		done += n | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Iterated writes to out the result of computing the Iterated and Salted S2K | ||
|  | // function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase, | ||
|  | // salt and iteration count. | ||
|  | func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) { | ||
|  | 	combined := make([]byte, len(in)+len(salt)) | ||
|  | 	copy(combined, salt) | ||
|  | 	copy(combined[len(salt):], in) | ||
|  | 
 | ||
|  | 	if count < len(combined) { | ||
|  | 		count = len(combined) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	done := 0 | ||
|  | 	var digest []byte | ||
|  | 	for i := 0; done < len(out); i++ { | ||
|  | 		h.Reset() | ||
|  | 		for j := 0; j < i; j++ { | ||
|  | 			h.Write(zero[:]) | ||
|  | 		} | ||
|  | 		written := 0 | ||
|  | 		for written < count { | ||
|  | 			if written+len(combined) > count { | ||
|  | 				todo := count - written | ||
|  | 				h.Write(combined[:todo]) | ||
|  | 				written = count | ||
|  | 			} else { | ||
|  | 				h.Write(combined) | ||
|  | 				written += len(combined) | ||
|  | 			} | ||
|  | 		} | ||
|  | 		digest = h.Sum(digest[:0]) | ||
|  | 		n := copy(out[done:], digest) | ||
|  | 		done += n | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Parse reads a binary specification for a string-to-key transformation from r | ||
|  | // and returns a function which performs that transform. | ||
|  | func Parse(r io.Reader) (f func(out, in []byte), err error) { | ||
|  | 	var buf [9]byte | ||
|  | 
 | ||
|  | 	_, err = io.ReadFull(r, buf[:2]) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	hash, ok := HashIdToHash(buf[1]) | ||
|  | 	if !ok { | ||
|  | 		return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) | ||
|  | 	} | ||
|  | 	if !hash.Available() { | ||
|  | 		return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hash))) | ||
|  | 	} | ||
|  | 	h := hash.New() | ||
|  | 
 | ||
|  | 	switch buf[0] { | ||
|  | 	case 0: | ||
|  | 		f := func(out, in []byte) { | ||
|  | 			Simple(out, h, in) | ||
|  | 		} | ||
|  | 		return f, nil | ||
|  | 	case 1: | ||
|  | 		_, err = io.ReadFull(r, buf[:8]) | ||
|  | 		if err != nil { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		f := func(out, in []byte) { | ||
|  | 			Salted(out, h, in, buf[:8]) | ||
|  | 		} | ||
|  | 		return f, nil | ||
|  | 	case 3: | ||
|  | 		_, err = io.ReadFull(r, buf[:9]) | ||
|  | 		if err != nil { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		count := decodeCount(buf[8]) | ||
|  | 		f := func(out, in []byte) { | ||
|  | 			Iterated(out, h, in, buf[:8], count) | ||
|  | 		} | ||
|  | 		return f, nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	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. 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(c.hash()) | ||
|  | 	salt := buf[2:10] | ||
|  | 	if _, err := io.ReadFull(rand, salt); err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 	encodedCount := c.encodedCount() | ||
|  | 	count := decodeCount(encodedCount) | ||
|  | 	buf[10] = encodedCount | ||
|  | 	if _, err := w.Write(buf[:]); err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	Iterated(key, c.hash().New(), passphrase, salt, count) | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // hashToHashIdMapping contains pairs relating OpenPGP's hash identifier with | ||
|  | // Go's crypto.Hash type. See RFC 4880, section 9.4. | ||
|  | var hashToHashIdMapping = []struct { | ||
|  | 	id   byte | ||
|  | 	hash crypto.Hash | ||
|  | 	name string | ||
|  | }{ | ||
|  | 	{1, crypto.MD5, "MD5"}, | ||
|  | 	{2, crypto.SHA1, "SHA1"}, | ||
|  | 	{3, crypto.RIPEMD160, "RIPEMD160"}, | ||
|  | 	{8, crypto.SHA256, "SHA256"}, | ||
|  | 	{9, crypto.SHA384, "SHA384"}, | ||
|  | 	{10, crypto.SHA512, "SHA512"}, | ||
|  | 	{11, crypto.SHA224, "SHA224"}, | ||
|  | } | ||
|  | 
 | ||
|  | // HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP | ||
|  | // hash id. | ||
|  | func HashIdToHash(id byte) (h crypto.Hash, ok bool) { | ||
|  | 	for _, m := range hashToHashIdMapping { | ||
|  | 		if m.id == id { | ||
|  | 			return m.hash, true | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return 0, false | ||
|  | } | ||
|  | 
 | ||
|  | // HashIdToString returns the name of the hash function corresponding to the | ||
|  | // given OpenPGP hash id. | ||
|  | func HashIdToString(id byte) (name string, ok bool) { | ||
|  | 	for _, m := range hashToHashIdMapping { | ||
|  | 		if m.id == id { | ||
|  | 			return m.name, true | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return "", false | ||
|  | } | ||
|  | 
 | ||
|  | // HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. | ||
|  | func HashToHashId(h crypto.Hash) (id byte, ok bool) { | ||
|  | 	for _, m := range hashToHashIdMapping { | ||
|  | 		if m.hash == h { | ||
|  | 			return m.id, true | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return 0, false | ||
|  | } |