Files
ham/protocol/meshcore/crypto/x25519.go
2026-02-17 23:30:49 +01:00

142 lines
2.9 KiB
Go

package crypto
import (
"crypto/aes"
"crypto/hmac"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"golang.org/x/crypto/curve25519"
)
type SharedSecret struct {
aBytes [32]byte
}
func MakeSharedSecret(key []byte) SharedSecret {
var secret SharedSecret
copy(secret.aBytes[:], key)
return secret
}
func MakeSharedSecretFromGroupSecret(group []byte) SharedSecret {
var secret SharedSecret
copy(secret.aBytes[:16], group)
return secret
}
func (ss *SharedSecret) HMAC(message []byte) uint16 {
h := hmac.New(sha256.New, ss.aBytes[:])
h.Write(message)
r := h.Sum(nil)
return binary.BigEndian.Uint16(r[:2])
}
func (ss *SharedSecret) key() []byte {
return ss.aBytes[:aes.BlockSize]
}
func (ss *SharedSecret) Decrypt(text []byte) ([]byte, error) {
block, err := aes.NewCipher(ss.key())
if err != nil {
return nil, err
}
length := len(text)
message := make([]byte, length)
copy(message, text)
const chunkSize = aes.BlockSize
remain := length % chunkSize
if remain > 0 {
padding := chunkSize - remain
message = append(message, make([]byte, padding)...)
}
for i, l := 0, len(message); i < l; i += chunkSize {
block.Decrypt(message[i:i+chunkSize], message[i:i+chunkSize])
}
return message[:length], nil
}
func (ss *SharedSecret) MACThenDecrypt(text []byte, mac uint16) ([]byte, error) {
if our := ss.HMAC(text); our != mac {
return nil, fmt.Errorf("expected MAC %04X, got %04X", mac, our)
}
return ss.Decrypt(text)
}
func (ss *SharedSecret) Encrypt(message []byte) ([]byte, error) {
block, err := aes.NewCipher(ss.key())
if err != nil {
return nil, err
}
text := make([]byte, len(message))
copy(text, message)
const chunkSize = aes.BlockSize
remain := len(text) % chunkSize
if remain > 0 {
padding := chunkSize - remain
text = append(text, make([]byte, padding)...)
}
for i, l := 0, len(text); i < l; i += chunkSize {
block.Encrypt(text[i:i+chunkSize], text[i:i+chunkSize])
}
return text, nil
}
func (ss *SharedSecret) EncryptThenMAC(message []byte) (uint16, []byte, error) {
text, err := ss.Encrypt(message)
if err != nil {
return 0, nil, err
}
return ss.HMAC(text), text, nil
}
type StaticSecret [32]byte
func (ss StaticSecret) PublicKey() (*PublicKey, error) {
pub, err := curve25519.X25519(ss[:], curve25519.Basepoint)
if err != nil {
return nil, err
}
return NewPublicKey(pub)
}
func (ss StaticSecret) DiffieHellman(other *PublicKey) (SharedSecret, error) {
shared, err := curve25519.X25519(ss[:], other.Bytes())
if err != nil {
return SharedSecret{}, err
}
var montgomery SharedSecret
copy(montgomery.aBytes[:], shared)
return montgomery, nil
}
type Signature [64]byte
func (sig Signature) MarshalJSON() ([]byte, error) {
s := hex.EncodeToString(sig[:])
return json.Marshal(s)
}
func (sig *Signature) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
copy((*sig)[:], []byte(s))
return nil
}