openpgp: ECDSA key and signature support
Add support for creating and signing with ECDSA OpenPGP keys Change-Id: I6d530325ab801ba800187b88e8cfea079cfa41ed Reviewed-on: https://go-review.googlesource.com/20608 Run-TryBot: Adam Langley <agl@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Jacob H. Haven <jacob@jhaven.me> Reviewed-by: Adam Langley <agl@golang.org>
This commit is contained in:
committed by
Adam Langley
parent
de93d05161
commit
6025851c7c
@@ -8,16 +8,18 @@ import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"golang.org/x/crypto/openpgp/elgamal"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/elgamal"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
|
||||
@@ -54,6 +56,13 @@ func NewElGamalPrivateKey(currentTime time.Time, priv *elgamal.PrivateKey) *Priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewECDSAPrivateKey(currentTime time.Time, priv *ecdsa.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewECDSAPublicKey(currentTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
err = (&pk.PublicKey).parse(r)
|
||||
if err != nil {
|
||||
@@ -139,6 +148,8 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
|
||||
err = serializeDSAPrivateKey(privateKeyBuf, priv)
|
||||
case *elgamal.PrivateKey:
|
||||
err = serializeElGamalPrivateKey(privateKeyBuf, priv)
|
||||
case *ecdsa.PrivateKey:
|
||||
err = serializeECDSAPrivateKey(privateKeyBuf, priv)
|
||||
default:
|
||||
err = errors.InvalidArgumentError("unknown private key type")
|
||||
}
|
||||
@@ -198,6 +209,10 @@ func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error {
|
||||
return writeBig(w, priv.X)
|
||||
}
|
||||
|
||||
func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error {
|
||||
return writeBig(w, priv.D)
|
||||
}
|
||||
|
||||
// Decrypt decrypts an encrypted private key using a passphrase.
|
||||
func (pk *PrivateKey) Decrypt(passphrase []byte) error {
|
||||
if !pk.Encrypted {
|
||||
@@ -249,6 +264,8 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) {
|
||||
return pk.parseDSAPrivateKey(data)
|
||||
case PubKeyAlgoElGamal:
|
||||
return pk.parseElGamalPrivateKey(data)
|
||||
case PubKeyAlgoECDSA:
|
||||
return pk.parseECDSAPrivateKey(data)
|
||||
}
|
||||
panic("impossible")
|
||||
}
|
||||
@@ -324,3 +341,22 @@ func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) {
|
||||
ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey)
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
d, _, err := readMPI(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pk.PrivateKey = &ecdsa.PrivateKey{
|
||||
PublicKey: *ecdsaPub,
|
||||
D: new(big.Int).SetBytes(d),
|
||||
}
|
||||
pk.Encrypted = false
|
||||
pk.encryptedData = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"hash"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -56,6 +62,57 @@ func TestPrivateKeyRead(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func populateHash(hashFunc crypto.Hash, msg []byte) (hash.Hash, error) {
|
||||
h := hashFunc.New()
|
||||
if _, err := h.Write(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func TestECDSAPrivateKey(t *testing.T) {
|
||||
ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := NewECDSAPrivateKey(time.Now(), ecdsaPriv).Serialize(&buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p, err := Read(&buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
priv, ok := p.(*PrivateKey)
|
||||
if !ok {
|
||||
t.Fatal("didn't parse private key")
|
||||
}
|
||||
|
||||
sig := &Signature{
|
||||
PubKeyAlgo: PubKeyAlgoECDSA,
|
||||
Hash: crypto.SHA256,
|
||||
}
|
||||
msg := []byte("Hello World!")
|
||||
|
||||
h, err := populateHash(sig.Hash, msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := sig.Sign(h, priv, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if h, err = populateHash(sig.Hash, msg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := priv.VerifySignature(h, sig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue11505(t *testing.T) {
|
||||
// parsing a rsa private key with p or q == 1 used to panic due to a divide by zero
|
||||
_, _ = Read(readerFromHex("9c3004303030300100000011303030000000000000010130303030303030303030303030303030303030303030303030303030303030303030303030303030303030"))
|
||||
|
||||
@@ -224,6 +224,32 @@ func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *Public
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoECDSA,
|
||||
PublicKey: pub,
|
||||
ec: new(ecdsaKey),
|
||||
}
|
||||
|
||||
switch pub.Curve {
|
||||
case elliptic.P256():
|
||||
pk.ec.oid = oidCurveP256
|
||||
case elliptic.P384():
|
||||
pk.ec.oid = oidCurveP384
|
||||
case elliptic.P521():
|
||||
pk.ec.oid = oidCurveP521
|
||||
default:
|
||||
panic("unknown elliptic curve")
|
||||
}
|
||||
|
||||
pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
pk.ec.p.bitLength = uint16(8 * len(pk.ec.p.bytes))
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.5.2
|
||||
var buf [6]byte
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
@@ -532,6 +533,12 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
|
||||
sig.DSASigS.bytes = s.Bytes()
|
||||
sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes))
|
||||
}
|
||||
case PubKeyAlgoECDSA:
|
||||
r, s, err := ecdsa.Sign(config.Random(), priv.PrivateKey.(*ecdsa.PrivateKey), digest)
|
||||
if err == nil {
|
||||
sig.ECDSASigR = fromBig(r)
|
||||
sig.ECDSASigS = fromBig(s)
|
||||
}
|
||||
default:
|
||||
err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -50,6 +50,20 @@ func TestSignDetachedDSA(t *testing.T) {
|
||||
testDetachedSignature(t, kring, out, signedInput, "check", testKey3KeyId)
|
||||
}
|
||||
|
||||
func TestSignDetachedP256(t *testing.T) {
|
||||
kring, _ := ReadKeyRing(readerFromHex(p256TestKeyPrivateHex))
|
||||
kring[0].PrivateKey.Decrypt([]byte("passphrase"))
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
message := bytes.NewBufferString(signedInput)
|
||||
err := DetachSign(out, kring[0], message, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
testDetachedSignature(t, kring, out, signedInput, "check", testKeyP256KeyId)
|
||||
}
|
||||
|
||||
func TestNewEntity(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user