/* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package utils import ( "crypto/ecdsa" "crypto/elliptic" "encoding/asn1" "errors" "fmt" "math/big" ) type ECDSASignature struct { R, S *big.Int } // curveHalfOrders contains the precomputed curve group orders halved. // It is used to ensure that signature' S value is lower or equal to the // curve group order halved. We accept only low-S signatures. // They are precomputed for efficiency reasons. var curveHalfOrders = map[elliptic.Curve]*big.Int{ elliptic.P224(): new(big.Int).Rsh(elliptic.P224().Params().N, 1), elliptic.P256(): new(big.Int).Rsh(elliptic.P256().Params().N, 1), elliptic.P384(): new(big.Int).Rsh(elliptic.P384().Params().N, 1), elliptic.P521(): new(big.Int).Rsh(elliptic.P521().Params().N, 1), } func GetCurveHalfOrdersAt(c elliptic.Curve) *big.Int { return big.NewInt(0).Set(curveHalfOrders[c]) } func MarshalECDSASignature(r, s *big.Int) ([]byte, error) { return asn1.Marshal(ECDSASignature{r, s}) } func UnmarshalECDSASignature(raw []byte) (*big.Int, *big.Int, error) { // Unmarshal sig := new(ECDSASignature) _, err := asn1.Unmarshal(raw, sig) if err != nil { return nil, nil, fmt.Errorf("failed unmashalling signature [%s]", err) } // Validate sig if sig.R == nil { return nil, nil, errors.New("invalid signature, R must be different from nil") } if sig.S == nil { return nil, nil, errors.New("invalid signature, S must be different from nil") } if sig.R.Sign() != 1 { return nil, nil, errors.New("invalid signature, R must be larger than zero") } if sig.S.Sign() != 1 { return nil, nil, errors.New("invalid signature, S must be larger than zero") } return sig.R, sig.S, nil } func SignatureToLowS(k *ecdsa.PublicKey, signature []byte) ([]byte, error) { r, s, err := UnmarshalECDSASignature(signature) if err != nil { return nil, err } s, err = ToLowS(k, s) if err != nil { return nil, err } return MarshalECDSASignature(r, s) } // IsLow checks that s is a low-S func IsLowS(k *ecdsa.PublicKey, s *big.Int) (bool, error) { halfOrder, ok := curveHalfOrders[k.Curve] if !ok { return false, fmt.Errorf("curve not recognized [%s]", k.Curve) } return s.Cmp(halfOrder) != 1, nil } func ToLowS(k *ecdsa.PublicKey, s *big.Int) (*big.Int, error) { lowS, err := IsLowS(k, s) if err != nil { return nil, err } if !lowS { // Set s to N - s that will be then in the lower part of signature space // less or equal to half order s.Sub(k.Params().N, s) return s, nil } return s, nil }