| 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 |
| } |