package cryptutil import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "math/big" ) // NewSigningKey generates a random P-256 ECDSA private key. // Go's P-256 is constant-time (which prevents certain types of attacks) // while its P-384 and P-521 are not. func NewSigningKey() (*ecdsa.PrivateKey, error) { return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } // Sign signs arbitrary data using ECDSA. func Sign(data []byte, privkey *ecdsa.PrivateKey) ([]byte, error) { // hash message digest := sha256.Sum256(data) // sign the hash r, s, err := ecdsa.Sign(rand.Reader, privkey, digest[:]) if err != nil { return nil, err } // encode the signature {R, S} // big.Int.Bytes() will need padding in the case of leading zero bytes params := privkey.Curve.Params() curveOrderByteSize := params.P.BitLen() / 8 rBytes, sBytes := r.Bytes(), s.Bytes() signature := make([]byte, curveOrderByteSize*2) copy(signature[curveOrderByteSize-len(rBytes):], rBytes) copy(signature[curveOrderByteSize*2-len(sBytes):], sBytes) return signature, nil } // Verify checks a raw ECDSA signature. // Returns true if it's valid and false if not. func Verify(data, signature []byte, pubkey *ecdsa.PublicKey) bool { // hash message digest := sha256.Sum256(data) curveOrderByteSize := pubkey.Curve.Params().P.BitLen() / 8 r, s := new(big.Int), new(big.Int) r.SetBytes(signature[:curveOrderByteSize]) s.SetBytes(signature[curveOrderByteSize:]) return ecdsa.Verify(pubkey, digest[:], r, s) }