Commit acc284d8 authored by Adam Langley's avatar Adam Langley

crypto/openpgp: add ability to reserialize keys.

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
parent 00f7cd4b
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"crypto/rsa" "crypto/rsa"
"io" "io"
"os" "os"
"time"
) )
// PublicKeyType is the armor type for a PGP public key. // PublicKeyType is the armor type for a PGP public key.
...@@ -476,3 +477,69 @@ func (e *Entity) SerializePrivate(w io.Writer) (err os.Error) { ...@@ -476,3 +477,69 @@ func (e *Entity) SerializePrivate(w io.Writer) (err os.Error) {
} }
return nil 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
}
...@@ -219,7 +219,11 @@ func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) { ...@@ -219,7 +219,11 @@ func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) {
panic("unknown public key algorithm") 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 { if err != nil {
return return
} }
...@@ -279,14 +283,14 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E ...@@ -279,14 +283,14 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E
switch pk.PubKeyAlgo { switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) 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 { if err != nil {
return error.SignatureError("RSA verification failure") return error.SignatureError("RSA verification failure")
} }
return nil return nil
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) 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 error.SignatureError("DSA verification failure")
} }
return nil return nil
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
package packet package packet
import ( import (
"big"
"crypto" "crypto"
"crypto/dsa" "crypto/dsa"
"crypto/openpgp/error" "crypto/openpgp/error"
...@@ -32,8 +31,11 @@ type Signature struct { ...@@ -32,8 +31,11 @@ type Signature struct {
HashTag [2]byte HashTag [2]byte
CreationTime uint32 // Unix epoch time CreationTime uint32 // Unix epoch time
RSASignature []byte RSASignature parsedMPI
DSASigR, DSASigS *big.Int DSASigR, DSASigS parsedMPI
// rawSubpackets contains the unparsed subpackets, in order.
rawSubpackets []outputSubpacket
// The following are optional so are nil when not included in the // The following are optional so are nil when not included in the
// signature. // signature.
...@@ -128,14 +130,11 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) { ...@@ -128,14 +130,11 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) {
switch sig.PubKeyAlgo { switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
sig.RSASignature, _, err = readMPI(r) sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r)
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
var rBytes, sBytes []byte sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r)
rBytes, _, err = readMPI(r)
sig.DSASigR = new(big.Int).SetBytes(rBytes)
if err == nil { if err == nil {
sBytes, _, err = readMPI(r) sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r)
sig.DSASigS = new(big.Int).SetBytes(sBytes)
} }
default: default:
panic("unreachable") panic("unreachable")
...@@ -179,7 +178,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r ...@@ -179,7 +178,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
// RFC 4880, section 5.2.3.1 // RFC 4880, section 5.2.3.1
var ( var (
length uint32 length uint32
packetType byte packetType signatureSubpacketType
isCritical bool isCritical bool
) )
switch { switch {
...@@ -211,10 +210,11 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r ...@@ -211,10 +210,11 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
err = error.StructuralError("zero length signature subpacket") err = error.StructuralError("zero length signature subpacket")
return return
} }
packetType = subpacket[0] & 0x7f packetType = signatureSubpacketType(subpacket[0] & 0x7f)
isCritical = subpacket[0]&0x80 == 0x80 isCritical = subpacket[0]&0x80 == 0x80
subpacket = subpacket[1:] subpacket = subpacket[1:]
switch signatureSubpacketType(packetType) { sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket})
switch packetType {
case creationTimeSubpacket: case creationTimeSubpacket:
if !isHashed { if !isHashed {
err = error.StructuralError("signature creation time in non-hashed area") err = error.StructuralError("signature creation time in non-hashed area")
...@@ -385,7 +385,6 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { ...@@ -385,7 +385,6 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. // buildHashSuffix constructs the HashSuffix member of sig in preparation for signing.
func (sig *Signature) buildHashSuffix() (err os.Error) { func (sig *Signature) buildHashSuffix() (err os.Error) {
sig.outSubpackets = sig.buildSubpackets()
hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true)
var ok bool var ok bool
...@@ -428,6 +427,7 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error) ...@@ -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. // 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. // 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) { func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) {
sig.outSubpackets = sig.buildSubpackets()
digest, err := sig.signPrepareHash(h) digest, err := sig.signPrepareHash(h)
if err != nil { if err != nil {
return return
...@@ -435,9 +435,16 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) { ...@@ -435,9 +435,16 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) {
switch priv.PubKeyAlgo { switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: 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: 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: default:
err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) 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 { ...@@ -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. // Serialize marshals sig to w. SignRSA or SignDSA must have been called first.
func (sig *Signature) Serialize(w io.Writer) (err os.Error) { 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") return error.InvalidArgumentError("Signature: need to call SignRSA or SignDSA before Serialize")
} }
sigLength := 0 sigLength := 0
switch sig.PubKeyAlgo { switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
sigLength = len(sig.RSASignature) sigLength = 2 + len(sig.RSASignature.bytes)
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
sigLength = mpiLength(sig.DSASigR) sigLength = 2 + len(sig.DSASigR.bytes)
sigLength += mpiLength(sig.DSASigS) sigLength += 2 + len(sig.DSASigS.bytes)
default: default:
panic("impossible") panic("impossible")
} }
...@@ -486,7 +496,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) { ...@@ -486,7 +496,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false)
length := len(sig.HashSuffix) - 6 /* trailer not included */ + length := len(sig.HashSuffix) - 6 /* trailer not included */ +
2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen +
2 /* hash tag */ + 2 /* length of signature MPI */ + sigLength 2 /* hash tag */ + sigLength
err = serializeHeader(w, packetTypeSignature, length) err = serializeHeader(w, packetTypeSignature, length)
if err != nil { if err != nil {
return return
...@@ -513,12 +523,9 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) { ...@@ -513,12 +523,9 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
switch sig.PubKeyAlgo { switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
err = writeMPI(w, 8*uint16(len(sig.RSASignature)), sig.RSASignature) err = writeMPIs(w, sig.RSASignature)
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
err = writeBig(w, sig.DSASigR) err = writeMPIs(w, sig.DSASigR, sig.DSASigS)
if err == nil {
err = writeBig(w, sig.DSASigS)
}
default: default:
panic("impossible") panic("impossible")
} }
...@@ -529,6 +536,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) { ...@@ -529,6 +536,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
type outputSubpacket struct { type outputSubpacket struct {
hashed bool // true if this subpacket is in the hashed area. hashed bool // true if this subpacket is in the hashed area.
subpacketType signatureSubpacketType subpacketType signatureSubpacketType
isCritical bool
contents []byte contents []byte
} }
...@@ -538,12 +546,12 @@ func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { ...@@ -538,12 +546,12 @@ func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) {
creationTime[1] = byte(sig.CreationTime >> 16) creationTime[1] = byte(sig.CreationTime >> 16)
creationTime[2] = byte(sig.CreationTime >> 8) creationTime[2] = byte(sig.CreationTime >> 8)
creationTime[3] = byte(sig.CreationTime) creationTime[3] = byte(sig.CreationTime)
subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, creationTime}) subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime})
if sig.IssuerKeyId != nil { if sig.IssuerKeyId != nil {
keyId := make([]byte, 8) keyId := make([]byte, 8)
binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId)
subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, keyId}) subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId})
} }
return return
......
...@@ -12,9 +12,7 @@ import ( ...@@ -12,9 +12,7 @@ import (
) )
func TestSignatureRead(t *testing.T) { func TestSignatureRead(t *testing.T) {
signatureData, _ := hex.DecodeString(signatureDataHex) packet, err := Read(readerFromHex(signatureDataHex))
buf := bytes.NewBuffer(signatureData)
packet, err := Read(buf)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
...@@ -25,4 +23,20 @@ func TestSignatureRead(t *testing.T) { ...@@ -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"
...@@ -33,6 +33,29 @@ func TestReadKeyRing(t *testing.T) { ...@@ -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) { func TestReadPrivateKeyRing(t *testing.T) {
kring, err := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) kring, err := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex))
if err != nil { if err != nil {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment