mirror of
https://github.com/golang/go
synced 2024-11-19 16:24:45 -07:00
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
This commit is contained in:
parent
00f7cd4b36
commit
acc284d847
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user