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:
Jacob H. Haven
2016-03-14 20:49:18 +00:00
committed by Adam Langley
parent de93d05161
commit 6025851c7c
6 changed files with 166 additions and 3 deletions
+39 -3
View File
@@ -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
}
+57
View File
@@ -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"))
+26
View File
@@ -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
+7
View File
@@ -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
+14
View File
@@ -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