cryptopasta

package module
v0.0.0-...-1f550f6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2017 License: CC0-1.0 Imports: 17 Imported by: 165

README

TL;DR- Copy & paste your crypto code from here instead of Stack Overflow.

This library demonstrates a suite of basic cryptography from the Go standard
library. To the extent possible, it tries to hide complexity and help you avoid
common mistakes. The recommendations were chosen as a compromise between
cryptographic qualities, the Go standard lib, and my existing use cases.

Some particular design choices I've made:

1. SHA-512/256 has been chosen as the default hash for the examples. It's
   faster on 64-bit machines and immune to length extension. If it doesn't work
   in your case, replace instances of it with ordinary SHA-256.

2. The specific ECDSA parameters were chosen to be compatible with RFC7518[1]
   while using the best implementation of ECDSA available. Go's P-256 is
   constant-time (which prevents certain types of attacks) while its P-384 and
   P-521 are not.

3. Key parameters are arrays rather than slices so the compiler can help you
   avoid mixing up the arguments. The signing and marshaling functions use the
   crypto/ecdsa key types directly for the same reason.

4. Public/private keypairs for signing are marshaled into and out of PEM
   format, making them relatively portable to other crypto software you're
   likely to use (openssl, cfssl, etc).

5. Key generation functions will panic if they can't read enough random bytes
   to generate the key. Key generation is critical, and if crypto/rand fails at
   that stage then you should stop doing cryptography on that machine immediately.

6. The license is a CC0 public domain dedication, with the intent that you can
   just copy bits of this directly into your code and never be required to
   acknowledge my copyright, provide source code, or do anything else commonly
   associated with open licenses.


The specific recommendations are:


Encryption - 256-bit AES-GCM with random 96-bit nonces

Using AES-GCM (instead of AES-CBC, AES-CFB, or AES-CTR, all of which Go also
offers) provides authentication in addition to confidentiality. This means that
the content of your data is hidden and that any modification of the encrypted
data will result in a failure to decrypt. This rules out entire classes of
possible attacks. Randomized nonces remove the choices around nonce generation
and management, which are another common source of error in crypto
implementations.

The interfaces in this library allow only the use of 256-bit keys.


Hashing - HMAC-SHA512/256

Using hash functions directly is fraught with various perils – it's common for
developers to accidentally write code that is subject to easy collision or
length extension attacks. HMAC is a function built on top of hashes and it
doesn't have those problems. Using SHA-512/256 as the underlying hash function
means the process will be faster on 64-bit machines, but the output will be the
same length as the more familiar SHA-256.

This interface encourages you to scope your hashes with an English-language
string (a "tag") that describes the purpose of the hash. Tagged hashes are a
common "security hygiene" measure to ensure that hashing the same data for
different purposes will produce different outputs.


Password hashing - bcrypt with work factor 14

Use this to store users' passwords and check them for login (e.g. in a web
backend). While they both have "hashing" in the name, password hashing is an
entirely different situation from ordinary hashing and requires its own
specialized algorithm. bcrypt is a hash function designed for password storage.
It can be made selectively slower (based on a "work factor") to increase the
difficulty of brute-force password cracking attempts.

As of 2016, a work factor of 14 should be well on the side of future-proofing
over performance. If it turns out to be too slow for your needs, you can try
using 13 or even 12. You should not go below work factor 12.


Symmetric Signatures / Message Authentication - HMAC-SHA512/256

When two parties share a secret key, they can use message authentication to
make sure that a piece of data hasn't been altered. You can think of it as a
"symmetric signature" - it proves both that the data is unchanged and that
someone who knows the shared secret key generated it. Anyone who does not know
the secret key can neither validate the data nor make valid alterations.

This comes up most often in the context of web stuff, such as:

1. Authenticating requests to your API. The most widely known example is
   probably the Amazon AWS API, which requires you to sign requests with
   HMAC-SHA256. In this type of use, the "secret key" is a token that the API
   provider issues to authorized API users.

2. Validating authenticated tokens (cookies, JWTs, etc) that are issued by a
   service but are stored by a user. In this case, the service wants to ensure
   that a user doesn't modify the data contained in the token.

As with encryption, you should always use a 256-bit random key to
authenticate messages.


Asymmetric Signatures - ECDSA on P-256 with SHA-256 message digests

These are the classic public/private keypair signatures that you probably think
of when you hear the word "signature". The holder of a private key can sign
data that anyone who has the corresponding public key can verify.

Go takes very good care of us here. In particular, the Go implementation of
P-256 is constant time to protect against side-channel attacks, and the Go
implementation of ECDSA generates safe nonces to protect against the type of
repeated-nonce attack that broke the PS3.

In terms of JWTs, this algorithm is called "ES256". The functions
"EncodeSignatureJWT" and "DecodeSignatureJWT" will convert the basic signature
format to and from the encoding specified by RFC7515[2]

[1] https://tools.ietf.org/html/rfc7518#section-3.1
[2] https://tools.ietf.org/html/rfc7515#appendix-A.3

Documentation

Overview

Provides symmetric authenticated encryption using 256-bit AES-GCM with a random nonce.

Provides a recommended hashing algorithm.

The hash function is HMAC-SHA512/256 where SHA512/256 is as described in FIPS 180-4. This construction avoids length-extension attacks while maintaining a widely compatible digest size with better performance on 64-bit systems.

Password hashing uses bcrypt with a work factor of 14.

Provides encoding and decoding routines for various cryptographic structures.

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

Provides a recommended TLS configuration.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckHMAC

func CheckHMAC(data, suppliedMAC []byte, key *[32]byte) bool

CheckHMAC securely checks the supplied MAC against a message using the shared secret key.

func CheckPasswordHash

func CheckPasswordHash(hash, password []byte) error

CheckPassword securely compares a bcrypt hashed password with its possible plaintext equivalent. Returns nil on success, or an error on failure.

func DecodePrivateKey

func DecodePrivateKey(encodedKey []byte) (*ecdsa.PrivateKey, error)

DecodePrivateKey decodes a PEM-encoded ECDSA private key.

func DecodePublicKey

func DecodePublicKey(encodedKey []byte) (*ecdsa.PublicKey, error)

DecodePublicKey decodes a PEM-encoded ECDSA public key.

func DecodeSignatureJWT

func DecodeSignatureJWT(b64sig string) ([]byte, error)

Decodes an ECDSA signature according to https://tools.ietf.org/html/rfc7515#appendix-A.3.1

func Decrypt

func Decrypt(ciphertext []byte, key *[32]byte) (plaintext []byte, err error)

Decrypt decrypts data using 256-bit AES-GCM. This both hides the content of the data and provides a check that it hasn't been altered. Expects input form nonce|ciphertext|tag where '|' indicates concatenation.

func DefaultTLSConfig

func DefaultTLSConfig() *tls.Config

func EncodePrivateKey

func EncodePrivateKey(key *ecdsa.PrivateKey) ([]byte, error)

EncodePrivateKey encodes an ECDSA private key to PEM format.

func EncodePublicKey

func EncodePublicKey(key *ecdsa.PublicKey) ([]byte, error)

EncodePublicKey encodes an ECDSA public key to PEM format.

func EncodeSignatureJWT

func EncodeSignatureJWT(sig []byte) string

Encodes an ECDSA signature according to https://tools.ietf.org/html/rfc7515#appendix-A.3.1

func Encrypt

func Encrypt(plaintext []byte, key *[32]byte) (ciphertext []byte, err error)

Encrypt encrypts data using 256-bit AES-GCM. This both hides the content of the data and provides a check that it hasn't been altered. Output takes the form nonce|ciphertext|tag where '|' indicates concatenation.

func GenerateHMAC

func GenerateHMAC(data []byte, key *[32]byte) []byte

GenerateHMAC produces a symmetric signature using a shared secret key.

func Hash

func Hash(tag string, data []byte) []byte

Hash generates a hash of data using HMAC-SHA-512/256. The tag is intended to be a natural-language string describing the purpose of the hash, such as "hash file for lookup key" or "master secret to client secret". It serves as an HMAC "key" and ensures that different purposes will have different hash output. This function is NOT suitable for hashing passwords.

Example
tag := "hashing file for lookup key"
contents, err := ioutil.ReadFile("testdata/random")
if err != nil {
	fmt.Printf("could not read file: %v\n", err)
	os.Exit(1)
}
digest := Hash(tag, contents)
fmt.Println(hex.EncodeToString(digest))
Output:

9f4c795d8ae5c207f19184ccebee6a606c1fdfe509c793614066d613580f03e1

func HashPassword

func HashPassword(password []byte) ([]byte, error)

HashPassword generates a bcrypt hash of the password using work factor 14.

func NewEncryptionKey

func NewEncryptionKey() *[32]byte

NewEncryptionKey generates a random 256-bit key for Encrypt() and Decrypt(). It panics if the source of randomness fails.

func NewHMACKey

func NewHMACKey() *[32]byte

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 NewSigningKey

func NewSigningKey() (*ecdsa.PrivateKey, error)

NewSigningKey generates a random P-256 ECDSA private key.

func Sign

func Sign(data []byte, privkey *ecdsa.PrivateKey) ([]byte, error)

Sign signs arbitrary data using ECDSA.

func Verify

func Verify(data, signature []byte, pubkey *ecdsa.PublicKey) bool

Verify checks a raw ECDSA signature. Returns true if it's valid and false if not.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL