Skip to content

Commit

Permalink
Implement ecdsa.{signer,verifier} using `subtle.{ECDSASigner,ECDSAV…
Browse files Browse the repository at this point in the history
…erifier}`

PiperOrigin-RevId: 698352802
Change-Id: I0d75f5ae76983df20733d968c8c842fee7931648
  • Loading branch information
morambro authored and copybara-github committed Nov 20, 2024
1 parent c41ea0e commit 8401621
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 108 deletions.
73 changes: 13 additions & 60 deletions signature/ecdsa/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,68 +15,42 @@
package ecdsa

import (
"crypto/ecdsa"
"crypto/rand"
"fmt"
"hash"
"math/big"
"slices"

"github.com/tink-crypto/tink-go/v2/insecuresecretdataaccess"
"github.com/tink-crypto/tink-go/v2/internal/internalapi"
internalecdsa "github.com/tink-crypto/tink-go/v2/internal/signature/ecdsa"
"github.com/tink-crypto/tink-go/v2/key"
"github.com/tink-crypto/tink-go/v2/subtle"
"github.com/tink-crypto/tink-go/v2/signature/subtle"
"github.com/tink-crypto/tink-go/v2/tink"
)

// signer is an implementation of the [tink.Signer] interface for ECDSA
// (RFC6979).
type signer struct {
hashFunc func() hash.Hash
encoding SignatureEncoding
privateKey *ecdsa.PrivateKey
prefix []byte
variant Variant
impl *subtle.ECDSASigner
prefix []byte
variant Variant
}

var _ tink.Signer = (*signer)(nil)

func ecdsaPublicKey(params *Parameters, publicPoint []byte) (*ecdsa.PublicKey, error) {
x, y, err := validateEncodingAndGetCoordinates(publicPoint, params.CurveType())
if err != nil {
return nil, err
}
curve := subtle.GetCurve(params.CurveType().String())
if curve == nil {
return nil, fmt.Errorf("ecdsa: invalid curve: %s", params.CurveType())
}
return &ecdsa.PublicKey{
Curve: curve,
X: new(big.Int).SetBytes(x),
Y: new(big.Int).SetBytes(y),
}, nil
}

// NewSigner creates a new instance of [Signer].
//
// This is an internal API.
func NewSigner(k *PrivateKey, _ internalapi.Token) (tink.Signer, error) {
params := k.publicKey.parameters
publicKey, err := ecdsaPublicKey(params, k.publicKey.publicPoint)
hasType := params.HashType().String()
encoding := params.SignatureEncoding().String()
curve := params.CurveType().String()
rawPrimitive, err := subtle.NewECDSASigner(hasType, curve, encoding, k.PrivateKeyValue().Data(insecuresecretdataaccess.Token{}))
if err != nil {
return nil, err
}
privKey := &ecdsa.PrivateKey{
PublicKey: *publicKey,
D: new(big.Int).SetBytes(k.PrivateKeyValue().Data(insecuresecretdataaccess.Token{})),
}
return &signer{
hashFunc: subtle.GetHashFunc(params.HashType().String()),
encoding: params.SignatureEncoding(),
privateKey: privKey,
prefix: k.OutputPrefix(),
variant: params.Variant(),
impl: rawPrimitive,
prefix: k.OutputPrefix(),
variant: params.Variant(),
}, nil
}

Expand All @@ -90,32 +64,11 @@ func (e *signer) Sign(data []byte) ([]byte, error) {
if e.variant == VariantLegacy {
toSign = slices.Concat(data, []byte{0})
}
digest, err := subtle.ComputeHash(e.hashFunc, toSign)
rawSignature, err := e.impl.Sign(toSign)
if err != nil {
return nil, err
}
var signatureBytes []byte
switch e.encoding {
case DER:
var err error
signatureBytes, err = ecdsa.SignASN1(rand.Reader, e.privateKey, digest)
if err != nil {
return nil, err
}
case IEEEP1363:
r, s, err := ecdsa.Sign(rand.Reader, e.privateKey, digest)
if err != nil {
return nil, err
}
sig := internalecdsa.Signature{R: r, S: s}
signatureBytes, err = internalecdsa.IEEEP1363Encode(&sig, e.privateKey.PublicKey.Curve.Params().Name)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("ecdsa: unsupported encoding: %s", e.encoding)
}
return slices.Concat(e.prefix, signatureBytes), nil
return slices.Concat(e.prefix, rawSignature), nil
}

func signerConstructor(key key.Key) (any, error) {
Expand Down
67 changes: 19 additions & 48 deletions signature/ecdsa/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,22 @@ package ecdsa

import (
"bytes"
"crypto/ecdsa"
"fmt"
"hash"
"slices"

"github.com/tink-crypto/tink-go/v2/internal/internalapi"
internalecdsa "github.com/tink-crypto/tink-go/v2/internal/signature/ecdsa"
"github.com/tink-crypto/tink-go/v2/key"
"github.com/tink-crypto/tink-go/v2/subtle"
signaturesubtle "github.com/tink-crypto/tink-go/v2/signature/subtle"
"github.com/tink-crypto/tink-go/v2/tink"
)

// verifier implements the [tink.Verifier] interface for ECDSA (RFC6979).
//
// It accepts signature in both ASN.1 and IEEE_P1363 encoding.
type verifier struct {
publicKey *ecdsa.PublicKey
hashFunc func() hash.Hash
encoding SignatureEncoding
prefix []byte
variant Variant
impl *signaturesubtle.ECDSAVerifier
prefix []byte
variant Variant
}

var _ tink.Verifier = (*verifier)(nil)
Expand All @@ -45,27 +40,29 @@ var _ tink.Verifier = (*verifier)(nil)
//
// This is an internal API.
func NewVerifier(publicKey *PublicKey, _ internalapi.Token) (tink.Verifier, error) {
pk, err := ecdsaPublicKey(publicKey.parameters, publicKey.publicPoint)
hashType := publicKey.parameters.HashType().String()
encoding := publicKey.parameters.SignatureEncoding().String()
curve := publicKey.parameters.CurveType().String()
x, y, err := validateEncodingAndGetCoordinates(publicKey.publicPoint, publicKey.parameters.CurveType())
if err != nil {
return nil, err
}
rawPrimitive, err := signaturesubtle.NewECDSAVerifier(hashType, curve, encoding, x, y)
if err != nil {
return nil, err
}
return &verifier{
publicKey: pk,
hashFunc: subtle.GetHashFunc(publicKey.parameters.HashType().String()),
encoding: publicKey.parameters.SignatureEncoding(),
prefix: publicKey.OutputPrefix(),
variant: publicKey.parameters.Variant(),
impl: rawPrimitive,
prefix: publicKey.OutputPrefix(),
variant: publicKey.parameters.Variant(),
}, nil
}

// Verify verifies whether the given signature is valid for the given data.
//
// The signature is expected to be of the form:
//
// <prefix> || signature
//
// where prefix is the key's output prefix and can be empty, and signature is
// the signature in the encoding specified by the key's parameters.
// The signature is expected to be of the form: prefix || signature, where
// prefix is the key's output prefix and can be empty, and signature is the
// signature in the encoding specified by the key's parameters.
func (e *verifier) Verify(signatureBytes, data []byte) error {
if !bytes.HasPrefix(signatureBytes, e.prefix) {
return fmt.Errorf("ecdsa_verifier: invalid signature prefix")
Expand All @@ -74,33 +71,7 @@ func (e *verifier) Verify(signatureBytes, data []byte) error {
if e.variant == VariantLegacy {
toSign = slices.Concat(data, []byte{0})
}
hashed, err := subtle.ComputeHash(e.hashFunc, toSign)
if err != nil {
return err
}

rawSignature := signatureBytes[len(e.prefix):]
var asn1Signature []byte
switch e.encoding {
case DER:
asn1Signature = rawSignature
case IEEEP1363:
decodedSig, err := internalecdsa.IEEEP1363Decode(rawSignature)
if err != nil {
return err
}
asn1Signature, err = internalecdsa.ASN1Encode(decodedSig)
if err != nil {
return err
}
default:
return fmt.Errorf("ecdsa: unsupported encoding: %s", e.encoding)
}

if ok := ecdsa.VerifyASN1(e.publicKey, hashed, asn1Signature); !ok {
return fmt.Errorf("ecdsa_verifier: invalid signature")
}
return nil
return e.impl.Verify(signatureBytes[len(e.prefix):], toSign)
}

func verifierConstructor(key key.Key) (any, error) {
Expand Down

0 comments on commit 8401621

Please sign in to comment.