diff options
Diffstat (limited to 'src/pkg/crypto')
40 files changed, 1243 insertions, 239 deletions
diff --git a/src/pkg/crypto/aes/cipher.go b/src/pkg/crypto/aes/cipher.go index 3a9d02318..73223531e 100644 --- a/src/pkg/crypto/aes/cipher.go +++ b/src/pkg/crypto/aes/cipher.go @@ -45,14 +45,14 @@ func NewCipher(key []byte) (*Cipher, os.Error) { // BlockSize returns the AES block size, 16 bytes. // It is necessary to satisfy the Cipher interface in the -// package "crypto/block". +// package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } // Encrypt encrypts the 16-byte buffer src using the key k // and stores the result in dst. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/block/cbc.go). +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) } // Decrypt decrypts the 16-byte buffer src using the key k diff --git a/src/pkg/crypto/blowfish/cipher.go b/src/pkg/crypto/blowfish/cipher.go index f3c5175ac..6c37dfe94 100644 --- a/src/pkg/crypto/blowfish/cipher.go +++ b/src/pkg/crypto/blowfish/cipher.go @@ -42,14 +42,14 @@ func NewCipher(key []byte) (*Cipher, os.Error) { // BlockSize returns the Blowfish block size, 8 bytes. // It is necessary to satisfy the Cipher interface in the -// package "crypto/block". +// package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } // Encrypt encrypts the 8-byte buffer src using the key k // and stores the result in dst. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/block/cbc.go). +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) diff --git a/src/pkg/crypto/cast5/cast5.go b/src/pkg/crypto/cast5/cast5.go index cb62e3132..e9d4a24e2 100644 --- a/src/pkg/crypto/cast5/cast5.go +++ b/src/pkg/crypto/cast5/cast5.go @@ -20,7 +20,7 @@ type Cipher struct { func NewCipher(key []byte) (c *Cipher, err os.Error) { if len(key) != KeySize { - return nil, os.ErrorString("CAST5: keys must be 16 bytes") + return nil, os.NewError("CAST5: keys must be 16 bytes") } c = new(Cipher) diff --git a/src/pkg/crypto/dsa/dsa.go b/src/pkg/crypto/dsa/dsa.go index f0af8bb42..a5f96fe94 100644 --- a/src/pkg/crypto/dsa/dsa.go +++ b/src/pkg/crypto/dsa/dsa.go @@ -79,7 +79,7 @@ func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes L = 3072 N = 256 default: - return os.ErrorString("crypto/dsa: invalid ParameterSizes") + return os.NewError("crypto/dsa: invalid ParameterSizes") } qBytes := make([]byte, N/8) @@ -158,7 +158,7 @@ GeneratePrimes: // PrivateKey must already be valid (see GenerateParameters). func GenerateKey(priv *PrivateKey, rand io.Reader) os.Error { if priv.P == nil || priv.Q == nil || priv.G == nil { - return os.ErrorString("crypto/dsa: parameters not set up before generating key") + return os.NewError("crypto/dsa: parameters not set up before generating key") } x := new(big.Int) diff --git a/src/pkg/crypto/ocsp/ocsp.go b/src/pkg/crypto/ocsp/ocsp.go index 57dbe7d2d..e725bded8 100644 --- a/src/pkg/crypto/ocsp/ocsp.go +++ b/src/pkg/crypto/ocsp/ocsp.go @@ -43,7 +43,7 @@ type certID struct { type responseASN1 struct { Status asn1.Enumerated - Response responseBytes "explicit,tag:0" + Response responseBytes `asn1:"explicit,tag:0"` } type responseBytes struct { @@ -55,30 +55,30 @@ type basicResponse struct { TBSResponseData responseData SignatureAlgorithm pkix.AlgorithmIdentifier Signature asn1.BitString - Certificates []asn1.RawValue "explicit,tag:0,optional" + Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"` } type responseData struct { Raw asn1.RawContent - Version int "optional,default:1,explicit,tag:0" - RequestorName pkix.RDNSequence "optional,explicit,tag:1" - KeyHash []byte "optional,explicit,tag:2" + Version int `asn1:"optional,default:1,explicit,tag:0"` + RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"` + KeyHash []byte `asn1:"optional,explicit,tag:2"` ProducedAt *time.Time Responses []singleResponse } type singleResponse struct { CertID certID - Good asn1.Flag "explicit,tag:0,optional" - Revoked revokedInfo "explicit,tag:1,optional" - Unknown asn1.Flag "explicit,tag:2,optional" + Good asn1.Flag `asn1:"explicit,tag:0,optional"` + Revoked revokedInfo `asn1:"explicit,tag:1,optional"` + Unknown asn1.Flag `asn1:"explicit,tag:2,optional"` ThisUpdate *time.Time - NextUpdate *time.Time "explicit,tag:0,optional" + NextUpdate *time.Time `asn1:"explicit,tag:0,optional"` } type revokedInfo struct { RevocationTime *time.Time - Reason int "explicit,tag:0,optional" + Reason int `asn1:"explicit,tag:0,optional"` } // This is the exposed reflection of the internal OCSP structures. diff --git a/src/pkg/crypto/openpgp/elgamal/Makefile b/src/pkg/crypto/openpgp/elgamal/Makefile new file mode 100644 index 000000000..f730255f8 --- /dev/null +++ b/src/pkg/crypto/openpgp/elgamal/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/openpgp/elgamal +GOFILES=\ + elgamal.go\ + +include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/elgamal/elgamal.go b/src/pkg/crypto/openpgp/elgamal/elgamal.go new file mode 100644 index 000000000..99a6e3e1f --- /dev/null +++ b/src/pkg/crypto/openpgp/elgamal/elgamal.go @@ -0,0 +1,122 @@ +// 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 elgamal implements ElGamal encryption, suitable for OpenPGP, +// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on +// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31, +// n. 4, 1985, pp. 469-472. +// +// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it +// unsuitable for other protocols. RSA should be used in preference in any +// case. +package elgamal + +import ( + "big" + "crypto/rand" + "crypto/subtle" + "io" + "os" +) + +// PublicKey represents an ElGamal public key. +type PublicKey struct { + G, P, Y *big.Int +} + +// PrivateKey represents an ElGamal private key. +type PrivateKey struct { + PublicKey + X *big.Int +} + +// Encrypt encrypts the given message to the given public key. The result is a +// pair of integers. Errors can result from reading random, or because msg is +// too large to be encrypted to the public key. +func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err os.Error) { + pLen := (pub.P.BitLen() + 7) / 8 + if len(msg) > pLen-11 { + err = os.NewError("elgamal: message too long") + return + } + + // EM = 0x02 || PS || 0x00 || M + em := make([]byte, pLen-1) + em[0] = 2 + ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] + err = nonZeroRandomBytes(ps, random) + if err != nil { + return + } + em[len(em)-len(msg)-1] = 0 + copy(mm, msg) + + m := new(big.Int).SetBytes(em) + + k, err := rand.Int(random, pub.P) + if err != nil { + return + } + + c1 = new(big.Int).Exp(pub.G, k, pub.P) + s := new(big.Int).Exp(pub.Y, k, pub.P) + c2 = s.Mul(s, m) + c2.Mod(c2, pub.P) + + return +} + +// Decrypt takes two integers, resulting from an ElGamal encryption, and +// returns the plaintext of the message. An error can result only if the +// ciphertext is invalid. Users should keep in mind that this is a padding +// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can +// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks +// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel +// Bleichenbacher, Advances in Cryptology (Crypto '98), +func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err os.Error) { + s := new(big.Int).Exp(c1, priv.X, priv.P) + s.ModInverse(s, priv.P) + s.Mul(s, c2) + s.Mod(s, priv.P) + em := s.Bytes() + + firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2) + + // The remainder of the plaintext must be a string of non-zero random + // octets, followed by a 0, followed by the message. + // lookingForIndex: 1 iff we are still looking for the zero. + // index: the offset of the first zero byte. + var lookingForIndex, index int + lookingForIndex = 1 + + for i := 1; i < len(em); i++ { + equals0 := subtle.ConstantTimeByteEq(em[i], 0) + index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index) + lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) + } + + if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 { + return nil, os.NewError("elgamal: decryption error") + } + return em[index+1:], nil +} + +// nonZeroRandomBytes fills the given slice with non-zero random octets. +func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) { + _, err = io.ReadFull(rand, s) + if err != nil { + return + } + + for i := 0; i < len(s); i++ { + for s[i] == 0 { + _, err = io.ReadFull(rand, s[i:i+1]) + if err != nil { + return + } + } + } + + return +} diff --git a/src/pkg/crypto/openpgp/elgamal/elgamal_test.go b/src/pkg/crypto/openpgp/elgamal/elgamal_test.go new file mode 100644 index 000000000..101121aa6 --- /dev/null +++ b/src/pkg/crypto/openpgp/elgamal/elgamal_test.go @@ -0,0 +1,49 @@ +// 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 elgamal + +import ( + "big" + "bytes" + "crypto/rand" + "testing" +) + +// This is the 1024-bit MODP group from RFC 5114, section 2.1: +const primeHex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371" + +const generatorHex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5" + +func fromHex(hex string) *big.Int { + n, ok := new(big.Int).SetString(hex, 16) + if !ok { + panic("failed to parse hex number") + } + return n +} + +func TestEncryptDecrypt(t *testing.T) { + priv := &PrivateKey{ + PublicKey: PublicKey{ + G: fromHex(generatorHex), + P: fromHex(primeHex), + }, + X: fromHex("42"), + } + priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P) + + message := []byte("hello world") + c1, c2, err := Encrypt(rand.Reader, &priv.PublicKey, message) + if err != nil { + t.Errorf("error encrypting: %s", err) + } + message2, err := Decrypt(priv, c1, c2) + if err != nil { + t.Errorf("error decrypting: %s", err) + } + if !bytes.Equal(message2, message) { + t.Errorf("decryption failed, got: %x, want: %x", message2, message) + } +} diff --git a/src/pkg/crypto/openpgp/keys.go b/src/pkg/crypto/openpgp/keys.go index 2acb7e612..c70fb7927 100644 --- a/src/pkg/crypto/openpgp/keys.go +++ b/src/pkg/crypto/openpgp/keys.go @@ -12,6 +12,7 @@ import ( "crypto/rsa" "io" "os" + "time" ) // PublicKeyType is the armor type for a PGP public key. @@ -64,6 +65,78 @@ type KeyRing interface { DecryptionKeys() []Key } +// primaryIdentity returns the Identity marked as primary or the first identity +// if none are so marked. +func (e *Entity) primaryIdentity() *Identity { + var firstIdentity *Identity + for _, ident := range e.Identities { + if firstIdentity == nil { + firstIdentity = ident + } + if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { + return ident + } + } + return firstIdentity +} + +// encryptionKey returns the best candidate Key for encrypting a message to the +// given Entity. +func (e *Entity) encryptionKey() Key { + candidateSubkey := -1 + + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications && subkey.PublicKey.PubKeyAlgo.CanEncrypt() { + candidateSubkey = i + break + } + } + + i := e.primaryIdentity() + + if e.PrimaryKey.PubKeyAlgo.CanEncrypt() { + // If we don't have any candidate subkeys for encryption and + // the primary key doesn't have any usage metadata then we + // assume that the primary key is ok. Or, if the primary key is + // marked as ok to encrypt to, then we can obviously use it. + if candidateSubkey == -1 && !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && i.SelfSignature.FlagsValid { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} + } + } + + if candidateSubkey != -1 { + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} + } + + // This Entity appears to be signing only. + return Key{} +} + +// signingKey return the best candidate Key for signing a message with this +// Entity. +func (e *Entity) signingKey() Key { + candidateSubkey := -1 + + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && subkey.Sig.FlagSign && subkey.PublicKey.PubKeyAlgo.CanSign() { + candidateSubkey = i + break + } + } + + i := e.primaryIdentity() + + // If we have no candidate subkey then we assume that it's ok to sign + // with the primary key. + if candidateSubkey == -1 || i.SelfSignature.FlagsValid && i.SelfSignature.FlagSign { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} + } + + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} +} + // An EntityList contains one or more Entities. type EntityList []*Entity @@ -199,6 +272,10 @@ func readEntity(packets *packet.Reader) (*Entity, os.Error) { } } + if !e.PrimaryKey.PubKeyAlgo.CanSign() { + return nil, error.StructuralError("primary key cannot be used for signatures") + } + var current *Identity EachPacket: for { @@ -229,7 +306,7 @@ EachPacket: return nil, error.StructuralError("user ID packet not followed by self-signature") } - if sig.SigType == packet.SigTypePositiveCert && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { + if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, sig); err != nil { return nil, error.StructuralError("user ID self-signature invalid: " + err.String()) } @@ -400,3 +477,69 @@ func (e *Entity) SerializePrivate(w io.Writer) (err os.Error) { } return nil } + +// Serialize writes the public part of the given Entity to w. (No private +// key material will be output). +func (e *Entity) Serialize(w io.Writer) os.Error { + err := e.PrimaryKey.Serialize(w) + if err != nil { + return err + } + for _, ident := range e.Identities { + err = ident.UserId.Serialize(w) + if err != nil { + return err + } + err = ident.SelfSignature.Serialize(w) + if err != nil { + return err + } + for _, sig := range ident.Signatures { + err = sig.Serialize(w) + if err != nil { + return err + } + } + } + for _, subkey := range e.Subkeys { + err = subkey.PublicKey.Serialize(w) + if err != nil { + return err + } + err = subkey.Sig.Serialize(w) + if err != nil { + return err + } + } + return nil +} + +// SignIdentity adds a signature to e, from signer, attesting that identity is +// associated with e. The provided identity must already be an element of +// e.Identities and the private key of signer must have been decrypted if +// necessary. +func (e *Entity) SignIdentity(identity string, signer *Entity) os.Error { + if signer.PrivateKey == nil { + return error.InvalidArgumentError("signing Entity must have a private key") + } + if signer.PrivateKey.Encrypted { + return error.InvalidArgumentError("signing Entity's private key must be decrypted") + } + ident, ok := e.Identities[identity] + if !ok { + return error.InvalidArgumentError("given identity string not found in Entity") + } + + sig := &packet.Signature{ + SigType: packet.SigTypeGenericCert, + PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, + Hash: crypto.SHA256, + CreationTime: uint32(time.Seconds()), + IssuerKeyId: &signer.PrivateKey.KeyId, + } + if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey); err != nil { + return err + } + ident.Signatures = append(ident.Signatures, sig) + return nil +} diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key.go b/src/pkg/crypto/openpgp/packet/encrypted_key.go index b11a9b830..b4730cbc9 100644 --- a/src/pkg/crypto/openpgp/packet/encrypted_key.go +++ b/src/pkg/crypto/openpgp/packet/encrypted_key.go @@ -5,6 +5,8 @@ package packet import ( + "big" + "crypto/openpgp/elgamal" "crypto/openpgp/error" "crypto/rand" "crypto/rsa" @@ -14,14 +16,17 @@ import ( "strconv" ) +const encryptedKeyVersion = 3 + // EncryptedKey represents a public-key encrypted session key. See RFC 4880, // section 5.1. type EncryptedKey struct { KeyId uint64 Algo PublicKeyAlgorithm - Encrypted []byte CipherFunc CipherFunction // only valid after a successful Decrypt Key []byte // only valid after a successful Decrypt + + encryptedMPI1, encryptedMPI2 []byte } func (e *EncryptedKey) parse(r io.Reader) (err os.Error) { @@ -30,37 +35,134 @@ func (e *EncryptedKey) parse(r io.Reader) (err os.Error) { if err != nil { return } - if buf[0] != 3 { + if buf[0] != encryptedKeyVersion { return error.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) } e.KeyId = binary.BigEndian.Uint64(buf[1:9]) e.Algo = PublicKeyAlgorithm(buf[9]) - if e.Algo == PubKeyAlgoRSA || e.Algo == PubKeyAlgoRSAEncryptOnly { - e.Encrypted, _, err = readMPI(r) + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + e.encryptedMPI1, _, err = readMPI(r) + case PubKeyAlgoElGamal: + e.encryptedMPI1, _, err = readMPI(r) + if err != nil { + return + } + e.encryptedMPI2, _, err = readMPI(r) } _, err = consumeAll(r) return } -// DecryptRSA decrypts an RSA encrypted session key with the given private key. -func (e *EncryptedKey) DecryptRSA(priv *rsa.PrivateKey) (err os.Error) { - if e.Algo != PubKeyAlgoRSA && e.Algo != PubKeyAlgoRSAEncryptOnly { - return error.InvalidArgumentError("EncryptedKey not RSA encrypted") +func checksumKeyMaterial(key []byte) uint16 { + var checksum uint16 + for _, v := range key { + checksum += uint16(v) } - b, err := rsa.DecryptPKCS1v15(rand.Reader, priv, e.Encrypted) + return checksum +} + +// Decrypt decrypts an encrypted session key with the given private key. The +// private key must have been decrypted first. +func (e *EncryptedKey) Decrypt(priv *PrivateKey) os.Error { + var err os.Error + var b []byte + + // TODO(agl): use session key decryption routines here to avoid + // padding oracle attacks. + switch priv.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + b, err = rsa.DecryptPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1) + case PubKeyAlgoElGamal: + c1 := new(big.Int).SetBytes(e.encryptedMPI1) + c2 := new(big.Int).SetBytes(e.encryptedMPI2) + b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2) + default: + err = error.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) + } + if err != nil { - return + return err } + e.CipherFunc = CipherFunction(b[0]) e.Key = b[1 : len(b)-2] expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) - var checksum uint16 - for _, v := range e.Key { - checksum += uint16(v) - } + checksum := checksumKeyMaterial(e.Key) if checksum != expectedChecksum { return error.StructuralError("EncryptedKey checksum incorrect") } - return + return nil +} + +// SerializeEncryptedKey serializes an encrypted key packet to w that contains +// key, encrypted to pub. +func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFunc CipherFunction, key []byte) os.Error { + var buf [10]byte + buf[0] = encryptedKeyVersion + binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) + buf[9] = byte(pub.PubKeyAlgo) + + keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */ ) + keyBlock[0] = byte(cipherFunc) + copy(keyBlock[1:], key) + checksum := checksumKeyMaterial(key) + keyBlock[1+len(key)] = byte(checksum >> 8) + keyBlock[1+len(key)+1] = byte(checksum) + + switch pub.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + return serializeEncryptedKeyRSA(w, rand, buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) + case PubKeyAlgoElGamal: + return serializeEncryptedKeyElGamal(w, rand, buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) + case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: + return error.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) + } + + return error.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) +} + +func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) os.Error { + cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock) + if err != nil { + return error.InvalidArgumentError("RSA encryption failed: " + err.String()) + } + + packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText) + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + return writeMPI(w, 8*uint16(len(cipherText)), cipherText) +} + +func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) os.Error { + c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock) + if err != nil { + return error.InvalidArgumentError("ElGamal encryption failed: " + err.String()) + } + + packetLen := 10 /* header length */ + packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8 + packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8 + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + err = writeBig(w, c1) + if err != nil { + return err + } + return writeBig(w, c2) } diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key_test.go b/src/pkg/crypto/openpgp/packet/encrypted_key_test.go index 755ae7a30..b0a14904a 100644 --- a/src/pkg/crypto/openpgp/packet/encrypted_key_test.go +++ b/src/pkg/crypto/openpgp/packet/encrypted_key_test.go @@ -6,6 +6,8 @@ package packet import ( "big" + "bytes" + "crypto/rand" "crypto/rsa" "fmt" "testing" @@ -19,7 +21,28 @@ func bigFromBase10(s string) *big.Int { return b } -func TestEncryptedKey(t *testing.T) { + +var encryptedKeyPub = rsa.PublicKey{ + E: 65537, + N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"), +} + +var encryptedKeyRSAPriv = &rsa.PrivateKey{ + PublicKey: encryptedKeyPub, + D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"), +} + +var encryptedKeyPriv = &PrivateKey{ + PublicKey: PublicKey{ + PubKeyAlgo: PubKeyAlgoRSA, + }, + PrivateKey: encryptedKeyRSAPriv, +} + +func TestDecryptingEncryptedKey(t *testing.T) { + const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" + const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" + p, err := Read(readerFromHex(encryptedKeyHex)) if err != nil { t.Errorf("error from Read: %s", err) @@ -36,23 +59,63 @@ func TestEncryptedKey(t *testing.T) { return } - pub := rsa.PublicKey{ - E: 65537, - N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"), + err = ek.Decrypt(encryptedKeyPriv) + if err != nil { + t.Errorf("error from Decrypt: %s", err) + return } - priv := &rsa.PrivateKey{ - PublicKey: pub, - D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"), + if ek.CipherFunc != CipherAES256 { + t.Errorf("unexpected EncryptedKey contents: %#v", ek) + return + } + + keyHex := fmt.Sprintf("%x", ek.Key) + if keyHex != expectedKeyHex { + t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) } +} - err = ek.DecryptRSA(priv) +func TestEncryptingEncryptedKey(t *testing.T) { + key := []byte{1, 2, 3, 4} + const expectedKeyHex = "01020304" + const keyId = 42 + + pub := &PublicKey{ + PublicKey: &encryptedKeyPub, + KeyId: keyId, + PubKeyAlgo: PubKeyAlgoRSAEncryptOnly, + } + + buf := new(bytes.Buffer) + err := SerializeEncryptedKey(buf, rand.Reader, pub, CipherAES128, key) + if err != nil { + t.Errorf("error writing encrypted key packet: %s", err) + } + + p, err := Read(buf) + if err != nil { + t.Errorf("error from Read: %s", err) + return + } + ek, ok := p.(*EncryptedKey) + if !ok { + t.Errorf("didn't parse an EncryptedKey, got %#v", p) + return + } + + if ek.KeyId != keyId || ek.Algo != PubKeyAlgoRSAEncryptOnly { + t.Errorf("unexpected EncryptedKey contents: %#v", ek) + return + } + + err = ek.Decrypt(encryptedKeyPriv) if err != nil { - t.Errorf("error from DecryptRSA: %s", err) + t.Errorf("error from Decrypt: %s", err) return } - if ek.CipherFunc != CipherAES256 { + if ek.CipherFunc != CipherAES128 { t.Errorf("unexpected EncryptedKey contents: %#v", ek) return } @@ -62,6 +125,3 @@ func TestEncryptedKey(t *testing.T) { t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) } } - -const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" -const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" diff --git a/src/pkg/crypto/openpgp/packet/one_pass_signature.go b/src/pkg/crypto/openpgp/packet/one_pass_signature.go index acbf58bbe..ca826e4f4 100644 --- a/src/pkg/crypto/openpgp/packet/one_pass_signature.go +++ b/src/pkg/crypto/openpgp/packet/one_pass_signature.go @@ -24,6 +24,8 @@ type OnePassSignature struct { IsLast bool } +const onePassSignatureVersion = 3 + func (ops *OnePassSignature) parse(r io.Reader) (err os.Error) { var buf [13]byte @@ -31,7 +33,7 @@ func (ops *OnePassSignature) parse(r io.Reader) (err os.Error) { if err != nil { return } - if buf[0] != 3 { + if buf[0] != onePassSignatureVersion { err = error.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) } @@ -47,3 +49,26 @@ func (ops *OnePassSignature) parse(r io.Reader) (err os.Error) { ops.IsLast = buf[12] != 0 return } + +// Serialize marshals the given OnePassSignature to w. +func (ops *OnePassSignature) Serialize(w io.Writer) os.Error { + var buf [13]byte + buf[0] = onePassSignatureVersion + buf[1] = uint8(ops.SigType) + var ok bool + buf[2], ok = s2k.HashToHashId(ops.Hash) + if !ok { + return error.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) + } + buf[3] = uint8(ops.PubKeyAlgo) + binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) + if ops.IsLast { + buf[12] = 1 + } + + if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { + return err + } + _, err := w.Write(buf[:]) + return err +} diff --git a/src/pkg/crypto/openpgp/packet/packet.go b/src/pkg/crypto/openpgp/packet/packet.go index 640a5b76f..1d7297e38 100644 --- a/src/pkg/crypto/openpgp/packet/packet.go +++ b/src/pkg/crypto/openpgp/packet/packet.go @@ -372,10 +372,30 @@ const ( PubKeyAlgoRSA PublicKeyAlgorithm = 1 PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 - PubKeyAlgoElgamal PublicKeyAlgorithm = 16 + PubKeyAlgoElGamal PublicKeyAlgorithm = 16 PubKeyAlgoDSA PublicKeyAlgorithm = 17 ) +// CanEncrypt returns true if it's possible to encrypt a message to a public +// key of the given type. +func (pka PublicKeyAlgorithm) CanEncrypt() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal: + return true + } + return false +} + +// CanSign returns true if it's possible for a public key of the given type to +// sign a message. +func (pka PublicKeyAlgorithm) CanSign() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: + return true + } + return false +} + // CipherFunction represents the different block ciphers specified for OpenPGP. See // http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 type CipherFunction uint8 @@ -387,8 +407,8 @@ const ( CipherAES256 CipherFunction = 9 ) -// keySize returns the key size, in bytes, of cipher. -func (cipher CipherFunction) keySize() int { +// KeySize returns the key size, in bytes, of cipher. +func (cipher CipherFunction) KeySize() int { switch cipher { case CipherCAST5: return cast5.KeySize diff --git a/src/pkg/crypto/openpgp/packet/private_key.go b/src/pkg/crypto/openpgp/packet/private_key.go index 92e7ee422..6f8133d98 100644 --- a/src/pkg/crypto/openpgp/packet/private_key.go +++ b/src/pkg/crypto/openpgp/packet/private_key.go @@ -9,6 +9,7 @@ import ( "bytes" "crypto/cipher" "crypto/dsa" + "crypto/openpgp/elgamal" "crypto/openpgp/error" "crypto/openpgp/s2k" "crypto/rsa" @@ -181,7 +182,7 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error { return nil } - key := make([]byte, pk.cipher.keySize()) + key := make([]byte, pk.cipher.KeySize()) pk.s2k(key, passphrase) block := pk.cipher.new(key) cfb := cipher.NewCFBDecrypter(block, pk.iv) @@ -224,6 +225,8 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) { return pk.parseRSAPrivateKey(data) case PubKeyAlgoDSA: return pk.parseDSAPrivateKey(data) + case PubKeyAlgoElGamal: + return pk.parseElGamalPrivateKey(data) } panic("impossible") } @@ -277,3 +280,22 @@ func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err os.Error) { return nil } + +func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err os.Error) { + pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey) + priv := new(elgamal.PrivateKey) + priv.PublicKey = *pub + + buf := bytes.NewBuffer(data) + x, _, err := readMPI(buf) + if err != nil { + return + } + + priv.X = new(big.Int).SetBytes(x) + pk.PrivateKey = priv + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} diff --git a/src/pkg/crypto/openpgp/packet/private_key_test.go b/src/pkg/crypto/openpgp/packet/private_key_test.go index e941cc735..60eebaa6b 100644 --- a/src/pkg/crypto/openpgp/packet/private_key_test.go +++ b/src/pkg/crypto/openpgp/packet/private_key_test.go @@ -8,30 +8,50 @@ import ( "testing" ) -func TestPrivateKeyRead(t *testing.T) { - packet, err := Read(readerFromHex(privKeyHex)) - if err != nil { - t.Error(err) - return - } - - privKey := packet.(*PrivateKey) - - if !privKey.Encrypted { - t.Error("private key isn't encrypted") - return - } - - err = privKey.Decrypt([]byte("testing")) - if err != nil { - t.Error(err) - return - } +var privateKeyTests = []struct { + privateKeyHex string + creationTime uint32 +}{ + { + privKeyRSAHex, + 0x4cc349a8, + }, + { + privKeyElGamalHex, + 0x4df9ee1a, + }, +} - if privKey.CreationTime != 0x4cc349a8 || privKey.Encrypted { - t.Errorf("failed to parse, got: %#v", privKey) +func TestPrivateKeyRead(t *testing.T) { + for i, test := range privateKeyTests { + packet, err := Read(readerFromHex(test.privateKeyHex)) + if err != nil { + t.Errorf("#%d: failed to parse: %s", i, err) + continue + } + + privKey := packet.(*PrivateKey) + + if !privKey.Encrypted { + t.Errorf("#%d: private key isn't encrypted", i) + continue + } + + err = privKey.Decrypt([]byte("testing")) + if err != nil { + t.Errorf("#%d: failed to decrypt: %s", i, err) + continue + } + + if privKey.CreationTime != test.creationTime || privKey.Encrypted { + t.Errorf("#%d: bad result, got: %#v", i, privKey) + } } } // Generated with `gpg --export-secret-keys "Test Key 2"` -const privKeyHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" +const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" + +// Generated by `gpg --export-secret-keys` followed by a manual extraction of +// the ElGamal subkey from the packets. +const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc" diff --git a/src/pkg/crypto/openpgp/packet/public_key.go b/src/pkg/crypto/openpgp/packet/public_key.go index 46d365b2a..e6b0ae5f3 100644 --- a/src/pkg/crypto/openpgp/packet/public_key.go +++ b/src/pkg/crypto/openpgp/packet/public_key.go @@ -7,6 +7,7 @@ package packet import ( "big" "crypto/dsa" + "crypto/openpgp/elgamal" "crypto/openpgp/error" "crypto/rsa" "crypto/sha1" @@ -69,6 +70,8 @@ func (pk *PublicKey) parse(r io.Reader) (err os.Error) { err = pk.parseRSA(r) case PubKeyAlgoDSA: err = pk.parseDSA(r) + case PubKeyAlgoElGamal: + err = pk.parseElGamal(r) default: err = error.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) } @@ -117,7 +120,7 @@ func (pk *PublicKey) parseRSA(r io.Reader) (err os.Error) { return } -// parseRSA parses DSA public key material from the given Reader. See RFC 4880, +// parseDSA parses DSA public key material from the given Reader. See RFC 4880, // section 5.5.2. func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) { pk.p.bytes, pk.p.bitLength, err = readMPI(r) @@ -146,6 +149,30 @@ func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) { return } +// parseElGamal parses ElGamal public key material from the given Reader. See +// RFC 4880, section 5.5.2. +func (pk *PublicKey) parseElGamal(r io.Reader) (err os.Error) { + pk.p.bytes, pk.p.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.g.bytes, pk.g.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.y.bytes, pk.y.bitLength, err = readMPI(r) + if err != nil { + return + } + + elgamal := new(elgamal.PublicKey) + elgamal.P = new(big.Int).SetBytes(pk.p.bytes) + elgamal.G = new(big.Int).SetBytes(pk.g.bytes) + elgamal.Y = new(big.Int).SetBytes(pk.y.bytes) + pk.PublicKey = elgamal + return +} + // SerializeSignaturePrefix writes the prefix for this public key to the given Writer. // The prefix is used when calculating a signature over this public key. See // RFC 4880, section 5.2.4. @@ -160,6 +187,10 @@ func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) { pLength += 2 + uint16(len(pk.q.bytes)) pLength += 2 + uint16(len(pk.g.bytes)) pLength += 2 + uint16(len(pk.y.bytes)) + case PubKeyAlgoElGamal: + pLength += 2 + uint16(len(pk.p.bytes)) + pLength += 2 + uint16(len(pk.g.bytes)) + pLength += 2 + uint16(len(pk.y.bytes)) default: panic("unknown public key algorithm") } @@ -180,9 +211,19 @@ func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) { length += 2 + len(pk.q.bytes) length += 2 + len(pk.g.bytes) length += 2 + len(pk.y.bytes) + case PubKeyAlgoElGamal: + length += 2 + len(pk.p.bytes) + length += 2 + len(pk.g.bytes) + length += 2 + len(pk.y.bytes) + default: + panic("unknown public key algorithm") } - err = serializeHeader(w, packetTypePublicKey, length) + packetType := packetTypePublicKey + if pk.IsSubkey { + packetType = packetTypePublicSubkey + } + err = serializeHeader(w, packetType, length) if err != nil { return } @@ -210,13 +251,15 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err os.Error) { return writeMPIs(w, pk.n, pk.e) case PubKeyAlgoDSA: return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) + case PubKeyAlgoElGamal: + return writeMPIs(w, pk.p, pk.g, pk.y) } return error.InvalidArgumentError("bad public-key algorithm") } // CanSign returns true iff this public key can generate signatures func (pk *PublicKey) CanSign() bool { - return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElgamal + return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal } // VerifySignature returns nil iff sig is a valid signature, made by this @@ -240,14 +283,14 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) - err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature) + err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes) if err != nil { return error.SignatureError("RSA verification failure") } return nil case PubKeyAlgoDSA: dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) - if !dsa.Verify(dsaPublicKey, hashBytes, sig.DSASigR, sig.DSASigS) { + if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { return error.SignatureError("DSA verification failure") } return nil diff --git a/src/pkg/crypto/openpgp/packet/signature.go b/src/pkg/crypto/openpgp/packet/signature.go index 3169bac1e..7577e2875 100644 --- a/src/pkg/crypto/openpgp/packet/signature.go +++ b/src/pkg/crypto/openpgp/packet/signature.go @@ -5,7 +5,6 @@ package packet import ( - "big" "crypto" "crypto/dsa" "crypto/openpgp/error" @@ -32,8 +31,11 @@ type Signature struct { HashTag [2]byte CreationTime uint32 // Unix epoch time - RSASignature []byte - DSASigR, DSASigS *big.Int + RSASignature parsedMPI + DSASigR, DSASigS parsedMPI + + // rawSubpackets contains the unparsed subpackets, in order. + rawSubpackets []outputSubpacket // The following are optional so are nil when not included in the // signature. @@ -128,14 +130,11 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) { switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature, _, err = readMPI(r) + sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) case PubKeyAlgoDSA: - var rBytes, sBytes []byte - rBytes, _, err = readMPI(r) - sig.DSASigR = new(big.Int).SetBytes(rBytes) + sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) if err == nil { - sBytes, _, err = readMPI(r) - sig.DSASigS = new(big.Int).SetBytes(sBytes) + sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) } default: panic("unreachable") @@ -177,7 +176,11 @@ const ( // parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err os.Error) { // RFC 4880, section 5.2.3.1 - var length uint32 + var ( + length uint32 + packetType signatureSubpacketType + isCritical bool + ) switch { case subpacket[0] < 192: length = uint32(subpacket[0]) @@ -207,10 +210,11 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r err = error.StructuralError("zero length signature subpacket") return } - packetType := subpacket[0] & 0x7f - isCritial := subpacket[0]&0x80 == 0x80 + packetType = signatureSubpacketType(subpacket[0] & 0x7f) + isCritical = subpacket[0]&0x80 == 0x80 subpacket = subpacket[1:] - switch signatureSubpacketType(packetType) { + sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) + switch packetType { case creationTimeSubpacket: if !isHashed { err = error.StructuralError("signature creation time in non-hashed area") @@ -309,7 +313,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r } default: - if isCritial { + if isCritical { err = error.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) return } @@ -381,7 +385,6 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { // buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. func (sig *Signature) buildHashSuffix() (err os.Error) { - sig.outSubpackets = sig.buildSubpackets() hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) var ok bool @@ -424,6 +427,7 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error) // the hash of the message to be signed and will be mutated by this function. // On success, the signature is stored in sig. Call Serialize to write it out. func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) { + sig.outSubpackets = sig.buildSubpackets() digest, err := sig.signPrepareHash(h) if err != nil { return @@ -431,9 +435,16 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) { switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature, err = rsa.SignPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) + sig.RSASignature.bytes, err = rsa.SignPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) + sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) case PubKeyAlgoDSA: - sig.DSASigR, sig.DSASigS, err = dsa.Sign(rand.Reader, priv.PrivateKey.(*dsa.PrivateKey), digest) + r, s, err := dsa.Sign(rand.Reader, priv.PrivateKey.(*dsa.PrivateKey), digest) + if err == nil { + sig.DSASigR.bytes = r.Bytes() + sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) + sig.DSASigS.bytes = s.Bytes() + sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) + } default: err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) } @@ -464,17 +475,20 @@ func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey) os.Error { // Serialize marshals sig to w. SignRSA or SignDSA must have been called first. func (sig *Signature) Serialize(w io.Writer) (err os.Error) { - if sig.RSASignature == nil && sig.DSASigR == nil { + if len(sig.outSubpackets) == 0 { + sig.outSubpackets = sig.rawSubpackets + } + if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { return error.InvalidArgumentError("Signature: need to call SignRSA or SignDSA before Serialize") } sigLength := 0 switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sigLength = len(sig.RSASignature) + sigLength = 2 + len(sig.RSASignature.bytes) case PubKeyAlgoDSA: - sigLength = mpiLength(sig.DSASigR) - sigLength += mpiLength(sig.DSASigS) + sigLength = 2 + len(sig.DSASigR.bytes) + sigLength += 2 + len(sig.DSASigS.bytes) default: panic("impossible") } @@ -482,7 +496,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) { unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) length := len(sig.HashSuffix) - 6 /* trailer not included */ + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + - 2 /* hash tag */ + 2 /* length of signature MPI */ + sigLength + 2 /* hash tag */ + sigLength err = serializeHeader(w, packetTypeSignature, length) if err != nil { return @@ -509,12 +523,9 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) { switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - err = writeMPI(w, 8*uint16(len(sig.RSASignature)), sig.RSASignature) + err = writeMPIs(w, sig.RSASignature) case PubKeyAlgoDSA: - err = writeBig(w, sig.DSASigR) - if err == nil { - err = writeBig(w, sig.DSASigS) - } + err = writeMPIs(w, sig.DSASigR, sig.DSASigS) default: panic("impossible") } @@ -525,6 +536,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) { type outputSubpacket struct { hashed bool // true if this subpacket is in the hashed area. subpacketType signatureSubpacketType + isCritical bool contents []byte } @@ -534,12 +546,12 @@ func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { creationTime[1] = byte(sig.CreationTime >> 16) creationTime[2] = byte(sig.CreationTime >> 8) creationTime[3] = byte(sig.CreationTime) - subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, creationTime}) + subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) if sig.IssuerKeyId != nil { keyId := make([]byte, 8) binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) - subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, keyId}) + subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) } return diff --git a/src/pkg/crypto/openpgp/packet/signature_test.go b/src/pkg/crypto/openpgp/packet/signature_test.go index 1305548b2..c1bbde8b0 100644 --- a/src/pkg/crypto/openpgp/packet/signature_test.go +++ b/src/pkg/crypto/openpgp/packet/signature_test.go @@ -12,9 +12,7 @@ import ( ) func TestSignatureRead(t *testing.T) { - signatureData, _ := hex.DecodeString(signatureDataHex) - buf := bytes.NewBuffer(signatureData) - packet, err := Read(buf) + packet, err := Read(readerFromHex(signatureDataHex)) if err != nil { t.Error(err) return @@ -25,4 +23,20 @@ func TestSignatureRead(t *testing.T) { } } -const signatureDataHex = "89011c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e" +func TestSignatureReserialize(t *testing.T) { + packet, _ := Read(readerFromHex(signatureDataHex)) + sig := packet.(*Signature) + out := new(bytes.Buffer) + err := sig.Serialize(out) + if err != nil { + t.Errorf("error reserializing: %s", err) + return + } + + expected, _ := hex.DecodeString(signatureDataHex) + if !bytes.Equal(expected, out.Bytes()) { + t.Errorf("output doesn't match input (got vs expected):\n%s\n%s", hex.Dump(out.Bytes()), hex.Dump(expected)) + } +} + +const signatureDataHex = "c2c05c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e" diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go index 25d264acf..ad4f1d621 100644 --- a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go +++ b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go @@ -42,7 +42,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err os.Error) { } ske.CipherFunc = CipherFunction(buf[1]) - if ske.CipherFunc.keySize() == 0 { + if ske.CipherFunc.KeySize() == 0 { return error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) } @@ -78,7 +78,7 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error { return nil } - key := make([]byte, ske.CipherFunc.keySize()) + key := make([]byte, ske.CipherFunc.KeySize()) ske.s2k(key, passphrase) if len(ske.encryptedKey) == 0 { @@ -109,7 +109,7 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error { // 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() + keySize := cipherFunc.KeySize() if keySize == 0 { return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) } diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go index 236c36774..e33c9f3a0 100644 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go +++ b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go @@ -47,7 +47,7 @@ func (se *SymmetricallyEncrypted) parse(r io.Reader) os.Error { // packet can be read. An incorrect key can, with high probability, be detected // immediately and this will result in a KeyIncorrect error being returned. func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, os.Error) { - keySize := c.keySize() + keySize := c.KeySize() if keySize == 0 { return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) } @@ -255,7 +255,7 @@ func (c noOpCloser) Close() os.Error { // 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) { + if c.KeySize() != len(key) { return nil, error.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") } writeCloser := noOpCloser{w} diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go index ba5606e6c..1054fc2f9 100644 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go +++ b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go @@ -81,7 +81,7 @@ const mdcPlaintextHex = "a302789c3b2d93c4e0eb9aba22283539b3203335af44a134afb800c func TestSerialize(t *testing.T) { buf := bytes.NewBuffer(nil) c := CipherAES128 - key := make([]byte, c.keySize()) + key := make([]byte, c.KeySize()) w, err := SerializeSymmetricallyEncrypted(buf, c, key) if err != nil { diff --git a/src/pkg/crypto/openpgp/read.go b/src/pkg/crypto/openpgp/read.go index 46fcde363..d95f613c6 100644 --- a/src/pkg/crypto/openpgp/read.go +++ b/src/pkg/crypto/openpgp/read.go @@ -10,7 +10,6 @@ import ( "crypto/openpgp/armor" "crypto/openpgp/error" "crypto/openpgp/packet" - "crypto/rsa" _ "crypto/sha256" "hash" "io" @@ -57,7 +56,6 @@ type MessageDetails struct { // been consumed. Once EOF has been seen, the following fields are // valid. (An authentication code failure is reported as a // SignatureError error when reading from UnverifiedBody.) - SignatureError os.Error // nil if the signature is good. Signature *packet.Signature // the signature packet itself. @@ -112,7 +110,10 @@ ParsePackets: case *packet.EncryptedKey: // This packet contains the decryption key encrypted to a public key. md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) - if p.Algo != packet.PubKeyAlgoRSA && p.Algo != packet.PubKeyAlgoRSAEncryptOnly { + switch p.Algo { + case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal: + break + default: continue } var keys []Key @@ -155,7 +156,7 @@ FindKey: } if !pk.key.PrivateKey.Encrypted { if len(pk.encryptedKey.Key) == 0 { - pk.encryptedKey.DecryptRSA(pk.key.PrivateKey.PrivateKey.(*rsa.PrivateKey)) + pk.encryptedKey.Decrypt(pk.key.PrivateKey) } if len(pk.encryptedKey.Key) == 0 { continue @@ -249,11 +250,12 @@ FindLiteralData: md.IsSigned = true md.SignedByKeyId = p.KeyId keys := keyring.KeysById(p.KeyId) - for _, key := range keys { + for i, key := range keys { if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { continue } - md.SignedBy = &key + md.SignedBy = &keys[i] + break } case *packet.LiteralData: md.LiteralData = p diff --git a/src/pkg/crypto/openpgp/read_test.go b/src/pkg/crypto/openpgp/read_test.go index a040b5b1b..4dc290ef2 100644 --- a/src/pkg/crypto/openpgp/read_test.go +++ b/src/pkg/crypto/openpgp/read_test.go @@ -33,6 +33,29 @@ func TestReadKeyRing(t *testing.T) { } } +func TestRereadKeyRing(t *testing.T) { + kring, err := ReadKeyRing(readerFromHex(testKeys1And2Hex)) + if err != nil { + t.Errorf("error in initial parse: %s", err) + return + } + out := new(bytes.Buffer) + err = kring[0].Serialize(out) + if err != nil { + t.Errorf("error in serialization: %s", err) + return + } + kring, err = ReadKeyRing(out) + if err != nil { + t.Errorf("error in second parse: %s", err) + return + } + + if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB { + t.Errorf("bad keyring: %#v", kring) + } +} + func TestReadPrivateKeyRing(t *testing.T) { kring, err := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) if err != nil { @@ -102,49 +125,71 @@ func TestTextSignedMessage(t *testing.T) { checkSignedMessage(t, signedTextMessageHex, signedTextInput) } -func TestSignedEncryptedMessage(t *testing.T) { - expected := "Signed and encrypted message\n" - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - prompt := func(keys []Key, symmetric bool) ([]byte, os.Error) { - if symmetric { - t.Errorf("prompt: message was marked as symmetrically encrypted") - return nil, error.KeyIncorrectError - } +var signedEncryptedMessageTests = []struct { + keyRingHex string + messageHex string + signedByKeyId uint64 + encryptedToKeyId uint64 +}{ + { + testKeys1And2PrivateHex, + signedEncryptedMessageHex, + 0xa34d7e18c20c31bb, + 0x2a67d68660df41c7, + }, + { + dsaElGamalTestKeysHex, + signedEncryptedMessage2Hex, + 0x33af447ccd759b09, + 0xcf6a7abcd43e3673, + }, +} - if len(keys) == 0 { - t.Error("prompt: no keys requested") - return nil, error.KeyIncorrectError +func TestSignedEncryptedMessage(t *testing.T) { + for i, test := range signedEncryptedMessageTests { + expected := "Signed and encrypted message\n" + kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) + prompt := func(keys []Key, symmetric bool) ([]byte, os.Error) { + if symmetric { + t.Errorf("prompt: message was marked as symmetrically encrypted") + return nil, error.KeyIncorrectError + } + + if len(keys) == 0 { + t.Error("prompt: no keys requested") + return nil, error.KeyIncorrectError + } + + err := keys[0].PrivateKey.Decrypt([]byte("passphrase")) + if err != nil { + t.Errorf("prompt: error decrypting key: %s", err) + return nil, error.KeyIncorrectError + } + + return nil, nil } - err := keys[0].PrivateKey.Decrypt([]byte("passphrase")) + md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt) if err != nil { - t.Errorf("prompt: error decrypting key: %s", err) - return nil, error.KeyIncorrectError + t.Errorf("#%d: error reading message: %s", i, err) + return } - return nil, nil - } - - md, err := ReadMessage(readerFromHex(signedEncryptedMessageHex), kring, prompt) - if err != nil { - t.Errorf("error reading message: %s", err) - return - } - - if !md.IsSigned || md.SignedByKeyId != 0xa34d7e18c20c31bb || md.SignedBy == nil || !md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) == 0 || md.EncryptedToKeyIds[0] != 0x2a67d68660df41c7 { - t.Errorf("bad MessageDetails: %#v", md) - } + if !md.IsSigned || md.SignedByKeyId != test.signedByKeyId || md.SignedBy == nil || !md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) == 0 || md.EncryptedToKeyIds[0] != test.encryptedToKeyId { + t.Errorf("#%d: bad MessageDetails: %#v", i, md) + } - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("error reading UnverifiedBody: %s", err) - } - if string(contents) != expected { - t.Errorf("bad UnverifiedBody got:%s want:%s", string(contents), expected) - } + contents, err := ioutil.ReadAll(md.UnverifiedBody) + if err != nil { + t.Errorf("#%d: error reading UnverifiedBody: %s", i, err) + } + if string(contents) != expected { + t.Errorf("#%d: bad UnverifiedBody got:%s want:%s", i, string(contents), expected) + } - if md.SignatureError != nil || md.Signature == nil { - t.Errorf("failed to validate: %s", md.SignatureError) + if md.SignatureError != nil || md.Signature == nil { + t.Errorf("#%d: failed to validate: %s", i, md.SignatureError) + } } } @@ -265,12 +310,16 @@ const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d1 const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd00110100010003ff4d91393b9a8e3430b14d6209df42f98dc927425b881f1209f319220841273a802a97c7bdb8b3a7740b3ab5866c4d1d308ad0d3a79bd1e883aacf1ac92dfe720285d10d08752a7efe3c609b1d00f17f2805b217be53999a7da7e493bfc3e9618fd17018991b8128aea70a05dbce30e4fbe626aa45775fa255dd9177aabf4df7cf0200c1ded12566e4bc2bb590455e5becfb2e2c9796482270a943343a7835de41080582c2be3caf5981aa838140e97afa40ad652a0b544f83eb1833b0957dce26e47b0200eacd6046741e9ce2ec5beb6fb5e6335457844fb09477f83b050a96be7da043e17f3a9523567ed40e7a521f818813a8b8a72209f1442844843ccc7eb9805442570200bdafe0438d97ac36e773c7162028d65844c4d463e2420aa2228c6e50dc2743c3d6c72d0d782a5173fe7be2169c8a9f4ef8a7cf3e37165e8c61b89c346cdc6c1799d2b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b00200009d01d8044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f00110100010003fd17a7490c22a79c59281fb7b20f5e6553ec0c1637ae382e8adaea295f50241037f8997cf42c1ce26417e015091451b15424b2c59eb8d4161b0975630408e394d3b00f88d4b4e18e2cc85e8251d4753a27c639c83f5ad4a571c4f19d7cd460b9b73c25ade730c99df09637bd173d8e3e981ac64432078263bb6dc30d3e974150dd0200d0ee05be3d4604d2146fb0457f31ba17c057560785aa804e8ca5530a7cd81d3440d0f4ba6851efcfd3954b7e68908fc0ba47f7ac37bf559c6c168b70d3a7c8cd0200da1c677c4bce06a068070f2b3733b0a714e88d62aa3f9a26c6f5216d48d5c2b5624144f3807c0df30be66b3268eeeca4df1fbded58faf49fc95dc3c35f134f8b01fd1396b6c0fc1b6c4f0eb8f5e44b8eace1e6073e20d0b8bc5385f86f1cf3f050f66af789f3ef1fc107b7f4421e19e0349c730c68f0a226981f4e889054fdb4dc149e8e889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab00200009501fe044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001fe030302e9030f3c783e14856063f16938530e148bc57a7aa3f3e4f90df9dceccdc779bc0835e1ad3d006e4a8d7b36d08b8e0de5a0d947254ecfbd22037e6572b426bcfdc517796b224b0036ff90bc574b5509bede85512f2eefb520fb4b02aa523ba739bff424a6fe81c5041f253f8d757e69a503d3563a104d0d49e9e890b9d0c26f96b55b743883b472caa7050c4acfd4a21f875bdf1258d88bd61224d303dc9df77f743137d51e6d5246b88c406780528fd9a3e15bab5452e5b93970d9dcc79f48b38651b9f15bfbcf6da452837e9cc70683d1bdca94507870f743e4ad902005812488dd342f836e72869afd00ce1850eea4cfa53ce10e3608e13d3c149394ee3cbd0e23d018fcbcb6e2ec5a1a22972d1d462ca05355d0d290dd2751e550d5efb38c6c89686344df64852bf4ff86638708f644e8ec6bd4af9b50d8541cb91891a431326ab2e332faa7ae86cfb6e0540aa63160c1e5cdd5a4add518b303fff0a20117c6bc77f7cfbaf36b04c865c6c2b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b00200009d01fe044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001fe030302e9030f3c783e148560f936097339ae381d63116efcf802ff8b1c9360767db5219cc987375702a4123fd8657d3e22700f23f95020d1b261eda5257e9a72f9a918e8ef22dd5b3323ae03bbc1923dd224db988cadc16acc04b120a9f8b7e84da9716c53e0334d7b66586ddb9014df604b41be1e960dcfcbc96f4ed150a1a0dd070b9eb14276b9b6be413a769a75b519a53d3ecc0c220e85cd91ca354d57e7344517e64b43b6e29823cbd87eae26e2b2e78e6dedfbb76e3e9f77bcb844f9a8932eb3db2c3f9e44316e6f5d60e9e2a56e46b72abe6b06dc9a31cc63f10023d1f5e12d2a3ee93b675c96f504af0001220991c88db759e231b3320dcedf814dcf723fd9857e3d72d66a0f2af26950b915abdf56c1596f46a325bf17ad4810d3535fb02a259b247ac3dbd4cc3ecf9c51b6c07cebb009c1506fba0a89321ec8683e3fd009a6e551d50243e2d5092fefb3321083a4bad91320dc624bd6b5dddf93553e3d53924c05bfebec1fb4bd47e89a1a889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020000" +const dsaElGamalTestKeysHex = "9501e1044dfcb16a110400aa3e5c1a1f43dd28c2ffae8abf5cfce555ee874134d8ba0a0f7b868ce2214beddc74e5e1e21ded354a95d18acdaf69e5e342371a71fbb9093162e0c5f3427de413a7f2c157d83f5cd2f9d791256dc4f6f0e13f13c3302af27f2384075ab3021dff7a050e14854bbde0a1094174855fc02f0bae8e00a340d94a1f22b32e48485700a0cec672ac21258fb95f61de2ce1af74b2c4fa3e6703ff698edc9be22c02ae4d916e4fa223f819d46582c0516235848a77b577ea49018dcd5e9e15cff9dbb4663a1ae6dd7580fa40946d40c05f72814b0f88481207e6c0832c3bded4853ebba0a7e3bd8e8c66df33d5a537cd4acf946d1080e7a3dcea679cb2b11a72a33a2b6a9dc85f466ad2ddf4c3db6283fa645343286971e3dd700703fc0c4e290d45767f370831a90187e74e9972aae5bff488eeff7d620af0362bfb95c1a6c3413ab5d15a2e4139e5d07a54d72583914661ed6a87cce810be28a0aa8879a2dd39e52fb6fe800f4f181ac7e328f740cde3d09a05cecf9483e4cca4253e60d4429ffd679d9996a520012aad119878c941e3cf151459873bdfc2a9563472fe0303027a728f9feb3b864260a1babe83925ce794710cfd642ee4ae0e5b9d74cee49e9c67b6cd0ea5dfbb582132195a121356a1513e1bca73e5b80c58c7ccb4164453412f456c47616d616c2054657374204b65792031886204131102002205024dfcb16a021b03060b090807030206150802090a0b0416020301021e01021780000a091033af447ccd759b09fadd00a0b8fd6f5a790bad7e9f2dbb7632046dc4493588db009c087c6a9ba9f7f49fab221587a74788c00db4889ab00200009d0157044dfcb16a1004008dec3f9291205255ccff8c532318133a6840739dd68b03ba942676f9038612071447bf07d00d559c5c0875724ea16a4c774f80d8338b55fca691a0522e530e604215b467bbc9ccfd483a1da99d7bc2648b4318fdbd27766fc8bfad3fddb37c62b8ae7ccfe9577e9b8d1e77c1d417ed2c2ef02d52f4da11600d85d3229607943700030503ff506c94c87c8cab778e963b76cf63770f0a79bf48fb49d3b4e52234620fc9f7657f9f8d56c96a2b7c7826ae6b57ebb2221a3fe154b03b6637cea7e6d98e3e45d87cf8dc432f723d3d71f89c5192ac8d7290684d2c25ce55846a80c9a7823f6acd9bb29fa6cd71f20bc90eccfca20451d0c976e460e672b000df49466408d527affe0303027a728f9feb3b864260abd761730327bca2aaa4ea0525c175e92bf240682a0e83b226f97ecb2e935b62c9a133858ce31b271fa8eb41f6a1b3cd72a63025ce1a75ee4180dcc284884904181102000905024dfcb16a021b0c000a091033af447ccd759b09dd0b009e3c3e7296092c81bee5a19929462caaf2fff3ae26009e218c437a2340e7ea628149af1ec98ec091a43992b00200009501e1044dfcb1be1104009f61faa61aa43df75d128cbe53de528c4aec49ce9360c992e70c77072ad5623de0a3a6212771b66b39a30dad6781799e92608316900518ec01184a85d872365b7d2ba4bacfb5882ea3c2473d3750dc6178cc1cf82147fb58caa28b28e9f12f6d1efcb0534abed644156c91cca4ab78834268495160b2400bc422beb37d237c2300a0cac94911b6d493bda1e1fbc6feeca7cb7421d34b03fe22cec6ccb39675bb7b94a335c2b7be888fd3906a1125f33301d8aa6ec6ee6878f46f73961c8d57a3e9544d8ef2a2cbfd4d52da665b1266928cfe4cb347a58c412815f3b2d2369dec04b41ac9a71cc9547426d5ab941cccf3b18575637ccfb42df1a802df3cfe0a999f9e7109331170e3a221991bf868543960f8c816c28097e503fe319db10fb98049f3a57d7c80c420da66d56f3644371631fad3f0ff4040a19a4fedc2d07727a1b27576f75a4d28c47d8246f27071e12d7a8de62aad216ddbae6aa02efd6b8a3e2818cda48526549791ab277e447b3a36c57cefe9b592f5eab73959743fcc8e83cbefec03a329b55018b53eec196765ae40ef9e20521a603c551efe0303020950d53a146bf9c66034d00c23130cce95576a2ff78016ca471276e8227fb30b1ffbd92e61804fb0c3eff9e30b1a826ee8f3e4730b4d86273ca977b4164453412f456c47616d616c2054657374204b65792032886204131102002205024dfcb1be021b03060b090807030206150802090a0b0416020301021e01021780000a0910a86bf526325b21b22bd9009e34511620415c974750a20df5cb56b182f3b48e6600a0a9466cb1a1305a84953445f77d461593f1d42bc1b00200009d0157044dfcb1be1004009565a951da1ee87119d600c077198f1c1bceb0f7aa54552489298e41ff788fa8f0d43a69871f0f6f77ebdfb14a4260cf9fbeb65d5844b4272a1904dd95136d06c3da745dc46327dd44a0f16f60135914368c8039a34033862261806bb2c5ce1152e2840254697872c85441ccb7321431d75a747a4bfb1d2c66362b51ce76311700030503fc0ea76601c196768070b7365a200e6ddb09307f262d5f39eec467b5f5784e22abdf1aa49226f59ab37cb49969d8f5230ea65caf56015abda62604544ed526c5c522bf92bed178a078789f6c807b6d34885688024a5bed9e9f8c58d11d4b82487b44c5f470c5606806a0443b79cadb45e0f897a561a53f724e5349b9267c75ca17fe0303020950d53a146bf9c660bc5f4ce8f072465e2d2466434320c1e712272fafc20e342fe7608101580fa1a1a367e60486a7cd1246b7ef5586cf5e10b32762b710a30144f12dd17dd4884904181102000905024dfcb1be021b0c000a0910a86bf526325b21b2904c00a0b2b66b4b39ccffda1d10f3ea8d58f827e30a8b8e009f4255b2d8112a184e40cde43a34e8655ca7809370b0020000" + const signedMessageHex = "a3019bc0cbccc0c4b8d8b74ee2108fe16ec6d3ca490cbe362d3f8333d3f352531472538b8b13d353b97232f352158c20943157c71c16064626063656269052062e4e01987e9b6fccff4b7df3a34c534b23e679cbec3bc0f8f6e64dfb4b55fe3f8efa9ce110ddb5cd79faf1d753c51aecfa669f7e7aa043436596cccc3359cb7dd6bbe9ecaa69e5989d9e57209571edc0b2fa7f57b9b79a64ee6e99ce1371395fee92fec2796f7b15a77c386ff668ee27f6d38f0baa6c438b561657377bf6acff3c5947befd7bf4c196252f1d6e5c524d0300" const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c178233d3f352531472538b8b13d35379b97232f352158ca0b4312f57c71c1646462606365626906a062e4e019811591798ff99bf8afee860b0d8a8c2a85c3387e3bcf0bb3b17987f2bbcfab2aa526d930cbfd3d98757184df3995c9f3e7790e36e3e9779f06089d4c64e9e47dd6202cb6e9bc73c5d11bb59fbaf89d22d8dc7cf199ddf17af96e77c5f65f9bbed56f427bd8db7af37f6c9984bf9385efaf5f184f986fb3e6adb0ecfe35bbf92d16a7aa2a344fb0bc52fb7624f0200" const signedEncryptedMessageHex = "848c032a67d68660df41c70103ff5789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8d2c03b018bd210b1d3791e1aba74b0f1034e122ab72e760492c192383cf5e20b5628bd043272d63df9b923f147eb6091cd897553204832aba48fec54aa447547bb16305a1024713b90e77fd0065f1918271947549205af3c74891af22ee0b56cd29bfec6d6e351901cd4ab3ece7c486f1e32a792d4e474aed98ee84b3f591c7dff37b64e0ecd68fd036d517e412dcadf85840ce184ad7921ad446c4ee28db80447aea1ca8d4f574db4d4e37688158ddd19e14ee2eab4873d46947d65d14a23e788d912cf9a19624ca7352469b72a83866b7c23cb5ace3deab3c7018061b0ba0f39ed2befe27163e5083cf9b8271e3e3d52cc7ad6e2a3bd81d4c3d7022f8d" +const signedEncryptedMessage2Hex = "85010e03cf6a7abcd43e36731003fb057f5495b79db367e277cdbe4ab90d924ddee0c0381494112ff8c1238fb0184af35d1731573b01bc4c55ecacd2aafbe2003d36310487d1ecc9ac994f3fada7f9f7f5c3a64248ab7782906c82c6ff1303b69a84d9a9529c31ecafbcdb9ba87e05439897d87e8a2a3dec55e14df19bba7f7bd316291c002ae2efd24f83f9e3441203fc081c0c23dc3092a454ca8a082b27f631abf73aca341686982e8fbda7e0e7d863941d68f3de4a755c2964407f4b5e0477b3196b8c93d551dd23c8beef7d0f03fbb1b6066f78907faf4bf1677d8fcec72651124080e0b7feae6b476e72ab207d38d90b958759fdedfc3c6c35717c9dbfc979b3cfbbff0a76d24a5e57056bb88acbd2a901ef64bc6e4db02adc05b6250ff378de81dca18c1910ab257dff1b9771b85bb9bbe0a69f5989e6d1710a35e6dfcceb7d8fb5ccea8db3932b3d9ff3fe0d327597c68b3622aec8e3716c83a6c93f497543b459b58ba504ed6bcaa747d37d2ca746fe49ae0a6ce4a8b694234e941b5159ff8bd34b9023da2814076163b86f40eed7c9472f81b551452d5ab87004a373c0172ec87ea6ce42ccfa7dbdad66b745496c4873d8019e8c28d6b3" + const symmetricallyEncryptedCompressedHex = "8c0d04030302eb4a03808145d0d260c92f714339e13de5a79881216431925bf67ee2898ea61815f07894cd0703c50d0a76ef64d482196f47a8bc729af9b80bb6" const dsaTestKeyHex = "9901a2044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" diff --git a/src/pkg/crypto/openpgp/write.go b/src/pkg/crypto/openpgp/write.go index 48c86f604..9884472ce 100644 --- a/src/pkg/crypto/openpgp/write.go +++ b/src/pkg/crypto/openpgp/write.go @@ -9,10 +9,13 @@ import ( "crypto/openpgp/armor" "crypto/openpgp/error" "crypto/openpgp/packet" + "crypto/openpgp/s2k" "crypto/rand" _ "crypto/sha256" + "hash" "io" "os" + "strconv" "time" ) @@ -98,7 +101,7 @@ type FileHints struct { } // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. -// The resulting WriteCloser MUST be closed after the contents of the file have +// 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 { @@ -115,3 +118,191 @@ func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHi } return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) } + +// intersectPreferences mutates and returns a prefix of a that contains only +// the values in the intersection of a and b. The order of a is preserved. +func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { + var j int + for _, v := range a { + for _, v2 := range b { + if v == v2 { + a[j] = v + j++ + break + } + } + } + + return a[:j] +} + +func hashToHashId(h crypto.Hash) uint8 { + v, ok := s2k.HashToHashId(h) + if !ok { + panic("tried to convert unknown hash") + } + return v +} + +// Encrypt encrypts a message to a number of recipients and, optionally, signs +// it. hints contains optional information, that is also encrypted, that aids +// the recipients in processing the message. The resulting WriteCloser must +// be closed after the contents of the file have been written. +func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints) (plaintext io.WriteCloser, err os.Error) { + var signer *packet.PrivateKey + if signed != nil { + signer = signed.signingKey().PrivateKey + if signer == nil || signer.Encrypted { + return nil, error.InvalidArgumentError("signing key must be decrypted") + } + } + + // These are the possible ciphers that we'll use for the message. + candidateCiphers := []uint8{ + uint8(packet.CipherAES128), + uint8(packet.CipherAES256), + uint8(packet.CipherCAST5), + } + // These are the possible hash functions that we'll use for the signature. + candidateHashes := []uint8{ + hashToHashId(crypto.SHA256), + hashToHashId(crypto.SHA512), + hashToHashId(crypto.SHA1), + hashToHashId(crypto.RIPEMD160), + } + // In the event that a recipient doesn't specify any supported ciphers + // or hash functions, these are the ones that we assume that every + // implementation supports. + defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] + defaultHashes := candidateHashes[len(candidateHashes)-1:] + + encryptKeys := make([]Key, len(to)) + for i := range to { + encryptKeys[i] = to[i].encryptionKey() + if encryptKeys[i].PublicKey == nil { + return nil, error.InvalidArgumentError("cannot encrypt a message to key id " + strconv.Uitob64(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") + } + + sig := to[i].primaryIdentity().SelfSignature + + preferredSymmetric := sig.PreferredSymmetric + if len(preferredSymmetric) == 0 { + preferredSymmetric = defaultCiphers + } + preferredHashes := sig.PreferredHash + if len(preferredHashes) == 0 { + preferredHashes = defaultHashes + } + candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) + candidateHashes = intersectPreferences(candidateHashes, preferredHashes) + } + + if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { + return nil, error.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") + } + + cipher := packet.CipherFunction(candidateCiphers[0]) + hash, _ := s2k.HashIdToHash(candidateHashes[0]) + symKey := make([]byte, cipher.KeySize()) + if _, err := io.ReadFull(rand.Reader, symKey); err != nil { + return nil, err + } + + for _, key := range encryptKeys { + if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil { + return nil, err + } + } + + encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey) + if err != nil { + return + } + + if signer != nil { + ops := &packet.OnePassSignature{ + SigType: packet.SigTypeBinary, + Hash: hash, + PubKeyAlgo: signer.PubKeyAlgo, + KeyId: signer.KeyId, + IsLast: true, + } + if err := ops.Serialize(encryptedData); err != nil { + return nil, err + } + } + + if hints == nil { + hints = &FileHints{} + } + + w := encryptedData + if signer != nil { + // If we need to write a signature packet after the literal + // data then we need to stop literalData from closing + // encryptedData. + w = noOpCloser{encryptedData} + + } + literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) + if err != nil { + return nil, err + } + + if signer != nil { + return signatureWriter{encryptedData, literalData, hash, hash.New(), signer}, nil + } + return literalData, nil +} + +// signatureWriter hashes the contents of a message while passing it along to +// literalData. When closed, it closes literalData, writes a signature packet +// to encryptedData and then also closes encryptedData. +type signatureWriter struct { + encryptedData io.WriteCloser + literalData io.WriteCloser + hashType crypto.Hash + h hash.Hash + signer *packet.PrivateKey +} + +func (s signatureWriter) Write(data []byte) (int, os.Error) { + s.h.Write(data) + return s.literalData.Write(data) +} + +func (s signatureWriter) Close() os.Error { + sig := &packet.Signature{ + SigType: packet.SigTypeBinary, + PubKeyAlgo: s.signer.PubKeyAlgo, + Hash: s.hashType, + CreationTime: uint32(time.Seconds()), + IssuerKeyId: &s.signer.KeyId, + } + + if err := sig.Sign(s.h, s.signer); err != nil { + return err + } + if err := s.literalData.Close(); err != nil { + return err + } + if err := sig.Serialize(s.encryptedData); err != nil { + return err + } + return s.encryptedData.Close() +} + +// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. +// TODO: we have two of these in OpenPGP packages alone. This probably needs +// to be promoted somewhere more common. +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 +} diff --git a/src/pkg/crypto/openpgp/write_test.go b/src/pkg/crypto/openpgp/write_test.go index 8551aeb63..c542dfa45 100644 --- a/src/pkg/crypto/openpgp/write_test.go +++ b/src/pkg/crypto/openpgp/write_test.go @@ -9,6 +9,7 @@ import ( "crypto/rand" "os" "io" + "io/ioutil" "testing" "time" ) @@ -120,3 +121,113 @@ func TestSymmetricEncryption(t *testing.T) { t.Errorf("recovered message incorrect got '%s', want '%s'", messageBuf.Bytes(), message) } } + +var testEncryptionTests = []struct { + keyRingHex string + isSigned bool +}{ + { + testKeys1And2PrivateHex, + false, + }, + { + testKeys1And2PrivateHex, + true, + }, + { + dsaElGamalTestKeysHex, + false, + }, + { + dsaElGamalTestKeysHex, + true, + }, +} + +func TestEncryption(t *testing.T) { + for i, test := range testEncryptionTests { + kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) + + passphrase := []byte("passphrase") + for _, entity := range kring { + if entity.PrivateKey != nil && entity.PrivateKey.Encrypted { + err := entity.PrivateKey.Decrypt(passphrase) + if err != nil { + t.Errorf("#%d: failed to decrypt key", i) + } + } + for _, subkey := range entity.Subkeys { + if subkey.PrivateKey != nil && subkey.PrivateKey.Encrypted { + err := subkey.PrivateKey.Decrypt(passphrase) + if err != nil { + t.Errorf("#%d: failed to decrypt subkey", i) + } + } + } + } + + var signed *Entity + if test.isSigned { + signed = kring[0] + } + + buf := new(bytes.Buffer) + w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */ ) + if err != nil { + t.Errorf("#%d: error in Encrypt: %s", i, err) + continue + } + + const message = "testing" + _, err = w.Write([]byte(message)) + if err != nil { + t.Errorf("#%d: error writing plaintext: %s", i, err) + continue + } + err = w.Close() + if err != nil { + t.Errorf("#%d: error closing WriteCloser: %s", i, err) + continue + } + + md, err := ReadMessage(buf, kring, nil /* no prompt */ ) + if err != nil { + t.Errorf("#%d: error reading message: %s", i, err) + continue + } + + if test.isSigned { + expectedKeyId := kring[0].signingKey().PublicKey.KeyId + if md.SignedByKeyId != expectedKeyId { + t.Errorf("#%d: message signed by wrong key id, got: %d, want: %d", i, *md.SignedBy, expectedKeyId) + } + if md.SignedBy == nil { + t.Errorf("#%d: failed to find the signing Entity", i) + } + } + + plaintext, err := ioutil.ReadAll(md.UnverifiedBody) + if err != nil { + t.Errorf("#%d: error reading encrypted contents: %s", i, err) + continue + } + + expectedKeyId := kring[0].encryptionKey().PublicKey.KeyId + if len(md.EncryptedToKeyIds) != 1 || md.EncryptedToKeyIds[0] != expectedKeyId { + t.Errorf("#%d: expected message to be encrypted to %v, but got %#v", i, expectedKeyId, md.EncryptedToKeyIds) + } + + if string(plaintext) != message { + t.Errorf("#%d: got: %s, want: %s", i, string(plaintext), message) + } + + if test.isSigned { + if md.SignatureError != nil { + t.Errorf("#%d: signature error: %s", i, err) + } + if md.Signature == nil { + t.Error("signature missing") + } + } + } +} diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go index 281d6dc6a..0eab6b213 100755 --- a/src/pkg/crypto/rand/rand_windows.go +++ b/src/pkg/crypto/rand/rand_windows.go @@ -19,7 +19,7 @@ func init() { Reader = &rngReader{} } // A rngReader satisfies reads by reading from the Windows CryptGenRandom API. type rngReader struct { - prov uint32 + prov syscall.Handle mu sync.Mutex } diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go index 3defa62ea..600623114 100644 --- a/src/pkg/crypto/rsa/pkcs1v15.go +++ b/src/pkg/crypto/rsa/pkcs1v15.go @@ -232,11 +232,11 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err os.Error) { hashLen = hash.Size() if inLen != hashLen { - return 0, nil, os.ErrorString("input must be hashed message") + return 0, nil, os.NewError("input must be hashed message") } prefix, ok := hashPrefixes[hash] if !ok { - return 0, nil, os.ErrorString("unsupported hash function") + return 0, nil, os.NewError("unsupported hash function") } return } diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index 380f71570..6957659f2 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -64,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.NewError("prime factor is composite") } } @@ -74,7 +74,7 @@ func (priv *PrivateKey) Validate() os.Error { modulus.Mul(modulus, prime) } if modulus.Cmp(priv.N) != 0 { - return os.ErrorString("invalid modulus") + return os.NewError("invalid modulus") } // Check that e and totient(Πprimes) are coprime. totient := new(big.Int).Set(bigOne) @@ -88,13 +88,13 @@ func (priv *PrivateKey) Validate() os.Error { y := new(big.Int) big.GcdInt(gcd, x, y, totient, e) if gcd.Cmp(bigOne) != 0 { - return os.ErrorString("invalid public exponent E") + return os.NewError("invalid public exponent E") } // Check that de ≡ 1 (mod totient(Πprimes)) de := new(big.Int).Mul(priv.D, e) de.Mod(de, totient) if de.Cmp(bigOne) != 0 { - return os.ErrorString("invalid private exponent D") + return os.NewError("invalid private exponent D") } return nil } @@ -127,7 +127,7 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *Priva priv.E = 3 if nprimes < 2 { - return nil, os.ErrorString("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") + return nil, os.NewError("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") } primes := make([]*big.Int, nprimes) diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go index 097e182bd..fac65afd9 100644 --- a/src/pkg/crypto/tls/conn.go +++ b/src/pkg/crypto/tls/conn.go @@ -790,10 +790,10 @@ func (c *Conn) VerifyHostname(host string) os.Error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if !c.isClient { - return os.ErrorString("VerifyHostname called on TLS server connection") + return os.NewError("VerifyHostname called on TLS server connection") } if !c.handshakeComplete { - return os.ErrorString("TLS handshake has not yet been performed") + return os.NewError("TLS handshake has not yet been performed") } return c.peerCertificates[0].VerifyHostname(host) } diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go index f46188879..41206e276 100644 --- a/src/pkg/crypto/tls/generate_cert.go +++ b/src/pkg/crypto/tls/generate_cert.go @@ -8,8 +8,10 @@ package main import ( - "crypto/rsa" + "big" + "crypto/x509/pkix" "crypto/rand" + "crypto/rsa" "crypto/x509" "encoding/pem" "flag" @@ -32,8 +34,8 @@ func main() { now := time.Seconds() template := x509.Certificate{ - SerialNumber: []byte{0}, - Subject: x509.Name{ + SerialNumber: new(big.Int).SetInt64(0), + Subject: pkix.Name{ CommonName: *hostName, Organization: []string{"Acme Co"}, }, diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go index c758c96d4..15604cea7 100644 --- a/src/pkg/crypto/tls/handshake_client.go +++ b/src/pkg/crypto/tls/handshake_client.go @@ -40,7 +40,7 @@ func (c *Conn) clientHandshake() os.Error { _, err := io.ReadFull(c.config.rand(), hello.random[4:]) if err != nil { c.sendAlert(alertInternalError) - return os.ErrorString("short read from Rand") + return os.NewError("short read from Rand") } finishedHash.Write(hello.marshal()) @@ -69,7 +69,7 @@ func (c *Conn) clientHandshake() os.Error { if !hello.nextProtoNeg && serverHello.nextProtoNeg { c.sendAlert(alertHandshakeFailure) - return os.ErrorString("server advertised unrequested NPN") + return os.NewError("server advertised unrequested NPN") } suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) @@ -92,7 +92,7 @@ func (c *Conn) clientHandshake() os.Error { cert, err := x509.ParseCertificate(asn1Data) if err != nil { c.sendAlert(alertBadCertificate) - return os.ErrorString("failed to parse certificate from server: " + err.String()) + return os.NewError("failed to parse certificate from server: " + err.String()) } certs[i] = cert } diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index e9431c6fa..44a324041 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -173,7 +173,7 @@ FindCipherSuite: cert, err := x509.ParseCertificate(asn1Data) if err != nil { c.sendAlert(alertBadCertificate) - return os.ErrorString("could not parse client's certificate: " + err.String()) + return os.NewError("could not parse client's certificate: " + err.String()) } certs[i] = cert } @@ -182,7 +182,7 @@ FindCipherSuite: for i := 1; i < len(certs); i++ { if err := certs[i-1].CheckSignatureFrom(certs[i]); err != nil { c.sendAlert(alertBadCertificate) - return os.ErrorString("could not validate certificate signature: " + err.String()) + return os.NewError("could not validate certificate signature: " + err.String()) } } @@ -229,7 +229,7 @@ FindCipherSuite: err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) if err != nil { c.sendAlert(alertBadCertificate) - return os.ErrorString("could not validate signature of connection nonces: " + err.String()) + return os.NewError("could not validate signature of connection nonces: " + err.String()) } finishedHash.Write(certVerify.marshal()) diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go index 84f90c45a..48472fb6a 100644 --- a/src/pkg/crypto/tls/key_agreement.go +++ b/src/pkg/crypto/tls/key_agreement.go @@ -32,11 +32,11 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe } if len(ckx.ciphertext) < 2 { - return nil, os.ErrorString("bad ClientKeyExchange") + return nil, os.NewError("bad ClientKeyExchange") } ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) if ciphertextLen != len(ckx.ciphertext)-2 { - return nil, os.ErrorString("bad ClientKeyExchange") + return nil, os.NewError("bad ClientKeyExchange") } ciphertext := ckx.ciphertext[2:] @@ -54,7 +54,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe } func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { - return os.ErrorString("unexpected ServerKeyExchange") + return os.NewError("unexpected ServerKeyExchange") } func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { @@ -146,7 +146,7 @@ Curve: md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams) sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, crypto.MD5SHA1, md5sha1) if err != nil { - return nil, os.ErrorString("failed to sign ECDHE parameters: " + err.String()) + return nil, os.NewError("failed to sign ECDHE parameters: " + err.String()) } skx := new(serverKeyExchangeMsg) @@ -162,11 +162,11 @@ Curve: func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { - return nil, os.ErrorString("bad ClientKeyExchange") + return nil, os.NewError("bad ClientKeyExchange") } x, y := ka.curve.Unmarshal(ckx.ciphertext[1:]) if x == nil { - return nil, os.ErrorString("bad ClientKeyExchange") + return nil, os.NewError("bad ClientKeyExchange") } x, _ = ka.curve.ScalarMult(x, y, ka.privateKey) preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) @@ -176,12 +176,14 @@ func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *cl return preMasterSecret, nil } +var errServerKeyExchange = os.NewError("invalid ServerKeyExchange") + func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { if len(skx.key) < 4 { - goto Error + return errServerKeyExchange } if skx.key[0] != 3 { // named curve - return os.ErrorString("server selected unsupported curve") + return os.NewError("server selected unsupported curve") } curveid := uint16(skx.key[1])<<8 | uint16(skx.key[2]) @@ -193,39 +195,36 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH case curveP521: ka.curve = elliptic.P521() default: - return os.ErrorString("server selected unsupported curve") + return os.NewError("server selected unsupported curve") } publicLen := int(skx.key[3]) if publicLen+4 > len(skx.key) { - goto Error + return errServerKeyExchange } ka.x, ka.y = ka.curve.Unmarshal(skx.key[4 : 4+publicLen]) if ka.x == nil { - goto Error + return errServerKeyExchange } serverECDHParams := skx.key[:4+publicLen] sig := skx.key[4+publicLen:] if len(sig) < 2 { - goto Error + return errServerKeyExchange } sigLen := int(sig[0])<<8 | int(sig[1]) if sigLen+2 != len(sig) { - goto Error + return errServerKeyExchange } sig = sig[2:] md5sha1 := md5SHA1Hash(clientHello.random, serverHello.random, serverECDHParams) return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), crypto.MD5SHA1, md5sha1, sig) - -Error: - return os.ErrorString("invalid ServerKeyExchange") } func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { if ka.curve == nil { - return nil, nil, os.ErrorString("missing ServerKeyExchange message") + return nil, nil, os.NewError("missing ServerKeyExchange message") } priv, mx, my, err := ka.curve.GenerateKey(config.rand()) if err != nil { diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go index 9e5c9270a..4f0859fee 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -147,19 +147,19 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err } if len(cert.Certificate) == 0 { - err = os.ErrorString("crypto/tls: failed to parse certificate PEM data") + err = os.NewError("crypto/tls: failed to parse certificate PEM data") return } keyDERBlock, _ := pem.Decode(keyPEMBlock) if keyDERBlock == nil { - err = os.ErrorString("crypto/tls: failed to parse key PEM data") + err = os.NewError("crypto/tls: failed to parse key PEM data") return } key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) if err != nil { - err = os.ErrorString("crypto/tls: failed to parse key: " + err.String()) + err = os.NewError("crypto/tls: failed to parse key: " + err.String()) return } @@ -173,7 +173,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err } if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 { - err = os.ErrorString("crypto/tls: private key does not match public key") + err = os.NewError("crypto/tls: private key does not match public key") return } diff --git a/src/pkg/crypto/twofish/twofish.go b/src/pkg/crypto/twofish/twofish.go index 1a1aac9b9..2e537c606 100644 --- a/src/pkg/crypto/twofish/twofish.go +++ b/src/pkg/crypto/twofish/twofish.go @@ -269,7 +269,7 @@ func h(in, key []byte, offset int) uint32 { // Encrypt encrypts a 16-byte block from src to dst, which may overlap. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/block/cbc.go). +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { S1 := c.s[0] S2 := c.s[1] diff --git a/src/pkg/crypto/x509/pkix/pkix.go b/src/pkg/crypto/x509/pkix/pkix.go index 7806b2a2e..266fd557a 100644 --- a/src/pkg/crypto/x509/pkix/pkix.go +++ b/src/pkg/crypto/x509/pkix/pkix.go @@ -16,7 +16,7 @@ import ( // 5280, section 4.1.1.2. type AlgorithmIdentifier struct { Algorithm asn1.ObjectIdentifier - Parameters asn1.RawValue "optional" + Parameters asn1.RawValue `asn1:"optional"` } type RDNSequence []RelativeDistinguishedNameSET @@ -32,7 +32,7 @@ type AttributeTypeAndValue struct { // 5280, section 4.2. type Extension struct { Id asn1.ObjectIdentifier - Critical bool "optional" + Critical bool `asn1:"optional"` Value []byte } @@ -149,13 +149,13 @@ func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { // 5280, section 5.1. type TBSCertificateList struct { Raw asn1.RawContent - Version int "optional,default:2" + Version int `asn1:"optional,default:2"` Signature AlgorithmIdentifier Issuer RDNSequence ThisUpdate *time.Time NextUpdate *time.Time - RevokedCertificates []RevokedCertificate "optional" - Extensions []Extension "tag:0,optional,explicit" + RevokedCertificates []RevokedCertificate `asn1:"optional"` + Extensions []Extension `asn1:"tag:0,optional,explicit"` } // RevokedCertificate represents the ASN.1 structure of the same name. See RFC @@ -163,5 +163,5 @@ type TBSCertificateList struct { type RevokedCertificate struct { SerialNumber *big.Int RevocationTime *time.Time - Extensions []Extension "optional" + Extensions []Extension `asn1:"optional"` } diff --git a/src/pkg/crypto/x509/verify.go b/src/pkg/crypto/x509/verify.go index 9145880a2..cad863db8 100644 --- a/src/pkg/crypto/x509/verify.go +++ b/src/pkg/crypto/x509/verify.go @@ -171,8 +171,14 @@ func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain [ chains = append(chains, appendToFreshChain(currentChain, root)) } +nextIntermediate: for _, intermediateNum := range opts.Intermediates.findVerifiedParents(c) { intermediate := opts.Intermediates.certs[intermediateNum] + for _, cert := range currentChain { + if cert == intermediate { + continue nextIntermediate + } + } err = intermediate.isValid(intermediateCertificate, opts) if err != nil { continue @@ -202,8 +208,8 @@ func matchHostnames(pattern, host string) bool { return false } - patternParts := strings.Split(pattern, ".", -1) - hostParts := strings.Split(host, ".", -1) + patternParts := strings.Split(pattern, ".") + hostParts := strings.Split(host, ".") if len(patternParts) != len(hostParts) { return false diff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go index 6a103dcfb..111f60eb1 100644 --- a/src/pkg/crypto/x509/verify_test.go +++ b/src/pkg/crypto/x509/verify_test.go @@ -72,23 +72,24 @@ var verifyTests = []verifyTest{ }, }, { - leaf: googleLeaf, - intermediates: []string{verisignRoot, thawteIntermediate}, - roots: []string{verisignRoot}, + leaf: dnssecExpLeaf, + intermediates: []string{startComIntermediate}, + roots: []string{startComRoot}, currentTime: 1302726541, expectedChains: [][]string{ - []string{"Google", "Thawte", "VeriSign"}, + []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, }, }, { leaf: dnssecExpLeaf, - intermediates: []string{startComIntermediate}, + intermediates: []string{startComIntermediate, startComRoot}, roots: []string{startComRoot}, currentTime: 1302726541, expectedChains: [][]string{ []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, + []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"}, }, }, } @@ -120,7 +121,7 @@ func expectAuthorityUnknown(t *testing.T, i int, err os.Error) (ok bool) { func certificateFromPEM(pemBytes string) (*Certificate, os.Error) { block, _ := pem.Decode([]byte(pemBytes)) if block == nil { - return nil, os.ErrorString("failed to decode PEM") + return nil, os.NewError("failed to decode PEM") } return ParseCertificate(block.Bytes) } diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index b10ffb0a2..348727a26 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -30,11 +30,11 @@ type pkcs1PrivateKey struct { P *big.Int Q *big.Int // We ignore these values, if present, because rsa will calculate them. - Dp *big.Int "optional" - Dq *big.Int "optional" - Qinv *big.Int "optional" + Dp *big.Int `asn1:"optional"` + Dq *big.Int `asn1:"optional"` + Qinv *big.Int `asn1:"optional"` - AdditionalPrimes []pkcs1AdditionalRSAPrime "optional" + AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"` } type pkcs1AdditionalRSAPrime struct { @@ -58,11 +58,11 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { } if priv.Version > 1 { - return nil, os.ErrorString("x509: unsupported private key version") + return nil, os.NewError("x509: unsupported private key version") } 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") + return nil, os.NewError("private key contains zero or negative value") } key = new(rsa.PrivateKey) @@ -77,7 +77,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { key.Primes[1] = priv.Q for i, a := range priv.AdditionalPrimes { if a.Prime.Sign() <= 0 { - return nil, os.ErrorString("private key contains zero or negative prime") + return nil, os.NewError("private key contains zero or negative prime") } key.Primes[i+2] = a.Prime // We ignore the other two values because rsa will calculate @@ -136,16 +136,16 @@ type certificate struct { type tbsCertificate struct { Raw asn1.RawContent - Version int "optional,explicit,default:1,tag:0" + Version int `asn1:"optional,explicit,default:1,tag:0"` SerialNumber *big.Int SignatureAlgorithm pkix.AlgorithmIdentifier Issuer pkix.RDNSequence Validity validity Subject pkix.RDNSequence PublicKey publicKeyInfo - UniqueId asn1.BitString "optional,tag:1" - SubjectUniqueId asn1.BitString "optional,tag:2" - Extensions []pkix.Extension "optional,explicit,tag:3" + UniqueId asn1.BitString `asn1:"optional,tag:1"` + SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` + Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"` } type dsaAlgorithmParameters struct { @@ -168,7 +168,7 @@ type publicKeyInfo struct { // RFC 5280, 4.2.1.1 type authKeyId struct { - Id []byte "optional,tag:0" + Id []byte `asn1:"optional,tag:0"` } type SignatureAlgorithm int @@ -457,10 +457,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature return err } if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { - return os.ErrorString("DSA signature contained zero or negative values") + return os.NewError("DSA signature contained zero or negative values") } if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { - return os.ErrorString("DSA verification failure") + return os.NewError("DSA verification failure") } return } @@ -480,8 +480,8 @@ func (h UnhandledCriticalExtension) String() string { } type basicConstraints struct { - IsCA bool "optional" - MaxPathLen int "optional" + IsCA bool `asn1:"optional"` + MaxPathLen int `asn1:"optional"` } type rsaPublicKey struct { @@ -497,14 +497,14 @@ type policyInformation struct { // RFC 5280, 4.2.1.10 type nameConstraints struct { - Permitted []generalSubtree "optional,tag:0" - Excluded []generalSubtree "optional,tag:1" + Permitted []generalSubtree `asn1:"optional,tag:0"` + Excluded []generalSubtree `asn1:"optional,tag:1"` } type generalSubtree struct { - Name string "tag:2,optional,ia5" - Min int "optional,tag:0" - Max int "optional,tag:1" + Name string `asn1:"tag:2,optional,ia5"` + Min int `asn1:"optional,tag:0"` + Max int `asn1:"optional,tag:1"` } func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) { @@ -535,7 +535,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ 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") + return nil, os.NewError("zero or negative DSA parameter") } pub := &dsa.PublicKey{ Parameters: dsa.Parameters{ @@ -571,7 +571,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { } if in.TBSCertificate.SerialNumber.Sign() < 0 { - return nil, os.ErrorString("negative serial number") + return nil, os.NewError("negative serial number") } out.Version = in.TBSCertificate.Version + 1 diff --git a/src/pkg/crypto/xtea/cipher.go b/src/pkg/crypto/xtea/cipher.go index f2a5da003..b3fba3c84 100644 --- a/src/pkg/crypto/xtea/cipher.go +++ b/src/pkg/crypto/xtea/cipher.go @@ -48,13 +48,13 @@ func NewCipher(key []byte) (*Cipher, os.Error) { // BlockSize returns the XTEA block size, 8 bytes. // It is necessary to satisfy the Cipher interface in the -// package "crypto/block". +// package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } // Encrypt encrypts the 8 byte buffer src using the key and stores the result in dst. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/block/cbc.go). +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c, dst, src) } // Decrypt decrypts the 8 byte buffer src using the key k and stores the result in dst. |