Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/haskell-cryptography/cacophony

A Haskell library implementing the Noise protocol.
https://github.com/haskell-cryptography/cacophony

cryptography haskell

Last synced: about 3 hours ago
JSON representation

A Haskell library implementing the Noise protocol.

Awesome Lists containing this project

README

        

# cacophony

[![Build Status](https://travis-ci.org/centromere/cacophony.svg?branch=master)](https://travis-ci.org/centromere/cacophony)
[![Haskell](http://b.repl.ca/v1/language-haskell-blue.png)](http://www.haskell.org)

This library implements the [Noise](https://noiseprotocol.org) protocol.

## Basic Usage

1. Import the modules for the kind of handshake you'd like to use.

For example, if you want to use `Noise_IK_25519_AESGCM_SHA256`, your imports would be:

```haskell
import Crypto.Noise
import Crypto.Noise.Cipher.AESGCM
import Crypto.Noise.DH -- Used to generate and manipulate keys
import Crypto.Noise.DH.Curve25519
import Crypto.Noise.Hash.SHA256
import Crypto.Noise.HandshakePatterns (noiseIK)
```

2. Set the handshake parameters.

Ensure that you provide the keys which are required by the handshake pattern you choose. For example,
the `Noise_IK` pattern requires that the initiator provides a local static key and a remote static key,
while the responder is only responsible for a local static key. You can use `defaultHandshakeOpts` to
return a default set of options in which all keys are set to `Nothing`. The initiator must set a
local ephemeral key for all handshake patterns. The responder must set a local ephemeral key for all
interactive (i.e. not one-way) patterns.

```haskell
-- Initiator
localEphemeralKey <- dhGenKey :: IO (KeyPair Curve25519)

let dho = defaultHandshakeOpts InitiatorRole "prologue" :: HandshakeOpts Curve25519
iho = setLocalStatic (Just localStaticKey)
. setLocalEphemeral (Just localEphemeralKey)
. setRemoteStatic (Just remoteStaticKey) -- communicated out-of-band
$ dho

-- Responder
localEphemeralKey <- dhGenKey :: IO (KeyPair Curve25519)

let dho = defaultHandshakeOpts ResponderRole "prologue" :: HandshakeOpts Curve25519
rho = setLocalStatic (Just localStaticKey)
. setLocalEphemeral (Just localEphemeralKey)
$ dho
```

3. Create the Noise state.

```haskell
-- Initiator
let ins = noiseState iho noiseIK :: NoiseState AESGCM Curve25519 SHA256

-- Responder
let rns = noiseState rho noiseIK :: NoiseState AESGCM Curve25519 SHA256
```

4. Send and receive messages.

```haskell
-- Initiator
let writeResult = writeMessage "They must find it difficult -- those who have taken authority as the truth, rather than truth as the authority." ins
case writeResult of
NoiseResultMessage ciphertext ins' -> ...
NoiseResultNeedPSK _ -> error "something terrible happened" -- will never happen in Noise_IK
NoiseResultException _ -> error "something terrible happened"

-- Responder
let readResult = readMessage ciphertext rns
case readResult of
NoiseResultMessage plaintext rns' -> ...
NoiseResultNeedPSK _ -> error "something terrible happened"
NoiseResultException _ -> error "something terrible happened"
```

**Ensure that you never re-use a NoiseState to send more than one message.**

Decrypted messages are stored internally as `ScrubbedBytes` and will be wiped from memory when they are
destroyed.

### Helper Functions

The following functions are found in `Crypto.Noise.DH` and are used to manipulate keys:

* `dhGenKey` -- Generate a fresh (private, public) key pair
* `dhPubToBytes` -- Convert a public key to `ScrubbedBytes`
* `dhBytesToPub` -- Convert `ScrubbedBytes` to a public key
* `dhSecToBytes` -- Convert a private key to `ScrubbedBytes`
* `dhBytesToPair` -- Convert `ScrubbedBytes` to a (private, public) key pair

The following functions are found in `Crypto.Noise`:

* `remoteStaticKey` -- For handshake patterns where the remote party's static key is transmitted, this function
can be used to retrieve it. This allows for the creation of public key-based access-control lists.

* `handshakeComplete` -- Returns `True` if the handshake is complete.

* `processPSKs` -- This function repeatedly applies PSKs to a NoiseState until the list of PSKs becomes empty
or the handshake pattern stops asking for PSKs.

* `handshakeHash` -- Retrieves the `h` value associated with the conversation's SymmetricState. This value is
intended to be used for channel binding. For example, the initiator might cryptographically sign this value
as part of some higher-level authentication scheme. See section 11.2 of the protocol for details.

* `rekeySending` and `rekeyReceiving` -- Rekeys the given NoiseState according to section 11.3 of the protocol.

## Supported Features

All combinations of the following handshake parameters are officially supported and covered by the unit tests:

* Patterns
* NN
* KN
* NK
* KK
* NX
* KX
* XN
* IN
* XK
* IK
* XX
* IX
* N
* K
* X
* NNpsk0
* NNpsk2
* NKpsk0
* NKpsk2
* NXpsk2
* XNpsk3
* XKpsk3
* XXpsk3
* KNpsk0
* KNpsk2
* KKpsk0
* KKpsk2
* KXpsk2
* INpsk1
* INpsk2
* IKpsk1
* IKpsk2
* IXpsk2
* Npsk0
* Kpsk0
* Xpsk1
* NK1
* NX1
* X1N
* X1K
* XK1
* X1K1
* X1X
* XX1
* X1X1
* K1N
* K1K
* KK1
* K1K1
* K1X
* KX1
* K1X1
* I1N
* I1K
* IK1
* I1K1
* I1X
* IX1
* I1X1

* Ciphers
* AESGCM
* ChaChaPoly1305

* Curves
* Curve25519
* Curve448

* Hashes
* BLAKE2b
* BLAKE2s
* SHA256
* SHA512

## Vectors

Test vectors can be generated and verified using the `vectors` program. It accepts no arguments. When run,
it will check for the existence of `vectors/cacophony.txt` within the current working directory. If it is not
found, it is generated. If it is found, it is verified. All files within the `vectors/` directory (regardless
of their name) are also verified. Note that this program can only generate and verify vectors whose handshake
patterns are pre-defined in this library.

## Custom Handshakes

If the built-in handshake patterns are insufficient for your application, you can define your own. Note that
this should be done with care.

Example:

```haskell
noiseFOOpsk0 :: HandshakePattern
noiseFOOpsk0 = handshakePattern "FOOpsk0" $
preInitiator s *>
preResponder s *>
initiator (psk *> e *> es *> ss) *>
responder (e *> ee *> se)
```

## Handshake Validation

`HandshakePattern`s can be validated for compliance as described in sections 7.1 and 9.3 of the protocol:

```
λ> let noiseBAD = handshakePattern "BAD" $ preResponder ss *> initiator (e *> se *> e)
[DHInPreMsg (0,0),InitMultipleETokens (1,2),InitSecretNotRandom (1,3)]

λ> validateHandshakePattern noiseKKpsk0
[]
```

See the `Crypto.Noise.Validation` module for details.

## Tools

### format-vectors.py

Vectors generated by the vector program are formatted as minified JSON. This python script takes the path
to a vector file as an argument and reformats it so that it conforms to
[the style](https://github.com/noiseprotocol/noise_wiki/wiki/Test-vectors) specified on the Noise Wiki.

### noise-repl

This program acts as a kind of REPL for Noise messages. It supports sending and receiving messages via UDP
or via a pipe to a shell command.

All messages transmitted via a pipe are expected to be prepended by a two byte big-endian length.