diff --git a/src/pkg/crypto/openpgp/keys.go b/src/pkg/crypto/openpgp/keys.go index d12d07d7e0..c70fb79270 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. @@ -476,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/public_key.go b/src/pkg/crypto/openpgp/packet/public_key.go index ba4d481f0f..e6b0ae5f3a 100644 --- a/src/pkg/crypto/openpgp/packet/public_key.go +++ b/src/pkg/crypto/openpgp/packet/public_key.go @@ -219,7 +219,11 @@ func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) { 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 } @@ -279,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 123c99fb23..7577e28758 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") @@ -179,7 +178,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r // RFC 4880, section 5.2.3.1 var ( length uint32 - packetType byte + packetType signatureSubpacketType isCritical bool ) switch { @@ -211,10 +210,11 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r err = error.StructuralError("zero length signature subpacket") return } - packetType = subpacket[0] & 0x7f + 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") @@ -385,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 @@ -428,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 @@ -435,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))) } @@ -468,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") } @@ -486,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 @@ -513,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") } @@ -529,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 } @@ -538,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 1305548b2a..c1bbde8b0c 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/read_test.go b/src/pkg/crypto/openpgp/read_test.go index 7182e94b5d..4dc290ef29 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 {