// cryptopasta - basic cryptography examples // // Written in 2015 by George Tankersley // // To the extent possible under law, the author(s) have dedicated all copyright // and related and neighboring rights to this software to the public domain // worldwide. This software is distributed without any warranty. // // You should have received a copy of the CC0 Public Domain Dedication along // with this software. If not, see // . // Provides message authentication and asymmetric signatures. // // Message authentication: HMAC SHA512/256 // This is a slight twist on the highly dependable HMAC-SHA256 that gains // performance on 64-bit systems and consistency with our hashing // recommendation. // // Asymmetric Signature: ECDSA using P256 and SHA256 // ECDSA is the best compromise between cryptographic concerns and support for // our internal use cases (e.g. RFC7518). The Go standard library // implementation has some protection against entropy problems, but is not // deterministic. See // https://github.com/golang/go/commit/8d7bf2291b095d3a2ecaa2609e1101be46d80deb package cryptopasta import ( "crypto/ecdsa" "crypto/elliptic" "crypto/hmac" "crypto/rand" "crypto/sha256" "crypto/sha512" "io" "math/big" ) // NewHMACKey generates a random 256-bit secret key for HMAC use. // Because key generation is critical, it panics if the source of randomness fails. func NewHMACKey() *[32]byte { key := &[32]byte{} _, err := io.ReadFull(rand.Reader, key[:]) if err != nil { panic(err) } return key } // GenerateHMAC produces a symmetric signature using a shared secret key. func GenerateHMAC(data []byte, key *[32]byte) []byte { h := hmac.New(sha512.New512_256, key[:]) h.Write(data) return h.Sum(nil) } // CheckHMAC securely checks the supplied MAC against a message using the shared secret key. func CheckHMAC(data, suppliedMAC []byte, key *[32]byte) bool { expectedMAC := GenerateHMAC(data, key) return hmac.Equal(expectedMAC, suppliedMAC) } // GenerateSigningKey generates a random P-256 ECDSA private key. func NewSigningKey() (*ecdsa.PrivateKey, error) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) return key, err } // 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) }