Commit 8035b360 authored by Jonathan Rudenberg's avatar Jonathan Rudenberg

Add docs

Signed-off-by: default avatarJonathan Rudenberg <jonathan@titanous.com>
parent d4248be2
......@@ -16,33 +16,62 @@ import (
"golang.org/x/crypto/curve25519"
)
// A DHKey is a keypair used for Diffie-Hellman key agreement.
type DHKey struct {
Private []byte
Public []byte
}
// A DHFunc implements Diffie-Hellman key agreement.
type DHFunc interface {
// GenerateKeypair generates a new keypair using random as a source of
// entropy.
GenerateKeypair(random io.Reader) DHKey
// DH performs a Diffie-Hellman calculation between the provided private and
// public keys and returns the result.
DH(privkey, pubkey []byte) []byte
// DHLen is the number of bytes returned by DH.
DHLen() int
// DHName is the name of the DH function.
DHName() string
}
// A HashFunc implements a cryptographic hash function.
type HashFunc interface {
// Hash returns a hash state.
Hash() hash.Hash
// HashName is the name of the hash function.
HashName() string
}
// A CipherFunc implements an AEAD symmetric cipher.
type CipherFunc interface {
// Cipher initializes the algorithm with the provided key and returns a Cipher.
Cipher(k [32]byte) Cipher
// CipherName is the name of the cipher.
CipherName() string
}
// A Cipher is a AEAD cipher that has been initialized with a key.
type Cipher interface {
// Encrypt encrypts the provided plaintext with a nonce and then appends the
// ciphertext to out along with an authentication tag over the ciphertext
// and optional authenticated data.
Encrypt(out []byte, n uint64, ad, plaintext []byte) []byte
// Decrypt authenticates the ciphertext and optional authenticated data and
// then decrypts the provided ciphertext using the provided nonce and
// appends it to out.
Decrypt(out []byte, n uint64, ad, ciphertext []byte) ([]byte, error)
}
// A CipherSuite is a set of cryptographic primitives used in a Noise protocol.
// It should be constructed with NewCipherSuite.
type CipherSuite interface {
DHFunc
CipherFunc
......@@ -50,6 +79,8 @@ type CipherSuite interface {
Name() []byte
}
// NewCipherSuite returns a CipherSuite constructed from the specified
// primitives.
func NewCipherSuite(dh DHFunc, c CipherFunc, h HashFunc) CipherSuite {
return ciphersuite{
DHFunc: dh,
......@@ -68,6 +99,7 @@ type ciphersuite struct {
func (s ciphersuite) Name() []byte { return s.name }
// DH25519 is the Curve25519 ECDH function.
var DH25519 DHFunc = dh25519{}
type dh25519 struct{}
......@@ -103,6 +135,7 @@ type cipherFn struct {
func (c cipherFn) Cipher(k [32]byte) Cipher { return c.fn(k) }
func (c cipherFn) CipherName() string { return c.name }
// CipherAESGCM is the AES256-GCM AEAD cipher.
var CipherAESGCM CipherFunc = cipherFn{
func(k [32]byte) Cipher {
c, err := aes.NewCipher(k[:])
......@@ -125,6 +158,7 @@ var CipherAESGCM CipherFunc = cipherFn{
"AESGCM",
}
// CipherChaChaPoly is the ChaCha20-Poly1305 AEAD cipher construction.
var CipherChaChaPoly CipherFunc = cipherFn{
func(k [32]byte) Cipher {
return aeadCipher{
......@@ -160,7 +194,14 @@ type hashFn struct {
func (h hashFn) Hash() hash.Hash { return h.fn() }
func (h hashFn) HashName() string { return h.name }
// HashSHA256 is the SHA-256 hash function.
var HashSHA256 HashFunc = hashFn{sha256.New, "SHA256"}
// HashSHA512 is the SHA-512 hash function.
var HashSHA512 HashFunc = hashFn{sha512.New, "SHA512"}
// HashBLAKE2b is the BLAKE2b hash function.
var HashBLAKE2b HashFunc = hashFn{blake2b.New, "BLAKE2b"}
// HashBLAKE2s is the BLAKE2s hash function.
var HashBLAKE2s HashFunc = hashFn{blake2s.New, "BLAKE2s"}
......@@ -5,7 +5,7 @@ import (
"hash"
)
func HKDF(h func() hash.Hash, out1, out2, chainingKey, inputKeyMaterial []byte) ([]byte, []byte) {
func hkdf(h func() hash.Hash, out1, out2, chainingKey, inputKeyMaterial []byte) ([]byte, []byte) {
if len(out1) > 0 {
panic("len(out1) > 0")
}
......
// Package noise implements the Noise Protocol Framework.
//
// Noise is a low-level framework for building crypto protocols. Noise protocols
// support mutual and optional authentication, identity hiding, forward secrecy,
// zero round-trip encryption, and other advanced features. For more details,
// visit http://noiseprotocol.org
package noise
import (
"crypto/rand"
"errors"
"io"
)
// A CipherState provides symmetric encryption and decryption after a successful
// handshake.
type CipherState struct {
cs CipherSuite
c Cipher
......@@ -14,6 +23,10 @@ type CipherState struct {
invalid bool
}
// Encrypt encrypts the plaintext and then appends the ciphertext and an
// authentication tag across the ciphertext and optional authenticated data to
// out. This method automatically increments the nonce after every call, so
// messages must be decrypted in the same order.
func (s *CipherState) Encrypt(out, ad, plaintext []byte) []byte {
if s.invalid {
panic("noise: CipherSuite has been copied, state is invalid")
......@@ -23,6 +36,10 @@ func (s *CipherState) Encrypt(out, ad, plaintext []byte) []byte {
return out
}
// Decrypt checks the authenticity of the ciphertext and authenticated data and
// then decrypts and appends the plaintext to out. This method automatically
// increments the nonce after every call, messages must be provided in the same
// order that they were encrypted with no missing messages.
func (s *CipherState) Decrypt(out, ad, ciphertext []byte) ([]byte, error) {
if s.invalid {
panic("noise: CipherSuite has been copied, state is invalid")
......@@ -68,7 +85,7 @@ func (s *symmetricState) MixKey(dhOutput []byte) {
s.n = 0
s.hasK = true
var hk []byte
s.ck, hk = HKDF(s.cs.Hash, s.ck[:0], s.k[:0], s.ck, dhOutput)
s.ck, hk = hkdf(s.cs.Hash, s.ck[:0], s.k[:0], s.ck, dhOutput)
copy(s.k[:], hk)
s.c = s.cs.Cipher(s.k)
}
......@@ -82,7 +99,7 @@ func (s *symmetricState) MixHash(data []byte) {
func (s *symmetricState) MixPresharedKey(presharedKey []byte) {
var temp []byte
s.ck, temp = HKDF(s.cs.Hash, s.ck[:0], nil, s.ck, presharedKey)
s.ck, temp = hkdf(s.cs.Hash, s.ck[:0], nil, s.ck, presharedKey)
s.MixHash(temp)
s.hasPSK = true
}
......@@ -112,7 +129,7 @@ func (s *symmetricState) DecryptAndHash(out, data []byte) ([]byte, error) {
func (s *symmetricState) Split() (*CipherState, *CipherState) {
s1, s2 := &CipherState{cs: s.cs}, &CipherState{cs: s.cs}
hk1, hk2 := HKDF(s.cs.Hash, s1.k[:0], s2.k[:0], s.ck, nil)
hk1, hk2 := hkdf(s.cs.Hash, s1.k[:0], s2.k[:0], s.ck, nil)
copy(s1.k[:], hk1)
copy(s2.k[:], hk2)
s1.c = s.cs.Cipher(s1.k)
......@@ -120,8 +137,11 @@ func (s *symmetricState) Split() (*CipherState, *CipherState) {
return s1, s2
}
// A MessagePattern is a single message or operation used in a Noise handshake.
type MessagePattern int
// A HandshakePattern is a list of messages and operations that are used to
// perform a specific Noise handshake.
type HandshakePattern struct {
Name string
InitiatorPreMessages []MessagePattern
......@@ -138,8 +158,12 @@ const (
MessagePatternDHSS
)
// MaxMsgLen is the maximum number of bytes that can be sent in a single Noise
// message.
const MaxMsgLen = 65535
// A HandshakeState tracks the state of a Noise handshake. It may be discarded
// after the handshake is complete.
type HandshakeState struct {
ss symmetricState
s DHKey // local static keypair
......@@ -152,19 +176,46 @@ type HandshakeState struct {
rng io.Reader
}
// A Config provides the details necessary to process a Noise handshake. It is
// never modified by this package, and can be reused, but care must be taken to
// generate a new ephemeral key for each handshake if they are used in the
// pattern.
type Config struct {
CipherSuite CipherSuite
Random io.Reader
Pattern HandshakePattern
Initiator bool
Prologue []byte
PresharedKey []byte
StaticKeypair DHKey
// CipherSuite is the set of cryptographic primitives that will be used.
CipherSuite CipherSuite
// Random is the source for cryptographically appropriate random bytes. If
// zero, it is automtically configed.
Random io.Reader
// Pattern is the pattern for the handshake.
Pattern HandshakePattern
// Initiator must be true if the first message in the handshake will be sent
// by this peer.
Initiator bool
// Prologue is an optional message that has already be communicated and must
// be identical on both sides for the handshake to succeed.
Prologue []byte
// PresharedKey is the optional preshared key for the handshake.
PresharedKey []byte
// StaticKeypair is this peer's static keypair.
StaticKeypair DHKey
// EphemeralKeypair is this peer's static keypair.
EphemeralKeypair DHKey
PeerStatic []byte
PeerEphemeral []byte
// PeerStatic is the static public key of the remote peer.
PeerStatic []byte
// PeerEphemeral is the ephemeral public key of the remote peer.
PeerEphemeral []byte
}
// NewHandshakeState starts a new handshake using the provided configuration.
func NewHandshakeState(c Config) *HandshakeState {
hs := &HandshakeState{
s: c.StaticKeypair,
......@@ -174,6 +225,9 @@ func NewHandshakeState(c Config) *HandshakeState {
shouldWrite: c.Initiator,
rng: c.Random,
}
if hs.rng == nil {
hs.rng = rand.Reader
}
if len(c.PeerEphemeral) > 0 {
hs.re = make([]byte, len(c.PeerEphemeral))
copy(hs.re, c.PeerEphemeral)
......@@ -215,6 +269,12 @@ func NewHandshakeState(c Config) *HandshakeState {
return hs
}
// WriteMessage appends a handshake message to out. The message will include the
// optional payload if provided. If the handshake is completed by the call, two
// CipherStates will be returned, one is used for encryption of messages to the
// remote peer, the other is used for decryption of messages from the remote
// peer. It is an error to call this method out of sync with the handshake
// pattern.
func (s *HandshakeState) WriteMessage(out, payload []byte) ([]byte, *CipherState, *CipherState) {
if !s.shouldWrite {
panic("noise: unexpected call to WriteMessage should be ReadMessage")
......@@ -262,8 +322,14 @@ func (s *HandshakeState) WriteMessage(out, payload []byte) ([]byte, *CipherState
return out, nil, nil
}
// ErrShortMessage is returned by ReadMessage if a message is not as long as it should be.
var ErrShortMessage = errors.New("noise: message is too short")
// ReadMessage processes a received handshake message and appends the payload,
// if any to out. If the handshake is completed by the call, two CipherStates
// will be returned, one is used for encryption of messages to the remote peer,
// the other is used for decryption of messages from the remote peer. It is an
// error to call this method out of sync with the handshake pattern.
func (s *HandshakeState) ReadMessage(out, message []byte) ([]byte, *CipherState, *CipherState, error) {
if s.shouldWrite {
panic("noise: unexpected call to ReadMessage should be WriteMessage")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment