mirror of https://github.com/waku-org/go-noise.git
138 lines
3.4 KiB
Go
138 lines
3.4 KiB
Go
package noise
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"errors"
|
|
)
|
|
|
|
type MessageNametag [MessageNametagLength]byte
|
|
|
|
const MessageNametagLength = 16
|
|
const MessageNametagBufferSize = 50
|
|
|
|
var (
|
|
ErrNametagNotFound = errors.New("message nametag not found in buffer")
|
|
ErrNametagNotExpected = errors.New("message nametag is present in buffer but is not the next expected nametag. One or more messages were probably lost")
|
|
)
|
|
|
|
// Converts a sequence or array (arbitrary size) to a MessageNametag
|
|
func BytesToMessageNametag(input []byte) MessageNametag {
|
|
var result MessageNametag
|
|
copy(result[:], input)
|
|
return result
|
|
}
|
|
|
|
func (t MessageNametag) String() string {
|
|
return hex.EncodeToString(t[:])
|
|
}
|
|
|
|
type MessageNametagBuffer struct {
|
|
buffer []MessageNametag
|
|
counter uint64
|
|
secret []byte
|
|
}
|
|
|
|
func NewMessageNametagBuffer(secret []byte) *MessageNametagBuffer {
|
|
return &MessageNametagBuffer{
|
|
secret: secret,
|
|
}
|
|
}
|
|
|
|
// Initializes the empty Message nametag buffer. The n-th nametag is equal to HKDF( secret || n )
|
|
func (m *MessageNametagBuffer) Init() {
|
|
// We default the counter and buffer fields
|
|
m.counter = 0
|
|
m.buffer = make([]MessageNametag, MessageNametagBufferSize)
|
|
if len(m.secret) != 0 {
|
|
for i := range m.buffer {
|
|
counterBytesLE := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(counterBytesLE, m.counter)
|
|
toHash := []byte{}
|
|
toHash = append(toHash, m.secret...)
|
|
toHash = append(toHash, counterBytesLE...)
|
|
d := sha256.Sum256(toHash)
|
|
m.buffer[i] = BytesToMessageNametag(d[:])
|
|
m.counter++
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *MessageNametagBuffer) Pop() MessageNametag {
|
|
// Note that if the input MessageNametagBuffer is set to default, an all 0 messageNametag is returned
|
|
if len(m.buffer) == 0 {
|
|
var m MessageNametag
|
|
return m
|
|
} else {
|
|
messageNametag := m.buffer[0]
|
|
m.Delete(1)
|
|
return messageNametag
|
|
}
|
|
}
|
|
|
|
// Checks if the input messageNametag is contained in the input MessageNametagBuffer
|
|
func (m *MessageNametagBuffer) CheckNametag(messageNametag MessageNametag) error {
|
|
if len(m.buffer) != MessageNametagBufferSize {
|
|
return nil
|
|
}
|
|
|
|
index := -1
|
|
for i, x := range m.buffer {
|
|
if bytes.Equal(x[:], messageNametag[:]) {
|
|
index = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if index == -1 {
|
|
return ErrNametagNotFound
|
|
} else if index > 0 {
|
|
return ErrNametagNotExpected
|
|
}
|
|
|
|
// index is 0, hence the read message tag is the next expected one
|
|
return nil
|
|
}
|
|
|
|
func rotateLeft(elems []MessageNametag, k int) []MessageNametag {
|
|
if k < 0 || len(elems) == 0 {
|
|
return elems
|
|
}
|
|
r := len(elems) - k%len(elems)
|
|
|
|
result := elems[r:]
|
|
result = append(result, elems[:r]...)
|
|
|
|
return result
|
|
}
|
|
|
|
// Deletes the first n elements in buffer and appends n new ones
|
|
func (m *MessageNametagBuffer) Delete(n int) {
|
|
if n <= 0 {
|
|
return
|
|
}
|
|
|
|
// We ensure n is at most MessageNametagBufferSize (the buffer will be fully replaced)
|
|
if n > MessageNametagBufferSize {
|
|
n = MessageNametagBufferSize
|
|
}
|
|
|
|
// We update the last n values in the array if a secret is set
|
|
// Note that if the input MessageNametagBuffer is set to default, nothing is done here
|
|
if len(m.secret) != 0 {
|
|
m.buffer = rotateLeft(m.buffer, n)
|
|
for i := 0; i < n; i++ {
|
|
counterBytesLE := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(counterBytesLE, m.counter)
|
|
toHash := []byte{}
|
|
toHash = append(toHash, m.secret...)
|
|
toHash = append(toHash, counterBytesLE...)
|
|
d := sha256.Sum256(toHash)
|
|
m.buffer[len(m.buffer)-n+i] = BytesToMessageNametag(d[:])
|
|
m.counter++
|
|
}
|
|
}
|
|
}
|