diff options
Diffstat (limited to 'src/pkg/crypto')
44 files changed, 1788 insertions, 436 deletions
| diff --git a/src/pkg/crypto/cipher/ocfb.go b/src/pkg/crypto/cipher/ocfb.go index b2d877591..031e74a9d 100644 --- a/src/pkg/crypto/cipher/ocfb.go +++ b/src/pkg/crypto/cipher/ocfb.go @@ -80,9 +80,10 @@ type ocfbDecrypter struct {  // NewOCFBDecrypter returns a Stream which decrypts data with OpenPGP's cipher  // feedback mode using the given Block. Prefix must be the first blockSize + 2  // bytes of the ciphertext, where blockSize is the Block's block size. If an -// incorrect key is detected then nil is returned. Resync determines if the -// "resynchronization step" from RFC 4880, 13.9 step 7 is performed. Different -// parts of OpenPGP vary on this point. +// incorrect key is detected then nil is returned. On successful exit, +// blockSize+2 bytes of decrypted data are written into prefix. Resync +// determines if the "resynchronization step" from RFC 4880, 13.9 step 7 is +// performed. Different parts of OpenPGP vary on this point.  func NewOCFBDecrypter(block Block, prefix []byte, resync OCFBResyncOption) Stream {  	blockSize := block.BlockSize()  	if len(prefix) != blockSize+2 { @@ -118,6 +119,7 @@ func NewOCFBDecrypter(block Block, prefix []byte, resync OCFBResyncOption) Strea  		x.fre[1] = prefix[blockSize+1]  		x.outUsed = 2  	} +	copy(prefix, prefixCopy)  	return x  } diff --git a/src/pkg/crypto/elliptic/elliptic.go b/src/pkg/crypto/elliptic/elliptic.go index 335c9645d..41835f1a9 100644 --- a/src/pkg/crypto/elliptic/elliptic.go +++ b/src/pkg/crypto/elliptic/elliptic.go @@ -284,7 +284,7 @@ func (curve *Curve) Marshal(x, y *big.Int) []byte {  	return ret  } -// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On +// Unmarshal converts a point, serialized by Marshal, into an x, y pair. On  // error, x = nil.  func (curve *Curve) Unmarshal(data []byte) (x, y *big.Int) {  	byteLen := (curve.BitSize + 7) >> 3 diff --git a/src/pkg/crypto/elliptic/elliptic_test.go b/src/pkg/crypto/elliptic/elliptic_test.go index 02083a986..b7e7f035f 100644 --- a/src/pkg/crypto/elliptic/elliptic_test.go +++ b/src/pkg/crypto/elliptic/elliptic_test.go @@ -321,8 +321,8 @@ func TestMarshal(t *testing.T) {  		t.Error(err)  		return  	} -	serialised := p224.Marshal(x, y) -	xx, yy := p224.Unmarshal(serialised) +	serialized := p224.Marshal(x, y) +	xx, yy := p224.Unmarshal(serialized)  	if xx == nil {  		t.Error("failed to unmarshal")  		return diff --git a/src/pkg/crypto/hmac/hmac_test.go b/src/pkg/crypto/hmac/hmac_test.go index 40adbad04..bcae63b8a 100644 --- a/src/pkg/crypto/hmac/hmac_test.go +++ b/src/pkg/crypto/hmac/hmac_test.go @@ -190,7 +190,7 @@ func TestHMAC(t *testing.T) {  				continue  			} -			// Repetive Sum() calls should return the same value +			// Repetitive Sum() calls should return the same value  			for k := 0; k < 2; k++ {  				sum := fmt.Sprintf("%x", h.Sum())  				if sum != tt.out { diff --git a/src/pkg/crypto/ocsp/ocsp.go b/src/pkg/crypto/ocsp/ocsp.go index acd75b8b0..57dbe7d2d 100644 --- a/src/pkg/crypto/ocsp/ocsp.go +++ b/src/pkg/crypto/ocsp/ocsp.go @@ -13,6 +13,7 @@ import (  	"crypto/rsa"  	_ "crypto/sha1"  	"crypto/x509" +	"crypto/x509/pkix"  	"os"  	"time"  ) @@ -32,21 +33,9 @@ const (  	ocspUnauthorized  = 5  ) -type rdnSequence []relativeDistinguishedNameSET - -type relativeDistinguishedNameSET []attributeTypeAndValue - -type attributeTypeAndValue struct { -	Type  asn1.ObjectIdentifier -	Value interface{} -} - -type algorithmIdentifier struct { -	Algorithm asn1.ObjectIdentifier -}  type certID struct { -	HashAlgorithm algorithmIdentifier +	HashAlgorithm pkix.AlgorithmIdentifier  	NameHash      []byte  	IssuerKeyHash []byte  	SerialNumber  asn1.RawValue @@ -64,16 +53,16 @@ type responseBytes struct {  type basicResponse struct {  	TBSResponseData    responseData -	SignatureAlgorithm algorithmIdentifier +	SignatureAlgorithm pkix.AlgorithmIdentifier  	Signature          asn1.BitString  	Certificates       []asn1.RawValue "explicit,tag:0,optional"  }  type responseData struct {  	Raw           asn1.RawContent -	Version       int         "optional,default:1,explicit,tag:0" -	RequestorName rdnSequence "optional,explicit,tag:1" -	KeyHash       []byte      "optional,explicit,tag:2" +	Version       int              "optional,default:1,explicit,tag:0" +	RequestorName pkix.RDNSequence "optional,explicit,tag:1" +	KeyHash       []byte           "optional,explicit,tag:2"  	ProducedAt    *time.Time  	Responses     []singleResponse  } diff --git a/src/pkg/crypto/openpgp/armor/armor.go b/src/pkg/crypto/openpgp/armor/armor.go index 8da612c50..9c4180d6d 100644 --- a/src/pkg/crypto/openpgp/armor/armor.go +++ b/src/pkg/crypto/openpgp/armor/armor.go @@ -153,7 +153,7 @@ func (r *openpgpReader) Read(p []byte) (n int, err os.Error) {  // Decode reads a PGP armored block from the given Reader. It will ignore  // leading garbage. If it doesn't find a block, it will return nil, os.EOF. The -// given Reader is not usable after calling this function: an arbitary amount +// given Reader is not usable after calling this function: an arbitrary amount  // of data may have been read past the end of the block.  func Decode(in io.Reader) (p *Block, err os.Error) {  	r, _ := bufio.NewReaderSize(in, 100) diff --git a/src/pkg/crypto/openpgp/keys.go b/src/pkg/crypto/openpgp/keys.go index 6c03f8828..2acb7e612 100644 --- a/src/pkg/crypto/openpgp/keys.go +++ b/src/pkg/crypto/openpgp/keys.go @@ -5,9 +5,11 @@  package openpgp  import ( +	"crypto"  	"crypto/openpgp/armor"  	"crypto/openpgp/error"  	"crypto/openpgp/packet" +	"crypto/rsa"  	"io"  	"os"  ) @@ -297,3 +299,104 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p  	e.Subkeys = append(e.Subkeys, subKey)  	return nil  } + +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, currentTimeSecs int64, name, comment, email string) (*Entity, os.Error) { +	uid := packet.NewUserId(name, comment, email) +	if uid == nil { +		return nil, error.InvalidArgumentError("user id field contained invalid characters") +	} +	signingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) +	if err != nil { +		return nil, err +	} +	encryptingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) +	if err != nil { +		return nil, err +	} + +	t := uint32(currentTimeSecs) + +	e := &Entity{ +		PrimaryKey: packet.NewRSAPublicKey(t, &signingPriv.PublicKey, false /* not a subkey */ ), +		PrivateKey: packet.NewRSAPrivateKey(t, signingPriv, false /* not a subkey */ ), +		Identities: make(map[string]*Identity), +	} +	isPrimaryId := true +	e.Identities[uid.Id] = &Identity{ +		Name:   uid.Name, +		UserId: uid, +		SelfSignature: &packet.Signature{ +			CreationTime: t, +			SigType:      packet.SigTypePositiveCert, +			PubKeyAlgo:   packet.PubKeyAlgoRSA, +			Hash:         crypto.SHA256, +			IsPrimaryId:  &isPrimaryId, +			FlagsValid:   true, +			FlagSign:     true, +			FlagCertify:  true, +			IssuerKeyId:  &e.PrimaryKey.KeyId, +		}, +	} + +	e.Subkeys = make([]Subkey, 1) +	e.Subkeys[0] = Subkey{ +		PublicKey:  packet.NewRSAPublicKey(t, &encryptingPriv.PublicKey, true /* is a subkey */ ), +		PrivateKey: packet.NewRSAPrivateKey(t, encryptingPriv, true /* is a subkey */ ), +		Sig: &packet.Signature{ +			CreationTime:              t, +			SigType:                   packet.SigTypeSubkeyBinding, +			PubKeyAlgo:                packet.PubKeyAlgoRSA, +			Hash:                      crypto.SHA256, +			FlagsValid:                true, +			FlagEncryptStorage:        true, +			FlagEncryptCommunications: true, +			IssuerKeyId:               &e.PrimaryKey.KeyId, +		}, +	} + +	return e, nil +} + +// 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 os.Error) { +	err = e.PrivateKey.Serialize(w) +	if err != nil { +		return +	} +	for _, ident := range e.Identities { +		err = ident.UserId.Serialize(w) +		if err != nil { +			return +		} +		err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey) +		if err != nil { +			return +		} +		err = ident.SelfSignature.Serialize(w) +		if err != nil { +			return +		} +	} +	for _, subkey := range e.Subkeys { +		err = subkey.PrivateKey.Serialize(w) +		if err != nil { +			return +		} +		err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey) +		if err != nil { +			return +		} +		err = subkey.Sig.Serialize(w) +		if err != nil { +			return +		} +	} +	return nil +} diff --git a/src/pkg/crypto/openpgp/packet/literal.go b/src/pkg/crypto/openpgp/packet/literal.go index 04f50e53e..9411572d7 100644 --- a/src/pkg/crypto/openpgp/packet/literal.go +++ b/src/pkg/crypto/openpgp/packet/literal.go @@ -51,3 +51,40 @@ func (l *LiteralData) parse(r io.Reader) (err os.Error) {  	l.Body = r  	return  } + +// SerializeLiteral serializes a literal data packet to w and returns a +// WriteCloser to which the data itself can be written and which MUST be closed +// on completion. The fileName is truncated to 255 bytes. +func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err os.Error) { +	var buf [4]byte +	buf[0] = 't' +	if isBinary { +		buf[0] = 'b' +	} +	if len(fileName) > 255 { +		fileName = fileName[:255] +	} +	buf[1] = byte(len(fileName)) + +	inner, err := serializeStreamHeader(w, packetTypeLiteralData) +	if err != nil { +		return +	} + +	_, err = inner.Write(buf[:2]) +	if err != nil { +		return +	} +	_, err = inner.Write([]byte(fileName)) +	if err != nil { +		return +	} +	binary.BigEndian.PutUint32(buf[:], time) +	_, err = inner.Write(buf[:]) +	if err != nil { +		return +	} + +	plaintext = inner +	return +} diff --git a/src/pkg/crypto/openpgp/packet/packet.go b/src/pkg/crypto/openpgp/packet/packet.go index c0ec44dd8..640a5b76f 100644 --- a/src/pkg/crypto/openpgp/packet/packet.go +++ b/src/pkg/crypto/openpgp/packet/packet.go @@ -2,7 +2,7 @@  // Use of this source code is governed by a BSD-style  // license that can be found in the LICENSE file. -// Package packet implements parsing and serialisation of OpenPGP packets, as +// Package packet implements parsing and serialization of OpenPGP packets, as  // specified in RFC 4880.  package packet @@ -92,6 +92,46 @@ func (r *partialLengthReader) Read(p []byte) (n int, err os.Error) {  	return  } +// partialLengthWriter writes a stream of data using OpenPGP partial lengths. +// See RFC 4880, section 4.2.2.4. +type partialLengthWriter struct { +	w          io.WriteCloser +	lengthByte [1]byte +} + +func (w *partialLengthWriter) Write(p []byte) (n int, err os.Error) { +	for len(p) > 0 { +		for power := uint(14); power < 32; power-- { +			l := 1 << power +			if len(p) >= l { +				w.lengthByte[0] = 224 + uint8(power) +				_, err = w.w.Write(w.lengthByte[:]) +				if err != nil { +					return +				} +				var m int +				m, err = w.w.Write(p[:l]) +				n += m +				if err != nil { +					return +				} +				p = p[l:] +				break +			} +		} +	} +	return +} + +func (w *partialLengthWriter) Close() os.Error { +	w.lengthByte[0] = 0 +	_, err := w.w.Write(w.lengthByte[:]) +	if err != nil { +		return err +	} +	return w.w.Close() +} +  // A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the  // underlying Reader returns EOF before the limit has been reached.  type spanReader struct { @@ -195,6 +235,20 @@ func serializeHeader(w io.Writer, ptype packetType, length int) (err os.Error) {  	return  } +// serializeStreamHeader writes an OpenPGP packet header to w where the +// length of the packet is unknown. It returns a io.WriteCloser which can be +// used to write the contents of the packet. See RFC 4880, section 4.2. +func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err os.Error) { +	var buf [1]byte +	buf[0] = 0x80 | 0x40 | byte(ptype) +	_, err = w.Write(buf[:]) +	if err != nil { +		return +	} +	out = &partialLengthWriter{w: w} +	return +} +  // Packet represents an OpenPGP packet. Users are expected to try casting  // instances of this interface to specific packet types.  type Packet interface { @@ -301,12 +355,12 @@ type SignatureType uint8  const (  	SigTypeBinary        SignatureType = 0 -	SigTypeText          = 1 -	SigTypeGenericCert   = 0x10 -	SigTypePersonaCert   = 0x11 -	SigTypeCasualCert    = 0x12 -	SigTypePositiveCert  = 0x13 -	SigTypeSubkeyBinding = 0x18 +	SigTypeText                        = 1 +	SigTypeGenericCert                 = 0x10 +	SigTypePersonaCert                 = 0x11 +	SigTypeCasualCert                  = 0x12 +	SigTypePositiveCert                = 0x13 +	SigTypeSubkeyBinding               = 0x18  )  // PublicKeyAlgorithm represents the different public key system specified for @@ -327,10 +381,10 @@ const (  type CipherFunction uint8  const ( -	CipherCAST5  = 3 -	CipherAES128 = 7 -	CipherAES192 = 8 -	CipherAES256 = 9 +	CipherCAST5  CipherFunction = 3 +	CipherAES128 CipherFunction = 7 +	CipherAES192 CipherFunction = 8 +	CipherAES256 CipherFunction = 9  )  // keySize returns the key size, in bytes, of cipher. @@ -386,6 +440,14 @@ func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err os.Error) {  	return  } +// mpiLength returns the length of the given *big.Int when serialized as an +// MPI. +func mpiLength(n *big.Int) (mpiLengthInBytes int) { +	mpiLengthInBytes = 2 /* MPI length */ +	mpiLengthInBytes += (n.BitLen() + 7) / 8 +	return +} +  // writeMPI serializes a big integer to w.  func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) {  	_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)}) diff --git a/src/pkg/crypto/openpgp/packet/packet_test.go b/src/pkg/crypto/openpgp/packet/packet_test.go index 1a4692cd4..23d9978ae 100644 --- a/src/pkg/crypto/openpgp/packet/packet_test.go +++ b/src/pkg/crypto/openpgp/packet/packet_test.go @@ -210,3 +210,47 @@ func TestSerializeHeader(t *testing.T) {  		}  	}  } + +func TestPartialLengths(t *testing.T) { +	buf := bytes.NewBuffer(nil) +	w := new(partialLengthWriter) +	w.w = noOpCloser{buf} + +	const maxChunkSize = 64 + +	var b [maxChunkSize]byte +	var n uint8 +	for l := 1; l <= maxChunkSize; l++ { +		for i := 0; i < l; i++ { +			b[i] = n +			n++ +		} +		m, err := w.Write(b[:l]) +		if m != l { +			t.Errorf("short write got: %d want: %d", m, l) +		} +		if err != nil { +			t.Errorf("error from write: %s", err) +		} +	} +	w.Close() + +	want := (maxChunkSize * (maxChunkSize + 1)) / 2 +	copyBuf := bytes.NewBuffer(nil) +	r := &partialLengthReader{buf, 0, true} +	m, err := io.Copy(copyBuf, r) +	if m != int64(want) { +		t.Errorf("short copy got: %d want: %d", m, want) +	} +	if err != nil { +		t.Errorf("error from copy: %s", err) +	} + +	copyBytes := copyBuf.Bytes() +	for i := 0; i < want; i++ { +		if copyBytes[i] != uint8(i) { +			t.Errorf("bad pattern in copy at %d", i) +			break +		} +	} +} diff --git a/src/pkg/crypto/openpgp/packet/private_key.go b/src/pkg/crypto/openpgp/packet/private_key.go index fde2a9933..92e7ee422 100644 --- a/src/pkg/crypto/openpgp/packet/private_key.go +++ b/src/pkg/crypto/openpgp/packet/private_key.go @@ -32,6 +32,13 @@ type PrivateKey struct {  	iv            []byte  } +func NewRSAPrivateKey(currentTimeSecs uint32, priv *rsa.PrivateKey, isSubkey bool) *PrivateKey { +	pk := new(PrivateKey) +	pk.PublicKey = *NewRSAPublicKey(currentTimeSecs, &priv.PublicKey, isSubkey) +	pk.PrivateKey = priv +	return pk +} +  func (pk *PrivateKey) parse(r io.Reader) (err os.Error) {  	err = (&pk.PublicKey).parse(r)  	if err != nil { @@ -91,6 +98,83 @@ func (pk *PrivateKey) parse(r io.Reader) (err os.Error) {  	return  } +func mod64kHash(d []byte) uint16 { +	h := uint16(0) +	for i := 0; i < len(d); i += 2 { +		v := uint16(d[i]) << 8 +		if i+1 < len(d) { +			v += uint16(d[i+1]) +		} +		h += v +	} +	return h +} + +func (pk *PrivateKey) Serialize(w io.Writer) (err os.Error) { +	// TODO(agl): support encrypted private keys +	buf := bytes.NewBuffer(nil) +	err = pk.PublicKey.serializeWithoutHeaders(buf) +	if err != nil { +		return +	} +	buf.WriteByte(0 /* no encryption */ ) + +	privateKeyBuf := bytes.NewBuffer(nil) + +	switch priv := pk.PrivateKey.(type) { +	case *rsa.PrivateKey: +		err = serializeRSAPrivateKey(privateKeyBuf, priv) +	default: +		err = error.InvalidArgumentError("non-RSA private key") +	} +	if err != nil { +		return +	} + +	ptype := packetTypePrivateKey +	contents := buf.Bytes() +	privateKeyBytes := privateKeyBuf.Bytes() +	if pk.IsSubkey { +		ptype = packetTypePrivateSubkey +	} +	err = serializeHeader(w, ptype, len(contents)+len(privateKeyBytes)+2) +	if err != nil { +		return +	} +	_, err = w.Write(contents) +	if err != nil { +		return +	} +	_, err = w.Write(privateKeyBytes) +	if err != nil { +		return +	} + +	checksum := mod64kHash(privateKeyBytes) +	var checksumBytes [2]byte +	checksumBytes[0] = byte(checksum >> 8) +	checksumBytes[1] = byte(checksum) +	_, err = w.Write(checksumBytes[:]) + +	return +} + +func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) os.Error { +	err := writeBig(w, priv.D) +	if err != nil { +		return err +	} +	err = writeBig(w, priv.Primes[1]) +	if err != nil { +		return err +	} +	err = writeBig(w, priv.Primes[0]) +	if err != nil { +		return err +	} +	return writeBig(w, priv.Precomputed.Qinv) +} +  // Decrypt decrypts an encrypted private key using a passphrase.  func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error {  	if !pk.Encrypted { diff --git a/src/pkg/crypto/openpgp/packet/public_key.go b/src/pkg/crypto/openpgp/packet/public_key.go index cd4a9aebb..46d365b2a 100644 --- a/src/pkg/crypto/openpgp/packet/public_key.go +++ b/src/pkg/crypto/openpgp/packet/public_key.go @@ -30,6 +30,28 @@ type PublicKey struct {  	n, e, p, q, g, y parsedMPI  } +func fromBig(n *big.Int) parsedMPI { +	return parsedMPI{ +		bytes:     n.Bytes(), +		bitLength: uint16(n.BitLen()), +	} +} + +// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey. +func NewRSAPublicKey(creationTimeSecs uint32, pub *rsa.PublicKey, isSubkey bool) *PublicKey { +	pk := &PublicKey{ +		CreationTime: creationTimeSecs, +		PubKeyAlgo:   PubKeyAlgoRSA, +		PublicKey:    pub, +		IsSubkey:     isSubkey, +		n:            fromBig(pub.N), +		e:            fromBig(big.NewInt(int64(pub.E))), +	} + +	pk.setFingerPrintAndKeyId() +	return pk +} +  func (pk *PublicKey) parse(r io.Reader) (err os.Error) {  	// RFC 4880, section 5.5.2  	var buf [6]byte @@ -54,14 +76,17 @@ func (pk *PublicKey) parse(r io.Reader) (err os.Error) {  		return  	} +	pk.setFingerPrintAndKeyId() +	return +} + +func (pk *PublicKey) setFingerPrintAndKeyId() {  	// RFC 4880, section 12.2  	fingerPrint := sha1.New()  	pk.SerializeSignaturePrefix(fingerPrint) -	pk.Serialize(fingerPrint) +	pk.serializeWithoutHeaders(fingerPrint)  	copy(pk.Fingerprint[:], fingerPrint.Sum())  	pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) - -	return  }  // parseRSA parses RSA public key material from the given Reader. See RFC 4880, @@ -143,9 +168,30 @@ func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) {  	return  } -// Serialize marshals the PublicKey to w in the form of an OpenPGP public key -// packet, not including the packet header.  func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) { +	length := 6 // 6 byte header + +	switch pk.PubKeyAlgo { +	case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: +		length += 2 + len(pk.n.bytes) +		length += 2 + len(pk.e.bytes) +	case PubKeyAlgoDSA: +		length += 2 + len(pk.p.bytes) +		length += 2 + len(pk.q.bytes) +		length += 2 + len(pk.g.bytes) +		length += 2 + len(pk.y.bytes) +	} + +	err = serializeHeader(w, packetTypePublicKey, length) +	if err != nil { +		return +	} +	return pk.serializeWithoutHeaders(w) +} + +// serializeWithoutHeaders marshals the PublicKey to w in the form of an +// OpenPGP public key packet, not including the packet header. +func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err os.Error) {  	var buf [6]byte  	buf[0] = 4  	buf[1] = byte(pk.CreationTime >> 24) @@ -211,34 +257,43 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E  	panic("unreachable")  } -// VerifyKeySignature returns nil iff sig is a valid signature, make by this -// public key, of the public key in signed. -func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err os.Error) { -	h := sig.Hash.New() +// keySignatureHash returns a Hash of the message that needs to be signed for +// pk to assert a subkey relationship to signed. +func keySignatureHash(pk, signed *PublicKey, sig *Signature) (h hash.Hash, err os.Error) { +	h = sig.Hash.New()  	if h == nil { -		return error.UnsupportedError("hash function") +		return nil, error.UnsupportedError("hash function")  	}  	// RFC 4880, section 5.2.4  	pk.SerializeSignaturePrefix(h) -	pk.Serialize(h) +	pk.serializeWithoutHeaders(h)  	signed.SerializeSignaturePrefix(h) -	signed.Serialize(h) +	signed.serializeWithoutHeaders(h) +	return +} +// VerifyKeySignature returns nil iff sig is a valid signature, made by this +// public key, of signed. +func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err os.Error) { +	h, err := keySignatureHash(pk, signed, sig) +	if err != nil { +		return err +	}  	return pk.VerifySignature(h, sig)  } -// VerifyUserIdSignature returns nil iff sig is a valid signature, make by this -// public key, of the given user id. -func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err os.Error) { -	h := sig.Hash.New() +// userIdSignatureHash returns a Hash of the message that needs to be signed +// to assert that pk is a valid key for id. +func userIdSignatureHash(id string, pk *PublicKey, sig *Signature) (h hash.Hash, err os.Error) { +	h = sig.Hash.New()  	if h == nil { -		return error.UnsupportedError("hash function") +		return nil, error.UnsupportedError("hash function")  	}  	// RFC 4880, section 5.2.4  	pk.SerializeSignaturePrefix(h) -	pk.Serialize(h) +	pk.serializeWithoutHeaders(h)  	var buf [5]byte  	buf[0] = 0xb4 @@ -249,6 +304,16 @@ func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err os.Er  	h.Write(buf[:])  	h.Write([]byte(id)) +	return +} + +// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this +// public key, of id. +func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err os.Error) { +	h, err := userIdSignatureHash(id, pk, sig) +	if err != nil { +		return err +	}  	return pk.VerifySignature(h, sig)  } @@ -272,7 +337,7 @@ type parsedMPI struct {  	bitLength uint16  } -// writeMPIs is a utility function for serialising several big integers to the +// writeMPIs is a utility function for serializing several big integers to the  // given Writer.  func writeMPIs(w io.Writer, mpis ...parsedMPI) (err os.Error) {  	for _, mpi := range mpis { diff --git a/src/pkg/crypto/openpgp/packet/public_key_test.go b/src/pkg/crypto/openpgp/packet/public_key_test.go index 069388c14..6e8bfbce6 100644 --- a/src/pkg/crypto/openpgp/packet/public_key_test.go +++ b/src/pkg/crypto/openpgp/packet/public_key_test.go @@ -28,12 +28,12 @@ func TestPublicKeyRead(t *testing.T) {  		packet, err := Read(readerFromHex(test.hexData))  		if err != nil {  			t.Errorf("#%d: Read error: %s", i, err) -			return +			continue  		}  		pk, ok := packet.(*PublicKey)  		if !ok {  			t.Errorf("#%d: failed to parse, got: %#v", i, packet) -			return +			continue  		}  		if pk.PubKeyAlgo != test.pubKeyAlgo {  			t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo) @@ -57,6 +57,38 @@ func TestPublicKeyRead(t *testing.T) {  	}  } +func TestPublicKeySerialize(t *testing.T) { +	for i, test := range pubKeyTests { +		packet, err := Read(readerFromHex(test.hexData)) +		if err != nil { +			t.Errorf("#%d: Read error: %s", i, err) +			continue +		} +		pk, ok := packet.(*PublicKey) +		if !ok { +			t.Errorf("#%d: failed to parse, got: %#v", i, packet) +			continue +		} +		serializeBuf := bytes.NewBuffer(nil) +		err = pk.Serialize(serializeBuf) +		if err != nil { +			t.Errorf("#%d: failed to serialize: %s", i, err) +			continue +		} + +		packet, err = Read(serializeBuf) +		if err != nil { +			t.Errorf("#%d: Read error (from serialized data): %s", i, err) +			continue +		} +		pk, ok = packet.(*PublicKey) +		if !ok { +			t.Errorf("#%d: failed to parse serialized data, got: %#v", i, packet) +			continue +		} +	} +} +  const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb"  const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" diff --git a/src/pkg/crypto/openpgp/packet/signature.go b/src/pkg/crypto/openpgp/packet/signature.go index 719657e76..3169bac1e 100644 --- a/src/pkg/crypto/openpgp/packet/signature.go +++ b/src/pkg/crypto/openpgp/packet/signature.go @@ -393,7 +393,7 @@ func (sig *Signature) buildHashSuffix() (err os.Error) {  	sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash)  	if !ok {  		sig.HashSuffix = nil -		return error.InvalidArgumentError("hash cannot be repesented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) +		return error.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash)))  	}  	sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8)  	sig.HashSuffix[5] = byte(hashedSubpacketsLen) @@ -420,28 +420,46 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error)  	return  } -// SignRSA signs a message with an RSA private key. The hash, h, must contain +// 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) SignRSA(h hash.Hash, priv *rsa.PrivateKey) (err os.Error) { +func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) {  	digest, err := sig.signPrepareHash(h)  	if err != nil {  		return  	} -	sig.RSASignature, err = rsa.SignPKCS1v15(rand.Reader, priv, sig.Hash, digest) + +	switch priv.PubKeyAlgo { +	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: +		sig.RSASignature, err = rsa.SignPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) +	case PubKeyAlgoDSA: +		sig.DSASigR, sig.DSASigS, err = dsa.Sign(rand.Reader, priv.PrivateKey.(*dsa.PrivateKey), digest) +	default: +		err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) +	} +  	return  } -// SignDSA signs a message with a DSA 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) SignDSA(h hash.Hash, priv *dsa.PrivateKey) (err os.Error) { -	digest, err := sig.signPrepareHash(h) +// 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(id string, pub *PublicKey, priv *PrivateKey) os.Error { +	h, err := userIdSignatureHash(id, pub, sig)  	if err != nil { -		return +		return nil  	} -	sig.DSASigR, sig.DSASigS, err = dsa.Sign(rand.Reader, priv, digest) -	return +	return sig.Sign(h, priv) +} + +// 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(pub *PublicKey, priv *PrivateKey) os.Error { +	h, err := keySignatureHash(&priv.PublicKey, pub, sig) +	if err != nil { +		return err +	} +	return sig.Sign(h, priv)  }  // Serialize marshals sig to w. SignRSA or SignDSA must have been called first. @@ -455,10 +473,8 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {  	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:  		sigLength = len(sig.RSASignature)  	case PubKeyAlgoDSA: -		sigLength = 2 /* MPI length */ -		sigLength += (sig.DSASigR.BitLen() + 7) / 8 -		sigLength += 2 /* MPI length */ -		sigLength += (sig.DSASigS.BitLen() + 7) / 8 +		sigLength = mpiLength(sig.DSASigR) +		sigLength += mpiLength(sig.DSASigS)  	default:  		panic("impossible")  	} diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go index d9010f88a..25d264acf 100644 --- a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go +++ b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go @@ -5,6 +5,7 @@  package packet  import ( +	"bytes"  	"crypto/cipher"  	"crypto/openpgp/error"  	"crypto/openpgp/s2k" @@ -27,6 +28,8 @@ type SymmetricKeyEncrypted struct {  	encryptedKey []byte  } +const symmetricKeyEncryptedVersion = 4 +  func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err os.Error) {  	// RFC 4880, section 5.3.  	var buf [2]byte @@ -34,7 +37,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err os.Error) {  	if err != nil {  		return  	} -	if buf[0] != 4 { +	if buf[0] != symmetricKeyEncryptedVersion {  		return error.UnsupportedError("SymmetricKeyEncrypted version")  	}  	ske.CipherFunc = CipherFunction(buf[1]) @@ -100,3 +103,60 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error {  	ske.Encrypted = false  	return nil  } + +// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. The +// 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 os.Error) { +	keySize := cipherFunc.keySize() +	if keySize == 0 { +		return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) +	} + +	s2kBuf := new(bytes.Buffer) +	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) +	if err != nil { +		return +	} +	s2kBytes := s2kBuf.Bytes() + +	packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize +	err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) +	if err != nil { +		return +	} + +	var buf [2]byte +	buf[0] = symmetricKeyEncryptedVersion +	buf[1] = byte(cipherFunc) +	_, err = w.Write(buf[:]) +	if err != nil { +		return +	} +	_, err = w.Write(s2kBytes) +	if err != nil { +		return +	} + +	sessionKey := make([]byte, keySize) +	_, err = io.ReadFull(rand, sessionKey) +	if err != nil { +		return +	} +	iv := make([]byte, cipherFunc.blockSize()) +	c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv) +	encryptedCipherAndKey := make([]byte, keySize+1) +	c.XORKeyStream(encryptedCipherAndKey, buf[1:]) +	c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey) +	_, err = w.Write(encryptedCipherAndKey) +	if err != nil { +		return +	} + +	key = sessionKey +	return +} diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go index 717c8ffa6..823ec400d 100644 --- a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go +++ b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go @@ -6,6 +6,7 @@ package packet  import (  	"bytes" +	"crypto/rand"  	"encoding/hex"  	"io/ioutil"  	"os" @@ -60,3 +61,41 @@ func TestSymmetricKeyEncrypted(t *testing.T) {  const symmetricallyEncryptedHex = "8c0d04030302371a0b38d884f02060c91cf97c9973b8e58e028e9501708ccfe618fb92afef7fa2d80ddadd93cf"  const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a" + +func TestSerializeSymmetricKeyEncrypted(t *testing.T) { +	buf := bytes.NewBuffer(nil) +	passphrase := []byte("testing") +	cipherFunc := CipherAES128 + +	key, err := SerializeSymmetricKeyEncrypted(buf, rand.Reader, passphrase, cipherFunc) +	if err != nil { +		t.Errorf("failed to serialize: %s", err) +		return +	} + +	p, err := Read(buf) +	if err != nil { +		t.Errorf("failed to reparse: %s", err) +		return +	} +	ske, ok := p.(*SymmetricKeyEncrypted) +	if !ok { +		t.Errorf("parsed a different packet type: %#v", p) +		return +	} + +	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) +	} +	err = ske.Decrypt(passphrase) +	if err != nil { +		t.Errorf("failed to decrypt reparsed SKE: %s", err) +		return +	} +	if !bytes.Equal(key, ske.Key) { +		t.Errorf("keys don't match after Decrpyt: %x (original) vs %x (parsed)", key, ske.Key) +	} +} diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go index fc19ffe80..236c36774 100644 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go +++ b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go @@ -7,6 +7,7 @@ package packet  import (  	"crypto/cipher"  	"crypto/openpgp/error" +	"crypto/rand"  	"crypto/sha1"  	"crypto/subtle"  	"hash" @@ -24,6 +25,8 @@ type SymmetricallyEncrypted struct {  	prefix   []byte  } +const symmetricallyEncryptedVersion = 1 +  func (se *SymmetricallyEncrypted) parse(r io.Reader) os.Error {  	if se.MDC {  		// See RFC 4880, section 5.13. @@ -32,7 +35,7 @@ func (se *SymmetricallyEncrypted) parse(r io.Reader) os.Error {  		if err != nil {  			return err  		} -		if buf[0] != 1 { +		if buf[0] != symmetricallyEncryptedVersion {  			return error.UnsupportedError("unknown SymmetricallyEncrypted version")  		}  	} @@ -174,6 +177,9 @@ func (ser *seMDCReader) Read(buf []byte) (n int, err os.Error) {  	return  } +// This is a new-format packet tag byte for a type 19 (MDC) packet. +const mdcPacketTagByte = byte(0x80) | 0x40 | 19 +  func (ser *seMDCReader) Close() os.Error {  	if ser.error {  		return error.SignatureError("error during reading") @@ -191,16 +197,95 @@ func (ser *seMDCReader) Close() os.Error {  		}  	} -	// This is a new-format packet tag byte for a type 19 (MDC) packet. -	const mdcPacketTagByte = byte(0x80) | 0x40 | 19  	if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size {  		return error.SignatureError("MDC packet not found")  	}  	ser.h.Write(ser.trailer[:2])  	final := ser.h.Sum() -	if subtle.ConstantTimeCompare(final, ser.trailer[2:]) == 1 { +	if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 {  		return error.SignatureError("hash mismatch")  	}  	return nil  } + +// An seMDCWriter writes through to an io.WriteCloser while maintains a running +// hash of the data written. On close, it emits an MDC packet containing the +// running hash. +type seMDCWriter struct { +	w io.WriteCloser +	h hash.Hash +} + +func (w *seMDCWriter) Write(buf []byte) (n int, err os.Error) { +	w.h.Write(buf) +	return w.w.Write(buf) +} + +func (w *seMDCWriter) Close() (err os.Error) { +	var buf [mdcTrailerSize]byte + +	buf[0] = mdcPacketTagByte +	buf[1] = sha1.Size +	w.h.Write(buf[:2]) +	digest := w.h.Sum() +	copy(buf[2:], digest) + +	_, err = w.w.Write(buf[:]) +	if err != nil { +		return +	} +	return w.w.Close() +} + +// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. +type noOpCloser struct { +	w io.Writer +} + +func (c noOpCloser) Write(data []byte) (n int, err os.Error) { +	return c.w.Write(data) +} + +func (c noOpCloser) Close() os.Error { +	return nil +} + +// 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, c CipherFunction, key []byte) (contents io.WriteCloser, err os.Error) { +	if c.keySize() != len(key) { +		return nil, error.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") +	} +	writeCloser := noOpCloser{w} +	ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC) +	if err != nil { +		return +	} + +	_, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion}) +	if err != nil { +		return +	} + +	block := c.new(key) +	blockSize := block.BlockSize() +	iv := make([]byte, blockSize) +	_, err = rand.Reader.Read(iv) +	if err != nil { +		return +	} +	s, prefix := cipher.NewOCFBEncrypter(block, iv, cipher.OCFBNoResync) +	_, err = ciphertext.Write(prefix) +	if err != nil { +		return +	} +	plaintext := cipher.StreamWriter{S: s, W: ciphertext} + +	h := sha1.New() +	h.Write(iv) +	h.Write(iv[blockSize-2:]) +	contents = &seMDCWriter{w: plaintext, h: h} +	return +} diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go index 5543b2029..ba5606e6c 100644 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go +++ b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go @@ -9,6 +9,7 @@ import (  	"crypto/openpgp/error"  	"crypto/sha1"  	"encoding/hex" +	"io"  	"io/ioutil"  	"os"  	"testing" @@ -76,3 +77,48 @@ func testMDCReader(t *testing.T) {  }  const mdcPlaintextHex = "a302789c3b2d93c4e0eb9aba22283539b3203335af44a134afb800c849cb4c4de10200aff40b45d31432c80cb384299a0655966d6939dfdeed1dddf980" + +func TestSerialize(t *testing.T) { +	buf := bytes.NewBuffer(nil) +	c := CipherAES128 +	key := make([]byte, c.keySize()) + +	w, err := SerializeSymmetricallyEncrypted(buf, c, key) +	if err != nil { +		t.Errorf("error from SerializeSymmetricallyEncrypted: %s", err) +		return +	} + +	contents := []byte("hello world\n") + +	w.Write(contents) +	w.Close() + +	p, err := Read(buf) +	if err != nil { +		t.Errorf("error from Read: %s", err) +		return +	} + +	se, ok := p.(*SymmetricallyEncrypted) +	if !ok { +		t.Errorf("didn't read a *SymmetricallyEncrypted") +		return +	} + +	r, err := se.Decrypt(c, key) +	if err != nil { +		t.Errorf("error from Decrypt: %s", err) +		return +	} + +	contentsCopy := bytes.NewBuffer(nil) +	_, err = io.Copy(contentsCopy, r) +	if err != nil { +		t.Errorf("error from io.Copy: %s", err) +		return +	} +	if !bytes.Equal(contentsCopy.Bytes(), contents) { +		t.Errorf("contents not equal got: %x want: %x", contentsCopy.Bytes(), contents) +	} +} diff --git a/src/pkg/crypto/openpgp/packet/userid.go b/src/pkg/crypto/openpgp/packet/userid.go index ed2ad7774..0580ba3ed 100644 --- a/src/pkg/crypto/openpgp/packet/userid.go +++ b/src/pkg/crypto/openpgp/packet/userid.go @@ -20,6 +20,51 @@ type UserId struct {  	Name, Comment, Email string  } +func hasInvalidCharacters(s string) bool { +	for _, c := range s { +		switch c { +		case '(', ')', '<', '>', 0: +			return true +		} +	} +	return false +} + +// NewUserId returns a UserId or nil if any of the arguments contain invalid +// characters. The invalid characters are '\x00', '(', ')', '<' and '>' +func NewUserId(name, comment, email string) *UserId { +	// RFC 4880 doesn't deal with the structure of userid strings; the +	// name, comment and email form is just a convention. However, there's +	// no convention about escaping the metacharacters and GPG just refuses +	// to create user ids where, say, the name contains a '('. We mirror +	// this behaviour. + +	if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) { +		return nil +	} + +	uid := new(UserId) +	uid.Name, uid.Comment, uid.Email = name, comment, email +	uid.Id = name +	if len(comment) > 0 { +		if len(uid.Id) > 0 { +			uid.Id += " " +		} +		uid.Id += "(" +		uid.Id += comment +		uid.Id += ")" +	} +	if len(email) > 0 { +		if len(uid.Id) > 0 { +			uid.Id += " " +		} +		uid.Id += "<" +		uid.Id += email +		uid.Id += ">" +	} +	return uid +} +  func (uid *UserId) parse(r io.Reader) (err os.Error) {  	// RFC 4880, section 5.11  	b, err := ioutil.ReadAll(r) @@ -31,6 +76,17 @@ func (uid *UserId) parse(r io.Reader) (err os.Error) {  	return  } +// Serialize marshals uid to w in the form of an OpenPGP packet, including +// header. +func (uid *UserId) Serialize(w io.Writer) os.Error { +	err := serializeHeader(w, packetTypeUserId, len(uid.Id)) +	if err != nil { +		return err +	} +	_, err = w.Write([]byte(uid.Id)) +	return err +} +  // parseUserId extracts the name, comment and email from a user id string that  // is formatted as "Full Name (Comment) <email@example.com>".  func parseUserId(id string) (name, comment, email string) { diff --git a/src/pkg/crypto/openpgp/packet/userid_test.go b/src/pkg/crypto/openpgp/packet/userid_test.go index 394873dc3..296819389 100644 --- a/src/pkg/crypto/openpgp/packet/userid_test.go +++ b/src/pkg/crypto/openpgp/packet/userid_test.go @@ -40,3 +40,48 @@ func TestParseUserId(t *testing.T) {  		}  	}  } + +var newUserIdTests = []struct { +	name, comment, email, id string +}{ +	{"foo", "", "", "foo"}, +	{"", "bar", "", "(bar)"}, +	{"", "", "baz", "<baz>"}, +	{"foo", "bar", "", "foo (bar)"}, +	{"foo", "", "baz", "foo <baz>"}, +	{"", "bar", "baz", "(bar) <baz>"}, +	{"foo", "bar", "baz", "foo (bar) <baz>"}, +} + +func TestNewUserId(t *testing.T) { +	for i, test := range newUserIdTests { +		uid := NewUserId(test.name, test.comment, test.email) +		if uid == nil { +			t.Errorf("#%d: returned nil", i) +			continue +		} +		if uid.Id != test.id { +			t.Errorf("#%d: got '%s', want '%s'", i, uid.Id, test.id) +		} +	} +} + +var invalidNewUserIdTests = []struct { +	name, comment, email string +}{ +	{"foo(", "", ""}, +	{"foo<", "", ""}, +	{"", "bar)", ""}, +	{"", "bar<", ""}, +	{"", "", "baz>"}, +	{"", "", "baz)"}, +	{"", "", "baz\x00"}, +} + +func TestNewUserIdWithInvalidInput(t *testing.T) { +	for i, test := range invalidNewUserIdTests { +		if uid := NewUserId(test.name, test.comment, test.email); uid != nil { +			t.Errorf("#%d: returned non-nil value: %#v", i, uid) +		} +	} +} diff --git a/src/pkg/crypto/openpgp/read.go b/src/pkg/crypto/openpgp/read.go index 4f84dff82..46fcde363 100644 --- a/src/pkg/crypto/openpgp/read.go +++ b/src/pkg/crypto/openpgp/read.go @@ -44,7 +44,7 @@ type MessageDetails struct {  	DecryptedWith            Key                 // the private key used to decrypt the message, if any.  	IsSigned                 bool                // true if the message is signed.  	SignedByKeyId            uint64              // the key id of the signer, if any. -	SignedBy                 *Key                // the key of the signer, if availible. +	SignedBy                 *Key                // the key of the signer, if available.  	LiteralData              *packet.LiteralData // the metadata of the contents  	UnverifiedBody           io.Reader           // the contents of the message. @@ -145,7 +145,7 @@ ParsePackets:  	// function so that it can decrypt a key or give us a passphrase.  FindKey:  	for { -		// See if any of the keys already have a private key availible +		// See if any of the keys already have a private key available  		candidates = candidates[:0]  		candidateFingerprints := make(map[string]bool) @@ -214,7 +214,7 @@ FindKey:  	return readSignedMessage(packets, md, keyring)  } -// readSignedMessage reads a possibily signed message if mdin is non-zero then +// readSignedMessage reads a possibly signed message if mdin is non-zero then  // that structure is updated and returned. Otherwise a fresh MessageDetails is  // used.  func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing) (md *MessageDetails, err os.Error) { @@ -274,13 +274,13 @@ FindLiteralData:  // hashForSignature returns a pair of hashes that can be used to verify a  // signature. The signature may specify that the contents of the signed message -// should be preprocessed (i.e. to normalise line endings). Thus this function +// should be preprocessed (i.e. to normalize line endings). Thus this function  // returns two hashes. The second should be used to hash the message itself and  // performs any needed preprocessing.  func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, os.Error) {  	h := hashId.New()  	if h == nil { -		return nil, nil, error.UnsupportedError("hash not availible: " + strconv.Itoa(int(hashId))) +		return nil, nil, error.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId)))  	}  	switch sigType { diff --git a/src/pkg/crypto/openpgp/read_test.go b/src/pkg/crypto/openpgp/read_test.go index 423c85b0f..a040b5b1b 100644 --- a/src/pkg/crypto/openpgp/read_test.go +++ b/src/pkg/crypto/openpgp/read_test.go @@ -193,9 +193,9 @@ func TestSymmetricallyEncrypted(t *testing.T) {  		t.Errorf("ReadAll: %s", err)  	} -	expectedCreatationTime := uint32(1295992998) -	if md.LiteralData.Time != expectedCreatationTime { -		t.Errorf("LiteralData.Time is %d, want %d", md.LiteralData.Time, expectedCreatationTime) +	expectedCreationTime := uint32(1295992998) +	if md.LiteralData.Time != expectedCreationTime { +		t.Errorf("LiteralData.Time is %d, want %d", md.LiteralData.Time, expectedCreationTime)  	}  	if string(contents) != expected { diff --git a/src/pkg/crypto/openpgp/s2k/s2k.go b/src/pkg/crypto/openpgp/s2k/s2k.go index 93b7582fa..da926a76e 100644 --- a/src/pkg/crypto/openpgp/s2k/s2k.go +++ b/src/pkg/crypto/openpgp/s2k/s2k.go @@ -90,7 +90,7 @@ func Parse(r io.Reader) (f func(out, in []byte), err os.Error) {  	}  	h := hash.New()  	if h == nil { -		return nil, error.UnsupportedError("hash not availible: " + strconv.Itoa(int(hash))) +		return nil, error.UnsupportedError("hash not available: " + strconv.Itoa(int(hash)))  	}  	switch buf[0] { @@ -123,6 +123,26 @@ func Parse(r io.Reader) (f func(out, in []byte), err os.Error) {  	return nil, error.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) os.Error { +	var buf [11]byte +	buf[0] = 3 /* iterated and salted */ +	buf[1], _ = HashToHashId(crypto.SHA1) +	salt := buf[2:10] +	if _, err := io.ReadFull(rand, salt); err != nil { +		return err +	} +	const count = 65536 // this is the default in gpg +	buf[10] = 96        // 65536 iterations +	if _, err := w.Write(buf[:]); err != nil { +		return err +	} + +	Iterated(key, crypto.SHA1.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 { diff --git a/src/pkg/crypto/openpgp/s2k/s2k_test.go b/src/pkg/crypto/openpgp/s2k/s2k_test.go index 75bc47ec1..27d2e9ae0 100644 --- a/src/pkg/crypto/openpgp/s2k/s2k_test.go +++ b/src/pkg/crypto/openpgp/s2k/s2k_test.go @@ -7,6 +7,7 @@ package s2k  import (  	"bytes"  	"crypto/sha1" +	"crypto/rand"  	"encoding/hex"  	"testing"  ) @@ -95,3 +96,26 @@ func TestParse(t *testing.T) {  		}  	}  } + + +func TestSerialize(t *testing.T) { +	buf := bytes.NewBuffer(nil) +	key := make([]byte, 16) +	passphrase := []byte("testing") +	err := Serialize(buf, key, rand.Reader, passphrase) +	if err != nil { +		t.Errorf("failed to serialize: %s", err) +		return +	} + +	f, err := Parse(buf) +	if err != nil { +		t.Errorf("failed to reparse: %s", err) +		return +	} +	key2 := make([]byte, len(key)) +	f(key2, passphrase) +	if !bytes.Equal(key2, key) { +		t.Errorf("keys don't match: %x (serialied) vs %x (parsed)", key, key2) +	} +} diff --git a/src/pkg/crypto/openpgp/write.go b/src/pkg/crypto/openpgp/write.go index ef7b11230..48c86f604 100644 --- a/src/pkg/crypto/openpgp/write.go +++ b/src/pkg/crypto/openpgp/write.go @@ -6,15 +6,13 @@ package openpgp  import (  	"crypto" -	"crypto/dsa"  	"crypto/openpgp/armor"  	"crypto/openpgp/error"  	"crypto/openpgp/packet" -	"crypto/rsa" +	"crypto/rand"  	_ "crypto/sha256"  	"io"  	"os" -	"strconv"  	"time"  ) @@ -77,20 +75,43 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S  	}  	io.Copy(wrappedHash, message) -	switch signer.PrivateKey.PubKeyAlgo { -	case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSASignOnly: -		priv := signer.PrivateKey.PrivateKey.(*rsa.PrivateKey) -		err = sig.SignRSA(h, priv) -	case packet.PubKeyAlgoDSA: -		priv := signer.PrivateKey.PrivateKey.(*dsa.PrivateKey) -		err = sig.SignDSA(h, priv) -	default: -		err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) -	} - +	err = sig.Sign(h, signer.PrivateKey)  	if err != nil {  		return  	}  	return sig.Serialize(w)  } + +// FileHints contains metadata about encrypted files. This metadata is, itself, +// encrypted. +type FileHints struct { +	// IsBinary can be set to hint that the contents are binary data. +	IsBinary bool +	// FileName hints at the name of the file that should be written. It's +	// truncated to 255 bytes if longer. It may be empty to suggest that the +	// file should not be written to disk. It may be equal to "_CONSOLE" to +	// suggest the data should not be written to disk. +	FileName string +	// EpochSeconds contains the modification time of the file, or 0 if not applicable. +	EpochSeconds uint32 +} + +// 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 os.Error) { +	if hints == nil { +		hints = &FileHints{} +	} + +	key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, rand.Reader, passphrase, packet.CipherAES128) +	if err != nil { +		return +	} +	w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, packet.CipherAES128, key) +	if err != nil { +		return +	} +	return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) +} diff --git a/src/pkg/crypto/openpgp/write_test.go b/src/pkg/crypto/openpgp/write_test.go index 42cd0d27f..8551aeb63 100644 --- a/src/pkg/crypto/openpgp/write_test.go +++ b/src/pkg/crypto/openpgp/write_test.go @@ -6,7 +6,11 @@ package openpgp  import (  	"bytes" +	"crypto/rand" +	"os" +	"io"  	"testing" +	"time"  )  func TestSignDetached(t *testing.T) { @@ -44,3 +48,75 @@ func TestSignDetachedDSA(t *testing.T) {  	testDetachedSignature(t, kring, out, signedInput, "check", testKey3KeyId)  } + +func TestNewEntity(t *testing.T) { +	if testing.Short() { +		return +	} + +	e, err := NewEntity(rand.Reader, time.Seconds(), "Test User", "test", "test@example.com") +	if err != nil { +		t.Errorf("failed to create entity: %s", err) +		return +	} + +	w := bytes.NewBuffer(nil) +	if err := e.SerializePrivate(w); err != nil { +		t.Errorf("failed to serialize entity: %s", err) +		return +	} +	serialized := w.Bytes() + +	el, err := ReadKeyRing(w) +	if err != nil { +		t.Errorf("failed to reparse entity: %s", err) +		return +	} + +	if len(el) != 1 { +		t.Errorf("wrong number of entities found, got %d, want 1", len(el)) +	} + +	w = bytes.NewBuffer(nil) +	if err := e.SerializePrivate(w); err != nil { +		t.Errorf("failed to serialize entity second time: %s", err) +		return +	} + +	if !bytes.Equal(w.Bytes(), serialized) { +		t.Errorf("results differed") +	} +} + +func TestSymmetricEncryption(t *testing.T) { +	buf := new(bytes.Buffer) +	plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil) +	if err != nil { +		t.Errorf("error writing headers: %s", err) +		return +	} +	message := []byte("hello world\n") +	_, err = plaintext.Write(message) +	if err != nil { +		t.Errorf("error writing to plaintext writer: %s", err) +	} +	err = plaintext.Close() +	if err != nil { +		t.Errorf("error closing plaintext writer: %s", err) +	} + +	md, err := ReadMessage(buf, nil, func(keys []Key, symmetric bool) ([]byte, os.Error) { +		return []byte("testing"), nil +	}) +	if err != nil { +		t.Errorf("error rereading message: %s", err) +	} +	messageBuf := bytes.NewBuffer(nil) +	_, err = io.Copy(messageBuf, md.UnverifiedBody) +	if err != nil { +		t.Errorf("error rereading message: %s", err) +	} +	if !bytes.Equal(message, messageBuf.Bytes()) { +		t.Errorf("recovered message incorrect got '%s', want '%s'", messageBuf.Bytes(), message) +	} +} diff --git a/src/pkg/crypto/rand/Makefile b/src/pkg/crypto/rand/Makefile index 88b6d71e3..d1321297d 100644 --- a/src/pkg/crypto/rand/Makefile +++ b/src/pkg/crypto/rand/Makefile @@ -8,6 +8,7 @@ TARG=crypto/rand  GOFILES=\  	rand.go\ +	util.go\  GOFILES_freebsd=\  	rand_unix.go\ diff --git a/src/pkg/crypto/rand/util.go b/src/pkg/crypto/rand/util.go new file mode 100644 index 000000000..77028476e --- /dev/null +++ b/src/pkg/crypto/rand/util.go @@ -0,0 +1,80 @@ +// 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 rand + +import ( +	"big" +	"io" +	"os" +) + +// Prime returns a number, p, of the given size, such that p is prime +// with high probability. +func Prime(rand io.Reader, bits int) (p *big.Int, err os.Error) { +	if bits < 1 { +		err = os.EINVAL +	} + +	b := uint(bits % 8) +	if b == 0 { +		b = 8 +	} + +	bytes := make([]byte, (bits+7)/8) +	p = new(big.Int) + +	for { +		_, err = io.ReadFull(rand, bytes) +		if err != nil { +			return nil, err +		} + +		// Clear bits in the first byte to make sure the candidate has a size <= bits. +		bytes[0] &= uint8(int(1<<b) - 1) +		// Don't let the value be too small, i.e, set the most significant bit. +		bytes[0] |= 1 << (b - 1) +		// Make the value odd since an even number this large certainly isn't prime. +		bytes[len(bytes)-1] |= 1 + +		p.SetBytes(bytes) +		if big.ProbablyPrime(p, 20) { +			return +		} +	} + +	return +} + +// Int returns a uniform random value in [0, max). +func Int(rand io.Reader, max *big.Int) (n *big.Int, err os.Error) { +	k := (max.BitLen() + 7) / 8 + +	// b is the number of bits in the most significant byte of max. +	b := uint(max.BitLen() % 8) +	if b == 0 { +		b = 8 +	} + +	bytes := make([]byte, k) +	n = new(big.Int) + +	for { +		_, err = io.ReadFull(rand, bytes) +		if err != nil { +			return nil, err +		} + +		// Clear bits in the first byte to increase the probability +		// that the candidate is < max. +		bytes[0] &= uint8(int(1<<b) - 1) + +		n.SetBytes(bytes) +		if n.Cmp(max) < 0 { +			return +		} +	} + +	return +} diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index e1813dbf9..380f71570 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -9,6 +9,7 @@ package rsa  import (  	"big" +	"crypto/rand"  	"crypto/subtle"  	"hash"  	"io" @@ -18,69 +19,6 @@ import (  var bigZero = big.NewInt(0)  var bigOne = big.NewInt(1) -// randomPrime returns a number, p, of the given size, such that p is prime -// with high probability. -func randomPrime(rand io.Reader, bits int) (p *big.Int, err os.Error) { -	if bits < 1 { -		err = os.EINVAL -	} - -	bytes := make([]byte, (bits+7)/8) -	p = new(big.Int) - -	for { -		_, err = io.ReadFull(rand, bytes) -		if err != nil { -			return -		} - -		// Don't let the value be too small. -		bytes[0] |= 0x80 -		// Make the value odd since an even number this large certainly isn't prime. -		bytes[len(bytes)-1] |= 1 - -		p.SetBytes(bytes) -		if big.ProbablyPrime(p, 20) { -			return -		} -	} - -	return -} - -// randomNumber returns a uniform random value in [0, max). -func randomNumber(rand io.Reader, max *big.Int) (n *big.Int, err os.Error) { -	k := (max.BitLen() + 7) / 8 - -	// r is the number of bits in the used in the most significant byte of -	// max. -	r := uint(max.BitLen() % 8) -	if r == 0 { -		r = 8 -	} - -	bytes := make([]byte, k) -	n = new(big.Int) - -	for { -		_, err = io.ReadFull(rand, bytes) -		if err != nil { -			return -		} - -		// Clear bits in the first byte to increase the probability -		// that the candidate is < max. -		bytes[0] &= uint8(int(1<<r) - 1) - -		n.SetBytes(bytes) -		if n.Cmp(max) < 0 { -			return -		} -	} - -	return -} -  // A PublicKey represents the public part of an RSA key.  type PublicKey struct {  	N *big.Int // modulus @@ -94,7 +32,7 @@ type PrivateKey struct {  	Primes    []*big.Int // prime factors of N, has >= 2 elements.  	// Precomputed contains precomputed values that speed up private -	// operations, if availible. +	// operations, if available.  	Precomputed PrecomputedValues  } @@ -126,7 +64,7 @@ func (priv *PrivateKey) Validate() os.Error {  	// easy for an attack to generate composites that pass this test.  	for _, prime := range priv.Primes {  		if !big.ProbablyPrime(prime, 20) { -			return os.ErrorString("Prime factor is composite") +			return os.ErrorString("prime factor is composite")  		}  	} @@ -162,8 +100,8 @@ func (priv *PrivateKey) Validate() os.Error {  }  // GenerateKey generates an RSA keypair of the given bit size. -func GenerateKey(rand io.Reader, bits int) (priv *PrivateKey, err os.Error) { -	return GenerateMultiPrimeKey(rand, 2, bits) +func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err os.Error) { +	return GenerateMultiPrimeKey(random, 2, bits)  }  // GenerateMultiPrimeKey generates a multi-prime RSA keypair of the given bit @@ -176,7 +114,7 @@ func GenerateKey(rand io.Reader, bits int) (priv *PrivateKey, err os.Error) {  //  // [1] US patent 4405829 (1972, expired)  // [2] http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf -func GenerateMultiPrimeKey(rand io.Reader, nprimes int, bits int) (priv *PrivateKey, err os.Error) { +func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *PrivateKey, err os.Error) {  	priv = new(PrivateKey)  	// Smaller public exponents lead to faster public key  	// operations. Since the exponent must be coprime to @@ -198,7 +136,7 @@ NextSetOfPrimes:  	for {  		todo := bits  		for i := 0; i < nprimes; i++ { -			primes[i], err = randomPrime(rand, todo/(nprimes-i)) +			primes[i], err = rand.Prime(random, todo/(nprimes-i))  			if err != nil {  				return nil, err  			} @@ -293,7 +231,7 @@ func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int {  // EncryptOAEP encrypts the given message with RSA-OAEP.  // The message must be no longer than the length of the public modulus less  // twice the hash length plus 2. -func EncryptOAEP(hash hash.Hash, rand io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err os.Error) { +func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err os.Error) {  	hash.Reset()  	k := (pub.N.BitLen() + 7) / 8  	if len(msg) > k-2*hash.Size()-2 { @@ -313,7 +251,7 @@ func EncryptOAEP(hash hash.Hash, rand io.Reader, pub *PublicKey, msg []byte, lab  	db[len(db)-len(msg)-1] = 1  	copy(db[len(db)-len(msg):], msg) -	_, err = io.ReadFull(rand, seed) +	_, err = io.ReadFull(random, seed)  	if err != nil {  		return  	} @@ -405,7 +343,7 @@ func (priv *PrivateKey) Precompute() {  // decrypt performs an RSA decryption, resulting in a plaintext integer. If a  // random source is given, RSA blinding is used. -func decrypt(rand io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.Error) { +func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.Error) {  	// TODO(agl): can we get away with reusing blinds?  	if c.Cmp(priv.N) > 0 {  		err = DecryptionError{} @@ -413,16 +351,16 @@ func decrypt(rand io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.E  	}  	var ir *big.Int -	if rand != nil { +	if random != nil {  		// Blinding enabled. Blinding involves multiplying c by r^e.  		// Then the decryption operation performs (m^e * r^e)^d mod n  		// which equals mr mod n. The factor of r can then be removed -		// by multipling by the multiplicative inverse of r. +		// by multiplying by the multiplicative inverse of r.  		var r *big.Int  		for { -			r, err = randomNumber(rand, priv.N) +			r, err = rand.Int(random, priv.N)  			if err != nil {  				return  			} @@ -483,7 +421,7 @@ func decrypt(rand io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.E  // DecryptOAEP decrypts ciphertext using RSA-OAEP.  // If rand != nil, DecryptOAEP uses RSA blinding to avoid timing side-channel attacks. -func DecryptOAEP(hash hash.Hash, rand io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err os.Error) { +func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err os.Error) {  	k := (priv.N.BitLen() + 7) / 8  	if len(ciphertext) > k ||  		k < hash.Size()*2+2 { @@ -493,7 +431,7 @@ func DecryptOAEP(hash hash.Hash, rand io.Reader, priv *PrivateKey, ciphertext []  	c := new(big.Int).SetBytes(ciphertext) -	m, err := decrypt(rand, priv, c) +	m, err := decrypt(random, priv, c)  	if err != nil {  		return  	} diff --git a/src/pkg/crypto/subtle/constant_time_test.go b/src/pkg/crypto/subtle/constant_time_test.go index b28b73581..adab8e2e8 100644 --- a/src/pkg/crypto/subtle/constant_time_test.go +++ b/src/pkg/crypto/subtle/constant_time_test.go @@ -14,14 +14,14 @@ type TestConstantTimeCompareStruct struct {  	out  int  } -var testConstandTimeCompareData = []TestConstantTimeCompareStruct{ +var testConstantTimeCompareData = []TestConstantTimeCompareStruct{  	{[]byte{}, []byte{}, 1},  	{[]byte{0x11}, []byte{0x11}, 1},  	{[]byte{0x12}, []byte{0x11}, 0},  }  func TestConstantTimeCompare(t *testing.T) { -	for i, test := range testConstandTimeCompareData { +	for i, test := range testConstantTimeCompareData {  		if r := ConstantTimeCompare(test.a, test.b); r != test.out {  			t.Errorf("#%d bad result (got %x, want %x)", i, r, test.out)  		} diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go index 204d25531..3efac9c13 100644 --- a/src/pkg/crypto/tls/common.go +++ b/src/pkg/crypto/tls/common.go @@ -87,7 +87,7 @@ const (  	certTypeRSASign    = 1 // A certificate containing an RSA key  	certTypeDSSSign    = 2 // A certificate containing a DSA key  	certTypeRSAFixedDH = 3 // A certificate containing a static DH key -	certTypeDSSFixedDH = 4 // A certficiate containing a static DH key +	certTypeDSSFixedDH = 4 // A certificate containing a static DH key  	// Rest of these are reserved by the TLS spec  ) @@ -100,6 +100,8 @@ type ConnectionState struct {  	// the certificate chain that was presented by the other side  	PeerCertificates []*x509.Certificate +	// the verified certificate chains built from PeerCertificates. +	VerifiedChains [][]*x509.Certificate  }  // A Config structure is used to configure a TLS client or server. After one diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go index 63d56310c..097e182bd 100644 --- a/src/pkg/crypto/tls/conn.go +++ b/src/pkg/crypto/tls/conn.go @@ -34,7 +34,7 @@ type Conn struct {  	cipherSuite       uint16  	ocspResponse      []byte // stapled OCSP response  	peerCertificates  []*x509.Certificate -	// verifedChains contains the certificate chains that we built, as +	// verifiedChains contains the certificate chains that we built, as  	// opposed to the ones presented by the server.  	verifiedChains [][]*x509.Certificate @@ -237,7 +237,7 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) {  			// "Password Interception in a SSL/TLS Channel", Brice  			// Canvel et al.  			// -			// However, our behaviour matches OpenSSL, so we leak +			// However, our behavior matches OpenSSL, so we leak  			// only as much as they do.  		default:  			panic("unknown cipher type") @@ -410,7 +410,7 @@ func (hc *halfConn) freeBlock(b *block) {  // splitBlock splits a block after the first n bytes,  // returning a block with those n bytes and a -// block with the remaindec.  the latter may be nil. +// block with the remainder.  the latter may be nil.  func (hc *halfConn) splitBlock(b *block, n int) (*block, *block) {  	if len(b.data) <= n {  		return b, nil @@ -768,6 +768,7 @@ func (c *Conn) ConnectionState() ConnectionState {  		state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback  		state.CipherSuite = c.cipherSuite  		state.PeerCertificates = c.peerCertificates +		state.VerifiedChains = c.verifiedChains  	}  	return state diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go index 5b8c700e5..f46188879 100644 --- a/src/pkg/crypto/tls/generate_cert.go +++ b/src/pkg/crypto/tls/generate_cert.go @@ -59,7 +59,7 @@ func main() {  	certOut.Close()  	log.Print("written cert.pem\n") -	keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0600) +	keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)  	if err != nil {  		log.Print("failed to open key.pem for writing:", err)  		return diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index 37c8d154a..e9431c6fa 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -209,10 +209,10 @@ FindCipherSuite:  	// If we received a client cert in response to our certificate request message,  	// the client will send us a certificateVerifyMsg immediately after the -	// clientKeyExchangeMsg.  This message is a MD5SHA1 digest of all preceeding +	// clientKeyExchangeMsg.  This message is a MD5SHA1 digest of all preceding  	// handshake-layer messages that is signed using the private key corresponding  	// to the client's certificate. This allows us to verify that the client is in -	// posession of the private key of the certificate. +	// possession of the private key of the certificate.  	if len(c.peerCertificates) > 0 {  		msg, err = c.readHandshake()  		if err != nil { diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go index 8edbb1190..84f90c45a 100644 --- a/src/pkg/crypto/tls/key_agreement.go +++ b/src/pkg/crypto/tls/key_agreement.go @@ -236,12 +236,12 @@ func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, client  	xBytes := x.Bytes()  	copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) -	serialised := ka.curve.Marshal(mx, my) +	serialized := ka.curve.Marshal(mx, my)  	ckx := new(clientKeyExchangeMsg) -	ckx.ciphertext = make([]byte, 1+len(serialised)) -	ckx.ciphertext[0] = byte(len(serialised)) -	copy(ckx.ciphertext[1:], serialised) +	ckx.ciphertext = make([]byte, 1+len(serialized)) +	ckx.ciphertext[0] = byte(len(serialized)) +	copy(ckx.ciphertext[1:], serialized)  	return preMasterSecret, ckx, nil  } diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go index 7d0bb9f34..9e5c9270a 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -159,7 +159,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err  	key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)  	if err != nil { -		err = os.ErrorString("crypto/tls: failed to parse key") +		err = os.ErrorString("crypto/tls: failed to parse key: " + err.String())  		return  	} diff --git a/src/pkg/crypto/twofish/twofish.go b/src/pkg/crypto/twofish/twofish.go index 9303f03ff..1a1aac9b9 100644 --- a/src/pkg/crypto/twofish/twofish.go +++ b/src/pkg/crypto/twofish/twofish.go @@ -116,7 +116,7 @@ func (c *Cipher) Reset() {  		c.k[i] = 0  	}  	for i := range c.s { -		for j := 0; j < 265; j++ { +		for j := 0; j < 256; j++ {  			c.s[i][j] = 0  		}  	} diff --git a/src/pkg/crypto/x509/cert_pool.go b/src/pkg/crypto/x509/cert_pool.go index c295fd97e..16cd92efc 100644 --- a/src/pkg/crypto/x509/cert_pool.go +++ b/src/pkg/crypto/x509/cert_pool.go @@ -5,6 +5,7 @@  package x509  import ( +	"crypto/x509/pkix"  	"encoding/pem"  	"strings"  ) @@ -25,7 +26,7 @@ func NewCertPool() *CertPool {  	}  } -func nameToKey(name *Name) string { +func nameToKey(name *pkix.Name) string {  	return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName  } diff --git a/src/pkg/crypto/x509/pkix/Makefile b/src/pkg/crypto/x509/pkix/Makefile new file mode 100644 index 000000000..e29b74c01 --- /dev/null +++ b/src/pkg/crypto/x509/pkix/Makefile @@ -0,0 +1,11 @@ +# 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. + +include ../../../../Make.inc + +TARG=crypto/x509/pkix +GOFILES=\ +	pkix.go\ + +include ../../../../Make.pkg diff --git a/src/pkg/crypto/x509/pkix/pkix.go b/src/pkg/crypto/x509/pkix/pkix.go new file mode 100644 index 000000000..7806b2a2e --- /dev/null +++ b/src/pkg/crypto/x509/pkix/pkix.go @@ -0,0 +1,167 @@ +// 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 pkix contains shared, low level structures used for ASN.1 parsing +// and serialization of X.509 certificates, CRL and OCSP. +package pkix + +import ( +	"asn1" +	"big" +	"time" +) + +// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.1.1.2. +type AlgorithmIdentifier struct { +	Algorithm  asn1.ObjectIdentifier +	Parameters asn1.RawValue "optional" +} + +type RDNSequence []RelativeDistinguishedNameSET + +type RelativeDistinguishedNameSET []AttributeTypeAndValue + +type AttributeTypeAndValue struct { +	Type  asn1.ObjectIdentifier +	Value interface{} +} + +// Extension represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.2. +type Extension struct { +	Id       asn1.ObjectIdentifier +	Critical bool "optional" +	Value    []byte +} + +// Name represents an X.509 distinguished name. This only includes the common +// elements of a DN.  Additional elements in the name are ignored. +type Name struct { +	Country, Organization, OrganizationalUnit []string +	Locality, Province                        []string +	StreetAddress, PostalCode                 []string +	SerialNumber, CommonName                  string +} + +func (n *Name) FillFromRDNSequence(rdns *RDNSequence) { +	for _, rdn := range *rdns { +		if len(rdn) == 0 { +			continue +		} +		atv := rdn[0] +		value, ok := atv.Value.(string) +		if !ok { +			continue +		} + +		t := atv.Type +		if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 { +			switch t[3] { +			case 3: +				n.CommonName = value +			case 5: +				n.SerialNumber = value +			case 6: +				n.Country = append(n.Country, value) +			case 7: +				n.Locality = append(n.Locality, value) +			case 8: +				n.Province = append(n.Province, value) +			case 9: +				n.StreetAddress = append(n.StreetAddress, value) +			case 10: +				n.Organization = append(n.Organization, value) +			case 11: +				n.OrganizationalUnit = append(n.OrganizationalUnit, value) +			case 17: +				n.PostalCode = append(n.PostalCode, value) +			} +		} +	} +} + +var ( +	oidCountry            = []int{2, 5, 4, 6} +	oidOrganization       = []int{2, 5, 4, 10} +	oidOrganizationalUnit = []int{2, 5, 4, 11} +	oidCommonName         = []int{2, 5, 4, 3} +	oidSerialNumber       = []int{2, 5, 4, 5} +	oidLocality           = []int{2, 5, 4, 7} +	oidProvince           = []int{2, 5, 4, 8} +	oidStreetAddress      = []int{2, 5, 4, 9} +	oidPostalCode         = []int{2, 5, 4, 17} +) + +// appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence +// and returns the new value. The relativeDistinguishedNameSET contains an +// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and +// search for AttributeTypeAndValue. +func appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence { +	if len(values) == 0 { +		return in +	} + +	s := make([]AttributeTypeAndValue, len(values)) +	for i, value := range values { +		s[i].Type = oid +		s[i].Value = value +	} + +	return append(in, s) +} + +func (n Name) ToRDNSequence() (ret RDNSequence) { +	ret = appendRDNs(ret, n.Country, oidCountry) +	ret = appendRDNs(ret, n.Organization, oidOrganization) +	ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit) +	ret = appendRDNs(ret, n.Locality, oidLocality) +	ret = appendRDNs(ret, n.Province, oidProvince) +	ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress) +	ret = appendRDNs(ret, n.PostalCode, oidPostalCode) +	if len(n.CommonName) > 0 { +		ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName) +	} +	if len(n.SerialNumber) > 0 { +		ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber) +	} + +	return ret +} + +// CertificateList represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the +// signature. +type CertificateList struct { +	TBSCertList        TBSCertificateList +	SignatureAlgorithm AlgorithmIdentifier +	SignatureValue     asn1.BitString +} + +// HasExpired returns true iff currentTimeSeconds is past the expiry time of +// certList. +func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { +	return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds +} + +// TBSCertificateList represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. +type TBSCertificateList struct { +	Raw                 asn1.RawContent +	Version             int "optional,default:2" +	Signature           AlgorithmIdentifier +	Issuer              RDNSequence +	ThisUpdate          *time.Time +	NextUpdate          *time.Time +	RevokedCertificates []RevokedCertificate "optional" +	Extensions          []Extension          "tag:0,optional,explicit" +} + +// RevokedCertificate represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. +type RevokedCertificate struct { +	SerialNumber   *big.Int +	RevocationTime *time.Time +	Extensions     []Extension "optional" +} diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index f2a039b5a..b10ffb0a2 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -11,9 +11,11 @@ import (  	"bytes"  	"container/vector"  	"crypto" +	"crypto/dsa"  	"crypto/rsa"  	"crypto/sha1" -	"hash" +	"crypto/x509/pkix" +	"encoding/pem"  	"io"  	"os"  	"time" @@ -22,30 +24,25 @@ import (  // pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.  type pkcs1PrivateKey struct {  	Version int -	N       asn1.RawValue +	N       *big.Int  	E       int -	D       asn1.RawValue -	P       asn1.RawValue -	Q       asn1.RawValue +	D       *big.Int +	P       *big.Int +	Q       *big.Int  	// We ignore these values, if present, because rsa will calculate them. -	Dp   asn1.RawValue "optional" -	Dq   asn1.RawValue "optional" -	Qinv asn1.RawValue "optional" +	Dp   *big.Int "optional" +	Dq   *big.Int "optional" +	Qinv *big.Int "optional" -	AdditionalPrimes []pkcs1AddtionalRSAPrime "optional" +	AdditionalPrimes []pkcs1AdditionalRSAPrime "optional"  } -type pkcs1AddtionalRSAPrime struct { -	Prime asn1.RawValue +type pkcs1AdditionalRSAPrime struct { +	Prime *big.Int  	// We ignore these values because rsa will calculate them. -	Exp   asn1.RawValue -	Coeff asn1.RawValue -} - -// rawValueIsInteger returns true iff the given ASN.1 RawValue is an INTEGER type. -func rawValueIsInteger(raw *asn1.RawValue) bool { -	return raw.Class == 0 && raw.Tag == 2 && raw.IsCompound == false +	Exp   *big.Int +	Coeff *big.Int  }  // ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. @@ -64,29 +61,25 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {  		return nil, os.ErrorString("x509: unsupported private key version")  	} -	if !rawValueIsInteger(&priv.N) || -		!rawValueIsInteger(&priv.D) || -		!rawValueIsInteger(&priv.P) || -		!rawValueIsInteger(&priv.Q) { -		err = asn1.StructuralError{"tags don't match"} -		return +	if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { +		return nil, os.ErrorString("private key contains zero or negative value")  	}  	key = new(rsa.PrivateKey)  	key.PublicKey = rsa.PublicKey{  		E: priv.E, -		N: new(big.Int).SetBytes(priv.N.Bytes), +		N: priv.N,  	} -	key.D = new(big.Int).SetBytes(priv.D.Bytes) +	key.D = priv.D  	key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes)) -	key.Primes[0] = new(big.Int).SetBytes(priv.P.Bytes) -	key.Primes[1] = new(big.Int).SetBytes(priv.Q.Bytes) +	key.Primes[0] = priv.P +	key.Primes[1] = priv.Q  	for i, a := range priv.AdditionalPrimes { -		if !rawValueIsInteger(&a.Prime) { -			return nil, asn1.StructuralError{"tags don't match"} +		if a.Prime.Sign() <= 0 { +			return nil, os.ErrorString("private key contains zero or negative prime")  		} -		key.Primes[i+2] = new(big.Int).SetBytes(a.Prime.Bytes) +		key.Primes[i+2] = a.Prime  		// We ignore the other two values because rsa will calculate  		// them as needed.  	} @@ -100,19 +93,6 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {  	return  } -// rawValueForBig returns an asn1.RawValue which represents the given integer. -func rawValueForBig(n *big.Int) asn1.RawValue { -	b := n.Bytes() -	if n.Sign() >= 0 && len(b) > 0 && b[0]&0x80 != 0 { -		// This positive number would be interpreted as a negative -		// number in ASN.1 because the MSB is set. -		padded := make([]byte, len(b)+1) -		copy(padded[1:], b) -		b = padded -	} -	return asn1.RawValue{Tag: 2, Bytes: b} -} -  // MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.  func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {  	key.Precompute() @@ -124,21 +104,21 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {  	priv := pkcs1PrivateKey{  		Version: version, -		N:       rawValueForBig(key.N), +		N:       key.N,  		E:       key.PublicKey.E, -		D:       rawValueForBig(key.D), -		P:       rawValueForBig(key.Primes[0]), -		Q:       rawValueForBig(key.Primes[1]), -		Dp:      rawValueForBig(key.Precomputed.Dp), -		Dq:      rawValueForBig(key.Precomputed.Dq), -		Qinv:    rawValueForBig(key.Precomputed.Qinv), +		D:       key.D, +		P:       key.Primes[0], +		Q:       key.Primes[1], +		Dp:      key.Precomputed.Dp, +		Dq:      key.Precomputed.Dq, +		Qinv:    key.Precomputed.Qinv,  	} -	priv.AdditionalPrimes = make([]pkcs1AddtionalRSAPrime, len(key.Precomputed.CRTValues)) +	priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))  	for i, values := range key.Precomputed.CRTValues { -		priv.AdditionalPrimes[i].Prime = rawValueForBig(key.Primes[2+i]) -		priv.AdditionalPrimes[i].Exp = rawValueForBig(values.Exp) -		priv.AdditionalPrimes[i].Coeff = rawValueForBig(values.Coeff) +		priv.AdditionalPrimes[i].Prime = key.Primes[2+i] +		priv.AdditionalPrimes[i].Exp = values.Exp +		priv.AdditionalPrimes[i].Coeff = values.Coeff  	}  	b, _ := asn1.Marshal(priv) @@ -150,35 +130,30 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {  type certificate struct {  	Raw                asn1.RawContent  	TBSCertificate     tbsCertificate -	SignatureAlgorithm algorithmIdentifier +	SignatureAlgorithm pkix.AlgorithmIdentifier  	SignatureValue     asn1.BitString  }  type tbsCertificate struct {  	Raw                asn1.RawContent  	Version            int "optional,explicit,default:1,tag:0" -	SerialNumber       asn1.RawValue -	SignatureAlgorithm algorithmIdentifier -	Issuer             rdnSequence +	SerialNumber       *big.Int +	SignatureAlgorithm pkix.AlgorithmIdentifier +	Issuer             pkix.RDNSequence  	Validity           validity -	Subject            rdnSequence +	Subject            pkix.RDNSequence  	PublicKey          publicKeyInfo -	UniqueId           asn1.BitString "optional,tag:1" -	SubjectUniqueId    asn1.BitString "optional,tag:2" -	Extensions         []extension    "optional,explicit,tag:3" +	UniqueId           asn1.BitString   "optional,tag:1" +	SubjectUniqueId    asn1.BitString   "optional,tag:2" +	Extensions         []pkix.Extension "optional,explicit,tag:3"  } -type algorithmIdentifier struct { -	Algorithm asn1.ObjectIdentifier +type dsaAlgorithmParameters struct { +	P, Q, G *big.Int  } -type rdnSequence []relativeDistinguishedNameSET - -type relativeDistinguishedNameSET []attributeTypeAndValue - -type attributeTypeAndValue struct { -	Type  asn1.ObjectIdentifier -	Value interface{} +type dsaSignature struct { +	R, S *big.Int  }  type validity struct { @@ -186,16 +161,11 @@ type validity struct {  }  type publicKeyInfo struct { -	Algorithm algorithmIdentifier +	Raw       asn1.RawContent +	Algorithm pkix.AlgorithmIdentifier  	PublicKey asn1.BitString  } -type extension struct { -	Id       asn1.ObjectIdentifier -	Critical bool "optional" -	Value    []byte -} -  // RFC 5280,  4.2.1.1  type authKeyId struct {  	Id []byte "optional,tag:0" @@ -211,6 +181,8 @@ const (  	SHA256WithRSA  	SHA384WithRSA  	SHA512WithRSA +	DSAWithSHA1 +	DSAWithSHA256  )  type PublicKeyAlgorithm int @@ -218,133 +190,96 @@ type PublicKeyAlgorithm int  const (  	UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota  	RSA +	DSA  ) -// Name represents an X.509 distinguished name. This only includes the common -// elements of a DN.  Additional elements in the name are ignored. -type Name struct { -	Country, Organization, OrganizationalUnit []string -	Locality, Province                        []string -	StreetAddress, PostalCode                 []string -	SerialNumber, CommonName                  string -} - -func (n *Name) fillFromRDNSequence(rdns *rdnSequence) { -	for _, rdn := range *rdns { -		if len(rdn) == 0 { -			continue -		} -		atv := rdn[0] -		value, ok := atv.Value.(string) -		if !ok { -			continue -		} - -		t := atv.Type -		if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 { -			switch t[3] { -			case 3: -				n.CommonName = value -			case 5: -				n.SerialNumber = value -			case 6: -				n.Country = append(n.Country, value) -			case 7: -				n.Locality = append(n.Locality, value) -			case 8: -				n.Province = append(n.Province, value) -			case 9: -				n.StreetAddress = append(n.StreetAddress, value) -			case 10: -				n.Organization = append(n.Organization, value) -			case 11: -				n.OrganizationalUnit = append(n.OrganizationalUnit, value) -			case 17: -				n.PostalCode = append(n.PostalCode, value) -			} -		} -	} -} - +// OIDs for signature algorithms +// +// pkcs-1 OBJECT IDENTIFIER ::= { +//    iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } +//  +//  +// RFC 3279 2.2.1 RSA Signature Algorithms +// +// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } +// +// md5WithRSAEncryption OBJECT IDENTIFER ::= { pkcs-1 4 } +// +// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } +//  +// dsaWithSha1 OBJECT IDENTIFIER ::= { +//    iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 }  +// +// +// RFC 4055 5 PKCS #1 Version 1.5 +//  +// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } +// +// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } +// +// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } +// +// +// RFC 5758 3.1 DSA Signature Algorithms +// +// dsaWithSha356 OBJECT IDENTIFER ::= { +//    joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) +//    algorithms(4) id-dsa-with-sha2(3) 2} +//  var ( -	oidCountry            = []int{2, 5, 4, 6} -	oidOrganization       = []int{2, 5, 4, 10} -	oidOrganizationalUnit = []int{2, 5, 4, 11} -	oidCommonName         = []int{2, 5, 4, 3} -	oidSerialNumber       = []int{2, 5, 4, 5} -	oidLocatity           = []int{2, 5, 4, 7} -	oidProvince           = []int{2, 5, 4, 8} -	oidStreetAddress      = []int{2, 5, 4, 9} -	oidPostalCode         = []int{2, 5, 4, 17} +	oidSignatureMD2WithRSA    = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} +	oidSignatureMD5WithRSA    = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} +	oidSignatureSHA1WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} +	oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} +	oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} +	oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} +	oidSignatureDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} +	oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}  ) -// appendRDNs appends a relativeDistinguishedNameSET to the given rdnSequence -// and returns the new value. The relativeDistinguishedNameSET contains an -// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and -// search for AttributeTypeAndValue. -func appendRDNs(in rdnSequence, values []string, oid asn1.ObjectIdentifier) rdnSequence { -	if len(values) == 0 { -		return in -	} - -	s := make([]attributeTypeAndValue, len(values)) -	for i, value := range values { -		s[i].Type = oid -		s[i].Value = value -	} - -	return append(in, s) -} - -func (n Name) toRDNSequence() (ret rdnSequence) { -	ret = appendRDNs(ret, n.Country, oidCountry) -	ret = appendRDNs(ret, n.Organization, oidOrganization) -	ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit) -	ret = appendRDNs(ret, n.Locality, oidLocatity) -	ret = appendRDNs(ret, n.Province, oidProvince) -	ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress) -	ret = appendRDNs(ret, n.PostalCode, oidPostalCode) -	if len(n.CommonName) > 0 { -		ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName) -	} -	if len(n.SerialNumber) > 0 { -		ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber) -	} - -	return ret -} - -func getSignatureAlgorithmFromOID(oid []int) SignatureAlgorithm { -	if len(oid) == 7 && oid[0] == 1 && oid[1] == 2 && oid[2] == 840 && -		oid[3] == 113549 && oid[4] == 1 && oid[5] == 1 { -		switch oid[6] { -		case 2: -			return MD2WithRSA -		case 4: -			return MD5WithRSA -		case 5: -			return SHA1WithRSA -		case 11: -			return SHA256WithRSA -		case 12: -			return SHA384WithRSA -		case 13: -			return SHA512WithRSA -		} +func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm { +	switch { +	case oid.Equal(oidSignatureMD2WithRSA): +		return MD2WithRSA +	case oid.Equal(oidSignatureMD5WithRSA): +		return MD5WithRSA +	case oid.Equal(oidSignatureSHA1WithRSA): +		return SHA1WithRSA +	case oid.Equal(oidSignatureSHA256WithRSA): +		return SHA256WithRSA +	case oid.Equal(oidSignatureSHA384WithRSA): +		return SHA384WithRSA +	case oid.Equal(oidSignatureSHA512WithRSA): +		return SHA512WithRSA +	case oid.Equal(oidSignatureDSAWithSHA1): +		return DSAWithSHA1 +	case oid.Equal(oidSignatureDSAWithSHA256): +		return DSAWithSHA256  	} -  	return UnknownSignatureAlgorithm  } -func getPublicKeyAlgorithmFromOID(oid []int) PublicKeyAlgorithm { -	if len(oid) == 7 && oid[0] == 1 && oid[1] == 2 && oid[2] == 840 && -		oid[3] == 113549 && oid[4] == 1 && oid[5] == 1 { -		switch oid[6] { -		case 1: -			return RSA -		} -	} +// RFC 3279, 2.3 Public Key Algorithms +// +// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) +//    rsadsi(113549) pkcs(1) 1 } +// +// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 } +// +// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) +//    x9-57(10040) x9cm(4) 1 } +var ( +	oidPublicKeyRsa = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} +	oidPublicKeyDsa = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} +) +func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm { +	switch { +	case oid.Equal(oidPublicKeyRsa): +		return RSA +	case oid.Equal(oidPublicKeyDsa): +		return DSA +	}  	return UnknownPublicKeyAlgorithm  } @@ -402,8 +337,10 @@ const (  // A Certificate represents an X.509 certificate.  type Certificate struct { -	Raw                []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). -	RawTBSCertificate  []byte // Certificate part of raw ASN.1 DER content. +	Raw                     []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). +	RawTBSCertificate       []byte // Certificate part of raw ASN.1 DER content. +	RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo. +  	Signature          []byte  	SignatureAlgorithm SignatureAlgorithm @@ -411,9 +348,9 @@ type Certificate struct {  	PublicKey          interface{}  	Version             int -	SerialNumber        []byte -	Issuer              Name -	Subject             Name +	SerialNumber        *big.Int +	Issuer              pkix.Name +	Subject             pkix.Name  	NotBefore, NotAfter *time.Time // Validity bounds.  	KeyUsage            KeyUsage @@ -482,26 +419,58 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) {  	// TODO(agl): don't ignore the path length constraint. -	var h hash.Hash +	return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature) +} + +// CheckSignature verifies that signature is a valid signature over signed from +// c's public key. +func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err os.Error) {  	var hashType crypto.Hash -	switch c.SignatureAlgorithm { -	case SHA1WithRSA: -		h = sha1.New() +	switch algo { +	case SHA1WithRSA, DSAWithSHA1:  		hashType = crypto.SHA1 +	case SHA256WithRSA, DSAWithSHA256: +		hashType = crypto.SHA256 +	case SHA384WithRSA: +		hashType = crypto.SHA384 +	case SHA512WithRSA: +		hashType = crypto.SHA512  	default:  		return UnsupportedAlgorithmError{}  	} -	pub, ok := parent.PublicKey.(*rsa.PublicKey) -	if !ok { +	h := hashType.New() +	if h == nil {  		return UnsupportedAlgorithmError{}  	} -	h.Write(c.RawTBSCertificate) +	h.Write(signed)  	digest := h.Sum() -	return rsa.VerifyPKCS1v15(pub, hashType, digest, c.Signature) +	switch pub := c.PublicKey.(type) { +	case *rsa.PublicKey: +		return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) +	case *dsa.PublicKey: +		dsaSig := new(dsaSignature) +		if _, err := asn1.Unmarshal(signature, dsaSig); err != nil { +			return err +		} +		if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { +			return os.ErrorString("DSA signature contained zero or negative values") +		} +		if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { +			return os.ErrorString("DSA verification failure") +		} +		return +	} +	return UnsupportedAlgorithmError{} +} + +// CheckCRLSignature checks that the signature in crl is from c. +func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err os.Error) { +	algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm) +	return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign())  }  type UnhandledCriticalExtension struct{} @@ -516,7 +485,7 @@ type basicConstraints struct {  }  type rsaPublicKey struct { -	N asn1.RawValue +	N *big.Int  	E int  } @@ -538,7 +507,8 @@ type generalSubtree struct {  	Max  int    "optional,tag:1"  } -func parsePublicKey(algo PublicKeyAlgorithm, asn1Data []byte) (interface{}, os.Error) { +func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) { +	asn1Data := keyData.PublicKey.RightAlign()  	switch algo {  	case RSA:  		p := new(rsaPublicKey) @@ -547,19 +517,38 @@ func parsePublicKey(algo PublicKeyAlgorithm, asn1Data []byte) (interface{}, os.E  			return nil, err  		} -		if !rawValueIsInteger(&p.N) { -			return nil, asn1.StructuralError{"tags don't match"} -		} -  		pub := &rsa.PublicKey{  			E: p.E, -			N: new(big.Int).SetBytes(p.N.Bytes), +			N: p.N, +		} +		return pub, nil +	case DSA: +		var p *big.Int +		_, err := asn1.Unmarshal(asn1Data, &p) +		if err != nil { +			return nil, err +		} +		paramsData := keyData.Algorithm.Parameters.FullBytes +		params := new(dsaAlgorithmParameters) +		_, err = asn1.Unmarshal(paramsData, params) +		if err != nil { +			return nil, err +		} +		if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { +			return nil, os.ErrorString("zero or negative DSA parameter") +		} +		pub := &dsa.PublicKey{ +			Parameters: dsa.Parameters{ +				P: params.P, +				Q: params.Q, +				G: params.G, +			}, +			Y: p,  		}  		return pub, nil  	default:  		return nil, nil  	} -  	panic("unreachable")  } @@ -567,6 +556,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) {  	out := new(Certificate)  	out.Raw = in.Raw  	out.RawTBSCertificate = in.TBSCertificate.Raw +	out.RawSubjectPublicKeyInfo = in.TBSCertificate.PublicKey.Raw  	out.Signature = in.SignatureValue.RightAlign()  	out.SignatureAlgorithm = @@ -575,15 +565,19 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) {  	out.PublicKeyAlgorithm =  		getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm)  	var err os.Error -	out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, in.TBSCertificate.PublicKey.PublicKey.RightAlign()) +	out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCertificate.PublicKey)  	if err != nil {  		return nil, err  	} +	if in.TBSCertificate.SerialNumber.Sign() < 0 { +		return nil, os.ErrorString("negative serial number") +	} +  	out.Version = in.TBSCertificate.Version + 1 -	out.SerialNumber = in.TBSCertificate.SerialNumber.Bytes -	out.Issuer.fillFromRDNSequence(&in.TBSCertificate.Issuer) -	out.Subject.fillFromRDNSequence(&in.TBSCertificate.Subject) +	out.SerialNumber = in.TBSCertificate.SerialNumber +	out.Issuer.FillFromRDNSequence(&in.TBSCertificate.Issuer) +	out.Subject.FillFromRDNSequence(&in.TBSCertificate.Subject)  	out.NotBefore = in.TBSCertificate.Validity.NotBefore  	out.NotAfter = in.TBSCertificate.Validity.NotAfter @@ -607,13 +601,13 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) {  				}  			case 19:  				// RFC 5280, 4.2.1.9 -				var constriants basicConstraints -				_, err := asn1.Unmarshal(e.Value, &constriants) +				var constraints basicConstraints +				_, err := asn1.Unmarshal(e.Value, &constraints)  				if err == nil {  					out.BasicConstraintsValid = true -					out.IsCA = constriants.IsCA -					out.MaxPathLen = constriants.MaxPathLen +					out.IsCA = constraints.IsCA +					out.MaxPathLen = constraints.MaxPathLen  					continue  				}  			case 17: @@ -841,8 +835,8 @@ var (  	oidExtensionNameConstraints     = []int{2, 5, 29, 30}  ) -func buildExtensions(template *Certificate) (ret []extension, err os.Error) { -	ret = make([]extension, 7 /* maximum number of elements. */ ) +func buildExtensions(template *Certificate) (ret []pkix.Extension, err os.Error) { +	ret = make([]pkix.Extension, 7 /* maximum number of elements. */ )  	n := 0  	if template.KeyUsage != 0 { @@ -959,7 +953,7 @@ var (  // The returned slice is the certificate in DER encoding.  func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err os.Error) {  	asn1PublicKey, err := asn1.Marshal(rsaPublicKey{ -		N: asn1.RawValue{Tag: 2, Bytes: pub.N.Bytes()}, +		N: pub.N,  		E: pub.E,  	})  	if err != nil { @@ -978,12 +972,12 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P  	encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey}  	c := tbsCertificate{  		Version:            2, -		SerialNumber:       asn1.RawValue{Bytes: template.SerialNumber, Tag: 2}, -		SignatureAlgorithm: algorithmIdentifier{oidSHA1WithRSA}, -		Issuer:             parent.Subject.toRDNSequence(), +		SerialNumber:       template.SerialNumber, +		SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, +		Issuer:             parent.Subject.ToRDNSequence(),  		Validity:           validity{template.NotBefore, template.NotAfter}, -		Subject:            template.Subject.toRDNSequence(), -		PublicKey:          publicKeyInfo{algorithmIdentifier{oidRSA}, encodedPublicKey}, +		Subject:            template.Subject.ToRDNSequence(), +		PublicKey:          publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey},  		Extensions:         extensions,  	} @@ -1006,8 +1000,75 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P  	cert, err = asn1.Marshal(certificate{  		nil,  		c, -		algorithmIdentifier{oidSHA1WithRSA}, +		pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},  		asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},  	})  	return  } + +// pemCRLPrefix is the magic string that indicates that we have a PEM encoded +// CRL. +var pemCRLPrefix = []byte("-----BEGIN X509 CRL") +// pemType is the type of a PEM encoded CRL. +var pemType = "X509 CRL" + +// ParseCRL parses a CRL from the given bytes. It's often the case that PEM +// encoded CRLs will appear where they should be DER encoded, so this function +// will transparently handle PEM encoding as long as there isn't any leading +// garbage. +func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err os.Error) { +	if bytes.HasPrefix(crlBytes, pemCRLPrefix) { +		block, _ := pem.Decode(crlBytes) +		if block != nil && block.Type == pemType { +			crlBytes = block.Bytes +		} +	} +	return ParseDERCRL(crlBytes) +} + +// ParseDERCRL parses a DER encoded CRL from the given bytes. +func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err os.Error) { +	certList = new(pkix.CertificateList) +	_, err = asn1.Unmarshal(derBytes, certList) +	if err != nil { +		certList = nil +	} +	return +} + +// CreateCRL returns a DER encoded CRL, signed by this Certificate, that +// contains the given list of revoked certificates. +func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry *time.Time) (crlBytes []byte, err os.Error) { +	tbsCertList := pkix.TBSCertificateList{ +		Version: 2, +		Signature: pkix.AlgorithmIdentifier{ +			Algorithm: oidSignatureSHA1WithRSA, +		}, +		Issuer:              c.Subject.ToRDNSequence(), +		ThisUpdate:          now, +		NextUpdate:          expiry, +		RevokedCertificates: revokedCerts, +	} + +	tbsCertListContents, err := asn1.Marshal(tbsCertList) +	if err != nil { +		return +	} + +	h := sha1.New() +	h.Write(tbsCertListContents) +	digest := h.Sum() + +	signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest) +	if err != nil { +		return +	} + +	return asn1.Marshal(pkix.CertificateList{ +		TBSCertList: tbsCertList, +		SignatureAlgorithm: pkix.AlgorithmIdentifier{ +			Algorithm: oidSignatureSHA1WithRSA, +		}, +		SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, +	}) +} diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index a42113add..dc216505e 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -7,8 +7,11 @@ package x509  import (  	"asn1"  	"big" +	"crypto/dsa"  	"crypto/rand"  	"crypto/rsa" +	"crypto/x509/pkix" +	"encoding/base64"  	"encoding/hex"  	"encoding/pem"  	"testing" @@ -54,6 +57,12 @@ func fromBase10(base10 string) *big.Int {  	return i  } +func bigFromHexString(s string) *big.Int { +	ret := new(big.Int) +	ret.SetString(s, 16) +	return ret +} +  var rsaPrivateKey = &rsa.PrivateKey{  	PublicKey: rsa.PublicKey{  		N: bigFromString("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077"), @@ -200,8 +209,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) {  	}  	template := Certificate{ -		SerialNumber: []byte{1}, -		Subject: Name{ +		SerialNumber: big.NewInt(1), +		Subject: pkix.Name{  			CommonName:   "test.example.com",  			Organization: []string{"Acme Co"},  		}, @@ -245,3 +254,178 @@ func TestCreateSelfSignedCertificate(t *testing.T) {  		return  	}  } + +// Self-signed certificate using DSA with SHA1 +var dsaCertPem = `-----BEGIN CERTIFICATE----- +MIIEDTCCA82gAwIBAgIJALHPghaoxeDhMAkGByqGSM44BAMweTELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAk5DMQ8wDQYDVQQHEwZOZXd0b24xFDASBgNVBAoTC0dvb2ds +ZSwgSW5jMRIwEAYDVQQDEwlKb24gQWxsaWUxIjAgBgkqhkiG9w0BCQEWE2pvbmFs +bGllQGdvb2dsZS5jb20wHhcNMTEwNTE0MDMwMTQ1WhcNMTEwNjEzMDMwMTQ1WjB5 +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCTkMxDzANBgNVBAcTBk5ld3RvbjEUMBIG +A1UEChMLR29vZ2xlLCBJbmMxEjAQBgNVBAMTCUpvbiBBbGxpZTEiMCAGCSqGSIb3 +DQEJARYTam9uYWxsaWVAZ29vZ2xlLmNvbTCCAbcwggEsBgcqhkjOOAQBMIIBHwKB +gQC8hLUnQ7FpFYu4WXTj6DKvXvz8QrJkNJCVMTpKAT7uBpobk32S5RrPKXocd4gN +8lyGB9ggS03EVlEwXvSmO0DH2MQtke2jl9j1HLydClMf4sbx5V6TV9IFw505U1iW +jL7awRMgxge+FsudtJK254FjMFo03ZnOQ8ZJJ9E6AEDrlwIVAJpnBn9moyP11Ox5 +Asc/5dnjb6dPAoGBAJFHd4KVv1iTVCvEG6gGiYop5DJh28hUQcN9kul+2A0yPUSC +X93oN00P8Vh3eYgSaCWZsha7zDG53MrVJ0Zf6v/X/CoZNhLldeNOepivTRAzn+Rz +kKUYy5l1sxYLHQKF0UGNCXfFKZT0PCmgU+PWhYNBBMn6/cIh44vp85ideo5CA4GE +AAKBgFmifCafzeRaohYKXJgMGSEaggCVCRq5xdyDCat+wbOkjC4mfG01/um3G8u5 +LxasjlWRKTR/tcAL7t0QuokVyQaYdVypZXNaMtx1db7YBuHjj3aP+8JOQRI9xz8c +bp5NDJ5pISiFOv4p3GZfqZPcqckDt78AtkQrmnal2txhhjF6o4HeMIHbMB0GA1Ud +DgQWBBQVyyr7hO11ZFFpWX50298Sa3V+rzCBqwYDVR0jBIGjMIGggBQVyyr7hO11 +ZFFpWX50298Sa3V+r6F9pHsweTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk5DMQ8w +DQYDVQQHEwZOZXd0b24xFDASBgNVBAoTC0dvb2dsZSwgSW5jMRIwEAYDVQQDEwlK +b24gQWxsaWUxIjAgBgkqhkiG9w0BCQEWE2pvbmFsbGllQGdvb2dsZS5jb22CCQCx +z4IWqMXg4TAMBgNVHRMEBTADAQH/MAkGByqGSM44BAMDLwAwLAIUPtn/5j8Q1jJI +7ggOIsgrhgUdjGQCFCsmDq1H11q9+9Wp9IMeGrTSKHIM +-----END CERTIFICATE----- +` + +func TestParseCertificateWithDsaPublicKey(t *testing.T) { +	expectedKey := &dsa.PublicKey{ +		Parameters: dsa.Parameters{ +			P: bigFromHexString("00BC84B52743B169158BB85974E3E832AF5EFCFC42B264349095313A4A013EEE069A1B937D92E51ACF297A1C77880DF25C8607D8204B4DC45651305EF4A63B40C7D8C42D91EDA397D8F51CBC9D0A531FE2C6F1E55E9357D205C39D395358968CBEDAC11320C607BE16CB9DB492B6E78163305A34DD99CE43C64927D13A0040EB97"), +			Q: bigFromHexString("009A67067F66A323F5D4EC7902C73FE5D9E36FA74F"), +			G: bigFromHexString("009147778295BF5893542BC41BA806898A29E43261DBC85441C37D92E97ED80D323D44825FDDE8374D0FF15877798812682599B216BBCC31B9DCCAD527465FEAFFD7FC2A193612E575E34E7A98AF4D10339FE47390A518CB9975B3160B1D0285D1418D0977C52994F43C29A053E3D685834104C9FAFDC221E38BE9F3989D7A8E42"), +		}, +		Y: bigFromHexString("59A27C269FCDE45AA2160A5C980C19211A820095091AB9C5DC8309AB7EC1B3A48C2E267C6D35FEE9B71BCBB92F16AC8E559129347FB5C00BEEDD10BA8915C90698755CA965735A32DC7575BED806E1E38F768FFBC24E41123DC73F1C6E9E4D0C9E692128853AFE29DC665FA993DCA9C903B7BF00B6442B9A76A5DADC6186317A"), +	} +	pemBlock, _ := pem.Decode([]byte(dsaCertPem)) +	cert, err := ParseCertificate(pemBlock.Bytes) +	if err != nil { +		t.Fatalf("Failed to parse certificate: %s", err) +	} +	if cert.PublicKeyAlgorithm != DSA { +		t.Errorf("Parsed key algorithm was not DSA") +	} +	parsedKey, ok := cert.PublicKey.(*dsa.PublicKey) +	if !ok { +		t.Fatalf("Parsed key was not a DSA key: %s", err) +	} +	if expectedKey.Y.Cmp(parsedKey.Y) != 0 || +		expectedKey.P.Cmp(parsedKey.P) != 0 || +		expectedKey.Q.Cmp(parsedKey.Q) != 0 || +		expectedKey.G.Cmp(parsedKey.G) != 0 { +		t.Fatal("Parsed key differs from expected key") +	} +} + +func TestParseCertificateWithDSASignatureAlgorithm(t *testing.T) { +	pemBlock, _ := pem.Decode([]byte(dsaCertPem)) +	cert, err := ParseCertificate(pemBlock.Bytes) +	if err != nil { +		t.Fatalf("Failed to parse certificate: %s", err) +	} +	if cert.SignatureAlgorithm != DSAWithSHA1 { +		t.Errorf("Parsed signature algorithm was not DSAWithSHA1") +	} +} + +func TestVerifyCertificateWithDSASignature(t *testing.T) { +	pemBlock, _ := pem.Decode([]byte(dsaCertPem)) +	cert, err := ParseCertificate(pemBlock.Bytes) +	if err != nil { +		t.Fatalf("Failed to parse certificate: %s", err) +	} +	// test cert is self-signed +	if err = cert.CheckSignatureFrom(cert); err != nil { +		t.Fatalf("DSA Certificate verfication failed: %s", err) +	} +} + +const pemCertificate = `-----BEGIN CERTIFICATE----- +MIIB5DCCAZCgAwIBAgIBATALBgkqhkiG9w0BAQUwLTEQMA4GA1UEChMHQWNtZSBDbzEZMBcGA1UE +AxMQdGVzdC5leGFtcGxlLmNvbTAeFw03MDAxMDEwMDE2NDBaFw03MDAxMDIwMzQ2NDBaMC0xEDAO +BgNVBAoTB0FjbWUgQ28xGTAXBgNVBAMTEHRlc3QuZXhhbXBsZS5jb20wWjALBgkqhkiG9w0BAQED +SwAwSAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0fd7Ai2KW5ToIwzFo +fvJcS/STa6HA5gQenRUCAwEAAaOBnjCBmzAOBgNVHQ8BAf8EBAMCAAQwDwYDVR0TAQH/BAUwAwEB +/zANBgNVHQ4EBgQEAQIDBDAPBgNVHSMECDAGgAQBAgMEMBsGA1UdEQQUMBKCEHRlc3QuZXhhbXBs +ZS5jb20wDwYDVR0gBAgwBjAEBgIqAzAqBgNVHR4EIzAhoB8wDoIMLmV4YW1wbGUuY29tMA2CC2V4 +YW1wbGUuY29tMAsGCSqGSIb3DQEBBQNBAHKZKoS1wEQOGhgklx4+/yFYQlnqwKXvar/ZecQvJwui +0seMQnwBhwdBkHfVIU2Fu5VUMRyxlf0ZNaDXcpU581k= +-----END CERTIFICATE-----` + +func TestCRLCreation(t *testing.T) { +	block, _ := pem.Decode([]byte(pemPrivateKey)) +	priv, _ := ParsePKCS1PrivateKey(block.Bytes) +	block, _ = pem.Decode([]byte(pemCertificate)) +	cert, _ := ParseCertificate(block.Bytes) + +	now := time.SecondsToUTC(1000) +	expiry := time.SecondsToUTC(10000) + +	revokedCerts := []pkix.RevokedCertificate{ +		{ +			SerialNumber:   big.NewInt(1), +			RevocationTime: now, +		}, +		{ +			SerialNumber:   big.NewInt(42), +			RevocationTime: now, +		}, +	} + +	crlBytes, err := cert.CreateCRL(rand.Reader, priv, revokedCerts, now, expiry) +	if err != nil { +		t.Errorf("error creating CRL: %s", err) +	} + +	_, err = ParseDERCRL(crlBytes) +	if err != nil { +		t.Errorf("error reparsing CRL: %s", err) +	} +} + +func fromBase64(in string) []byte { +	out := make([]byte, base64.StdEncoding.DecodedLen(len(in))) +	_, err := base64.StdEncoding.Decode(out, []byte(in)) +	if err != nil { +		panic("failed to base64 decode") +	} +	return out +} + +func TestParseDERCRL(t *testing.T) { +	derBytes := fromBase64(derCRLBase64) +	certList, err := ParseDERCRL(derBytes) +	if err != nil { +		t.Errorf("error parsing: %s", err) +		return +	} +	numCerts := len(certList.TBSCertList.RevokedCertificates) +	expected := 88 +	if numCerts != expected { +		t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) +	} + +	if certList.HasExpired(1302517272) { +		t.Errorf("CRL has expired (but shouldn't have)") +	} + +	// Can't check the signature here without a package cycle. +} + +func TestParsePEMCRL(t *testing.T) { +	pemBytes := fromBase64(pemCRLBase64) +	certList, err := ParseCRL(pemBytes) +	if err != nil { +		t.Errorf("error parsing: %s", err) +		return +	} +	numCerts := len(certList.TBSCertList.RevokedCertificates) +	expected := 2 +	if numCerts != expected { +		t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) +	} + +	if certList.HasExpired(1302517272) { +		t.Errorf("CRL has expired (but shouldn't have)") +	} + +	// Can't check the signature here without a package cycle. +} + +const derCRLBase64 = "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" + +const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RFFZSktvWklodmNOQVFFRkJRQXdiREVhTUJnR0ExVUVDaE1SVWxOQklGTmxZM1Z5DQphWFI1SUVsdVl5NHhIakFjQmdOVkJBTVRGVkpUUVNCUWRXSnNhV01nVW05dmRDQkRRU0IyTVRFdU1Dd0dDU3FHDQpTSWIzRFFFSkFSWWZjbk5oYTJWdmJuSnZiM1J6YVdkdVFISnpZWE5sWTNWeWFYUjVMbU52YlJjTk1URXdNakl6DQpNVGt5T0RNd1doY05NVEV3T0RJeU1Ua3lPRE13V2pDQmpEQktBaEVBckRxb2g5RkhKSFhUN09QZ3V1bjQrQmNODQpNRGt4TVRBeU1UUXlOekE1V2pBbU1Bb0dBMVVkRlFRRENnRUpNQmdHQTFVZEdBUVJHQTh5TURBNU1URXdNakUwDQpNalExTlZvd1BnSVJBTEd6blowOTVQQjVhQU9MUGc1N2ZNTVhEVEF5TVRBeU16RTBOVEF4TkZvd0dqQVlCZ05WDQpIUmdFRVJnUE1qQXdNakV3TWpNeE5EVXdNVFJhb0RBd0xqQWZCZ05WSFNNRUdEQVdnQlQxVERGNlVRTS9MTmVMDQpsNWx2cUhHUXEzZzltekFMQmdOVkhSUUVCQUlDQUlRd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUZVNUFzNk16DQpxNVBSc2lmYW9iUVBHaDFhSkx5QytNczVBZ2MwYld5QTNHQWR4dXI1U3BQWmVSV0NCamlQL01FSEJXSkNsQkhQDQpHUmNxNXlJZDNFakRrYUV5eFJhK2k2N0x6dmhJNmMyOUVlNks5cFNZd2ppLzdSVWhtbW5Qclh0VHhsTDBsckxyDQptUVFKNnhoRFJhNUczUUE0Q21VZHNITnZicnpnbUNZcHZWRT0NCi0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0NCg0K" diff --git a/src/pkg/crypto/xtea/block.go b/src/pkg/crypto/xtea/block.go index 3ac36d038..bf5d24599 100644 --- a/src/pkg/crypto/xtea/block.go +++ b/src/pkg/crypto/xtea/block.go @@ -22,7 +22,7 @@ func blockToUint32(src []byte) (uint32, uint32) {  	return r0, r1  } -// uint32ToBlock writes two unint32s into an 8 byte data block. +// uint32ToBlock writes two uint32s into an 8 byte data block.  // Values are written as big endian.  func uint32ToBlock(v0, v1 uint32, dst []byte) {  	dst[0] = byte(v0 >> 24) diff --git a/src/pkg/crypto/xtea/xtea_test.go b/src/pkg/crypto/xtea/xtea_test.go index 03934f169..217d96adc 100644 --- a/src/pkg/crypto/xtea/xtea_test.go +++ b/src/pkg/crypto/xtea/xtea_test.go @@ -8,7 +8,7 @@ import (  	"testing"  ) -// A sample test key for when we just want to initialise a cipher +// A sample test key for when we just want to initialize a cipher  var testKey = []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}  // Test that the block size for XTEA is correct @@ -26,12 +26,12 @@ func TestBlocksize(t *testing.T) {  	result := c.BlockSize()  	if result != 8 { -		t.Errorf("BlockSize function - expected 8, gotr %d", result) +		t.Errorf("BlockSize function - expected 8, got %d", result)  		return  	}  } -// A series of test values to confirm that the Cipher.table array was initialised correctly +// A series of test values to confirm that the Cipher.table array was initialized correctly  var testTable = []uint32{  	0x00112233, 0x6B1568B8, 0xE28CE030, 0xC5089E2D, 0xC5089E2D, 0x1EFBD3A2, 0xA7845C2A, 0x78EF0917,  	0x78EF0917, 0x172682D0, 0x5B6AC714, 0x822AC955, 0x3DE68511, 0xDC1DFECA, 0x2062430E, 0x3611343F, @@ -43,7 +43,7 @@ var testTable = []uint32{  	0x4E22726F, 0x309E306C, 0x309E306C, 0x8A9165E1, 0x1319EE69, 0xF595AC66, 0xF595AC66, 0x4F88E1DB,  } -// Test that the cipher context is initialised correctly +// Test that the cipher context is initialized correctly  func TestCipherInit(t *testing.T) {  	c, err := NewCipher(testKey)  	if err != nil { @@ -53,7 +53,7 @@ func TestCipherInit(t *testing.T) {  	for i := 0; i < len(c.table); i++ {  		if c.table[i] != testTable[i] { -			t.Errorf("NewCipher() failed to initialise Cipher.table[%d] correctly. Expected %08X, got %08X", i, testTable[i], c.table[i]) +			t.Errorf("NewCipher() failed to initialize Cipher.table[%d] correctly. Expected %08X, got %08X", i, testTable[i], c.table[i])  			break  		}  	} | 
