forked from iarv/meshstream
73 lines
2.0 KiB
Go
73 lines
2.0 KiB
Go
package decoder
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
// XOR encrypts or decrypts text with the specified key. It requires the packetID and sending node ID for the AES IV
|
|
func XOR(text []byte, key []byte, packetID, fromNode uint32) ([]byte, error) {
|
|
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
|
|
return nil, fmt.Errorf("key length must be 16, 24, or 32 bytes")
|
|
}
|
|
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// The IV needs to be unique, but not secure. It's common to include it at
|
|
// the beginning of the text. In CTR mode, the IV size is equal to the block size.
|
|
//if len(text) < aes.BlockSize {
|
|
// return nil, fmt.Errorf("text too short")
|
|
//}
|
|
iv, err := CreateNonce(packetID, fromNode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
//text = text[aes.BlockSize:]
|
|
|
|
// CTR mode is the same for both encryption and decryption, so we use
|
|
// the NewCTR function rather than NewCBCDecrypter.
|
|
stream := cipher.NewCTR(block, iv)
|
|
|
|
// XORKeyStream can work in-place if the two arguments are the same.
|
|
plaintext := make([]byte, len(text))
|
|
stream.XORKeyStream(plaintext, text)
|
|
|
|
return plaintext, nil
|
|
}
|
|
|
|
// CreateNonce creates a 128-bit nonce.
|
|
// It takes a uint32 packetId, converts it to a uint64, and a uint32 fromNode.
|
|
// The nonce is concatenated as [64-bit packetId][32-bit fromNode][32-bit block counter].
|
|
func CreateNonce(packetId uint32, fromNode uint32) ([]byte, error) {
|
|
// Expand packetId to 64 bits
|
|
packetId64 := uint64(packetId)
|
|
|
|
// Initialize block counter (32-bit, starts at zero)
|
|
blockCounter := uint32(0)
|
|
|
|
// Create a buffer for the nonce
|
|
buf := new(bytes.Buffer)
|
|
|
|
// Write packetId, fromNode, and block counter to the buffer
|
|
err := binary.Write(buf, binary.LittleEndian, packetId64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = binary.Write(buf, binary.LittleEndian, fromNode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = binary.Write(buf, binary.LittleEndian, blockCounter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
}
|