Files
cunicu/pkg/crypto/types.go
Steffen Vogel db6294b9f0 make auto-generated address prefixes configurable
Signed-off-by: Steffen Vogel <post@steffenvogel.de>
2022-10-07 18:30:50 +02:00

167 lines
3.1 KiB
Go

package crypto
import (
"encoding/base64"
"errors"
"math/big"
"net"
"github.com/dchest/siphash"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/curve25519"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// Keys
const (
KeyLength = 32
)
var (
// A cunīcu 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}
argonSalt = [...]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
func GenerateKeyFromPassword(pw string) Key {
key := argon2.IDKey([]byte(pw), argonSalt[:], 1, 64*1024, 4, KeyLength)
// 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
}
return ParseKeyBytes(k)
}
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[:]
}
func (k Key) IPAddress(p net.IPNet) net.IPNet {
l := len(p.IP)
ones, _ := p.Mask.Size()
hash := siphash.New128(addrHashKey[:])
if n, err := hash.Write(k[:]); err != nil {
panic(err)
} else if n != KeyLength {
panic("incomplete hash")
}
// Append interface identifier from the hash function
var db []byte
db = hash.Sum(db)
d := new(big.Int).SetBytes(db[:l])
i := new(big.Int).SetBytes(p.IP)
m := new(big.Int).SetBytes(p.Mask)
d.Rsh(d, uint(ones))
i.And(i, m)
d.Or(d, i)
p.IP = d.Bytes()
return p
}
// Checks if the key is not zero
func (k Key) IsSet() bool {
return k != Key{}
}
// A key which uses GenerateKeyFromPassword() for UnmarshalText()
type KeyPassphrase Key
func (k *KeyPassphrase) UnmarshalText(text []byte) error {
*k = KeyPassphrase(GenerateKeyFromPassword(string(text)))
return nil
}
type KeyPair struct {
Ours Key `json:"ours"`
Theirs Key `json:"theirs"`
}
type PublicKeyPair KeyPair
func (kp KeyPair) Shared() Key {
shared, err := curve25519.X25519(kp.Ours[:], kp.Theirs[:])
if err != nil {
panic(err)
}
return *(*Key)(shared)
}
func (kp KeyPair) Public() PublicKeyPair {
return PublicKeyPair{
Ours: kp.Ours.PublicKey(),
Theirs: kp.Theirs,
}
}