blob: fdef90c16c2203c40e69ac01d1e76ea2c5150ba2 [file] [log] [blame]
package cose
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"encoding/asn1"
"errors"
"fmt"
"io"
"math/big"
_ "google3/go/tools/nogo/allowlist/crypto/elliptic"
)
// I2OSP - Integer-to-Octet-String primitive converts a nonnegative integer to
// an octet string of a specified length `len(buf)`, and stores it in `buf`.
// I2OSP is used for encoding ECDSA signature (r, s) into byte strings.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8017#section-4.1
func I2OSP(x *big.Int, buf []byte) error {
if x.Sign() < 0 {
return errors.New("I2OSP: negative integer")
}
if x.BitLen() > len(buf)*8 {
return errors.New("I2OSP: integer too large")
}
x.FillBytes(buf)
return nil
}
// OS2IP - Octet-String-to-Integer primitive converts an octet string to a
// nonnegative integer.
// OS2IP is used for decoding ECDSA signature (r, s) from byte strings.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8017#section-4.2
func OS2IP(x []byte) *big.Int {
return new(big.Int).SetBytes(x)
}
// ecdsaKeySigner is a ECDSA based signer with golang built-in keys.
type ecdsaKeySigner struct {
alg Algorithm
key *ecdsa.PrivateKey
}
// Algorithm returns the signing algorithm associated with the private key.
func (es *ecdsaKeySigner) Algorithm() Algorithm {
return es.alg
}
// Sign signs digest with the private key, possibly using entropy from rand.
// The resulting signature should follow RFC 8152 section 8.1.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1
func (es *ecdsaKeySigner) Sign(rand io.Reader, digest []byte) ([]byte, error) {
r, s, err := ecdsa.Sign(rand, es.key, digest)
if err != nil {
return nil, err
}
return encodeECDSASignature(es.key.Curve, r, s)
}
// ecdsaKeySigner is a ECDSA based signer with a generic crypto.Signer.
type ecdsaCryptoSigner struct {
alg Algorithm
key *ecdsa.PublicKey
signer crypto.Signer
}
// Algorithm returns the signing algorithm associated with the private key.
func (es *ecdsaCryptoSigner) Algorithm() Algorithm {
return es.alg
}
// Sign signs digest with the private key, possibly using entropy from rand.
// The resulting signature should follow RFC 8152 section 8.1.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1
func (es *ecdsaCryptoSigner) Sign(rand io.Reader, digest []byte) ([]byte, error) {
sigASN1, err := es.signer.Sign(rand, digest, nil)
if err != nil {
return nil, err
}
// decode ASN.1 decoded signature
var sig struct {
R, S *big.Int
}
if _, err := asn1.Unmarshal(sigASN1, &sig); err != nil {
return nil, err
}
// encode signature in the COSE form
return encodeECDSASignature(es.key.Curve, sig.R, sig.S)
}
// encodeECDSASignature encodes (r, s) into a signature binary string using the
// method specified by RFC 8152 section 8.1.
func encodeECDSASignature(curve elliptic.Curve, r, s *big.Int) ([]byte, error) {
n := (curve.Params().BitSize + 7) / 8
sig := make([]byte, n*2)
if err := I2OSP(r, sig[:n]); err != nil {
return nil, err
}
if err := I2OSP(s, sig[n:]); err != nil {
return nil, err
}
return sig, nil
}
// decodeECDSASignature decodes (r, s) from a signature binary string using the
// method specified by RFC 8152 section 8.1.
func decodeECDSASignature(curve elliptic.Curve, sig []byte) (r, s *big.Int, err error) {
n := (curve.Params().BitSize + 7) / 8
if len(sig) != n*2 {
return nil, nil, fmt.Errorf("invalid signature length: %d", len(sig))
}
return OS2IP(sig[:n]), OS2IP(sig[n:]), nil
}
// ecdsaVerifier is a ECDSA based verifier with golang built-in keys.
type ecdsaVerifier struct {
alg Algorithm
key *ecdsa.PublicKey
}
// Algorithm returns the signing algorithm associated with the public key.
func (ev *ecdsaVerifier) Algorithm() Algorithm {
return ev.alg
}
// Verify verifies digest with the public key, returning nil for success.
// Otherwise, it returns ErrVerification.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1
func (ev *ecdsaVerifier) Verify(digest []byte, signature []byte) error {
// verify digest size
if h, ok := ev.alg.hashFunc(); !ok || h.Size() != len(digest) {
return ErrVerification
}
// verify signature
r, s, err := decodeECDSASignature(ev.key.Curve, signature)
if err != nil {
return ErrVerification
}
if verified := ecdsa.Verify(ev.key, digest, r, s); !verified {
return ErrVerification
}
return nil
}