Commit df184ff2 authored by Adam Langley's avatar Adam Langley

crypto/openpgp: add DSA signature support.

R=bradfitzgo, nsz
CC=golang-dev
https://golang.org/cl/4280041
parent 7b094182
......@@ -7,6 +7,7 @@
package packet
import (
"big"
"crypto/aes"
"crypto/cast5"
"crypto/cipher"
......@@ -385,7 +386,7 @@ func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err os.Error) {
return
}
// writeMPI serializes a big integer to r.
// writeMPI serializes a big integer to w.
func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) {
_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
if err == nil {
......@@ -393,3 +394,8 @@ func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) {
}
return
}
// writeBig serializes a *big.Int to w.
func writeBig(w io.Writer, i *big.Int) os.Error {
return writeMPI(w, uint16(i.BitLen()), i.Bytes())
}
......@@ -8,6 +8,7 @@ import (
"big"
"bytes"
"crypto/cipher"
"crypto/dsa"
"crypto/openpgp/error"
"crypto/openpgp/s2k"
"crypto/rsa"
......@@ -134,7 +135,16 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error {
}
func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) {
// TODO(agl): support DSA and ECDSA private keys.
switch pk.PublicKey.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly:
return pk.parseRSAPrivateKey(data)
case PubKeyAlgoDSA:
return pk.parseDSAPrivateKey(data)
}
panic("impossible")
}
func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err os.Error) {
rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey)
rsaPriv := new(rsa.PrivateKey)
rsaPriv.PublicKey = *rsaPub
......@@ -162,3 +172,22 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) {
return nil
}
func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err os.Error) {
dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey)
dsaPriv := new(dsa.PrivateKey)
dsaPriv.PublicKey = *dsaPub
buf := bytes.NewBuffer(data)
x, _, err := readMPI(buf)
if err != nil {
return
}
dsaPriv.X = new(big.Int).SetBytes(x)
pk.PrivateKey = dsaPriv
pk.Encrypted = false
pk.encryptedData = nil
return nil
}
......@@ -179,12 +179,6 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E
return error.InvalidArgumentError("public key cannot generate signatures")
}
rsaPublicKey, ok := pk.PublicKey.(*rsa.PublicKey)
if !ok {
// TODO(agl): support DSA and ECDSA keys.
return error.UnsupportedError("non-RSA public key")
}
signed.Write(sig.HashSuffix)
hashBytes := signed.Sum()
......@@ -192,11 +186,28 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E
return error.SignatureError("hash tag doesn't match")
}
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.Signature)
if err != nil {
return error.SignatureError("RSA verification failure")
if pk.PubKeyAlgo != sig.PubKeyAlgo {
return error.InvalidArgumentError("public key and signature use different algorithms")
}
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature)
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) {
return error.SignatureError("DSA verification failure")
}
return nil
default:
panic("shouldn't happen")
}
return nil
panic("unreachable")
}
// VerifyKeySignature returns nil iff sig is a valid signature, make by this
......
......@@ -5,7 +5,9 @@
package packet
import (
"big"
"crypto"
"crypto/dsa"
"crypto/openpgp/error"
"crypto/openpgp/s2k"
"crypto/rand"
......@@ -29,7 +31,9 @@ type Signature struct {
// of bad signed data.
HashTag [2]byte
CreationTime uint32 // Unix epoch time
Signature []byte
RSASignature []byte
DSASigR, DSASigS *big.Int
// The following are optional so are nil when not included in the
// signature.
......@@ -66,7 +70,7 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) {
sig.SigType = SignatureType(buf[0])
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA:
default:
err = error.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
return
......@@ -122,8 +126,20 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) {
return
}
// We have already checked that the public key algorithm is RSA.
sig.Signature, _, err = readMPI(r)
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
sig.RSASignature, _, err = readMPI(r)
case PubKeyAlgoDSA:
var rBytes, sBytes []byte
rBytes, _, err = readMPI(r)
sig.DSASigR = new(big.Int).SetBytes(rBytes)
if err == nil {
sBytes, _, err = readMPI(r)
sig.DSASigS = new(big.Int).SetBytes(sBytes)
}
default:
panic("unreachable")
}
return
}
......@@ -392,31 +408,65 @@ func (sig *Signature) buildHashSuffix() (err os.Error) {
return
}
// SignRSA signs a message with an RSA private key. The hash, h, must contain
// the hash of message to be signed and will be mutated by this function.
func (sig *Signature) SignRSA(h hash.Hash, priv *rsa.PrivateKey) (err os.Error) {
func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error) {
err = sig.buildHashSuffix()
if err != nil {
return
}
h.Write(sig.HashSuffix)
digest := h.Sum()
digest = h.Sum()
copy(sig.HashTag[:], digest)
sig.Signature, err = rsa.SignPKCS1v15(rand.Reader, priv, sig.Hash, digest)
return
}
// Serialize marshals sig to w. SignRSA must have been called first.
// SignRSA signs a message with an RSA private key. The hash, h, must contain
// 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) SignRSA(h hash.Hash, priv *rsa.PrivateKey) (err os.Error) {
digest, err := sig.signPrepareHash(h)
if err != nil {
return
}
sig.RSASignature, err = rsa.SignPKCS1v15(rand.Reader, priv, sig.Hash, digest)
return
}
// SignDSA signs a message with a DSA private key. The hash, h, must contain
// 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) SignDSA(h hash.Hash, priv *dsa.PrivateKey) (err os.Error) {
digest, err := sig.signPrepareHash(h)
if err != nil {
return
}
sig.DSASigR, sig.DSASigS, err = dsa.Sign(rand.Reader, priv, digest)
return
}
// 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.Signature == nil {
return error.InvalidArgumentError("Signature: need to call SignRSA before Serialize")
if sig.RSASignature == nil && sig.DSASigR == 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)
case PubKeyAlgoDSA:
sigLength = 2 /* MPI length */
sigLength += (sig.DSASigR.BitLen() + 7) / 8
sigLength += 2 /* MPI length */
sigLength += (sig.DSASigS.BitLen() + 7) / 8
default:
panic("impossible")
}
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 */ + len(sig.Signature)
2 /* hash tag */ + 2 /* length of signature MPI */ + sigLength
err = serializeHeader(w, packetTypeSignature, length)
if err != nil {
return
......@@ -440,7 +490,19 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
if err != nil {
return
}
return writeMPI(w, 8*uint16(len(sig.Signature)), sig.Signature)
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
err = writeMPI(w, 8*uint16(len(sig.RSASignature)), sig.RSASignature)
case PubKeyAlgoDSA:
err = writeBig(w, sig.DSASigR)
if err == nil {
err = writeBig(w, sig.DSASigS)
}
default:
panic("impossible")
}
return
}
// outputSubpacket represents a subpacket to be marshaled.
......
This diff is collapsed.
......@@ -6,6 +6,7 @@ package openpgp
import (
"crypto"
"crypto/dsa"
"crypto/openpgp/armor"
"crypto/openpgp/error"
"crypto/openpgp/packet"
......@@ -80,6 +81,9 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S
case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSASignOnly:
priv := signer.PrivateKey.PrivateKey.(*rsa.PrivateKey)
err = sig.SignRSA(h, priv)
case packet.PubKeyAlgoDSA:
priv := signer.PrivateKey.PrivateKey.(*dsa.PrivateKey)
err = sig.SignDSA(h, priv)
default:
err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
}
......
......@@ -18,7 +18,7 @@ func TestSignDetached(t *testing.T) {
t.Error(err)
}
testDetachedSignature(t, kring, out, signedInput, "check")
testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId)
}
func TestSignTextDetached(t *testing.T) {
......@@ -30,5 +30,17 @@ func TestSignTextDetached(t *testing.T) {
t.Error(err)
}
testDetachedSignature(t, kring, out, signedInput, "check")
testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId)
}
func TestSignDetachedDSA(t *testing.T) {
kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyPrivateHex))
out := bytes.NewBuffer(nil)
message := bytes.NewBufferString(signedInput)
err := DetachSign(out, kring[0], message)
if err != nil {
t.Error(err)
}
testDetachedSignature(t, kring, out, signedInput, "check", testKey3KeyId)
}
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