# Gastronomy

Gastronomy provides a range of common cryptographic operations through a simple, typesafe and immutable API.

## Features

- hashing of simple and primitive Scala types
- generically-derived digests for all product and coproduct types
- supports SHA-256, SHA-1 and MD5 hash algorithms
- symmetric encryption with AES
- asymmetric encryption/decryption using RSA
- signing with DSA
- AES, RSA and DSA key generation
- calculation of HMACs for SHA-256, SHA-1 and MD5
- encoding into Hex, BASE-64, and URL-safe BASE-64
- serializers and parsers for PEM-encoded data

## Getting Started

**Gastronomy** provides representations of public, private and symmetric keys which offer a number
of cryptographic methods:

`PublicKey`

provides:`verify`

for verifying signatures`encrypt`

for encrypting data`pem`

to provide the public key as a PEM-encoded string

`PrivateKey`

provides:`sign`

for signing data`decrypt`

for decrypting encrypted data`public`

to derive a`PublicKey`

from the`PrivateKey`

`pem`

to provide the private key as a PEM-encoded string

`SymmetricKey`

provides`verify`

,`encrypt`

,`pem`

,`sign`

and`decrypt`

in a single key.

Additionally, the extension methods, `digest`

and `hmac`

are provided for any value which can be
serialized to bytes.

The objects `PrivateKey`

and `SymmetricKey`

both have `generate`

methods which will generate new
random keys.

## Signing

Given, for example, a `PrivateKey[Dsa[1024]]`

instance, `key`

, data may be signed with, for example,

`val signature: Signature[Dsa[1024]] = key.sign(data)`

This works for any value, `data`

, that has an appropriate `ByteCodec`

instance. The type parameter
of the signature will depend on the type parameter of the private key.

## Verifying a signature

A public key, `pubKey`

, which could, for example, be derived from the private key in the previous
example,

`val pubKey = key.public`

may be used to verify a signature of type `Signature[Dsa[1024]]`

with:

`val valid: Boolean = pubKey.verify(data, signature)`

Here, `data`

must be the same object that was used (with the private key) to produce the signature,
and may be any type that has a contextual `ByteCodec`

instance.

## Encryption

A public key instance, for example, `pubKey`

of type `PublicKey[Rsa[2048]]`

, can encrypt some data
by calling,

`val encrypted: Message[Rsa[2048]] = pubKey.encrypt(data)`

## Decryption

An encrypted message may conversely be decrypted using the corresponding `PrivateKey[Rsa[2048]]`

instance, `key`

:

`val data: String = key.decrypt[String](encrypted)`

The return type (`String`

in the above example) must be specified as a parameter to the `decrypt`

method, and may be any type for which a corresponding `ByteCodec`

exists in context. However, the
type should be the same as the type of the object that was originally encrypted, otherwise it may
fail to decode.

## Digests

A cryptographic digest (or hash) of any value may be calculated by calling `digest[A]`

on that
value, for an appropriate choice of `A`

, provided a `Hashable`

instance is in context for that type
of object. `Hashable`

instances exist for `String`

s, primitive types, sequences of these types, and
product and coproduct types consisting of just other hashable types.

Cryptographic digests have the type `Digest[A]`

where `A`

is the algorithm type.

For example,

`val digest: Digest[Sha2[384]] = (10, "alpha", 'z').digest[Sha2[384]]`

## HMACs

Any value whose type has a corresponding `ByteCodec`

instance in context may have an HMAC value
calculated, of type `Hmac[A]`

(where `A`

is the cryptographic algorithm). As a parameter, this
needs an `IArray[Byte]`

representing (in some form) the key to be used.

Here is an example using SHA-512:

`val hmac: Hmac[Sha2[512]] = "Hello world".hmac("secret".bytes)`

## Type inference

Whenever an expression is used in a position with an expected type, the type parameters of the
methods `decrypt`

, `digest`

and `hmac`

may be omitted, for example given the case class,

`case class Block(digest: Digest[Sha2[256]], json: Json, hmac: Hmac[Sha2[512]])`

we can instantiate it with just,

`val block = Block(data.digest, data.decrypt, value.hmac)`

Alternatively, a particular given may be imported directly into the current scope to prioritize it, such that it may be used in preference to the alternatives.

## Byte data

Representations of binary data are common with low-level cryptographic operations. All operations in
Gastronomy use the immutable `IArray[Byte]`

type as the underlying representation of binary data,
but typically wrap the data in a type which more precisely indicates the content of that data.

These types include the key types, `PublicKey`

, `PrivateKey`

and `SymmetricKey`

, and result types,
`Signature`

, `Hmac`

, `Digest`

and `Message`

.

These types are all further refined with a type parameter representing the cryptographic algorithm
associated with that data. For example, an MD5 digest is typed as, `Digest[Md5]`

and a 384-bit SHA-2
HMAC has the type, `Hmac[Sha2[384]]`

.

In order to make it easier to share these values, they can be encoded to and from `String`

s using
a number of different encodings:

- binary (
`Binary`

) - hexadecimal (
`Hex`

) - BASE-64 (
`Base64`

) - URL-safe BASE-64 (
`Base64Url`

)

The `encode`

method, which exists as an extension on `IArray[Byte]`

, as well as (directly) on all
types representing byte data. It takes one of these as a type parameter to produce a `String`

of
that data, encoded with the specified encoding.

### Algorithms

Gastronomy's cryptographic functions are implemented through different algorithms, which are represented by types. Their names follow the conventions of other Scala types, hence:

`Rsa[B]`

for`B`

of`1024`

or`2048`

,`Dsa[B]`

for`B`

of`512`

,`1024`

,`2048`

or`3072`

,`Aes[B]`

for`B`

of`128`

,`192`

or`256`

,`Sha1`

,`Sha2[B]`

for`B`

of`224`

,`256`

,`384`

or`512`

, and`Md5`

Additionally, the types `Base64`

, `Base64Url`

, `Hex`

and `Binary`

represent non-cryptographic
byte-to-string encodings.

## Generating keys

The `PrivateKey`

object provides the `generate[A]()`

method, where `A`

is `Rsa[B]`

, `Dsa[B]`

or
`Aes[B]`

for an appropriate choice of `B`

.

The algorithm `Aes[B]`

can also be used with the `SymmetricKey`

object to get a symmetric key which
has the functionality of both a public and private key.

## Byte codecs

Any object which can be serialized to bytes may be digested, signed, verified, HMACked or encrypted,
and can be returned from a decryption operation, provided a corresponding `ByteCodec`

instance is
available for that type.

`ByteCodec`

s are provided for `IArray[Byte]`

(trivially) and for `String`

s (assuming a UTF-8
encoding).

## PEM encoding

The *Privacy-Enhanced Mail* format is commonly used for exchanging cryptographic values safely in
ASCII-only environments.

A `Pem`

type is provided for reading, writing and representing this data. The case class `Pem`

has
two fields: `kind`

, which is the label that appears after the words `BEGIN`

and `END`

in the
serialized format, and `data`

, which is an `IArray[Byte]`

of the byte data.

The `serialize`

method will produce a `String`

of the data, properly encoded as BASE-64, and
delimited.

The method `Pem.parse`

will attempt to parse a `String`

containing PEM-encoded data.

All Gastronomy's key types offer a `pem`

method which will return an appropriately-labelled `Pem`

value containing that key, however to avoid the risk of accidentally exposing a private key, the
`pem`

method of `PrivateKey`

must be called with a special singleton value, like so:

`privateKey.pem(RevealSecretKey)`

## Other Cryptographic Algorithms

Gastronomy may be easily extended to support other cryptographic algorithms. The existing
implementations of `Rsa`

, `Dsa`

, `Aes`

, `Sha1`

, `Sha2`

and `Md5`

should be studied to investigate
this possibility.

## Related Projects

The following *Niveau* libraries are dependencies of *Gastronomy*:

The following *Niveau* libraries are dependents of *Gastronomy*:

## Status

Gastronomy is classified as **maturescent**. Propensive defines the following five stability levels for open-source projects:

*embryonic*: for experimental or demonstrative purposes only, without any guarantees of longevity*fledgling*: of proven utility, seeking contributions, but liable to significant redesigns*maturescent*: major design decisions broady settled, seeking probatory adoption and refinement*dependable*: production-ready, subject to controlled ongoing maintenance and enhancement; tagged as version`1.0`

or later*adamantine*: proven, reliable and production-ready, with no further breaking changes ever anticipated

Gastronomy is designed to be *small*. Its entire source code currently consists of 441 lines of code.

## Building

Gastronomy can be built on Linux or Mac OS with Vex, by running the `vex`

script in the root directory:

`./vex`

This script will download `vex`

the first time it is run, start a daemon process, and run the build. Subsequent
invocations will be near-instantaneous.

## Contributing

Contributors to Gastronomy are welcome and encouraged. New contributors may like to look for issues marked .

We suggest that all contributors read the Contributing Guide to make the process of contributing to Gastronomy easier.

Please **do not** contact project maintainers privately with questions. While it can be tempting to repsond to
such questions, private answers cannot be shared with a wider audience, and it can result in duplication of
effort.

## Author

Gastronomy was designed and developed by Jon Pretty, and commercial support and training is available from Propensive OÜ.

## Name

Gastronomy is named after the art and science of "good eating", which leads to digestion, since the library consumes data to produce digests (but has subsequently grown in scope).

## License

Gastronomy is copyright © 2018-22 Jon Pretty & Propensive OÜ, and is made available under the Apache 2.0 License.