Files
cunicu/pkg/crypto/types.go
2022-05-04 13:57:58 +02:00

170 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package crypto
import (
"crypto/hmac"
"crypto/sha512"
"encoding/base64"
"errors"
"net"
"github.com/aead/siphash"
"github.com/pion/dtls/v2/pkg/crypto/hash"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/pbkdf2"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// Keys
const (
KeyLength = 32
SignatureLength = 64
pbkdf2Iterations = 4096
)
var (
// A ɯice specific key for siphash to generate unique IPv6 addresses from the
// interfaces public key
addrHashKey = [...]byte{0x67, 0x67, 0x2c, 0x05, 0xd1, 0x3e, 0x11, 0x94, 0xbb, 0x38, 0x91, 0xff, 0x4f, 0x80, 0xb3, 0x97}
pbkdf2Salt = [...]byte{0x77, 0x31, 0x63, 0x33, 0x63, 0x30, 0x6e, 0x6e, 0x33, 0x63, 0x74, 0x73, 0x33, 0x76, 0x65, 0x72, 0x79, 0x62, 0x30, 0x64, 0x79}
)
type Nonce []byte
type Key [KeyLength]byte
type Signature [SignatureLength]byte
func GenerateKeyFromPassword(pw string) Key {
key := pbkdf2.Key([]byte(pw), pbkdf2Salt[:], pbkdf2Iterations, KeyLength, sha512.New)
// Modify random bytes using algorithm described at:
// https://cr.yp.to/ecdh.html.
key[0] &= 248
key[31] &= 127
key[31] |= 64
return *(*Key)(key)
}
func GenerateKey() (Key, error) {
key, err := wgtypes.GenerateKey()
if err != nil {
return Key{}, err
}
return Key(key), nil
}
func GeneratePrivateKey() (Key, error) {
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
return Key{}, err
}
return Key(key), nil
}
func ParseKey(str string) (Key, error) {
k, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return Key{}, err
}
var key Key
copy(key[:], k[:KeyLength])
return key, nil
}
func ParseKeyBytes(buf []byte) (Key, error) {
if len(buf) != KeyLength {
return Key{}, errors.New("invalid length")
}
return *(*Key)(buf), nil
}
func (k Key) String() string {
return base64.StdEncoding.EncodeToString(k[:])
}
func (k Key) MarshalText() ([]byte, error) {
return []byte(k.String()), nil
}
func (k *Key) UnmarshalText(text []byte) error {
var err error
*k, err = ParseKey(string(text))
return err
}
func (k Key) PublicKey() Key {
key := wgtypes.Key(k)
return Key(key.PublicKey())
}
func (k Key) Bytes() []byte {
return k[:]
}
// IPv6Address derives an IPv6 link local address from they key
func (k Key) IPv6Address() *net.IPNet {
ip := net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0}
hash, _ := siphash.New64(addrHashKey[:])
hash.Write(k[:])
// Append interface identifier from the hash function
ip = hash.Sum(ip)
return &net.IPNet{
IP: ip,
Mask: net.CIDRMask(64, 128),
}
}
// Checks if the key is not zero
func (k Key) IsSet() bool {
return k != Key{}
}
func (k Signature) String() string {
return base64.StdEncoding.EncodeToString(k[:])
}
type KeyPair struct {
Ours Key `json:"ours"`
Theirs Key `json:"theirs"`
}
type PublicKeyPair KeyPair
func (kp KeyPair) ID(key []byte) string {
if key == nil {
key = []byte{}
}
ctx := hmac.New(hash.SHA512.CryptoHash().HashFunc().New, key)
ctx.Write(kp.Ours[:])
ctx.Write(kp.Theirs[:])
mac := ctx.Sum(nil)
return base64.URLEncoding.EncodeToString(mac)
}
func (kp KeyPair) Shared() Key {
shared, _ := curve25519.X25519(kp.Ours[:], kp.Theirs[:])
return *(*Key)(shared)
}
func (kp KeyPair) Public() PublicKeyPair {
return PublicKeyPair{
Ours: kp.Ours.PublicKey(),
Theirs: kp.Theirs,
}
}