go-noise/messagenametag.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++
}
}
}