Documentation ¶
Overview ¶
Crypto Helper: A friendly cryptographic helper for common use cases.
Crypto Helper is distributed under the NCSA license. This license should be inculded in the source distribution; if you've received a copy of this source and no associated license, please visit:
Index ¶
- Constants
- Variables
- func DecodeTimeBasedIV(iv []byte) (time.Time, []byte, error)
- func Dumphex(buf []byte) string
- func GenerateKey(length int) ([]byte, error)
- func GenerateKeyString(length int) (string, error)
- type Base64Serializer
- type Base64SizedSerializer
- type ByteSerializer
- type CompositeKey
- type CompositeKeyer
- type Encrypter
- type EngineConfiguration
- type IVGenerator
- type ImprovedByteSerializer
- type JSONSerializer
- type Key
- type Keyer
- type Payload
- type PayloadValidator
- type Sealer
- type Serializer
- type SerializerFunc
- type Signer
- type SigningEncrypter
- type SigningEncryptionEngine
- func (engine *SigningEncryptionEngine) Decrypt(payload *Payload) []byte
- func (engine *SigningEncryptionEngine) Encrypt(data []byte) (*Payload, error)
- func (engine *SigningEncryptionEngine) NewSubkey() ([]byte, []byte, error)
- func (engine *SigningEncryptionEngine) Open(message []byte) []byte
- func (engine *SigningEncryptionEngine) OpenValidator(message []byte, validator PayloadValidator) []byte
- func (engine *SigningEncryptionEngine) Seal(ciphertext []byte) (buf []byte)
- func (engine *SigningEncryptionEngine) SealAsPayload(ciphertext []byte) *Payload
- func (engine *SigningEncryptionEngine) Serialize(payload *Payload) ([]byte, error)
- func (engine *SigningEncryptionEngine) SerializeForSigning(payload *Payload) ([]byte, error)
- func (engine *SigningEncryptionEngine) SetHMAC(key []byte)
- func (engine *SigningEncryptionEngine) SetIVGenerator(generator IVGenerator)
- func (engine *SigningEncryptionEngine) SetKey(key []byte) error
- func (engine *SigningEncryptionEngine) SetSerializer(serializer SerializerFunc)
- func (engine *SigningEncryptionEngine) Sign(ciphertext []byte) []byte
- func (engine *SigningEncryptionEngine) SignPayload(payload *Payload) (*Payload, error)
- func (engine *SigningEncryptionEngine) Unserialize(message []byte) *Payload
- func (engine *SigningEncryptionEngine) ValidateSubkey(cipherkey, hmackey []byte) bool
- func (engine *SigningEncryptionEngine) Verify(signature, message []byte) bool
- func (engine *SigningEncryptionEngine) VerifyBytes(message []byte) bool
- func (engine *SigningEncryptionEngine) VerifyPayload(payload *Payload) (bool, error)
- type SizedSerializer
- type SubkeySigner
Constants ¶
const CryptoVersion = "0.1.0"
CryptoVersion declares the version used by the crypto API defined in this package and its associated data types. Semver is followed somewhat closely by this identifier: Patch levels indicate fixes to the crypto spec where published behavior and code behavior may not align, minor version bumps indicate new features or changes to the crypto API that may induce minor code breakage, and major version bumps indicate significant crypto API changes that are guaranteed to break downstream code.
Variables ¶
var DefaultIVGenerator = func(size int) ([]byte, error) { if size == 0 { return []byte{}, ErrInvalidIVSize } buf := make([]byte, size) num, err := rand.Read(buf) if err != nil { return nil, err } if num < size { return nil, fmt.Errorf("wrote %d bytes out of %d required", num, size) } return buf, nil }
DefaultIVGenerator is the default initialization vector generator function. This function doesn't do anything fancy: It just creates an initialization vector of size `size` containing cryptographically-secure randomly generated bytes.
var ErrCreatingCipher = errors.NewError("could not create new cipher")
ErrCreatingCipher is typically returned when initializing a cipher fails. As we only use AES in this package, this is most likely due to key length errors, which should be either 16 (AES-128), 24 (AES-192), or 32 (AES-256) bytes. See https://golang.org/pkg/crypto/aes/
var ErrCreatingEngine = errors.NewError("error creating engine")
ErrCreatingEngine is return when a crypto engine cannot be created. This error should typically not be returned through most use cases, but it is likely to appear if an implementer does not properly adhere to the interfaces defined in api.go and one of the related convenience New* methods fails to cast it to the appropriate interface.
var ErrCreatingIV = errors.NewError("could not create initialization vector")
ErrCreatingIV is typically returned when an encryption call cannot create the initialization vector. This may be due to incorrect block size or padding and may be implementation dependent. If this error is returned, it may be necessary to examine the type of block cipher in use (typically AES) as it may not be compliant with our use case.
var ErrCreatingKey = errors.NewError("could not create key or subkey")
ErrCreatingKey is returned whenever a new derived key or subkey cannot be generated. This may be due to problems bubbling up from the underlying OS and insufficient entropy provided to api.GenerateKeyData.
var ErrDecoding = errors.NewError("base64 decoding error")
ErrDecoding is returned when a base64 decoding error occurs. Drilling down into this will typically yield why the error occurred in the first place (see Error.OriginalError).
var ErrGeneratingKey = errors.NewError("error generating key")
ErrGeneratingKey is typically returned when an error occurs generating the key. This is usually the result of a failure during entropy collection but may have other pathologies.
var ErrInvalidIVSize = errors.NewError("invalid initialization vector size")
ErrInvalidIVSize is returned if a size of zero (0) is requested from IV generators. This is also returned by ImprovedByteSerializer if IV lengthst exceed serialization limits (currently 256 bytes or the maximum value of a uint8).
var ErrKeySizeMismatch = errors.NewError("key sizes are mismatched")
ErrKeySizeMismatch is returned when a key replacement is attempted with a key that does not match the length of the prior key.
var ErrSerializationFailed = errors.NewError("payload serialization failed")
ErrSerializationFailed is returned whenever a payload serialization is attempted but fails.
var MaxKeySize = 1024 * 1024 * 10
MaxKeySize defines the maximum key size for Crypto Helper. By default, this is 10MiB.
var TimeBasedIVGenerator = func(size int) ([]byte, error) { if size == 0 { return []byte{}, ErrInvalidIVSize } writer := &bytes.Buffer{} buf := make([]byte, size-8) now := time.Now().UTC() err := binary.Write(writer, binary.LittleEndian, now.UnixNano()) if err != nil { return nil, err } num, err := rand.Read(buf) if err == nil && num == size-8 { writer.Write(buf) } else { if err != nil { return nil, err } return nil, fmt.Errorf("wrote %d bytes out of %d required", num, size-8) } return writer.Bytes(), nil }
TimeBasedIVGenerator is an initialization vector generator that creates vectors based in part on the current timestamp. This is ideal for initialization vectors 16 bytes or larger in size (the algorithms supported here require 16-byte IVs). This works by dividing the initialization vector space into two 8-byte chunks: The first chunk is devoted to containing a byte-encoded float64 timestamp (second and nanosecond components), and the second contains 8 cryptographically-secure generated bytes. These are then concatenated into a single 16-byte IV.
Since initialization vectors are transmitted in the clear and are part of our signed payload, we can validate tokens that use time-based IVs by decoding the timestamp component and comparing it to our configured expiration time. If the timestamp of the IV exceeds the expiration, then we know the payload is no longer valid, and we need not decrypt the payload or store additional timestamp data therein.
Functions ¶
func DecodeTimeBasedIV ¶
DecodeTimeBasedIV decodes initialization vectors created by the TimeBasedIVGenerator. This performs the generation task in reverse: We extract the first 8 bytes of the IV, read them into a float64, and create a timestamp instance from the data. The decoding function doesn't validate the timestamp--that's up to the caller.
Be aware that Crypto Helper always signs its payloads, which includes the encrypted data plus the initialization vector. Time-based IVs are never decoded before they are signature-validated.
func GenerateKey ¶
GenerateKeyData generates a cryptographically-secure byte slice for use as a key of size `length`.
func GenerateKeyString ¶
GenerateKeyString returns a base64 implementation of a random series of bytes as generated by GenerateKey.
Types ¶
type Base64Serializer ¶
type Base64Serializer struct {
// contains filtered or unexported fields
}
Base64Serializer encodes serialized payloads first as a slice of bytes and then as base64 output.
func (*Base64Serializer) Serialize ¶
func (s *Base64Serializer) Serialize(payload *Payload) ([]byte, error)
func (*Base64Serializer) SerializeForSigning ¶
func (s *Base64Serializer) SerializeForSigning(payload *Payload) ([]byte, error)
func (*Base64Serializer) Unserialize ¶
func (s *Base64Serializer) Unserialize(src []byte) *Payload
type Base64SizedSerializer ¶
type Base64SizedSerializer struct { *Base64Serializer // contains filtered or unexported fields }
func (*Base64SizedSerializer) SetBlockSize ¶
func (s *Base64SizedSerializer) SetBlockSize(i int)
func (*Base64SizedSerializer) SetDigestSize ¶
func (s *Base64SizedSerializer) SetDigestSize(i int)
type ByteSerializer ¶
type ByteSerializer struct {
// contains filtered or unexported fields
}
func (*ByteSerializer) Serialize ¶
func (b *ByteSerializer) Serialize(payload *Payload) ([]byte, error)
func (*ByteSerializer) SerializeForSigning ¶
func (b *ByteSerializer) SerializeForSigning(payload *Payload) ([]byte, error)
func (*ByteSerializer) SetBlockSize ¶
func (b *ByteSerializer) SetBlockSize(s int)
func (*ByteSerializer) SetDigestSize ¶
func (b *ByteSerializer) SetDigestSize(s int)
func (*ByteSerializer) Unserialize ¶
func (b *ByteSerializer) Unserialize(data []byte) *Payload
type CompositeKey ¶
type CompositeKey struct {
// contains filtered or unexported fields
}
func NewCompositeKey ¶
func NewCompositeKey(hmac, cipher Keyer) *CompositeKey
func (*CompositeKey) Cipher ¶
func (c *CompositeKey) Cipher() Keyer
func (*CompositeKey) HMAC ¶
func (c *CompositeKey) HMAC() Keyer
type CompositeKeyer ¶
CompositeKeyer defines a basic interface type for crypto calls that require both a cipher and an HMAC key.
type Encrypter ¶
type Encrypter interface { Encrypt([]byte) (*Payload, error) Decrypt(*Payload) []byte SetKey(key []byte) error SetIVGenerator(generator IVGenerator) SetSerializer(SerializerFunc) }
type EngineConfiguration ¶
type EngineConfiguration struct { // Sets the block cipher to use. This should typically be AES. BlockCipher func([]byte) (cipher.Block, error) // Sets the hash algorithm for use in message signing. Hasher func() hash.Hash // Enables MAC truncation. If set, the full signature will not be concatenated // to the ciphertext. How these behave depends on the HMAC algorithm's output // size (hash.Hash.Size()) and the MaxTruncatedLength. TruncatedMAC bool // If TruncatedMAC is enabled, this value controls the maximum truncated // length. This value cannot be less than 16 and should be 32 or greater. If // this value is 8 or less, it is interpreted as a fractional denominator, // e.g. a value of 2 will divide the length of the HMAC in half, 4 will divide // it into a quarter, and so forth. Fractional denominators will likewise // generate output no less than 16 bytes. MaxTruncatedLength uint16 // Composite key, optionally used for both encryption and signing. CompositeKey CompositeKeyer // Cipher key used for encryption. CipherKey []byte // HMAC key used for signing. HMACKey []byte // Configures the serializer to use for managing cipher text, signature, and // initialization vector output. This value may be nil which implicitly sets // the default serializer to ByteSerializer. Serializer SerializerFunc // Configures the initialization vector generator to use. This value may be // nil which implicitly sets the default IVGenerator to DefaultIVGenerator. IVGenerator IVGenerator }
EngineConfiguration sets engine attributes and behaviors. Refer to individual fields for documentation.
type IVGenerator ¶
type ImprovedByteSerializer ¶
type ImprovedByteSerializer struct{}
ImprovedByteSerializer need not know the block or digest size ahead of time; instead, it encodes the length of the cipher text and initialization vector at the start of the byte stream. These values are used to deduce the length of the payload's signature, once encoded.
Limitation: Initialization vectors cannot be longer than 256 bytes in length.
func (*ImprovedByteSerializer) Serialize ¶
func (ser *ImprovedByteSerializer) Serialize(payload *Payload) ([]byte, error)
func (*ImprovedByteSerializer) SerializeForSigning ¶
func (ser *ImprovedByteSerializer) SerializeForSigning(payload *Payload) ([]byte, error)
func (*ImprovedByteSerializer) Unserialize ¶
func (ser *ImprovedByteSerializer) Unserialize(data []byte) *Payload
type JSONSerializer ¶
type JSONSerializer struct{}
func (*JSONSerializer) Serialize ¶
func (s *JSONSerializer) Serialize(payload *Payload) ([]byte, error)
func (*JSONSerializer) SerializeForSigning ¶
func (s *JSONSerializer) SerializeForSigning(payload *Payload) ([]byte, error)
func (*JSONSerializer) Unserialize ¶
func (s *JSONSerializer) Unserialize(data []byte) *Payload
type Key ¶
func (*Key) MustGetBytes ¶
MustGetBytes returns the decoded byte array from the specified key. If the decoding process fails, it panics.
type Keyer ¶
type Keyer interface { // Bytes returns the byte array representation of the key or an error if the key could not be decoded. Bytes() ([]byte, error) // MustGetBytes returns the decoded byte representation of the key or panics if decoding fails. // Details of this method are up to the implementer. The Key type, as provided by this library, strictly wraps a []byte array. MustGetBytes() []byte }
Keyer provides methods for implementations to offer decoding methods for keys. Since decoding could be an expensive operation, it can be conducted on demand by using this interface to define key types.
type Payload ¶
type PayloadValidator ¶
PayloadValidator can be used to validate a payload plus plaintext data. The first argument is the Payload; the second is the decrypted plaintext. This is useful for validating time-based initialization vectors.
func TimeBasedIVValidator ¶
func TimeBasedIVValidator(maxage time.Duration) PayloadValidator
TimeBasedIVValidator is a closure that accepts a maximum token age (maxage) and returns a validator for use with Sealer's OpenValidator() function.
type Sealer ¶
type Sealer interface { Open([]byte) []byte OpenValidator([]byte, PayloadValidator) []byte Seal([]byte) []byte SealAsPayload([]byte) *Payload SetSerializer(SerializerFunc) }
func NewSealer ¶
func NewSealer(config *EngineConfiguration) (Sealer, error)
NewSealer creates and returns an implementation returning the Sealer interface based on the configuration provided.
type Serializer ¶
type Serializer interface { Serialize(*Payload) ([]byte, error) SerializeForSigning(*Payload) ([]byte, error) Unserialize([]byte) *Payload }
func NewBase64Serializer ¶
func NewBase64Serializer() Serializer
func NewByteSerializer ¶
func NewByteSerializer() Serializer
func NewImprovedByteSerializer ¶
func NewImprovedByteSerializer() Serializer
func NewJSONSerializer ¶
func NewJSONSerializer() Serializer
type SerializerFunc ¶
type SerializerFunc func() Serializer
SerializerFunc is a convenience type that describes the signature of serializer factory function that returns a pre-configured Serializer instance.
func NewBase64SerializerWith ¶
func NewBase64SerializerWith(serializer Serializer) SerializerFunc
type Signer ¶
type Signer interface { Sign(message []byte) []byte SignPayload(*Payload) (*Payload, error) Verify(signature, message []byte) bool VerifyBytes(message []byte) bool VerifyPayload(payload *Payload) (bool, error) SetHMAC(hmac []byte) SetSerializer(serializer SerializerFunc) }
func NewSigner ¶
func NewSigner(config *EngineConfiguration) (Signer, error)
NewSigner creates an implementation returning the Signer interface based on the configuration provided.
type SigningEncrypter ¶
type SigningEncrypter interface { Encrypt([]byte) (*Payload, error) Decrypt(*Payload) []byte Sign([]byte) []byte SignPayload(*Payload) (*Payload, error) Verify([]byte, []byte) bool VerifyBytes([]byte) bool VerifyPayload(*Payload) (bool, error) SetHMAC(hmac []byte) SetIVGenerator(generator IVGenerator) SetKey(key []byte) error SetSerializer(SerializerFunc) }
func NewSigningEncrypterEngine ¶
func NewSigningEncrypterEngine(config *EngineConfiguration) (SigningEncrypter, error)
NewSigningEncrypterEngine creates an implementation returning the SigningEncrypter interface based on the configuration provided.
type SigningEncryptionEngine ¶
type SigningEncryptionEngine struct {
// contains filtered or unexported fields
}
SigningEncryptionEngine defines an encryption engine that implements the interfaces Signer, Encrypter, and Sealer.
func (*SigningEncryptionEngine) Decrypt ¶
func (engine *SigningEncryptionEngine) Decrypt(payload *Payload) []byte
Decrypt the provided payload and return a byte slice of the plain text component. The returned plain text may need to be further decoded or processed.
func (*SigningEncryptionEngine) Encrypt ¶
func (engine *SigningEncryptionEngine) Encrypt(data []byte) (*Payload, error)
Encrypt the byte slice input returning a processed Payload containing the ciphertext, signature, and initialization vector.
func (*SigningEncryptionEngine) NewSubkey ¶
func (engine *SigningEncryptionEngine) NewSubkey() ([]byte, []byte, error)
NewSubkey creates a new subkey from the configured cipher and HMAC keys for this engine. Both keys inherit the length of their parents and cannot be changed. Key validation is performed by using the parent HMAC key to sign the subkey's randomly-generated cipher key; this hash is then used with a key derivation formula (PBKDF2) to create a signing key of the appropriate length. The KDF salt is generated randomly and prepended to the signing key.
Signing keys will always be digest_size * 2 with the first 16 bytes reserved for the random salt.
func (*SigningEncryptionEngine) Open ¶
func (engine *SigningEncryptionEngine) Open(message []byte) []byte
Open the provided message, validating its signature, decrypting it, and return the resulting plain text.
Contrasted with the Decrypt() and Verify() methods, this one does not return an error. Instead, if an error condition is encountered during the verification or decryption processes, an empty byte slice is returned to prevent information leakage.
Most applications that require encrypt-then-MAC behavior should use this function instead of either the Signer or Encrypter interfaces.
func (*SigningEncryptionEngine) OpenValidator ¶
func (engine *SigningEncryptionEngine) OpenValidator(message []byte, validator PayloadValidator) []byte
OpenValidator is like Open() but accepts a validation function that itself accepts a payload plus the decrypted ciphertext against which is may validate further. As with Open(), this will return an empty byte slice if the validation fails.
func (*SigningEncryptionEngine) Seal ¶
func (engine *SigningEncryptionEngine) Seal(ciphertext []byte) (buf []byte)
Seal the provided ciphertext. This will generate a payload internally, serializing it using the serializer configured for this engine, and return the resulting byte array.
Contracted with the Encrypt() and Sign() methods, this does not return an error. Instead, if an error condition occurs during the signing or decryption processes, an empty byte array is returned to prevent information leakage.
Most applications requiring encrypt-then-MAC should use this function instead of either the Signer or Encrypter interface.
func (*SigningEncryptionEngine) SealAsPayload ¶
func (engine *SigningEncryptionEngine) SealAsPayload(ciphertext []byte) *Payload
SealAsPayload seals the provided payload, updating it with the generated ciphertext, signature, and initialization vector. This function is mostly useful for callers who wish to serialize the payload on their own, independently from the one configured for this engine.
func (*SigningEncryptionEngine) Serialize ¶
func (engine *SigningEncryptionEngine) Serialize(payload *Payload) ([]byte, error)
Serialize the provided payload returning a byte slice defined by the serializer type. Most configurations will likely use the ByteSerializer which concatenates the payload using cipher and HMAC lengths to unserialize.
Other serializers include the JSON serializer which returns the payload as a JSON-formatted string represented by a byte slice.
func (*SigningEncryptionEngine) SerializeForSigning ¶
func (engine *SigningEncryptionEngine) SerializeForSigning(payload *Payload) ([]byte, error)
SerializeForSigning serializes the payload for signing. This differs from Serialize() in that it partially concatenates the payload (ciphertext and initalization vector) and returns the result.
func (*SigningEncryptionEngine) SetHMAC ¶
func (engine *SigningEncryptionEngine) SetHMAC(key []byte)
SetHMAC configures the HMAC key to use for signing. Be aware that this should typically not be used on engines that are intended to persist for long periods of time.
This method may be useful for keys that are on an expiration rotation and allows callers to cycle through multiple key versions before encountering one that works.
func (*SigningEncryptionEngine) SetIVGenerator ¶
func (engine *SigningEncryptionEngine) SetIVGenerator(generator IVGenerator)
SetIVGenerator changes the generator used to create initialization vectors. This is not typically needed and is not recommended.
func (*SigningEncryptionEngine) SetKey ¶
func (engine *SigningEncryptionEngine) SetKey(key []byte) error
SetKey changes the configured cipher key and reinitializes associated data structures. New keys must be exactly the same length as the key they're replacing.
This method may be useful for keys that are on an expiration rotation and allows callers to cycle through multiple key versions before encountering one that works.
func (*SigningEncryptionEngine) SetSerializer ¶
func (engine *SigningEncryptionEngine) SetSerializer(serializer SerializerFunc)
SetSerializer changes the configured serializer. This accepts a serializer function which is then passed the cipher blocksize and the HMAC algorithm's output size. This function must return the Serializer interface and is defined in api.go.
func (*SigningEncryptionEngine) Sign ¶
func (engine *SigningEncryptionEngine) Sign(ciphertext []byte) []byte
Sign the provided ciphertext using the configured HMAC hash algorithm.
func (*SigningEncryptionEngine) SignPayload ¶
func (engine *SigningEncryptionEngine) SignPayload(payload *Payload) (*Payload, error)
SignPayload is similar to Sign() with the exception that it accepts a Payload argument rather than a pre-assembled byte slice. This uses the configured serializer to convert the payload to an array of bytes.
Unlike Sign(), this method may return an error if the serialization process fails.
func (*SigningEncryptionEngine) Unserialize ¶
func (engine *SigningEncryptionEngine) Unserialize(message []byte) *Payload
Unserialize parses the payload using the configured cipher and HMAC, calculating the expected lengths of each (including the initialization vector), splitting apart the slice and returning a generated Payload.
func (*SigningEncryptionEngine) ValidateSubkey ¶
func (engine *SigningEncryptionEngine) ValidateSubkey(cipherkey, hmackey []byte) bool
ValidateSubkey accepts two byte slices, one for the subkey's cipher and one for the subkey's HMAC. Referring to NewSubkey()'s documentation, we know that the HMAC key is derived from signing the subkey's cipher with the master HMAC key and passing it through a key derivation formula (PBKDF2). To validate the key pair, we simply need to re-sign the cipher key, run it through the KDF, and compare the hashes.
Note that the first 16 bytes of the HMAC key will always be the salt used by the KDF.
func (*SigningEncryptionEngine) Verify ¶
func (engine *SigningEncryptionEngine) Verify(signature, message []byte) bool
Verify the signature and the provided message. This uses hmac.Equal for constant-time comparison of the provided signature versus the HMAC we generate for validation.
func (*SigningEncryptionEngine) VerifyBytes ¶
func (engine *SigningEncryptionEngine) VerifyBytes(message []byte) bool
VerifyBytes is like Verify() but unserializes the byte array first. The message argument is expected to contain the entire signed payload (this includes the initialization vector).
func (*SigningEncryptionEngine) VerifyPayload ¶
func (engine *SigningEncryptionEngine) VerifyPayload(payload *Payload) (bool, error)
VerifyPayload is similar to Verify() with the exception that it accepts a Payload argument rather than a pre-assembled byte slice. This users the configured serializer to convert the payload to an array of bytes.
Unlike Verify(), this method may return an error if the serialization process fails. Clients that don't wish to leak information about the verification process even in the event of a serialize failure can simply return the boolean value as is.