x/crypto/openpgp: support crypto.Signer private keys
This adds support for crypto.Signer-based RSA and ECDSA private keys. This enables using opaque signing keys as explained in the documentation for crypto.Signer. The support is in the form of a new NewSignerPrivateKey function which makes a PrivateKey from a crypto.Signer. Fixes golang/go#15841. Change-Id: Ice2ec2793a9f5409a5bfd4e5e49d919e14ede1e0 Reviewed-on: https://go-review.googlesource.com/23802 Run-TryBot: Adam Langley <agl@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
This commit is contained in:
committed by
Adam Langley
parent
69267d27d2
commit
00c1479577
@@ -6,6 +6,7 @@ package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/cipher"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
@@ -30,7 +31,7 @@ type PrivateKey struct {
|
||||
encryptedData []byte
|
||||
cipher CipherFunction
|
||||
s2k func(out, in []byte)
|
||||
PrivateKey interface{} // An *rsa.PrivateKey or *dsa.PrivateKey.
|
||||
PrivateKey interface{} // An *{rsa|dsa|ecdsa}.PrivateKey or a crypto.Signer.
|
||||
sha1Checksum bool
|
||||
iv []byte
|
||||
}
|
||||
@@ -63,6 +64,23 @@ func NewECDSAPrivateKey(currentTime time.Time, priv *ecdsa.PrivateKey) *PrivateK
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewSignerPrivateKey creates a sign-only PrivateKey from a crypto.Signer that
|
||||
// implements RSA or ECDSA.
|
||||
func NewSignerPrivateKey(currentTime time.Time, signer crypto.Signer) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
switch pubkey := signer.Public().(type) {
|
||||
case rsa.PublicKey:
|
||||
pk.PublicKey = *NewRSAPublicKey(currentTime, &pubkey)
|
||||
pk.PubKeyAlgo = PubKeyAlgoRSASignOnly
|
||||
case ecdsa.PublicKey:
|
||||
pk.PublicKey = *NewECDSAPublicKey(currentTime, &pubkey)
|
||||
default:
|
||||
panic("openpgp: unknown crypto.Signer type in NewSignerPrivateKey")
|
||||
}
|
||||
pk.PrivateKey = signer
|
||||
return pk
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
err = (&pk.PublicKey).parse(r)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,7 +10,11 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -70,6 +74,50 @@ func populateHash(hashFunc crypto.Hash, msg []byte) (hash.Hash, error) {
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func TestRSAPrivateKey(t *testing.T) {
|
||||
privKeyDER, _ := hex.DecodeString(pkcs1PrivKeyHex)
|
||||
rsaPriv, err := x509.ParsePKCS1PrivateKey(privKeyDER)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := NewRSAPrivateKey(time.Now(), rsaPriv).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: PubKeyAlgoRSA,
|
||||
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 TestECDSAPrivateKey(t *testing.T) {
|
||||
ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
@@ -113,6 +161,98 @@ func TestECDSAPrivateKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type rsaSigner struct {
|
||||
priv *rsa.PrivateKey
|
||||
}
|
||||
|
||||
func (s *rsaSigner) Public() crypto.PublicKey {
|
||||
return s.priv.PublicKey
|
||||
}
|
||||
|
||||
func (s *rsaSigner) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
return s.priv.Sign(rand, msg, opts)
|
||||
}
|
||||
|
||||
func TestRSASignerPrivateKey(t *testing.T) {
|
||||
rsaPriv, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
priv := NewSignerPrivateKey(time.Now(), &rsaSigner{rsaPriv})
|
||||
|
||||
if priv.PubKeyAlgo != PubKeyAlgoRSASignOnly {
|
||||
t.Fatal("NewSignerPrivateKey should have made a sign-only RSA private key")
|
||||
}
|
||||
|
||||
sig := &Signature{
|
||||
PubKeyAlgo: PubKeyAlgoRSASignOnly,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
type ecdsaSigner struct {
|
||||
priv *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
func (s *ecdsaSigner) Public() crypto.PublicKey {
|
||||
return s.priv.PublicKey
|
||||
}
|
||||
|
||||
func (s *ecdsaSigner) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
return s.priv.Sign(rand, msg, opts)
|
||||
}
|
||||
|
||||
func TestECDSASignerPrivateKey(t *testing.T) {
|
||||
ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
priv := NewSignerPrivateKey(time.Now(), &ecdsaSigner{ecdsaPriv})
|
||||
|
||||
if priv.PubKeyAlgo != PubKeyAlgoECDSA {
|
||||
t.Fatal("NewSignerPrivateKey should have made a ECSDA 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"))
|
||||
@@ -124,3 +264,7 @@ const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5
|
||||
// Generated by `gpg --export-secret-keys` followed by a manual extraction of
|
||||
// the ElGamal subkey from the packets.
|
||||
const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc"
|
||||
|
||||
// pkcs1PrivKeyHex is a PKCS#1, RSA private key.
|
||||
// Generated by `openssl genrsa 1024 | openssl rsa -outform DER | xxd -p`
|
||||
const pkcs1PrivKeyHex = "3082025d02010002818100e98edfa1c3b35884a54d0b36a6a603b0290fa85e49e30fa23fc94fef9c6790bc4849928607aa48d809da326fb42a969d06ad756b98b9c1a90f5d4a2b6d0ac05953c97f4da3120164a21a679793ce181c906dc01d235cc085ddcdf6ea06c389b6ab8885dfd685959e693138856a68a7e5db263337ff82a088d583a897cf2d59e9020301000102818100b6d5c9eb70b02d5369b3ee5b520a14490b5bde8a317d36f7e4c74b7460141311d1e5067735f8f01d6f5908b2b96fbd881f7a1ab9a84d82753e39e19e2d36856be960d05ac9ef8e8782ea1b6d65aee28fdfe1d61451e8cff0adfe84322f12cf455028b581cf60eb9e0e140ba5d21aeba6c2634d7c65318b9a665fc01c3191ca21024100fa5e818da3705b0fa33278bb28d4b6f6050388af2d4b75ec9375dd91ccf2e7d7068086a8b82a8f6282e4fbbdb8a7f2622eb97295249d87acea7f5f816f54d347024100eecf9406d7dc49cdfb95ab1eff4064de84c7a30f64b2798936a0d2018ba9eb52e4b636f82e96c49cc63b80b675e91e40d1b2e4017d4b9adaf33ab3d9cf1c214f024100c173704ace742c082323066226a4655226819a85304c542b9dacbeacbf5d1881ee863485fcf6f59f3a604f9b42289282067447f2b13dfeed3eab7851fc81e0550240741fc41f3fc002b382eed8730e33c5d8de40256e4accee846667f536832f711ab1d4590e7db91a8a116ac5bff3be13d3f9243ff2e976662aa9b395d907f8e9c9024046a5696c9ef882363e06c9fa4e2f5b580906452befba03f4a99d0f873697ef1f851d2226ca7934b30b7c3e80cb634a67172bbbf4781735fe3e09263e2dd723e7"
|
||||
|
||||
@@ -9,10 +9,11 @@ import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"encoding/asn1"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -516,7 +517,8 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
|
||||
|
||||
switch priv.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
sig.RSASignature.bytes, err = rsa.SignPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest)
|
||||
// supports both *rsa.PrivateKey and crypto.Signer
|
||||
sig.RSASignature.bytes, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash)
|
||||
sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes))
|
||||
case PubKeyAlgoDSA:
|
||||
dsaPriv := priv.PrivateKey.(*dsa.PrivateKey)
|
||||
@@ -534,7 +536,17 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
|
||||
sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes))
|
||||
}
|
||||
case PubKeyAlgoECDSA:
|
||||
r, s, err := ecdsa.Sign(config.Random(), priv.PrivateKey.(*ecdsa.PrivateKey), digest)
|
||||
var r, s *big.Int
|
||||
if pk, ok := priv.PrivateKey.(*ecdsa.PrivateKey); ok {
|
||||
// direct support, avoid asn1 wrapping/unwrapping
|
||||
r, s, err = ecdsa.Sign(config.Random(), pk, digest)
|
||||
} else {
|
||||
var b []byte
|
||||
b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, nil)
|
||||
if err == nil {
|
||||
r, s, err = unwrapECDSASig(b)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
sig.ECDSASigR = fromBig(r)
|
||||
sig.ECDSASigS = fromBig(s)
|
||||
@@ -546,6 +558,19 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
|
||||
return
|
||||
}
|
||||
|
||||
// unwrapECDSASig parses the two integer components of an ASN.1-encoded ECDSA
|
||||
// signature.
|
||||
func unwrapECDSASig(b []byte) (r, s *big.Int, err error) {
|
||||
var ecsdaSig struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
_, err = asn1.Unmarshal(b, &ecsdaSig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return ecsdaSig.R, ecsdaSig.S, nil
|
||||
}
|
||||
|
||||
// SignUserId computes a signature from priv, asserting that pub is a valid
|
||||
// key for the identity id. On success, the signature is stored in sig. Call
|
||||
// Serialize to write it out.
|
||||
|
||||
Reference in New Issue
Block a user