aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Langley <agl@golang.org>2011-07-01 13:53:12 -0400
committerAdam Langley <agl@golang.org>2011-07-01 13:53:12 -0400
commitacc284d8478696c94dfcee74cd5b90c2da692b38 (patch)
treebf94d9d7679440220b8d2fac114c1be75351c2bf
parent00f7cd4b36ae04237ffb062967e9dd36e62bf592 (diff)
downloadgo-acc284d8478696c94dfcee74cd5b90c2da692b38.tar.gz
go-acc284d8478696c94dfcee74cd5b90c2da692b38.zip
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
-rw-r--r--src/pkg/crypto/openpgp/keys.go67
-rw-r--r--src/pkg/crypto/openpgp/packet/public_key.go10
-rw-r--r--src/pkg/crypto/openpgp/packet/signature.go62
-rw-r--r--src/pkg/crypto/openpgp/packet/signature_test.go22
-rw-r--r--src/pkg/crypto/openpgp/read_test.go23
5 files changed, 150 insertions, 34 deletions
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 {