mirror of
https://github.com/logos-messaging/noise.git
synced 2026-01-02 14:13:07 +00:00
Add docs
Signed-off-by: Jonathan Rudenberg <jonathan@titanous.com>
This commit is contained in:
parent
d4248be25d
commit
8035b36042
@ -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"}
|
||||
|
||||
2
hkdf.go
2
hkdf.go
@ -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")
|
||||
}
|
||||
|
||||
90
state.go
90
state.go
@ -1,10 +1,19 @@
|
||||
// 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")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user