WIP: Save agent roles integration work before CHORUS rebrand
- Agent roles and coordination features - Chat API integration testing - New configuration and workspace management 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1
vendor/github.com/flynn/noise/CONTRIBUTING.md
generated
vendored
Normal file
1
vendor/github.com/flynn/noise/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
See the [Flynn contributing guide](https://flynn.io/docs/contributing).
|
||||
29
vendor/github.com/flynn/noise/LICENSE
generated
vendored
Normal file
29
vendor/github.com/flynn/noise/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
Flynn® is a trademark of Prime Directive, Inc.
|
||||
|
||||
Copyright (c) 2015 Prime Directive, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Prime Directive, Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
5
vendor/github.com/flynn/noise/README.md
generated
vendored
Normal file
5
vendor/github.com/flynn/noise/README.md
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# noise [](https://pkg.go.dev/github.com/flynn/noise) [](https://github.com/flynn/noise/actions)
|
||||
|
||||
This is a Go package that implements the [Noise Protocol
|
||||
Framework](https://noiseprotocol.org). See [the
|
||||
documentation](https://pkg.go.dev/github.com/flynn/noise) for usage information.
|
||||
224
vendor/github.com/flynn/noise/cipher_suite.go
generated
vendored
Normal file
224
vendor/github.com/flynn/noise/cipher_suite.go
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
package noise
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
"golang.org/x/crypto/blake2s"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"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, error)
|
||||
|
||||
// DH performs a Diffie-Hellman calculation between the provided private and
|
||||
// public keys and returns the result.
|
||||
DH(privkey, pubkey []byte) ([]byte, error)
|
||||
|
||||
// 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
|
||||
HashFunc
|
||||
Name() []byte
|
||||
}
|
||||
|
||||
// NewCipherSuite returns a CipherSuite constructed from the specified
|
||||
// primitives.
|
||||
func NewCipherSuite(dh DHFunc, c CipherFunc, h HashFunc) CipherSuite {
|
||||
return ciphersuite{
|
||||
DHFunc: dh,
|
||||
CipherFunc: c,
|
||||
HashFunc: h,
|
||||
name: []byte(dh.DHName() + "_" + c.CipherName() + "_" + h.HashName()),
|
||||
}
|
||||
}
|
||||
|
||||
type ciphersuite struct {
|
||||
DHFunc
|
||||
CipherFunc
|
||||
HashFunc
|
||||
name []byte
|
||||
}
|
||||
|
||||
func (s ciphersuite) Name() []byte { return s.name }
|
||||
|
||||
// DH25519 is the Curve25519 ECDH function.
|
||||
var DH25519 DHFunc = dh25519{}
|
||||
|
||||
type dh25519 struct{}
|
||||
|
||||
func (dh25519) GenerateKeypair(rng io.Reader) (DHKey, error) {
|
||||
privkey := make([]byte, 32)
|
||||
if rng == nil {
|
||||
rng = rand.Reader
|
||||
}
|
||||
if _, err := io.ReadFull(rng, privkey); err != nil {
|
||||
return DHKey{}, err
|
||||
}
|
||||
pubkey, err := curve25519.X25519(privkey, curve25519.Basepoint)
|
||||
if err != nil {
|
||||
return DHKey{}, err
|
||||
}
|
||||
return DHKey{Private: privkey, Public: pubkey}, nil
|
||||
}
|
||||
|
||||
func (dh25519) DH(privkey, pubkey []byte) ([]byte, error) {
|
||||
return curve25519.X25519(privkey, pubkey)
|
||||
}
|
||||
|
||||
func (dh25519) DHLen() int { return 32 }
|
||||
func (dh25519) DHName() string { return "25519" }
|
||||
|
||||
type cipherFn struct {
|
||||
fn func([32]byte) Cipher
|
||||
name string
|
||||
}
|
||||
|
||||
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{cipherAESGCM, "AESGCM"}
|
||||
|
||||
func cipherAESGCM(k [32]byte) Cipher {
|
||||
c, err := aes.NewCipher(k[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return aeadCipher{
|
||||
gcm,
|
||||
func(n uint64) []byte {
|
||||
var nonce [12]byte
|
||||
binary.BigEndian.PutUint64(nonce[4:], n)
|
||||
return nonce[:]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CipherChaChaPoly is the ChaCha20-Poly1305 AEAD cipher construction.
|
||||
var CipherChaChaPoly CipherFunc = cipherFn{cipherChaChaPoly, "ChaChaPoly"}
|
||||
|
||||
func cipherChaChaPoly(k [32]byte) Cipher {
|
||||
c, err := chacha20poly1305.New(k[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return aeadCipher{
|
||||
c,
|
||||
func(n uint64) []byte {
|
||||
var nonce [12]byte
|
||||
binary.LittleEndian.PutUint64(nonce[4:], n)
|
||||
return nonce[:]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type aeadCipher struct {
|
||||
cipher.AEAD
|
||||
nonce func(uint64) []byte
|
||||
}
|
||||
|
||||
func (c aeadCipher) Encrypt(out []byte, n uint64, ad, plaintext []byte) []byte {
|
||||
return c.Seal(out, c.nonce(n), plaintext, ad)
|
||||
}
|
||||
|
||||
func (c aeadCipher) Decrypt(out []byte, n uint64, ad, ciphertext []byte) ([]byte, error) {
|
||||
return c.Open(out, c.nonce(n), ciphertext, ad)
|
||||
}
|
||||
|
||||
type hashFn struct {
|
||||
fn func() hash.Hash
|
||||
name string
|
||||
}
|
||||
|
||||
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"}
|
||||
|
||||
func blake2bNew() hash.Hash {
|
||||
h, err := blake2b.New512(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// HashBLAKE2b is the BLAKE2b hash function.
|
||||
var HashBLAKE2b HashFunc = hashFn{blake2bNew, "BLAKE2b"}
|
||||
|
||||
func blake2sNew() hash.Hash {
|
||||
h, err := blake2s.New256(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// HashBLAKE2s is the BLAKE2s hash function.
|
||||
var HashBLAKE2s HashFunc = hashFn{blake2sNew, "BLAKE2s"}
|
||||
49
vendor/github.com/flynn/noise/hkdf.go
generated
vendored
Normal file
49
vendor/github.com/flynn/noise/hkdf.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package noise
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"hash"
|
||||
)
|
||||
|
||||
func hkdf(h func() hash.Hash, outputs int, out1, out2, out3, chainingKey, inputKeyMaterial []byte) ([]byte, []byte, []byte) {
|
||||
if len(out1) > 0 {
|
||||
panic("len(out1) > 0")
|
||||
}
|
||||
if len(out2) > 0 {
|
||||
panic("len(out2) > 0")
|
||||
}
|
||||
if len(out3) > 0 {
|
||||
panic("len(out3) > 0")
|
||||
}
|
||||
if outputs > 3 {
|
||||
panic("outputs > 3")
|
||||
}
|
||||
|
||||
tempMAC := hmac.New(h, chainingKey)
|
||||
tempMAC.Write(inputKeyMaterial)
|
||||
tempKey := tempMAC.Sum(out2)
|
||||
|
||||
out1MAC := hmac.New(h, tempKey)
|
||||
out1MAC.Write([]byte{0x01})
|
||||
out1 = out1MAC.Sum(out1)
|
||||
|
||||
if outputs == 1 {
|
||||
return out1, nil, nil
|
||||
}
|
||||
|
||||
out2MAC := hmac.New(h, tempKey)
|
||||
out2MAC.Write(out1)
|
||||
out2MAC.Write([]byte{0x02})
|
||||
out2 = out2MAC.Sum(out2)
|
||||
|
||||
if outputs == 2 {
|
||||
return out1, out2, nil
|
||||
}
|
||||
|
||||
out3MAC := hmac.New(h, tempKey)
|
||||
out3MAC.Write(out2)
|
||||
out3MAC.Write([]byte{0x03})
|
||||
out3 = out3MAC.Sum(out3)
|
||||
|
||||
return out1, out2, out3
|
||||
}
|
||||
141
vendor/github.com/flynn/noise/patterns.go
generated
vendored
Normal file
141
vendor/github.com/flynn/noise/patterns.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package noise
|
||||
|
||||
var HandshakeNN = HandshakePattern{
|
||||
Name: "NN",
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE},
|
||||
{MessagePatternE, MessagePatternDHEE},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeKN = HandshakePattern{
|
||||
Name: "KN",
|
||||
InitiatorPreMessages: []MessagePattern{MessagePatternS},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE},
|
||||
{MessagePatternE, MessagePatternDHEE, MessagePatternDHSE},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeNK = HandshakePattern{
|
||||
Name: "NK",
|
||||
ResponderPreMessages: []MessagePattern{MessagePatternS},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternDHES},
|
||||
{MessagePatternE, MessagePatternDHEE},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeKK = HandshakePattern{
|
||||
Name: "KK",
|
||||
InitiatorPreMessages: []MessagePattern{MessagePatternS},
|
||||
ResponderPreMessages: []MessagePattern{MessagePatternS},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternDHES, MessagePatternDHSS},
|
||||
{MessagePatternE, MessagePatternDHEE, MessagePatternDHSE},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeNX = HandshakePattern{
|
||||
Name: "NX",
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE},
|
||||
{MessagePatternE, MessagePatternDHEE, MessagePatternS, MessagePatternDHES},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeKX = HandshakePattern{
|
||||
Name: "KX",
|
||||
InitiatorPreMessages: []MessagePattern{MessagePatternS},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE},
|
||||
{MessagePatternE, MessagePatternDHEE, MessagePatternDHSE, MessagePatternS, MessagePatternDHES},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeXN = HandshakePattern{
|
||||
Name: "XN",
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE},
|
||||
{MessagePatternE, MessagePatternDHEE},
|
||||
{MessagePatternS, MessagePatternDHSE},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeIN = HandshakePattern{
|
||||
Name: "IN",
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternS},
|
||||
{MessagePatternE, MessagePatternDHEE, MessagePatternDHSE},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeXK = HandshakePattern{
|
||||
Name: "XK",
|
||||
ResponderPreMessages: []MessagePattern{MessagePatternS},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternDHES},
|
||||
{MessagePatternE, MessagePatternDHEE},
|
||||
{MessagePatternS, MessagePatternDHSE},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeIK = HandshakePattern{
|
||||
Name: "IK",
|
||||
ResponderPreMessages: []MessagePattern{MessagePatternS},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternDHES, MessagePatternS, MessagePatternDHSS},
|
||||
{MessagePatternE, MessagePatternDHEE, MessagePatternDHSE},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeXX = HandshakePattern{
|
||||
Name: "XX",
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE},
|
||||
{MessagePatternE, MessagePatternDHEE, MessagePatternS, MessagePatternDHES},
|
||||
{MessagePatternS, MessagePatternDHSE},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeXXfallback = HandshakePattern{
|
||||
Name: "XXfallback",
|
||||
ResponderPreMessages: []MessagePattern{MessagePatternE},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternDHEE, MessagePatternS, MessagePatternDHSE},
|
||||
{MessagePatternS, MessagePatternDHES},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeIX = HandshakePattern{
|
||||
Name: "IX",
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternS},
|
||||
{MessagePatternE, MessagePatternDHEE, MessagePatternDHSE, MessagePatternS, MessagePatternDHES},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeN = HandshakePattern{
|
||||
Name: "N",
|
||||
ResponderPreMessages: []MessagePattern{MessagePatternS},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternDHES},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeK = HandshakePattern{
|
||||
Name: "K",
|
||||
InitiatorPreMessages: []MessagePattern{MessagePatternS},
|
||||
ResponderPreMessages: []MessagePattern{MessagePatternS},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternDHES, MessagePatternDHSS},
|
||||
},
|
||||
}
|
||||
|
||||
var HandshakeX = HandshakePattern{
|
||||
Name: "X",
|
||||
ResponderPreMessages: []MessagePattern{MessagePatternS},
|
||||
Messages: [][]MessagePattern{
|
||||
{MessagePatternE, MessagePatternDHES, MessagePatternS, MessagePatternDHSS},
|
||||
},
|
||||
}
|
||||
600
vendor/github.com/flynn/noise/state.go
generated
vendored
Normal file
600
vendor/github.com/flynn/noise/state.go
generated
vendored
Normal file
@@ -0,0 +1,600 @@
|
||||
// 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 https://noiseprotocol.org.
|
||||
package noise
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
// A CipherState provides symmetric encryption and decryption after a successful
|
||||
// handshake.
|
||||
type CipherState struct {
|
||||
cs CipherSuite
|
||||
c Cipher
|
||||
k [32]byte
|
||||
n uint64
|
||||
|
||||
invalid bool
|
||||
}
|
||||
|
||||
// MaxNonce is the maximum value of n that is allowed. ErrMaxNonce is returned
|
||||
// by Encrypt and Decrypt after this has been reached. 2^64-1 is reserved for rekeys.
|
||||
const MaxNonce = uint64(math.MaxUint64) - 1
|
||||
|
||||
var ErrMaxNonce = errors.New("noise: cipherstate has reached maximum n, a new handshake must be performed")
|
||||
var ErrCipherSuiteCopied = errors.New("noise: CipherSuite has been copied, state is invalid")
|
||||
|
||||
// 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. ErrMaxNonce is returned after
|
||||
// the maximum nonce of 2^64-2 is reached.
|
||||
func (s *CipherState) Encrypt(out, ad, plaintext []byte) ([]byte, error) {
|
||||
if s.invalid {
|
||||
return nil, ErrCipherSuiteCopied
|
||||
}
|
||||
if s.n > MaxNonce {
|
||||
return nil, ErrMaxNonce
|
||||
}
|
||||
out = s.c.Encrypt(out, s.n, ad, plaintext)
|
||||
s.n++
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// 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. ErrMaxNonce is
|
||||
// returned after the maximum nonce of 2^64-2 is reached.
|
||||
func (s *CipherState) Decrypt(out, ad, ciphertext []byte) ([]byte, error) {
|
||||
if s.invalid {
|
||||
return nil, ErrCipherSuiteCopied
|
||||
}
|
||||
if s.n > MaxNonce {
|
||||
return nil, ErrMaxNonce
|
||||
}
|
||||
out, err := s.c.Decrypt(out, s.n, ad, ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.n++
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Cipher returns the low-level symmetric encryption primitive. It should only
|
||||
// be used if nonces need to be managed manually, for example with a network
|
||||
// protocol that can deliver out-of-order messages. This is dangerous, users
|
||||
// must ensure that they are incrementing a nonce after every encrypt operation.
|
||||
// After calling this method, it is an error to call Encrypt/Decrypt on the
|
||||
// CipherState.
|
||||
func (s *CipherState) Cipher() Cipher {
|
||||
s.invalid = true
|
||||
return s.c
|
||||
}
|
||||
|
||||
// Nonce returns the current value of n. This can be used to determine if a
|
||||
// new handshake should be performed due to approaching MaxNonce.
|
||||
func (s *CipherState) Nonce() uint64 {
|
||||
return s.n
|
||||
}
|
||||
|
||||
func (s *CipherState) Rekey() {
|
||||
var zeros [32]byte
|
||||
var out []byte
|
||||
out = s.c.Encrypt(out, math.MaxUint64, []byte{}, zeros[:])
|
||||
copy(s.k[:], out[:32])
|
||||
s.c = s.cs.Cipher(s.k)
|
||||
}
|
||||
|
||||
type symmetricState struct {
|
||||
CipherState
|
||||
hasK bool
|
||||
ck []byte
|
||||
h []byte
|
||||
|
||||
prevCK []byte
|
||||
prevH []byte
|
||||
}
|
||||
|
||||
func (s *symmetricState) InitializeSymmetric(handshakeName []byte) {
|
||||
h := s.cs.Hash()
|
||||
if len(handshakeName) <= h.Size() {
|
||||
s.h = make([]byte, h.Size())
|
||||
copy(s.h, handshakeName)
|
||||
} else {
|
||||
h.Write(handshakeName)
|
||||
s.h = h.Sum(nil)
|
||||
}
|
||||
s.ck = make([]byte, len(s.h))
|
||||
copy(s.ck, s.h)
|
||||
}
|
||||
|
||||
func (s *symmetricState) MixKey(dhOutput []byte) {
|
||||
s.n = 0
|
||||
s.hasK = true
|
||||
var hk []byte
|
||||
s.ck, hk, _ = hkdf(s.cs.Hash, 2, s.ck[:0], s.k[:0], nil, s.ck, dhOutput)
|
||||
copy(s.k[:], hk)
|
||||
s.c = s.cs.Cipher(s.k)
|
||||
}
|
||||
|
||||
func (s *symmetricState) MixHash(data []byte) {
|
||||
h := s.cs.Hash()
|
||||
h.Write(s.h)
|
||||
h.Write(data)
|
||||
s.h = h.Sum(s.h[:0])
|
||||
}
|
||||
|
||||
func (s *symmetricState) MixKeyAndHash(data []byte) {
|
||||
var hk []byte
|
||||
var temp []byte
|
||||
s.ck, temp, hk = hkdf(s.cs.Hash, 3, s.ck[:0], temp, s.k[:0], s.ck, data)
|
||||
s.MixHash(temp)
|
||||
copy(s.k[:], hk)
|
||||
s.c = s.cs.Cipher(s.k)
|
||||
s.n = 0
|
||||
s.hasK = true
|
||||
}
|
||||
|
||||
func (s *symmetricState) EncryptAndHash(out, plaintext []byte) ([]byte, error) {
|
||||
if !s.hasK {
|
||||
s.MixHash(plaintext)
|
||||
return append(out, plaintext...), nil
|
||||
}
|
||||
ciphertext, err := s.Encrypt(out, s.h, plaintext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.MixHash(ciphertext[len(out):])
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func (s *symmetricState) DecryptAndHash(out, data []byte) ([]byte, error) {
|
||||
if !s.hasK {
|
||||
s.MixHash(data)
|
||||
return append(out, data...), nil
|
||||
}
|
||||
plaintext, err := s.Decrypt(out, s.h, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.MixHash(data)
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func (s *symmetricState) Split() (*CipherState, *CipherState) {
|
||||
s1, s2 := &CipherState{cs: s.cs}, &CipherState{cs: s.cs}
|
||||
hk1, hk2, _ := hkdf(s.cs.Hash, 2, s1.k[:0], s2.k[:0], nil, s.ck, nil)
|
||||
copy(s1.k[:], hk1)
|
||||
copy(s2.k[:], hk2)
|
||||
s1.c = s.cs.Cipher(s1.k)
|
||||
s2.c = s.cs.Cipher(s2.k)
|
||||
return s1, s2
|
||||
}
|
||||
|
||||
func (s *symmetricState) Checkpoint() {
|
||||
if len(s.ck) > cap(s.prevCK) {
|
||||
s.prevCK = make([]byte, len(s.ck))
|
||||
}
|
||||
s.prevCK = s.prevCK[:len(s.ck)]
|
||||
copy(s.prevCK, s.ck)
|
||||
|
||||
if len(s.h) > cap(s.prevH) {
|
||||
s.prevH = make([]byte, len(s.h))
|
||||
}
|
||||
s.prevH = s.prevH[:len(s.h)]
|
||||
copy(s.prevH, s.h)
|
||||
}
|
||||
|
||||
func (s *symmetricState) Rollback() {
|
||||
s.ck = s.ck[:len(s.prevCK)]
|
||||
copy(s.ck, s.prevCK)
|
||||
s.h = s.h[:len(s.prevH)]
|
||||
copy(s.h, s.prevH)
|
||||
}
|
||||
|
||||
// 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
|
||||
ResponderPreMessages []MessagePattern
|
||||
Messages [][]MessagePattern
|
||||
}
|
||||
|
||||
const (
|
||||
MessagePatternS MessagePattern = iota
|
||||
MessagePatternE
|
||||
MessagePatternDHEE
|
||||
MessagePatternDHES
|
||||
MessagePatternDHSE
|
||||
MessagePatternDHSS
|
||||
MessagePatternPSK
|
||||
)
|
||||
|
||||
// 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
|
||||
e DHKey // local ephemeral keypair
|
||||
rs []byte // remote party's static public key
|
||||
re []byte // remote party's ephemeral public key
|
||||
psk []byte // preshared key, maybe zero length
|
||||
messagePatterns [][]MessagePattern
|
||||
shouldWrite bool
|
||||
initiator bool
|
||||
msgIdx int
|
||||
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.
|
||||
type Config struct {
|
||||
// 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 automatically configured.
|
||||
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
|
||||
|
||||
// PresharedKeyPlacement specifies the placement position of the PSK token
|
||||
// when PresharedKey is specified
|
||||
PresharedKeyPlacement int
|
||||
|
||||
// StaticKeypair is this peer's static keypair, required if part of the
|
||||
// handshake.
|
||||
StaticKeypair DHKey
|
||||
|
||||
// EphemeralKeypair is this peer's ephemeral keypair that was provided as
|
||||
// a pre-message in the handshake.
|
||||
EphemeralKeypair DHKey
|
||||
|
||||
// PeerStatic is the static public key of the remote peer that was provided
|
||||
// as a pre-message in the handshake.
|
||||
PeerStatic []byte
|
||||
|
||||
// PeerEphemeral is the ephemeral public key of the remote peer that was
|
||||
// provided as a pre-message in the handshake.
|
||||
PeerEphemeral []byte
|
||||
}
|
||||
|
||||
// NewHandshakeState starts a new handshake using the provided configuration.
|
||||
func NewHandshakeState(c Config) (*HandshakeState, error) {
|
||||
hs := &HandshakeState{
|
||||
s: c.StaticKeypair,
|
||||
e: c.EphemeralKeypair,
|
||||
rs: c.PeerStatic,
|
||||
psk: c.PresharedKey,
|
||||
messagePatterns: c.Pattern.Messages,
|
||||
shouldWrite: c.Initiator,
|
||||
initiator: 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)
|
||||
}
|
||||
hs.ss.cs = c.CipherSuite
|
||||
pskModifier := ""
|
||||
if len(hs.psk) > 0 {
|
||||
if len(hs.psk) != 32 {
|
||||
return nil, errors.New("noise: specification mandates 256-bit preshared keys")
|
||||
}
|
||||
pskModifier = fmt.Sprintf("psk%d", c.PresharedKeyPlacement)
|
||||
hs.messagePatterns = append([][]MessagePattern(nil), hs.messagePatterns...)
|
||||
if c.PresharedKeyPlacement == 0 {
|
||||
hs.messagePatterns[0] = append([]MessagePattern{MessagePatternPSK}, hs.messagePatterns[0]...)
|
||||
} else {
|
||||
hs.messagePatterns[c.PresharedKeyPlacement-1] = append(hs.messagePatterns[c.PresharedKeyPlacement-1], MessagePatternPSK)
|
||||
}
|
||||
}
|
||||
hs.ss.InitializeSymmetric([]byte("Noise_" + c.Pattern.Name + pskModifier + "_" + string(hs.ss.cs.Name())))
|
||||
hs.ss.MixHash(c.Prologue)
|
||||
for _, m := range c.Pattern.InitiatorPreMessages {
|
||||
switch {
|
||||
case c.Initiator && m == MessagePatternS:
|
||||
hs.ss.MixHash(hs.s.Public)
|
||||
case c.Initiator && m == MessagePatternE:
|
||||
hs.ss.MixHash(hs.e.Public)
|
||||
case !c.Initiator && m == MessagePatternS:
|
||||
hs.ss.MixHash(hs.rs)
|
||||
case !c.Initiator && m == MessagePatternE:
|
||||
hs.ss.MixHash(hs.re)
|
||||
}
|
||||
}
|
||||
for _, m := range c.Pattern.ResponderPreMessages {
|
||||
switch {
|
||||
case !c.Initiator && m == MessagePatternS:
|
||||
hs.ss.MixHash(hs.s.Public)
|
||||
case !c.Initiator && m == MessagePatternE:
|
||||
hs.ss.MixHash(hs.e.Public)
|
||||
case c.Initiator && m == MessagePatternS:
|
||||
hs.ss.MixHash(hs.rs)
|
||||
case c.Initiator && m == MessagePatternE:
|
||||
hs.ss.MixHash(hs.re)
|
||||
}
|
||||
}
|
||||
return hs, nil
|
||||
}
|
||||
|
||||
// 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, error) {
|
||||
if !s.shouldWrite {
|
||||
return nil, nil, nil, errors.New("noise: unexpected call to WriteMessage should be ReadMessage")
|
||||
}
|
||||
if s.msgIdx > len(s.messagePatterns)-1 {
|
||||
return nil, nil, nil, errors.New("noise: no handshake messages left")
|
||||
}
|
||||
if len(payload) > MaxMsgLen {
|
||||
return nil, nil, nil, errors.New("noise: message is too long")
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, msg := range s.messagePatterns[s.msgIdx] {
|
||||
switch msg {
|
||||
case MessagePatternE:
|
||||
e, err := s.ss.cs.GenerateKeypair(s.rng)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.e = e
|
||||
out = append(out, s.e.Public...)
|
||||
s.ss.MixHash(s.e.Public)
|
||||
if len(s.psk) > 0 {
|
||||
s.ss.MixKey(s.e.Public)
|
||||
}
|
||||
case MessagePatternS:
|
||||
if len(s.s.Public) == 0 {
|
||||
return nil, nil, nil, errors.New("noise: invalid state, s.Public is nil")
|
||||
}
|
||||
out, err = s.ss.EncryptAndHash(out, s.s.Public)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
case MessagePatternDHEE:
|
||||
dh, err := s.ss.cs.DH(s.e.Private, s.re)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
case MessagePatternDHES:
|
||||
if s.initiator {
|
||||
dh, err := s.ss.cs.DH(s.e.Private, s.rs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
} else {
|
||||
dh, err := s.ss.cs.DH(s.s.Private, s.re)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
}
|
||||
case MessagePatternDHSE:
|
||||
if s.initiator {
|
||||
dh, err := s.ss.cs.DH(s.s.Private, s.re)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
} else {
|
||||
dh, err := s.ss.cs.DH(s.e.Private, s.rs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
}
|
||||
case MessagePatternDHSS:
|
||||
dh, err := s.ss.cs.DH(s.s.Private, s.rs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
case MessagePatternPSK:
|
||||
s.ss.MixKeyAndHash(s.psk)
|
||||
}
|
||||
}
|
||||
s.shouldWrite = false
|
||||
s.msgIdx++
|
||||
out, err = s.ss.EncryptAndHash(out, payload)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if s.msgIdx >= len(s.messagePatterns) {
|
||||
cs1, cs2 := s.ss.Split()
|
||||
return out, cs1, cs2, nil
|
||||
}
|
||||
|
||||
return out, nil, 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 {
|
||||
return nil, nil, nil, errors.New("noise: unexpected call to ReadMessage should be WriteMessage")
|
||||
}
|
||||
if s.msgIdx > len(s.messagePatterns)-1 {
|
||||
return nil, nil, nil, errors.New("noise: no handshake messages left")
|
||||
}
|
||||
|
||||
rsSet := false
|
||||
s.ss.Checkpoint()
|
||||
|
||||
var err error
|
||||
for _, msg := range s.messagePatterns[s.msgIdx] {
|
||||
switch msg {
|
||||
case MessagePatternE, MessagePatternS:
|
||||
expected := s.ss.cs.DHLen()
|
||||
if msg == MessagePatternS && s.ss.hasK {
|
||||
expected += 16
|
||||
}
|
||||
if len(message) < expected {
|
||||
return nil, nil, nil, ErrShortMessage
|
||||
}
|
||||
switch msg {
|
||||
case MessagePatternE:
|
||||
if cap(s.re) < s.ss.cs.DHLen() {
|
||||
s.re = make([]byte, s.ss.cs.DHLen())
|
||||
}
|
||||
s.re = s.re[:s.ss.cs.DHLen()]
|
||||
copy(s.re, message)
|
||||
s.ss.MixHash(s.re)
|
||||
if len(s.psk) > 0 {
|
||||
s.ss.MixKey(s.re)
|
||||
}
|
||||
case MessagePatternS:
|
||||
if len(s.rs) > 0 {
|
||||
return nil, nil, nil, errors.New("noise: invalid state, rs is not nil")
|
||||
}
|
||||
s.rs, err = s.ss.DecryptAndHash(s.rs[:0], message[:expected])
|
||||
rsSet = true
|
||||
}
|
||||
if err != nil {
|
||||
s.ss.Rollback()
|
||||
if rsSet {
|
||||
s.rs = nil
|
||||
}
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
message = message[expected:]
|
||||
case MessagePatternDHEE:
|
||||
dh, err := s.ss.cs.DH(s.e.Private, s.re)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
case MessagePatternDHES:
|
||||
if s.initiator {
|
||||
dh, err := s.ss.cs.DH(s.e.Private, s.rs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
} else {
|
||||
dh, err := s.ss.cs.DH(s.s.Private, s.re)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
}
|
||||
case MessagePatternDHSE:
|
||||
if s.initiator {
|
||||
dh, err := s.ss.cs.DH(s.s.Private, s.re)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
} else {
|
||||
dh, err := s.ss.cs.DH(s.e.Private, s.rs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
}
|
||||
case MessagePatternDHSS:
|
||||
dh, err := s.ss.cs.DH(s.s.Private, s.rs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.ss.MixKey(dh)
|
||||
case MessagePatternPSK:
|
||||
s.ss.MixKeyAndHash(s.psk)
|
||||
}
|
||||
}
|
||||
out, err = s.ss.DecryptAndHash(out, message)
|
||||
if err != nil {
|
||||
s.ss.Rollback()
|
||||
if rsSet {
|
||||
s.rs = nil
|
||||
}
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.shouldWrite = true
|
||||
s.msgIdx++
|
||||
|
||||
if s.msgIdx >= len(s.messagePatterns) {
|
||||
cs1, cs2 := s.ss.Split()
|
||||
return out, cs1, cs2, nil
|
||||
}
|
||||
|
||||
return out, nil, nil, nil
|
||||
}
|
||||
|
||||
// ChannelBinding provides a value that uniquely identifies the session and can
|
||||
// be used as a channel binding. It is an error to call this method before the
|
||||
// handshake is complete.
|
||||
func (s *HandshakeState) ChannelBinding() []byte {
|
||||
return s.ss.h
|
||||
}
|
||||
|
||||
// PeerStatic returns the static key provided by the remote peer during
|
||||
// a handshake. It is an error to call this method if a handshake message
|
||||
// containing a static key has not been read.
|
||||
func (s *HandshakeState) PeerStatic() []byte {
|
||||
return s.rs
|
||||
}
|
||||
|
||||
// MessageIndex returns the current handshake message id
|
||||
func (s *HandshakeState) MessageIndex() int {
|
||||
return s.msgIdx
|
||||
}
|
||||
|
||||
// PeerEphemeral returns the ephemeral key provided by the remote peer during
|
||||
// a handshake. It is an error to call this method if a handshake message
|
||||
// containing a static key has not been read.
|
||||
func (s *HandshakeState) PeerEphemeral() []byte {
|
||||
return s.re
|
||||
}
|
||||
|
||||
// LocalEphemeral returns the local ephemeral key pair generated during
|
||||
// a handshake.
|
||||
func (s *HandshakeState) LocalEphemeral() DHKey {
|
||||
return s.e
|
||||
}
|
||||
28640
vendor/github.com/flynn/noise/vectors.txt
generated
vendored
Normal file
28640
vendor/github.com/flynn/noise/vectors.txt
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user