mirror of
https://github.com/logos-messaging/noise.git
synced 2026-01-05 15:43:13 +00:00
Compare commits
No commits in common. "master" and "v1.0.1-handshakeMessages" have entirely different histories.
master
...
v1.0.1-han
@ -28,9 +28,6 @@ type DHFunc interface {
|
|||||||
// entropy.
|
// entropy.
|
||||||
GenerateKeypair(random io.Reader) (DHKey, error)
|
GenerateKeypair(random io.Reader) (DHKey, error)
|
||||||
|
|
||||||
// GenerateKeypairFromPrivateKEy generates a keypair from a private key
|
|
||||||
GenerateKeyPairFromPrivateKey(privkey []byte) (DHKey, error)
|
|
||||||
|
|
||||||
// DH performs a Diffie-Hellman calculation between the provided private and
|
// DH performs a Diffie-Hellman calculation between the provided private and
|
||||||
// public keys and returns the result.
|
// public keys and returns the result.
|
||||||
DH(privkey, pubkey []byte) ([]byte, error)
|
DH(privkey, pubkey []byte) ([]byte, error)
|
||||||
@ -107,7 +104,7 @@ var DH25519 DHFunc = dh25519{}
|
|||||||
|
|
||||||
type dh25519 struct{}
|
type dh25519 struct{}
|
||||||
|
|
||||||
func (d dh25519) GenerateKeypair(rng io.Reader) (DHKey, error) {
|
func (dh25519) GenerateKeypair(rng io.Reader) (DHKey, error) {
|
||||||
privkey := make([]byte, 32)
|
privkey := make([]byte, 32)
|
||||||
if rng == nil {
|
if rng == nil {
|
||||||
rng = rand.Reader
|
rng = rand.Reader
|
||||||
@ -115,11 +112,6 @@ func (d dh25519) GenerateKeypair(rng io.Reader) (DHKey, error) {
|
|||||||
if _, err := io.ReadFull(rng, privkey); err != nil {
|
if _, err := io.ReadFull(rng, privkey); err != nil {
|
||||||
return DHKey{}, err
|
return DHKey{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.GenerateKeyPairFromPrivateKey(privkey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d dh25519) GenerateKeyPairFromPrivateKey(privkey []byte) (DHKey, error) {
|
|
||||||
pubkey, err := curve25519.X25519(privkey, curve25519.Basepoint)
|
pubkey, err := curve25519.X25519(privkey, curve25519.Basepoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DHKey{}, err
|
return DHKey{}, err
|
||||||
|
|||||||
10
go.mod
10
go.mod
@ -1,14 +1,8 @@
|
|||||||
module github.com/waku-org/noise
|
module github.com/flynn/noise
|
||||||
|
|
||||||
go 1.17
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/kr/pretty v0.2.1 // indirect
|
|
||||||
github.com/kr/text v0.1.0 // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
|
|
||||||
)
|
|
||||||
|
|||||||
181
state.go
181
state.go
@ -10,7 +10,6 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
@ -150,16 +149,12 @@ func (s *symmetricState) MixKeyAndHash(data []byte) {
|
|||||||
s.hasK = true
|
s.hasK = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that by setting extraAd, it is possible to pass extra additional data that will be concatenated to the ad specified by Noise (can be used to authenticate messageNametag)
|
func (s *symmetricState) EncryptAndHash(out, plaintext []byte) ([]byte, error) {
|
||||||
func (s *symmetricState) EncryptAndHash(out, plaintext []byte, extraAd ...byte) ([]byte, error) {
|
|
||||||
if !s.hasK {
|
if !s.hasK {
|
||||||
s.MixHash(plaintext)
|
s.MixHash(plaintext)
|
||||||
return append(out, plaintext...), nil
|
return append(out, plaintext...), nil
|
||||||
}
|
}
|
||||||
|
ciphertext, err := s.Encrypt(out, s.h, plaintext)
|
||||||
ad := append([]byte(nil), s.h...)
|
|
||||||
ad = append(ad, extraAd...)
|
|
||||||
ciphertext, err := s.Encrypt(out, ad, plaintext)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -167,15 +162,12 @@ func (s *symmetricState) EncryptAndHash(out, plaintext []byte, extraAd ...byte)
|
|||||||
return ciphertext, nil
|
return ciphertext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *symmetricState) DecryptAndHash(out, data []byte, extraAd ...byte) ([]byte, error) {
|
func (s *symmetricState) DecryptAndHash(out, data []byte) ([]byte, error) {
|
||||||
if !s.hasK {
|
if !s.hasK {
|
||||||
s.MixHash(data)
|
s.MixHash(data)
|
||||||
return append(out, data...), nil
|
return append(out, data...), nil
|
||||||
}
|
}
|
||||||
|
plaintext, err := s.Decrypt(out, s.h, data)
|
||||||
ad := append([]byte(nil), s.h...)
|
|
||||||
ad = append(ad, extraAd...)
|
|
||||||
plaintext, err := s.Decrypt(out, ad, data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -363,40 +355,21 @@ func NewHandshakeState(c Config) (*HandshakeState, error) {
|
|||||||
return hs, nil
|
return hs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HandshakeState) H() []byte {
|
|
||||||
return append([]byte(nil), s.ss.h...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *HandshakeState) RS() []byte {
|
|
||||||
return append([]byte(nil), s.rs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteMessage appends a handshake message to out. The message will include the
|
// 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
|
// 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
|
// 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
|
// 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
|
// peer. It is an error to call this method out of sync with the handshake
|
||||||
// pattern.
|
// pattern.
|
||||||
func (s *HandshakeState) WriteMessage(out, payload []byte, extraAd ...byte) ([]byte, *CipherState, *CipherState, error) {
|
func (s *HandshakeState) WriteMessage(out, payload []byte) ([]byte, *CipherState, *CipherState, error) {
|
||||||
out, _, cs1, cs2, err := s.WriteMessageAndGetPK(out, [][]byte{}, payload, extraAd)
|
|
||||||
return out, cs1, cs2, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteMessageAndGetPK appends a handshake message to out. outPK can possibly contain the
|
|
||||||
// party public keys. 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) WriteMessageAndGetPK(out []byte, outPK [][]byte, payload []byte, extraAd []byte) ([]byte, [][]byte, *CipherState, *CipherState, error) {
|
|
||||||
if !s.shouldWrite {
|
if !s.shouldWrite {
|
||||||
return nil, nil, nil, nil, errors.New("noise: unexpected call to WriteMessage should be ReadMessage")
|
return nil, nil, nil, errors.New("noise: unexpected call to WriteMessage should be ReadMessage")
|
||||||
}
|
}
|
||||||
if s.msgIdx > len(s.messagePatterns)-1 {
|
if s.msgIdx > len(s.messagePatterns)-1 {
|
||||||
return nil, nil, nil, nil, errors.New("noise: no handshake messages left")
|
return nil, nil, nil, errors.New("noise: no handshake messages left")
|
||||||
}
|
}
|
||||||
if len(payload) > MaxMsgLen {
|
if len(payload) > MaxMsgLen {
|
||||||
return nil, nil, nil, nil, errors.New("noise: message is too long")
|
return nil, nil, nil, errors.New("noise: message is too long")
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -405,43 +378,39 @@ func (s *HandshakeState) WriteMessageAndGetPK(out []byte, outPK [][]byte, payloa
|
|||||||
case MessagePatternE:
|
case MessagePatternE:
|
||||||
e, err := s.ss.cs.GenerateKeypair(s.rng)
|
e, err := s.ss.cs.GenerateKeypair(s.rng)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
s.e = e
|
s.e = e
|
||||||
out = append(out, s.e.Public...)
|
out = append(out, s.e.Public...)
|
||||||
outPK = append(outPK, s.e.Public)
|
|
||||||
|
|
||||||
s.ss.MixHash(s.e.Public)
|
s.ss.MixHash(s.e.Public)
|
||||||
if len(s.psk) > 0 {
|
if len(s.psk) > 0 {
|
||||||
s.ss.MixKey(s.e.Public)
|
s.ss.MixKey(s.e.Public)
|
||||||
}
|
}
|
||||||
case MessagePatternS:
|
case MessagePatternS:
|
||||||
if len(s.s.Public) == 0 {
|
if len(s.s.Public) == 0 {
|
||||||
return nil, nil, nil, nil, errors.New("noise: invalid state, s.Public is nil")
|
return nil, nil, nil, errors.New("noise: invalid state, s.Public is nil")
|
||||||
}
|
}
|
||||||
out, err = s.ss.EncryptAndHash(out, s.s.Public)
|
out, err = s.ss.EncryptAndHash(out, s.s.Public)
|
||||||
outPK = append(outPK, out)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case MessagePatternDHEE:
|
case MessagePatternDHEE:
|
||||||
dh, err := s.ss.cs.DH(s.e.Private, s.re)
|
dh, err := s.ss.cs.DH(s.e.Private, s.re)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
s.ss.MixKey(dh)
|
s.ss.MixKey(dh)
|
||||||
case MessagePatternDHES:
|
case MessagePatternDHES:
|
||||||
if s.initiator {
|
if s.initiator {
|
||||||
dh, err := s.ss.cs.DH(s.e.Private, s.rs)
|
dh, err := s.ss.cs.DH(s.e.Private, s.rs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
s.ss.MixKey(dh)
|
s.ss.MixKey(dh)
|
||||||
} else {
|
} else {
|
||||||
dh, err := s.ss.cs.DH(s.s.Private, s.re)
|
dh, err := s.ss.cs.DH(s.s.Private, s.re)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
s.ss.MixKey(dh)
|
s.ss.MixKey(dh)
|
||||||
}
|
}
|
||||||
@ -449,20 +418,20 @@ func (s *HandshakeState) WriteMessageAndGetPK(out []byte, outPK [][]byte, payloa
|
|||||||
if s.initiator {
|
if s.initiator {
|
||||||
dh, err := s.ss.cs.DH(s.s.Private, s.re)
|
dh, err := s.ss.cs.DH(s.s.Private, s.re)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
s.ss.MixKey(dh)
|
s.ss.MixKey(dh)
|
||||||
} else {
|
} else {
|
||||||
dh, err := s.ss.cs.DH(s.e.Private, s.rs)
|
dh, err := s.ss.cs.DH(s.e.Private, s.rs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
s.ss.MixKey(dh)
|
s.ss.MixKey(dh)
|
||||||
}
|
}
|
||||||
case MessagePatternDHSS:
|
case MessagePatternDHSS:
|
||||||
dh, err := s.ss.cs.DH(s.s.Private, s.rs)
|
dh, err := s.ss.cs.DH(s.s.Private, s.rs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
s.ss.MixKey(dh)
|
s.ss.MixKey(dh)
|
||||||
case MessagePatternPSK:
|
case MessagePatternPSK:
|
||||||
@ -471,21 +440,121 @@ func (s *HandshakeState) WriteMessageAndGetPK(out []byte, outPK [][]byte, payloa
|
|||||||
}
|
}
|
||||||
s.shouldWrite = false
|
s.shouldWrite = false
|
||||||
s.msgIdx++
|
s.msgIdx++
|
||||||
out, err = s.ss.EncryptAndHash(out, payload, extraAd...)
|
out, err = s.ss.EncryptAndHash(out, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.msgIdx >= len(s.messagePatterns) {
|
if s.msgIdx >= len(s.messagePatterns) {
|
||||||
cs1, cs2 := s.ss.Split()
|
cs1, cs2 := s.ss.Split()
|
||||||
return out, outPK, cs1, cs2, nil
|
return out, cs1, cs2, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return out, outPK, nil, nil, nil
|
return out, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HandshakeState) Hash() hash.Hash {
|
// WriteMessageAndGetPK appends a handshake message to out. outPK can possibly contain the
|
||||||
return s.ss.cs.Hash()
|
// party public keys. 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) WriteMessageAndGetPK(out []byte, outNoisePK *[][]byte, 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...)
|
||||||
|
if outNoisePK != nil {
|
||||||
|
*outNoisePK = append(*outNoisePK, 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
|
||||||
|
}
|
||||||
|
if outNoisePK != nil {
|
||||||
|
*outNoisePK = append(*outNoisePK, out)
|
||||||
|
}
|
||||||
|
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.
|
// ErrShortMessage is returned by ReadMessage if a message is not as long as it should be.
|
||||||
@ -496,7 +565,7 @@ var ErrShortMessage = errors.New("noise: message is too short")
|
|||||||
// will be returned, one is used for encryption of messages to the remote peer,
|
// 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
|
// 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.
|
// error to call this method out of sync with the handshake pattern.
|
||||||
func (s *HandshakeState) ReadMessage(out, message []byte, extraAd ...byte) ([]byte, *CipherState, *CipherState, error) {
|
func (s *HandshakeState) ReadMessage(out, message []byte) ([]byte, *CipherState, *CipherState, error) {
|
||||||
if s.shouldWrite {
|
if s.shouldWrite {
|
||||||
return nil, nil, nil, errors.New("noise: unexpected call to ReadMessage should be WriteMessage")
|
return nil, nil, nil, errors.New("noise: unexpected call to ReadMessage should be WriteMessage")
|
||||||
}
|
}
|
||||||
@ -588,7 +657,7 @@ func (s *HandshakeState) ReadMessage(out, message []byte, extraAd ...byte) ([]by
|
|||||||
s.ss.MixKeyAndHash(s.psk)
|
s.ss.MixKeyAndHash(s.psk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out, err = s.ss.DecryptAndHash(out, message, extraAd...)
|
out, err = s.ss.DecryptAndHash(out, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.ss.Rollback()
|
s.ss.Rollback()
|
||||||
if rsSet {
|
if rsSet {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
. "github.com/waku-org/noise"
|
. "github.com/flynn/noise"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user