mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-06-17 04:18:53 -04:00
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.8.0 to 1.9.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v1.8.0...v1.9.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-version: 1.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
201 lines
6.7 KiB
Go
201 lines
6.7 KiB
Go
package dsig
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/rand"
|
|
"encoding/asn1"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
|
|
"github.com/lestrrat-go/dsig/internal/ecutil"
|
|
)
|
|
|
|
|
|
func ecdsaGetSignerKey(key any) (*ecdsa.PrivateKey, crypto.Signer, bool, error) {
|
|
cs, isCryptoSigner := key.(crypto.Signer)
|
|
if isCryptoSigner {
|
|
if !isValidECDSAKey(key) {
|
|
return nil, nil, false, fmt.Errorf(`invalid key type %T for ECDSA algorithm`, key)
|
|
}
|
|
|
|
switch key.(type) {
|
|
case ecdsa.PrivateKey, *ecdsa.PrivateKey:
|
|
// if it's ecdsa.PrivateKey, it's more efficient to
|
|
// go through the non-crypto.Signer route. Set isCryptoSigner to false
|
|
isCryptoSigner = false
|
|
}
|
|
}
|
|
|
|
if isCryptoSigner {
|
|
return nil, cs, true, nil
|
|
}
|
|
|
|
privkey, ok := key.(*ecdsa.PrivateKey)
|
|
if !ok {
|
|
return nil, nil, false, fmt.Errorf(`invalid key type %T. *ecdsa.PrivateKey is required`, key)
|
|
}
|
|
return privkey, nil, false, nil
|
|
}
|
|
|
|
// UnpackASN1ECDSASignature unpacks an ASN.1 encoded ECDSA signature into r and s values.
|
|
// This is typically used when working with crypto.Signer interfaces that return ASN.1 encoded signatures.
|
|
func UnpackASN1ECDSASignature(signed []byte, r, s *big.Int) error {
|
|
// Okay, this is silly, but hear me out. When we use the
|
|
// crypto.Signer interface, the PrivateKey is hidden.
|
|
// But we need some information about the key (its bit size).
|
|
//
|
|
// So while silly, we're going to have to make another call
|
|
// here and fetch the Public key.
|
|
// (This probably means that this information should be cached somewhere)
|
|
var p struct {
|
|
R *big.Int // TODO: get this from a pool?
|
|
S *big.Int
|
|
}
|
|
if _, err := asn1.Unmarshal(signed, &p); err != nil {
|
|
return fmt.Errorf(`failed to unmarshal ASN1 encoded signature: %w`, err)
|
|
}
|
|
|
|
r.Set(p.R)
|
|
s.Set(p.S)
|
|
return nil
|
|
}
|
|
|
|
// UnpackECDSASignature unpacks a JWS-format ECDSA signature into r and s values.
|
|
// The signature should be in the format specified by RFC 7515 (r||s as fixed-length byte arrays).
|
|
func UnpackECDSASignature(signature []byte, pubkey *ecdsa.PublicKey, r, s *big.Int) error {
|
|
keySize := ecutil.CalculateKeySize(pubkey.Curve)
|
|
if len(signature) != keySize*2 {
|
|
return fmt.Errorf(`invalid signature length for curve %q`, pubkey.Curve.Params().Name)
|
|
}
|
|
|
|
r.SetBytes(signature[:keySize])
|
|
s.SetBytes(signature[keySize:])
|
|
|
|
return nil
|
|
}
|
|
|
|
// PackECDSASignature packs the r and s values from an ECDSA signature into a JWS-format byte slice.
|
|
// The output format follows RFC 7515: r||s as fixed-length byte arrays.
|
|
func PackECDSASignature(r *big.Int, sbig *big.Int, curveBits int) ([]byte, error) {
|
|
keyBytes := curveBits / 8
|
|
if curveBits%8 > 0 {
|
|
keyBytes++
|
|
}
|
|
|
|
// Serialize r and s into fixed-length bytes
|
|
rBytes := r.Bytes()
|
|
rBytesPadded := make([]byte, keyBytes)
|
|
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
|
|
|
sBytes := sbig.Bytes()
|
|
sBytesPadded := make([]byte, keyBytes)
|
|
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
|
|
|
// Output as r||s
|
|
return append(rBytesPadded, sBytesPadded...), nil
|
|
}
|
|
|
|
// SignECDSA generates an ECDSA signature for the given payload using the specified private key and hash.
|
|
// The raw parameter should be the pre-computed signing input (typically header.payload).
|
|
//
|
|
// rr is an io.Reader that provides randomness for signing. if rr is nil, it defaults to rand.Reader.
|
|
func SignECDSA(key *ecdsa.PrivateKey, payload []byte, h crypto.Hash, rr io.Reader) ([]byte, error) {
|
|
if !isValidECDSAKey(key) {
|
|
return nil, fmt.Errorf(`invalid key type %T for ECDSA algorithm`, key)
|
|
}
|
|
hh := h.New()
|
|
if _, err := hh.Write(payload); err != nil {
|
|
return nil, fmt.Errorf(`failed to write payload using ecdsa: %w`, err)
|
|
}
|
|
digest := hh.Sum(nil)
|
|
|
|
if rr == nil {
|
|
rr = rand.Reader
|
|
}
|
|
|
|
// Sign and get r, s values
|
|
r, s, err := ecdsa.Sign(rr, key, digest)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`failed to sign payload using ecdsa: %w`, err)
|
|
}
|
|
|
|
return PackECDSASignature(r, s, key.Curve.Params().BitSize)
|
|
}
|
|
|
|
// SignECDSACryptoSigner generates an ECDSA signature using a crypto.Signer interface.
|
|
// This function works with hardware security modules and other crypto.Signer implementations.
|
|
// The signature is converted from ASN.1 format to JWS format (r||s).
|
|
//
|
|
// rr is an io.Reader that provides randomness for signing. If rr is nil, it defaults to rand.Reader.
|
|
func SignECDSACryptoSigner(signer crypto.Signer, raw []byte, h crypto.Hash, rr io.Reader) ([]byte, error) {
|
|
signed, err := SignCryptoSigner(signer, raw, h, h, rr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`failed to sign payload using crypto.Signer: %w`, err)
|
|
}
|
|
|
|
return signECDSACryptoSigner(signer, signed)
|
|
}
|
|
|
|
func signECDSACryptoSigner(signer crypto.Signer, signed []byte) ([]byte, error) {
|
|
cpub := signer.Public()
|
|
pubkey, ok := cpub.(*ecdsa.PublicKey)
|
|
if !ok {
|
|
return nil, fmt.Errorf(`expected *ecdsa.PublicKey, got %T`, pubkey)
|
|
}
|
|
curveBits := pubkey.Curve.Params().BitSize
|
|
|
|
var r, s big.Int
|
|
if err := UnpackASN1ECDSASignature(signed, &r, &s); err != nil {
|
|
return nil, fmt.Errorf(`failed to unpack ASN1 encoded signature: %w`, err)
|
|
}
|
|
|
|
return PackECDSASignature(&r, &s, curveBits)
|
|
}
|
|
|
|
func ecdsaVerify(key *ecdsa.PublicKey, buf []byte, h crypto.Hash, r, s *big.Int) error {
|
|
hasher := h.New()
|
|
hasher.Write(buf)
|
|
digest := hasher.Sum(nil)
|
|
if !ecdsa.Verify(key, digest, r, s) {
|
|
return NewVerificationError("invalid ECDSA signature")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// VerifyECDSA verifies an ECDSA signature for the given payload.
|
|
// This function verifies the signature using the specified public key and hash algorithm.
|
|
// The payload parameter should be the pre-computed signing input (typically header.payload).
|
|
func VerifyECDSA(key *ecdsa.PublicKey, payload, signature []byte, h crypto.Hash) error {
|
|
var r, s big.Int
|
|
if err := UnpackECDSASignature(signature, key, &r, &s); err != nil {
|
|
return fmt.Errorf("dsig.VerifyECDSA: failed to unpack ECDSA signature: %w", err)
|
|
}
|
|
|
|
return ecdsaVerify(key, payload, h, &r, &s)
|
|
}
|
|
|
|
// VerifyECDSACryptoSigner verifies an ECDSA signature for crypto.Signer implementations.
|
|
// This function is useful for verifying signatures created by hardware security modules
|
|
// or other implementations of the crypto.Signer interface.
|
|
// The payload parameter should be the pre-computed signing input (typically header.payload).
|
|
func VerifyECDSACryptoSigner(signer crypto.Signer, payload, signature []byte, h crypto.Hash) error {
|
|
var pubkey *ecdsa.PublicKey
|
|
switch cpub := signer.Public(); cpub := cpub.(type) {
|
|
case ecdsa.PublicKey:
|
|
pubkey = &cpub
|
|
case *ecdsa.PublicKey:
|
|
pubkey = cpub
|
|
default:
|
|
return fmt.Errorf(`dsig.VerifyECDSACryptoSigner: expected *ecdsa.PublicKey, got %T`, cpub)
|
|
}
|
|
|
|
var r, s big.Int
|
|
if err := UnpackECDSASignature(signature, pubkey, &r, &s); err != nil {
|
|
return fmt.Errorf("dsig.VerifyECDSACryptoSigner: failed to unpack ASN.1 encoded ECDSA signature: %w", err)
|
|
}
|
|
|
|
return ecdsaVerify(pubkey, payload, h, &r, &s)
|
|
}
|