mirror of
https://github.com/golang/go
synced 2024-11-18 00:54:45 -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
|
package packet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"big"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cast5"
|
"crypto/cast5"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
@ -385,7 +386,7 @@ func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err os.Error) {
|
|||||||
return
|
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) {
|
func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) {
|
||||||
_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
|
_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -393,3 +394,8 @@ func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) {
|
|||||||
}
|
}
|
||||||
return
|
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"
|
"big"
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/dsa"
|
||||||
"crypto/openpgp/error"
|
"crypto/openpgp/error"
|
||||||
"crypto/openpgp/s2k"
|
"crypto/openpgp/s2k"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
@ -134,7 +135,16 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pk *PrivateKey) parsePrivateKey(data []byte) (err 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)
|
rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey)
|
||||||
rsaPriv := new(rsa.PrivateKey)
|
rsaPriv := new(rsa.PrivateKey)
|
||||||
rsaPriv.PublicKey = *rsaPub
|
rsaPriv.PublicKey = *rsaPub
|
||||||
@ -162,3 +172,22 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) {
|
|||||||
|
|
||||||
return nil
|
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")
|
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)
|
signed.Write(sig.HashSuffix)
|
||||||
hashBytes := signed.Sum()
|
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")
|
return error.SignatureError("hash tag doesn't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.Signature)
|
if pk.PubKeyAlgo != sig.PubKeyAlgo {
|
||||||
if err != nil {
|
return error.InvalidArgumentError("public key and signature use different algorithms")
|
||||||
return error.SignatureError("RSA verification failure")
|
|
||||||
}
|
}
|
||||||
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
|
// VerifyKeySignature returns nil iff sig is a valid signature, make by this
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
package packet
|
package packet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"big"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/dsa"
|
||||||
"crypto/openpgp/error"
|
"crypto/openpgp/error"
|
||||||
"crypto/openpgp/s2k"
|
"crypto/openpgp/s2k"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@ -29,7 +31,9 @@ type Signature struct {
|
|||||||
// of bad signed data.
|
// of bad signed data.
|
||||||
HashTag [2]byte
|
HashTag [2]byte
|
||||||
CreationTime uint32 // Unix epoch time
|
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
|
// The following are optional so are nil when not included in the
|
||||||
// signature.
|
// signature.
|
||||||
@ -66,7 +70,7 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) {
|
|||||||
sig.SigType = SignatureType(buf[0])
|
sig.SigType = SignatureType(buf[0])
|
||||||
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
|
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
|
||||||
switch sig.PubKeyAlgo {
|
switch sig.PubKeyAlgo {
|
||||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA:
|
||||||
default:
|
default:
|
||||||
err = error.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
|
err = error.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
|
||||||
return
|
return
|
||||||
@ -122,8 +126,20 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have already checked that the public key algorithm is RSA.
|
switch sig.PubKeyAlgo {
|
||||||
sig.Signature, _, err = readMPI(r)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,31 +408,65 @@ func (sig *Signature) buildHashSuffix() (err os.Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignRSA signs a message with an RSA private key. The hash, h, must contain
|
func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error) {
|
||||||
// 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) {
|
|
||||||
err = sig.buildHashSuffix()
|
err = sig.buildHashSuffix()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Write(sig.HashSuffix)
|
h.Write(sig.HashSuffix)
|
||||||
digest := h.Sum()
|
digest = h.Sum()
|
||||||
copy(sig.HashTag[:], digest)
|
copy(sig.HashTag[:], digest)
|
||||||
sig.Signature, err = rsa.SignPKCS1v15(rand.Reader, priv, sig.Hash, digest)
|
|
||||||
return
|
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) {
|
func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
|
||||||
if sig.Signature == nil {
|
if sig.RSASignature == nil && sig.DSASigR == nil {
|
||||||
return error.InvalidArgumentError("Signature: need to call SignRSA before Serialize")
|
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)
|
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 */ + len(sig.Signature)
|
2 /* hash tag */ + 2 /* length of signature MPI */ + sigLength
|
||||||
err = serializeHeader(w, packetTypeSignature, length)
|
err = serializeHeader(w, packetTypeSignature, length)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -440,7 +490,19 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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.
|
// 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 (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/dsa"
|
||||||
"crypto/openpgp/armor"
|
"crypto/openpgp/armor"
|
||||||
"crypto/openpgp/error"
|
"crypto/openpgp/error"
|
||||||
"crypto/openpgp/packet"
|
"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:
|
case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSASignOnly:
|
||||||
priv := signer.PrivateKey.PrivateKey.(*rsa.PrivateKey)
|
priv := signer.PrivateKey.PrivateKey.(*rsa.PrivateKey)
|
||||||
err = sig.SignRSA(h, priv)
|
err = sig.SignRSA(h, priv)
|
||||||
|
case packet.PubKeyAlgoDSA:
|
||||||
|
priv := signer.PrivateKey.PrivateKey.(*dsa.PrivateKey)
|
||||||
|
err = sig.SignDSA(h, priv)
|
||||||
default:
|
default:
|
||||||
err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
|
err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ func TestSignDetached(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
testDetachedSignature(t, kring, out, signedInput, "check")
|
testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignTextDetached(t *testing.T) {
|
func TestSignTextDetached(t *testing.T) {
|
||||||
@ -30,5 +30,17 @@ func TestSignTextDetached(t *testing.T) {
|
|||||||
t.Error(err)
|
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