crypto: finish RSA

This commit is contained in:
Michael Muré
2025-07-09 17:58:09 +02:00
parent 2284bd6487
commit 61617d6af6
21 changed files with 320 additions and 142 deletions

View File

@@ -190,16 +190,7 @@ func TestSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](t *testing.T, har
pub, priv, err := harness.GenerateKeyPair()
require.NoError(t, err)
spub, ok := (crypto.PublicKey(pub)).(crypto.PublicKeySigning)
if !ok {
t.Skip("Signature is not implemented")
}
spriv, ok := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigning)
if !ok {
t.Skip("Signature is not implemented")
}
for _, tc := range []struct {
type testcase struct {
name string
signer func(msg []byte, opts ...crypto.SigningOption) ([]byte, error)
verifier func(msg []byte, sig []byte, opts ...crypto.SigningOption) bool
@@ -207,25 +198,43 @@ func TestSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](t *testing.T, har
stats *int
defaultHash crypto.Hash
otherHashes []crypto.Hash
}{
{
name: "Bytes signature",
signer: spriv.SignToBytes,
verifier: spub.VerifyBytes,
expectedSize: harness.SignatureBytesSize,
stats: &stats.sigRawSize,
defaultHash: harness.DefaultHash,
otherHashes: harness.OtherHashes,
},
{
name: "ASN.1 signature",
signer: spriv.SignToASN1,
verifier: spub.VerifyASN1,
stats: &stats.sigAsn1Size,
defaultHash: harness.DefaultHash,
otherHashes: harness.OtherHashes,
},
} {
}
var tcs []testcase
if pubImplements[PubT, crypto.PublicKeySigningBytes]() {
t.Run("Bytes signature", func(t *testing.T) {
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigningBytes)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningBytes)
tcs = append(tcs, testcase{
name: "Bytes signature",
signer: spriv.SignToBytes,
verifier: spub.VerifyBytes,
expectedSize: harness.SignatureBytesSize,
stats: &stats.sigRawSize,
defaultHash: harness.DefaultHash,
otherHashes: harness.OtherHashes,
})
})
}
if pubImplements[PubT, crypto.PublicKeySigningASN1]() {
t.Run("ASN.1 signature", func(t *testing.T) {
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigningASN1)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningASN1)
tcs = append(tcs, testcase{
name: "ASN.1 signature",
signer: spriv.SignToASN1,
verifier: spub.VerifyASN1,
stats: &stats.sigAsn1Size,
defaultHash: harness.DefaultHash,
otherHashes: harness.OtherHashes,
})
})
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
msg := []byte("message")
@@ -455,15 +464,15 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
})
b.Run("Signatures", func(b *testing.B) {
if !pubImplements[PubT, crypto.PublicKeySigning]() {
b.Skip("Signature is not implemented")
}
b.Run("Sign to Bytes signature", func(b *testing.B) {
if !pubImplements[PubT, crypto.PublicKeySigningBytes]() {
b.Skip("Signature to bytes is not implemented")
}
_, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigning)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningBytes)
b.ResetTimer()
b.ReportAllocs()
@@ -474,11 +483,15 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
})
b.Run("Verify from Bytes signature", func(b *testing.B) {
if !pubImplements[PubT, crypto.PublicKeySigningBytes]() {
b.Skip("Signature to bytes is not implemented")
}
pub, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigning)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigning)
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigningBytes)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningBytes)
sig, err := spriv.SignToBytes([]byte("message"))
require.NoError(b, err)
@@ -491,10 +504,14 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
})
b.Run("Sign to ASN.1 signature", func(b *testing.B) {
if !pubImplements[PubT, crypto.PublicKeySigningASN1]() {
b.Skip("Signature to ASN.1 is not implemented")
}
_, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigning)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningASN1)
b.ResetTimer()
b.ReportAllocs()
@@ -505,11 +522,15 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
})
b.Run("Verify from ASN.1 signature", func(b *testing.B) {
if !pubImplements[PubT, crypto.PublicKeySigningASN1]() {
b.Skip("Signature to ASN.1 is not implemented")
}
pub, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigning)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigning)
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigningASN1)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningASN1)
sig, err := spriv.SignToASN1([]byte("message"))
require.NoError(b, err)
@@ -524,7 +545,7 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
b.Run("Key exchange", func(b *testing.B) {
if !privImplements[PrivT, crypto.PrivateKeyKeyExchange]() {
b.Skip("Key echange is not implemented")
b.Skip("Key exchange is not implemented")
}
b.Run("KeyExchange", func(b *testing.B) {

View File

@@ -11,7 +11,8 @@ import (
"github.com/INFURA/go-did/crypto"
)
var _ crypto.PrivateKeySigning = &PrivateKey{}
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
type PrivateKey struct {

View File

@@ -13,7 +13,8 @@ import (
"github.com/INFURA/go-did/crypto/internal"
)
var _ crypto.PublicKeySigning = PublicKey{}
var _ crypto.PublicKeySigningBytes = PublicKey{}
var _ crypto.PublicKeySigningASN1 = PublicKey{}
var _ crypto.PublicKeyToBytes = PublicKey{}
type PublicKey struct {

View File

@@ -26,13 +26,17 @@ type PublicKeyToBytes interface {
ToBytes() []byte
}
type PublicKeySigning interface {
type PublicKeySigningBytes interface {
PublicKey
// VerifyBytes checks a signature in the "raw bytes" format.
// This format can make some assumptions and may not be what you expect.
// Ideally, this format is defined by the same specification as the underlying crypto scheme.
VerifyBytes(message, signature []byte, opts ...SigningOption) bool
}
type PublicKeySigningASN1 interface {
PublicKey
// VerifyASN1 checks a signature in the ASN.1 format.
VerifyASN1(message, signature []byte, opts ...SigningOption) bool
@@ -63,13 +67,17 @@ type PrivateKeyToBytes interface {
ToBytes() []byte
}
type PrivateKeySigning interface {
type PrivateKeySigningBytes interface {
PrivateKey
// SignToBytes creates a signature in the "raw bytes" format.
// This format can make some assumptions and may not be what you expect.
// Ideally, this format is defined by the same specification as the underlying crypto scheme.
SignToBytes(message []byte, opts ...SigningOption) ([]byte, error)
}
type PrivateKeySigningASN1 interface {
PrivateKey
// SignToASN1 creates a signature in the ASN.1 format.
SignToASN1(message []byte, opts ...SigningOption) ([]byte, error)

View File

@@ -1,8 +1,11 @@
package p256
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/require"
"github.com/INFURA/go-did/crypto"
"github.com/INFURA/go-did/crypto/_testsuite"
)
@@ -32,3 +35,25 @@ func TestSuite(t *testing.T) {
func BenchmarkSuite(b *testing.B) {
testsuite.BenchSuite(b, harness)
}
func TestSignatureASN1(t *testing.T) {
// openssl ecparam -genkey -name prime256v1 -noout -out private.pem
// openssl ec -in private.pem -pubout -out public.pem
// echo -n "message" | openssl dgst -sha256 -sign private.pem -out signature.der
// echo -n "message" | openssl dgst -sha256 -verify public.pem -signature signature.der
pubPem := `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+UhEHZqcaKn+qhNtMmW843ZTRkX/
6GzxOWoRD2nv3EewARM90akj2UAKwQjJR9ibm78XtdlryvWG1v8TWb8INA==
-----END PUBLIC KEY-----
`
pub, err := PublicKeyFromX509PEM(pubPem)
require.NoError(t, err)
b64sig := `MEQCIHPslthrLAYgwfqYaUmtGJqwmH7sRf5FEnnKgzcHIF8fAiB9+qovdvN6yJKkBwoQCw798uWr
0nOUE55ftB8EgX/Jbg==`
sig, err := base64.StdEncoding.DecodeString(b64sig)
require.NoError(t, err)
require.True(t, pub.VerifyASN1([]byte("message"), sig))
}

View File

@@ -12,7 +12,8 @@ import (
"github.com/INFURA/go-did/crypto"
)
var _ crypto.PrivateKeySigning = &PrivateKey{}
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
var _ crypto.PrivateKeyKeyExchange = &PrivateKey{}

View File

@@ -12,7 +12,8 @@ import (
helpers "github.com/INFURA/go-did/crypto/internal"
)
var _ crypto.PublicKeySigning = &PublicKey{}
var _ crypto.PublicKeySigningBytes = &PublicKey{}
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
var _ crypto.PublicKeyToBytes = &PublicKey{}
type PublicKey struct {

View File

@@ -12,7 +12,8 @@ import (
"github.com/INFURA/go-did/crypto"
)
var _ crypto.PrivateKeySigning = &PrivateKey{}
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
var _ crypto.PrivateKeyKeyExchange = &PrivateKey{}

View File

@@ -12,7 +12,8 @@ import (
helpers "github.com/INFURA/go-did/crypto/internal"
)
var _ crypto.PublicKeySigning = &PublicKey{}
var _ crypto.PublicKeySigningBytes = &PublicKey{}
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
var _ crypto.PublicKeyToBytes = &PublicKey{}
type PublicKey struct {

View File

@@ -12,7 +12,8 @@ import (
"github.com/INFURA/go-did/crypto"
)
var _ crypto.PrivateKeySigning = &PrivateKey{}
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
var _ crypto.PrivateKeyKeyExchange = &PrivateKey{}

View File

@@ -12,7 +12,8 @@ import (
helpers "github.com/INFURA/go-did/crypto/internal"
)
var _ crypto.PublicKeySigning = &PublicKey{}
var _ crypto.PublicKeySigningBytes = &PublicKey{}
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
var _ crypto.PublicKeyToBytes = &PublicKey{}
type PublicKey struct {

View File

@@ -4,6 +4,8 @@ import (
"crypto/rand"
"crypto/rsa"
"fmt"
"github.com/INFURA/go-did/crypto"
)
const (
@@ -28,3 +30,14 @@ const (
pemPubBlockType = "PUBLIC KEY"
pemPrivBlockType = "PRIVATE KEY"
)
func defaultSigHash(keyLen int) crypto.Hash {
switch {
case keyLen <= 2048:
return crypto.SHA256
case keyLen <= 3072:
return crypto.SHA384
default:
return crypto.SHA512
}
}

View File

@@ -1,14 +1,16 @@
package rsa
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/require"
"github.com/INFURA/go-did/crypto"
"github.com/INFURA/go-did/crypto/_testsuite"
)
var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{
var harness2048 = testsuite.TestHarness[*PublicKey, *PrivateKey]{
Name: "rsa-2048",
GenerateKeyPair: func() (*PublicKey, *PrivateKey, error) { return GenerateKeyPair(2048) },
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
@@ -17,15 +19,58 @@ var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
MultibaseCode: MultibaseCode,
SignatureBytesSize: 123456,
DefaultHash: crypto.SHA256,
OtherHashes: []crypto.Hash{crypto.SHA384, crypto.SHA512},
}
func TestSuite(t *testing.T) {
testsuite.TestSuite(t, harness)
var harness3072 = testsuite.TestHarness[*PublicKey, *PrivateKey]{
Name: "rsa-3072",
GenerateKeyPair: func() (*PublicKey, *PrivateKey, error) { return GenerateKeyPair(3072) },
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
PublicKeyFromX509DER: PublicKeyFromX509DER,
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
MultibaseCode: MultibaseCode,
DefaultHash: crypto.SHA384,
OtherHashes: []crypto.Hash{crypto.SHA512},
}
func BenchmarkSuite(b *testing.B) {
testsuite.BenchSuite(b, harness)
var harness4096 = testsuite.TestHarness[*PublicKey, *PrivateKey]{
Name: "rsa-4096",
GenerateKeyPair: func() (*PublicKey, *PrivateKey, error) { return GenerateKeyPair(4096) },
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
PublicKeyFromX509DER: PublicKeyFromX509DER,
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
MultibaseCode: MultibaseCode,
DefaultHash: crypto.SHA512,
OtherHashes: []crypto.Hash{},
}
func TestSuite2048(t *testing.T) {
testsuite.TestSuite(t, harness2048)
}
func TestSuite3072(t *testing.T) {
testsuite.TestSuite(t, harness3072)
}
func TestSuite4096(t *testing.T) {
testsuite.TestSuite(t, harness4096)
}
func BenchmarkSuite2048(b *testing.B) {
testsuite.BenchSuite(b, harness2048)
}
func BenchmarkSuite3072(b *testing.B) {
testsuite.BenchSuite(b, harness3072)
}
func BenchmarkSuite4096(b *testing.B) {
testsuite.BenchSuite(b, harness4096)
}
func TestPublicKeyX509(t *testing.T) {
@@ -87,3 +132,33 @@ XnARctVkIcUYORcYwvuu9meDkw==
rt := priv.ToPKCS8PEM()
require.Equal(t, pem, rt)
}
func TestSignatureASN1(t *testing.T) {
// openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
// openssl pkey -in private.pem -pubout -out public.pem
// echo -n "message" | openssl dgst -sha256 -sign private.pem -out signature.der
// echo -n "message" | openssl dgst -sha256 -verify public.pem -signature signature.der
pubPem := `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmtKXCTkUDcKbZGsEEUTo
16xblCyh6zmA8pXVGgmC66QiTNdKzxdwTykXTDs9sEre9ea34h2M7dwrA1weAmBu
grAXe0QmIXIqjFKRKdfty09yWVtKF7FGwEMlhKftWC225R+tRuLwbKG4cCSzHxcf
JfqCYqGDM7BrF39ilQzFYw5sUiWn3ppRPWa2oEV3cw19zFnHMbEHIQIdFyCcIv5x
GUSJ6sJVp0YvsODsZbA+Zyb2UMRfXD8fDHm9bJQCY0x/wGJLfvJmWtZLciwc145U
BN3SezY30NviZtZBKWjXgb6gL69L94U10/8ghmA30DY7bKs4+/7R2nOw91CO4rCo
1QIDAQAB
-----END PUBLIC KEY-----
`
pub, err := PublicKeyFromX509PEM(pubPem)
require.NoError(t, err)
b64sig := `BdvBkZWxIVE2mfM48H1WlOs3k9NzyS4oUxAMOZWNNTYDU6+DLbhZ7Hnt3rRKX3m6f1cX5DCsHcPC
6sNtsR8Xp9u09GWCN/K28fF7Pcl0E87MdhAUL7jKNK5bb1XWx/GCUmoKXRZiR/gA10iB2Lmjd1MC
HItTCig91gmFm5PO67u9yM+cqE2nGyOh13/kT5Np9MUyaE9dkjoQGum23Ta6m7v0atWsPhO5aVVI
76vLwGhYAhQe22RxBlPRXyRInr0EnVgHQOe211o//erPZYQAm+N1kK+yjV8NbPxJX+r5sYUE19NL
MCB+kOgWk51uJwuiuHlffGMBPxku/t+skxI7Bw==`
sig, err := base64.StdEncoding.DecodeString(b64sig)
require.NoError(t, err)
require.True(t, pub.VerifyASN1([]byte("message"), sig))
}

View File

@@ -1,6 +1,8 @@
package rsa
import (
stdcrypto "crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
@@ -10,7 +12,7 @@ import (
"github.com/INFURA/go-did/crypto"
)
var _ crypto.PrivateKeySigning = &PrivateKey{}
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
type PrivateKey struct {
k *rsa.PrivateKey
@@ -148,34 +150,18 @@ func (p *PrivateKey) ToPKCS8PEM() string {
}))
}
func (p *PrivateKey) SignToBytes(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}
// SignToASN1 produce a PKCS#1 v1.5 signature.
// The default signing hash is:
// - SHA-256 for keys of length 2048 bits and under
// - SHA-384 for keys of length 3072 bits and under
// - SHA-512 for higher key length
func (p *PrivateKey) SignToASN1(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}
params := crypto.CollectSigningOptions(opts)
// func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
// if _, ok := remote.(*PublicKey); ok {
// return true
// }
// return false
// }
//
// func (p *PrivateKey) KeyExchange(remote crypto.PublicKey) ([]byte, error) {
// if remote, ok := remote.(*PublicKey); ok {
// // First, we need to convert the ECDSA (signing only) to the equivalent ECDH keys
// ecdhPriv, err := p.k.ECDH()
// if err != nil {
// return nil, err
// }
// ecdhPub, err := remote.k.ECDH()
// if err != nil {
// return nil, err
// }
//
// return ecdhPriv.ECDH(ecdhPub)
// }
// return nil, fmt.Errorf("incompatible public key")
// }
hashCode := params.HashOrDefault(defaultSigHash(p.k.N.BitLen()))
hasher := hashCode.New()
hasher.Write(message)
hash := hasher.Sum(nil)
return rsa.SignPKCS1v15(rand.Reader, p.k, stdcrypto.Hash(hashCode), hash)
}

View File

@@ -1,6 +1,7 @@
package rsa
import (
stdcrypto "crypto"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
@@ -11,7 +12,7 @@ import (
helpers "github.com/INFURA/go-did/crypto/internal"
)
var _ crypto.PublicKeySigning = &PublicKey{}
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
type PublicKey struct {
k *rsa.PublicKey
@@ -125,10 +126,19 @@ func (p *PublicKey) ToX509PEM() string {
}))
}
func (p *PublicKey) VerifyBytes(message, signature []byte, opts ...crypto.SigningOption) bool {
return false
}
// VerifyASN1 verifies a PKCS#1 v1.5 signature.
// The default signing hash is:
// - SHA-256 for keys of length 2048 bits and under
// - SHA-384 for keys of length 3072 bits and under
// - SHA-512 for higher key length
func (p *PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
return false
params := crypto.CollectSigningOptions(opts)
hashCode := params.HashOrDefault(defaultSigHash(p.k.N.BitLen()))
hasher := hashCode.New()
hasher.Write(message)
hash := hasher.Sum(nil)
err := rsa.VerifyPKCS1v15(p.k, stdcrypto.Hash(hashCode), hash, signature)
return err == nil
}

View File

@@ -12,7 +12,7 @@ const (
// PrivateKeyBytesSize is the size, in bytes, of private keys in raw bytes.
PrivateKeyBytesSize = secp256k1.PrivKeyBytesLen
// SignatureBytesSize is the size, in bytes, of signatures in raw bytes.
SignatureBytesSize = 123456
SignatureBytesSize = 64
MultibaseCode = uint64(0xe7)

View File

@@ -1,6 +1,7 @@
package secp256k1
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/require"
@@ -80,3 +81,25 @@ Uwyag4V8qWsP8e5ZSOOXDSYMplwbsAzsko9NYw4Jy9RHYHwFQ7dV
_, _ = PrivateKeyFromPKCS8PEM(data)
})
}
func TestSignatureASN1(t *testing.T) {
// openssl ecparam -genkey -name secp256k1 -noout -out private.pem
// openssl ec -in private.pem -pubout -out public.pem
// echo -n "message" | openssl dgst -sha256 -sign private.pem -out signature.der
// echo -n "message" | openssl dgst -sha256 -verify public.pem -signature signature.der
pubPem := `-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEszL1+ZFqUMAHjLAyzMW7xMBPZek/8cNj
1qI7EgQooB3f8Sh7JwvXu8cosRnjjvYVvS7OliRsbvuceCQ7HBC4fA==
-----END PUBLIC KEY-----
`
pub, err := PublicKeyFromX509PEM(pubPem)
require.NoError(t, err)
b64sig := `MEYCIQDv5SLy768FbOafzDlrxIeeoEn7tKpYBSK6WcKaOZ6AJAIhAKXV6VAwiPq4uk9TpGyFN5JK
8jZPrQ7hdRR5veKKDX2w`
sig, err := base64.StdEncoding.DecodeString(b64sig)
require.NoError(t, err)
require.True(t, pub.VerifyASN1([]byte("message"), sig))
}

View File

@@ -12,7 +12,8 @@ import (
"github.com/INFURA/go-did/crypto"
)
var _ crypto.PrivateKeySigning = &PrivateKey{}
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
var _ crypto.PrivateKeyKeyExchange = &PrivateKey{}
type PrivateKey struct {
@@ -179,17 +180,28 @@ func (p *PrivateKey) SignToBytes(message []byte, opts ...crypto.SigningOption) (
hasher.Write(message)
hash := hasher.Sum(nil)
// TODO
return ecdsa.SignCompact(p.k, hash[:], false), nil
sig := ecdsa.Sign(p.k, hash)
r := sig.R()
s := sig.S()
res := make([]byte, SignatureBytesSize)
r.PutBytesUnchecked(res[:SignatureBytesSize/2])
s.PutBytesUnchecked(res[SignatureBytesSize/2:])
return res, nil
}
// The default signing hash is SHA-256.
func (p *PrivateKey) SignToASN1(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
// // Hash the message with SHA-256
// hash := sha256.Sum256(message)
//
// return ecdsa.SignASN1(rand.Reader, p.k.ToECDSA(), hash[:])
return nil, fmt.Errorf("not implemented")
params := crypto.CollectSigningOptions(opts)
hasher := params.HashOrDefault(crypto.SHA256).New()
hasher.Write(message)
hash := hasher.Sum(nil)
sig := ecdsa.Sign(p.k, hash)
return sig.Serialize(), nil
}
func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
@@ -200,19 +212,8 @@ func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
}
func (p *PrivateKey) KeyExchange(remote crypto.PublicKey) ([]byte, error) {
// if remote, ok := remote.(*PublicKey); ok {
// // First, we need to convert the ECDSA (signing only) to the equivalent ECDH keys
// ecdhPriv, err := p.k.ECDH()
// if err != nil {
// return nil, err
// }
// ecdhPub, err := remote.k.ECDH()
// if err != nil {
// return nil, err
// }
//
// return ecdhPriv.ECDH(ecdhPub)
// }
// return nil, fmt.Errorf("incompatible public key")
panic("not implemented")
if remote, ok := remote.(*PublicKey); ok {
return secp256k1.GenerateSharedSecret(p.k, remote.k), nil
}
return nil, fmt.Errorf("incompatible public key")
}

View File

@@ -7,12 +7,14 @@ import (
"fmt"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
"github.com/INFURA/go-did/crypto"
helpers "github.com/INFURA/go-did/crypto/internal"
)
var _ crypto.PublicKeySigning = &PublicKey{}
var _ crypto.PublicKeySigningBytes = &PublicKey{}
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
type PublicKey struct {
k *secp256k1.PublicKey
@@ -166,33 +168,39 @@ func (p *PublicKey) ToX509PEM() string {
}))
}
/*
Note: signatures for the crypto.PrivateKeySigning interface assumes SHA256,
which should be correct almost always. If there is a need to use a different
hash function, we can add separate functions that have that flexibility.
*/
// The default signing hash is SHA-256.
func (p *PublicKey) VerifyBytes(message, signature []byte, opts ...crypto.SigningOption) bool {
// if len(signature) != SignatureBytesSize {
// return false
// }
//
// // Hash the message with SHA-256
// hash := sha256.Sum256(message)
//
// r := new(big.Int).SetBytes(signature[:SignatureBytesSize/2])
// s := new(big.Int).SetBytes(signature[SignatureBytesSize/2:])
//
// return ecdsa.Verify(p.k, hash[:], r, s)
panic("not implemented")
if len(signature) != SignatureBytesSize {
return false
}
params := crypto.CollectSigningOptions(opts)
hasher := params.HashOrDefault(crypto.SHA256).New()
hasher.Write(message)
hash := hasher.Sum(nil)
var r, s secp256k1.ModNScalar
r.SetByteSlice(signature[:32])
s.SetByteSlice(signature[32:])
return ecdsa.NewSignature(&r, &s).Verify(hash, p.k)
}
// The default signing hash is SHA-256.
func (p *PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
// // Hash the message with SHA-256
// hash := sha256.Sum256(message)
//
// return ecdsa.VerifyASN1(p.k, hash[:], signature)
panic("not implemented")
params := crypto.CollectSigningOptions(opts)
hasher := params.HashOrDefault(crypto.SHA256).New()
hasher.Write(message)
hash := hasher.Sum(nil)
sig, err := ecdsa.ParseDERSignature(signature)
if err != nil {
return false
}
return sig.Verify(hash, p.k)
}
func must[T any](v T, err error) T {