mirror of
https://github.com/golang/go
synced 2024-11-21 22:54:40 -07:00
crypto/openpgp: add DSA signature support.
R=bradfitzgo, nsz CC=golang-dev https://golang.org/cl/4280041
This commit is contained in:
parent
7b094182e4
commit
df184ff2f0
@ -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")
|
||||
}
|
||||
return nil
|
||||
|
||||
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")
|
||||
}
|
||||
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.
|
||||
|
File diff suppressed because one or more lines are too long
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user