|
| 1 | +# Crypto package |
| 2 | + |
| 3 | +This crypto package is a thin ergonomic layer on top of the normal golang crypto packages or `x/crypto`. |
| 4 | + |
| 5 | +It aims to solve the following problems with the standard crypto packages: |
| 6 | +- different algorithms have different APIs and ergonomics, which makes it hard to use them interchangeably |
| 7 | +- occasionally, it's quite hard to figure out how to do simple tasks (like encoding/decoding keys) |
| 8 | +- it's still necessary to make some educated choices (e.g. which hash function to use for signatures) |
| 9 | +- sometimes features are left out (e.g. ed25519 to X25519 for key exchange, secp256k1...) |
| 10 | +- some hash functions are not available in the standard library with no easy way to extend it (e.g. KECCAK-256) |
| 11 | + |
| 12 | +To do so, this package provides and implements a set of shared interfaces for all algorithms. As not all algorithms |
| 13 | +support all features (e.g. RSA keys don't support key exchange), some interfaces are optionally implemented. |
| 14 | + |
| 15 | +An additional benefit of shared interfaces is that a shared test suite can be written to test all algorithms, which this |
| 16 | +package does. |
| 17 | + |
| 18 | +## Example |
| 19 | + |
| 20 | +```go |
| 21 | +// This example demonstrates how to use the crypto package without going over all the features. |
| 22 | +// We will use P-256 keys, but they all work the same way (although not all have all the features). |
| 23 | + |
| 24 | +// 0: Generate a key pair |
| 25 | +pubAlice, privAlice, err := p256.GenerateKeyPair() |
| 26 | +handleErr(err) |
| 27 | + |
| 28 | +// 1: Serialize a key, read it back, verify it's the same |
| 29 | +privAliceBytes := privAlice.ToPKCS8DER() |
| 30 | +privAlice2, err := p256.PrivateKeyFromPKCS8DER(privAliceBytes) |
| 31 | +handleErr(err) |
| 32 | +fmt.Println("Keys are equals:", privAlice.Equal(privAlice2)) |
| 33 | + |
| 34 | +// 2: Sign a message, verify the signature. |
| 35 | +// Signatures can be made in raw bytes (SignToBytes) or ASN.1 DER format (SignToASN1). |
| 36 | +msg := []byte("hello world") |
| 37 | +sig, err := privAlice.SignToBytes(msg) |
| 38 | +handleErr(err) |
| 39 | +fmt.Println("Signature is valid:", pubAlice.VerifyBytes(msg, sig)) |
| 40 | + |
| 41 | +// 3: Signatures are done with an opinionated default configuration, but you can override it. |
| 42 | +// For example, the default hash function for P-256 is SHA-256, but you can use SHA-384 instead. |
| 43 | +opts := []crypto.SigningOption{crypto.WithSigningHash(crypto.SHA384)} |
| 44 | +sig384, err := privAlice.SignToBytes(msg, opts...) |
| 45 | +handleErr(err) |
| 46 | +fmt.Println("Signature is valid (SHA-384):", pubAlice.VerifyBytes(msg, sig384, opts...)) |
| 47 | + |
| 48 | +// 4: Key exchange: generate a second key-pair and compute a shared secret. |
| 49 | +// ⚠️ Security Warning: The shared secret returned by key agreement should NOT be used directly as an encryption key. |
| 50 | +// It must be processed through a Key Derivation Function (KDF) such as HKDF before being used in cryptographic protocols. |
| 51 | +// Using the raw shared secret directly can lead to security vulnerabilities. |
| 52 | +pubBob, privBob, err := p256.GenerateKeyPair() |
| 53 | +handleErr(err) |
| 54 | +shared1, err := privAlice.KeyExchange(pubBob) |
| 55 | +handleErr(err) |
| 56 | +shared2, err := privBob.KeyExchange(pubAlice) |
| 57 | +handleErr(err) |
| 58 | +fmt.Println("Shared secrets are identical:", bytes.Equal(shared1, shared2)) |
| 59 | + |
| 60 | +// 5: Bonus: one very annoying thing in cryptographic protocols is that the other side needs to know the configuration |
| 61 | +// you used for your signature. Having defaults or implied config only work sor far. |
| 62 | +// To solve this problem, this package integrates varsig: a format to describe the signing configuration. This varsig |
| 63 | +// can be attached to the signature, and the other side doesn't have to guess any more. Here is how it works: |
| 64 | +varsigBytes := privAlice.Varsig(opts...).Encode() |
| 65 | +fmt.Println("Varsig:", base64.StdEncoding.EncodeToString(varsigBytes)) |
| 66 | +sig, err = privAlice.SignToBytes(msg, opts...) |
| 67 | +handleErr(err) |
| 68 | +varsigDecoded, err := varsig.Decode(varsigBytes) |
| 69 | +handleErr(err) |
| 70 | +fmt.Println("Signature with varsig is valid:", pubAlice.VerifyBytes(msg, sig, crypto.WithVarsig(varsigDecoded))) |
| 71 | + |
| 72 | +// Output: |
| 73 | +// Keys are equals: true |
| 74 | +// Signature is valid: true |
| 75 | +// Signature is valid (SHA-384): true |
| 76 | +// Shared secrets are identical: true |
| 77 | +// Varsig: NAHsAYAkIF8= |
| 78 | +// Signature with varsig is valid: true |
| 79 | +``` |
| 80 | + |
| 81 | +## Supported Cryptographic Algorithms |
| 82 | + |
| 83 | +| Algorithm | Signature Format | Public Key Formats | Private Key Formats | Key Agreement | |
| 84 | +|-----------------|-------------------|-------------------------------------|---------------------------|----------------| |
| 85 | +| Ed25519 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ (via X25519) | |
| 86 | +| ECDSA P-256 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ | |
| 87 | +| ECDSA P-384 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ | |
| 88 | +| ECDSA P-521 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ | |
| 89 | +| ECDSA secp256k1 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ | |
| 90 | +| RSA | PKCS#1 v1.5 ASN.1 | X.509 DER/PEM, Multibase | PKCS#8 DER/PEM | ❌ | |
| 91 | +| X25519 | ❌ | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ | |
0 commit comments