]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/openpgp: add ability to reserialize keys.
authorAdam Langley <agl@golang.org>
Fri, 1 Jul 2011 17:53:12 +0000 (13:53 -0400)
committerAdam Langley <agl@golang.org>
Fri, 1 Jul 2011 17:53:12 +0000 (13:53 -0400)
This changes Signature so that parsed signatures can be reserialized
exactly. With this ability we can add Serialize to Entity and also the
ability to sign other public keys.

R=bradfitz
CC=golang-dev
https://golang.org/cl/4627084

src/pkg/crypto/openpgp/keys.go
src/pkg/crypto/openpgp/packet/public_key.go
src/pkg/crypto/openpgp/packet/signature.go
src/pkg/crypto/openpgp/packet/signature_test.go
src/pkg/crypto/openpgp/read_test.go

index d12d07d7e01a48297170397920e57e1535020a22..c70fb79270488b37919628e62aa2366297362081 100644 (file)
@@ -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
+}
index ba4d481f0f710f38fa17dc520e0eb3a0ad77f7e9..e6b0ae5f3afc13b99284b07b5f6845656fef7747 100644 (file)
@@ -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
index 123c99fb23220db79d7b6ceed2f1b25eb67d05dd..7577e28758810826b651e1ac7cb2a0859e5b3756 100644 (file)
@@ -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
index 1305548b2aeb3af95cb8d8c27b227b1a73a541a4..c1bbde8b0c3b0933b5890af10037664ed576faad 100644 (file)
@@ -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"
index 7182e94b5d2346ba0e5a519623d4e520a23f4adb..4dc290ef29d5842c4f0de0788624e03f6fd14581 100644 (file)
@@ -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 {