package wireguard import ( "bytes" "crypto/rand" "encoding/base64" "errors" "golang.org/x/crypto/curve25519" ) const ( // PrivateKeySize is the length in bytes of a Wireguard Private Key PrivateKeySize = 32 // PublicKeySize is the length in bytes of a Wireguard Public Key PublicKeySize = 32 ) var ( // ErrInvalidKeySize indicates the key size is wrong ErrInvalidKeySize = errors.New("invalid key size") ) type ( // PrivateKey is a binary Wireguard Private Key PrivateKey []byte // PublicKey is a binary Wireguard Public Key PublicKey []byte ) func (key PrivateKey) String() string { return encodeKey(key) } func (pub PublicKey) String() string { return encodeKey(pub) } // IsZero tells if the key hasn't been set func (key PrivateKey) IsZero() bool { return len(key) == 0 } // IsZero tells if the key hasn't been set func (pub PublicKey) IsZero() bool { return len(pub) == 0 } // Equal checks if two private keys are identical func (key PrivateKey) Equal(alter PrivateKey) bool { return bytes.Equal(key, alter) } // Equal checks if two public keys are identical func (pub PublicKey) Equal(alter PublicKey) bool { return bytes.Equal(pub, alter) } // PrivateKeyFromBase64 decodes a base64-based string into // a [PrivateKey] func PrivateKeyFromBase64(data string) (PrivateKey, error) { b, err := decodeKey(data, PrivateKeySize) return b, err } // PublicKeyFromBase64 decodes a base64-based string into // a [PublicKey] func PublicKeyFromBase64(data string) (PublicKey, error) { b, err := decodeKey(data, PublicKeySize) return b, err } func encodeKey(b []byte) string { switch { case len(b) == 0: return "" default: return base64.StdEncoding.EncodeToString(b) } } func decodeKey(data string, size int) ([]byte, error) { b, err := base64.StdEncoding.DecodeString(data) switch { case err != nil: return []byte{}, err case len(b) != size: err = ErrInvalidKeySize return []byte{}, err default: return b, nil } } // NewPrivateKey creates a new PrivateKey func NewPrivateKey() (PrivateKey, error) { var s [PrivateKeySize]byte _, err := rand.Read(s[:]) if err != nil { return []byte{}, err } // apply same clamping as wireguard-go/device/noise-helpers.go s[0] &= 0xf8 s[31] = (s[31] & 0x7f) | 0x40 return s[:], nil } // Public generates the corresponding PublicKey func (key PrivateKey) Public() PublicKey { if len(key) != PrivateKeySize { return []byte{} } out := [PublicKeySize]byte{} in := (*[PrivateKeySize]byte)(key) curve25519.ScalarBaseMult(&out, in) return out[:] } // KeyPair holds a Key pair type KeyPair struct { PrivateKey PrivateKey PublicKey PublicKey } // NewKeyPair creates a new KeyPair for Wireguard func NewKeyPair() (*KeyPair, error) { key, err := NewPrivateKey() if err != nil { return nil, err } out := &KeyPair{ PrivateKey: key, PublicKey: key.Public(), } return out, nil }