142 lines
2.9 KiB
Go
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
|
|
}
|