cacophony

A library implementing the Noise protocol.

https://github.com/centromere/cacophony

Version on this page:0.9.2
LTS Haskell 22.13:0.10.1
Stackage Nightly 2024-03-14:0.10.1
Latest on Hackage:0.10.1

See all snapshots cacophony appears in

LicenseRef-PublicDomain licensed by John Galt
Maintained by [email protected]
This version can be pinned in stack with:cacophony-0.9.2@sha256:f376b0ef519c063fa7a9a0cd7abf3f0a0be6e0e479d8d53ae12fdcdd41470101,5339

Module documentation for 0.9.2

cacophony

Build Status Haskell

This library implements the Noise 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:

    import Control.Lens
    
    import Crypto.Noise
    import Crypto.Noise.Cipher.AESGCM
    import Crypto.Noise.DH
    import Crypto.Noise.DH.Curve25519
    import Crypto.Noise.Hash.SHA256
    import Crypto.Noise.HandshakePatterns (noiseIK)
    
  2. Set the handshake parameters.

    Select a handshake pattern to use. Patterns are defined in the Crypto.Noise.HandshakePatterns module. 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 the prologue is an empty string, PSKs are disabled, and all keys are set to Nothing. You must set the local ephemeral key for all handshake patterns, and it should never be reused.

    Functions for manipulating DH keys can be found in the Crypto.Noise.DH module.

    -- Initiator
    local_ephemeral_key <- dhGenKey :: IO (KeyPair Curve25519)
    
    let dho = defaultHandshakeOpts noiseIK InitiatorRole :: HandshakeOpts Curve25519
         iho = dho & hoPrologue       .~ "prologue"
                   & hoPreSharedKey   .~ Just pre_shared_key
                   & hoLocalStatic    .~ Just local_static_key
                   & hoLocalEphemeral .~ Just local_ephemeral_key
                   & hoRemoteStatic   .~ Just remote_static_key -- communicated out-of-band
    
    -- Responder
    local_ephemeral_key <- dhGenKey :: IO (KeyPair Curve25519)
    
    let dho = defaultHandshakeOpts noiseIK ResponderRole :: HandshakeOpts Curve25519
         rho = dho & hoPrologue       .~ "prologue"
                   & hoPreSharedKey   .~ Just pre_shared_key
                   & hoLocalStatic    .~ Just local_static_key
                   & hoLocalEphemeral .~ Just local_ephemeral_key
    
  3. Create the Noise state.

    -- Initiator
    let ins = noiseState iho :: NoiseState AESGCM Curve25519 SHA256
    
    -- Responder
    let rns = noiseState rho :: NoiseState AESGCM Curve25519 SHA256
    
  4. Send and receive messages.

    -- Initiator
    let writeResult        = writeMessage ins "They must find it difficult -- those who have taken authority as the truth, rather than truth as the authority."
         (ciphertext, ins') = either (error "something terrible happened") id writeResult
    
    -- Responder
    let readResult        = readMessage rns ciphertext
         (plaintext, rns') = either (error "something terrible happened") id readResult
    

    Ensure that you never re-use a noise state 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 and can be helpful when designing an application which uses 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.

  • 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 9.4 of the protocol for details.

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.

The generated vectors are minified JSON. There is a small python script within the tools/ directory that formats the JSON-blob in to something more readable.

Example code

An echo-server and echo-client are located within the examples/ directory. The binary protocol they use to communicate is as follows:

C -> S: [psk byte] [pattern byte] [cipher byte] [dh byte] [hash byte]
C -> S: [num message bytes (uint16 big endian)] [message]
S -> C: [num message bytes (uint16 big endian)] [message]
...

where message is any raw Noise handshake or message data.

The prologue is set to the 5 header bytes to prevent a MITM attack.

For these example programs, the server chooses the value of the PSK, and the client chooses whether or not to use a PSK-enabled handshake. Both the server and client expect the PSK file to be base64 encoded. One way to generate the PSK file is as follows:

head -c 32 /dev/random | base64 > psk

To include these examples in your build, pass the -fbuild-examples flag to Cabal.

Byte definitions

byte psk pattern cipher dh hash
0x00 disabled NN ChaChaPoly 25519 SHA256
0x01 enabled KN AESGCM 448 SHA512
0x02 NK BLAKE2s
0x03 KK BLAKE2b
0x04 NX
0x05 KX
0x06 XN
0x07 IN
0x08 XK
0x09 IK
0x0a XX
0x0b IX

Changes

0.9.2

  • Added ability to export raw symmetric keys

0.9.1

  • Enabled llvm flag support on executables

  • Removed deepseq library dependency

  • Disallowed reserved nonce (2^64 - 1)

  • Fixed problem with CipherState count not incrementing

0.9.0

  • Removed secondary key support (rev 31)

  • Renamed dh tokens (rev 31)

  • Added Noise-C vectors

  • Regenerated test vectors

  • Now using IsString instance of ScrubbedBytes from memory package

  • Linting

0.8.0

  • Exceptions are now provided by the safe-exceptions package (breaking API change)

  • Added Noise_XXfallback pattern

  • Minor improvements to handshake pattern definition

  • Updated non-standard handshake patterns to conform with rev 30

  • Fixed bug which caused echo-server to read wrong public key

0.7.0

  • Major API overhaul and refactoring

  • Added test vector support

  • Added secondary symmetric key support

  • Added GHC 8.0.2 to unit tests

  • Removed Noise_XR

  • General code cleanup and other minor tweaks

0.6.0

  • Added ability to abort handshakes based on the remote party’s public key

  • Improved documentation

  • Factored out ScrubbedBytes utilities to separate module

  • Added echo-server and echo-client example

  • Renamed HandshakeStateParams to HandshakeOpts

0.5.0

  • Added Curve448 support

  • Major refactoring and API changes A DSL was created to represent handshake patterns.

  • Added GHC 7.10.3 to unit tests

0.4.0

  • Improved documentation

  • Added basic benchmarks

  • Added better exception handling

  • Improved handshakeState API

  • Added psk2 functionality

  • Unit test cleanup

  • Renamed symmetricHandshake to symmetricState

  • Added BLAKE2, SHA512, AESGCM support

0.3.0

  • Brought API up to date with current version of spec (17)

0.2.0

  • Added support for one-way handshakes

  • Fixed Noise_IX

  • Added helper functions for ScrubbedBytes / ByteString conversion

0.1.0.0

  • First version.