"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20poly1305"
- "golang.zx2c4.com/wireguard/wgcfg"
)
type CookieChecker struct {
}
}
-func (st *CookieChecker) Init(pk wgcfg.Key) {
+func (st *CookieChecker) Init(pk NoisePublicKey) {
st.Lock()
defer st.Unlock()
return reply, nil
}
-func (st *CookieGenerator) Init(pk wgcfg.Key) {
+func (st *CookieGenerator) Init(pk NoisePublicKey) {
st.Lock()
defer st.Unlock()
import (
"testing"
-
- "golang.zx2c4.com/wireguard/wgcfg"
)
func TestCookieMAC1(t *testing.T) {
checker CookieChecker
)
- sk, err := wgcfg.NewPrivateKey()
+ sk, err := newPrivateKey()
if err != nil {
t.Fatal(err)
}
- pk := sk.Public()
+ pk := sk.publicKey()
generator.Init(pk)
checker.Init(pk)
"golang.zx2c4.com/wireguard/ratelimiter"
"golang.zx2c4.com/wireguard/rwcancel"
"golang.zx2c4.com/wireguard/tun"
- "golang.zx2c4.com/wireguard/wgcfg"
)
type Device struct {
staticIdentity struct {
sync.RWMutex
- privateKey wgcfg.PrivateKey
- publicKey wgcfg.Key
+ privateKey NoisePrivateKey
+ publicKey NoisePublicKey
}
peers struct {
sync.RWMutex
- keyMap map[wgcfg.Key]*Peer
+ keyMap map[NoisePublicKey]*Peer
}
// unprotected / "self-synchronising resources"
*
* Must hold device.peers.Mutex
*/
-func unsafeRemovePeer(device *Device, peer *Peer, key wgcfg.Key) {
+func unsafeRemovePeer(device *Device, peer *Peer, key NoisePublicKey) {
// stop routing and processing of packets
return until.After(now)
}
-func (device *Device) SetPrivateKey(sk wgcfg.PrivateKey) error {
+func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
// lock required resources
device.staticIdentity.Lock()
defer device.staticIdentity.Unlock()
- if sk.Equal(device.staticIdentity.privateKey) {
+ if sk.Equals(device.staticIdentity.privateKey) {
return nil
}
// remove peers with matching public keys
- publicKey := sk.Public()
+ publicKey := sk.publicKey()
for key, peer := range device.peers.keyMap {
- if peer.handshake.remoteStatic.Equal(publicKey) {
+ if peer.handshake.remoteStatic.Equals(publicKey) {
unsafeRemovePeer(device, peer, key)
}
}
expiredPeers := make([]*Peer, 0, len(device.peers.keyMap))
for _, peer := range device.peers.keyMap {
handshake := &peer.handshake
- handshake.precomputedStaticStatic = device.staticIdentity.privateKey.SharedSecret(handshake.remoteStatic)
+ handshake.precomputedStaticStatic = device.staticIdentity.privateKey.sharedSecret(handshake.remoteStatic)
expiredPeers = append(expiredPeers, peer)
}
}
device.tun.mtu = int32(mtu)
- device.peers.keyMap = make(map[wgcfg.Key]*Peer)
+ device.peers.keyMap = make(map[NoisePublicKey]*Peer)
device.rate.limiter.Init()
device.rate.underLoadUntil.Store(time.Time{})
return device
}
-func (device *Device) LookupPeer(pk wgcfg.Key) *Peer {
+func (device *Device) LookupPeer(pk NoisePublicKey) *Peer {
device.peers.RLock()
defer device.peers.RUnlock()
return device.peers.keyMap[pk]
}
-func (device *Device) RemovePeer(key wgcfg.Key) {
+func (device *Device) RemovePeer(key NoisePublicKey) {
device.peers.Lock()
defer device.peers.Unlock()
// stop peer and remove from routing
unsafeRemovePeer(device, peer, key)
}
- device.peers.keyMap = make(map[wgcfg.Key]*Peer)
+ device.peers.keyMap = make(map[NoisePublicKey]*Peer)
}
func (device *Device) FlushPacketQueues() {
"time"
"golang.zx2c4.com/wireguard/tun/tuntest"
- "golang.zx2c4.com/wireguard/wgcfg"
)
func TestTwoDevicePing(t *testing.T) {
}
func randDevice(t *testing.T) *Device {
- sk, err := wgcfg.NewPrivateKey()
+ sk, err := newPrivateKey()
if err != nil {
t.Fatal(err)
}
import (
"crypto/hmac"
+ "crypto/rand"
"crypto/subtle"
"hash"
"golang.org/x/crypto/blake2s"
+ "golang.org/x/crypto/curve25519"
)
/* KDF related functions.
arr[i] = 0
}
}
+
+func (sk *NoisePrivateKey) clamp() {
+ sk[0] &= 248
+ sk[31] = (sk[31] & 127) | 64
+}
+
+func newPrivateKey() (sk NoisePrivateKey, err error) {
+ _, err = rand.Read(sk[:])
+ sk.clamp()
+ return
+}
+
+func (sk *NoisePrivateKey) publicKey() (pk NoisePublicKey) {
+ apk := (*[NoisePublicKeySize]byte)(&pk)
+ ask := (*[NoisePrivateKeySize]byte)(sk)
+ curve25519.ScalarBaseMult(apk, ask)
+ return
+}
+
+func (sk *NoisePrivateKey) sharedSecret(pk NoisePublicKey) (ss [NoisePublicKeySize]byte) {
+ apk := (*[NoisePublicKeySize]byte)(&pk)
+ ask := (*[NoisePrivateKeySize]byte)(sk)
+ curve25519.ScalarMult(&ss, ask, apk)
+ return ss
+}
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/poly1305"
"golang.zx2c4.com/wireguard/tai64n"
- "golang.zx2c4.com/wireguard/wgcfg"
)
type handshakeState int
type MessageInitiation struct {
Type uint32
Sender uint32
- Ephemeral wgcfg.Key
- Static [wgcfg.KeySize + poly1305.TagSize]byte
+ Ephemeral NoisePublicKey
+ Static [NoisePublicKeySize + poly1305.TagSize]byte
Timestamp [tai64n.TimestampSize + poly1305.TagSize]byte
MAC1 [blake2s.Size128]byte
MAC2 [blake2s.Size128]byte
Type uint32
Sender uint32
Receiver uint32
- Ephemeral wgcfg.Key
+ Ephemeral NoisePublicKey
Empty [poly1305.TagSize]byte
MAC1 [blake2s.Size128]byte
MAC2 [blake2s.Size128]byte
type Handshake struct {
state handshakeState
mutex sync.RWMutex
- hash [blake2s.Size]byte // hash value
- chainKey [blake2s.Size]byte // chain key
- presharedKey wgcfg.SymmetricKey // psk
- localEphemeral wgcfg.PrivateKey // ephemeral secret key
- localIndex uint32 // used to clear hash-table
- remoteIndex uint32 // index for sending
- remoteStatic wgcfg.Key // long term key
- remoteEphemeral wgcfg.Key // ephemeral public key
- precomputedStaticStatic [wgcfg.KeySize]byte // precomputed shared secret
+ hash [blake2s.Size]byte // hash value
+ chainKey [blake2s.Size]byte // chain key
+ presharedKey NoiseSymmetricKey // psk
+ localEphemeral NoisePrivateKey // ephemeral secret key
+ localIndex uint32 // used to clear hash-table
+ remoteIndex uint32 // index for sending
+ remoteStatic NoisePublicKey // long term key
+ remoteEphemeral NoisePublicKey // ephemeral public key
+ precomputedStaticStatic [NoisePublicKeySize]byte // precomputed shared secret
lastTimestamp tai64n.Timestamp
lastInitiationConsumption time.Time
lastSentHandshake time.Time
var err error
handshake.hash = InitialHash
handshake.chainKey = InitialChainKey
- handshake.localEphemeral, err = wgcfg.NewPrivateKey()
+ handshake.localEphemeral, err = newPrivateKey()
if err != nil {
return nil, err
}
msg := MessageInitiation{
Type: MessageInitiationType,
- Ephemeral: handshake.localEphemeral.Public(),
+ Ephemeral: handshake.localEphemeral.publicKey(),
}
handshake.mixKey(msg.Ephemeral[:])
handshake.mixHash(msg.Ephemeral[:])
// encrypt static key
- ss := handshake.localEphemeral.SharedSecret(handshake.remoteStatic)
+ ss := handshake.localEphemeral.sharedSecret(handshake.remoteStatic)
if isZero(ss[:]) {
return nil, errZeroECDHResult
}
// decrypt static key
var err error
- var peerPK wgcfg.Key
+ var peerPK NoisePublicKey
var key [chacha20poly1305.KeySize]byte
- ss := device.staticIdentity.privateKey.SharedSecret(msg.Ephemeral)
+ ss := device.staticIdentity.privateKey.sharedSecret(msg.Ephemeral)
if isZero(ss[:]) {
return nil
}
// create ephemeral key
- handshake.localEphemeral, err = wgcfg.NewPrivateKey()
+ handshake.localEphemeral, err = newPrivateKey()
if err != nil {
return nil, err
}
- msg.Ephemeral = handshake.localEphemeral.Public()
+ msg.Ephemeral = handshake.localEphemeral.publicKey()
handshake.mixHash(msg.Ephemeral[:])
handshake.mixKey(msg.Ephemeral[:])
func() {
- ss := handshake.localEphemeral.SharedSecret(handshake.remoteEphemeral)
+ ss := handshake.localEphemeral.sharedSecret(handshake.remoteEphemeral)
handshake.mixKey(ss[:])
- ss = handshake.localEphemeral.SharedSecret(handshake.remoteStatic)
+ ss = handshake.localEphemeral.sharedSecret(handshake.remoteStatic)
handshake.mixKey(ss[:])
}()
mixKey(&chainKey, &handshake.chainKey, msg.Ephemeral[:])
func() {
- ss := handshake.localEphemeral.SharedSecret(msg.Ephemeral)
+ ss := handshake.localEphemeral.sharedSecret(msg.Ephemeral)
mixKey(&chainKey, &chainKey, ss[:])
setZero(ss[:])
}()
func() {
- ss := device.staticIdentity.privateKey.SharedSecret(msg.Ephemeral)
+ ss := device.staticIdentity.privateKey.sharedSecret(msg.Ephemeral)
mixKey(&chainKey, &chainKey, ss[:])
setZero(ss[:])
}()
--- /dev/null
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package device
+
+import (
+ "crypto/subtle"
+ "encoding/hex"
+ "errors"
+
+ "golang.org/x/crypto/chacha20poly1305"
+)
+
+const (
+ NoisePublicKeySize = 32
+ NoisePrivateKeySize = 32
+)
+
+type (
+ NoisePublicKey [NoisePublicKeySize]byte
+ NoisePrivateKey [NoisePrivateKeySize]byte
+ NoiseSymmetricKey [chacha20poly1305.KeySize]byte
+ NoiseNonce uint64 // padded to 12-bytes
+)
+
+func loadExactHex(dst []byte, src string) error {
+ slice, err := hex.DecodeString(src)
+ if err != nil {
+ return err
+ }
+ if len(slice) != len(dst) {
+ return errors.New("hex string does not fit the slice")
+ }
+ copy(dst, slice)
+ return nil
+}
+
+func (key NoisePrivateKey) IsZero() bool {
+ var zero NoisePrivateKey
+ return key.Equals(zero)
+}
+
+func (key NoisePrivateKey) Equals(tar NoisePrivateKey) bool {
+ return subtle.ConstantTimeCompare(key[:], tar[:]) == 1
+}
+
+func (key *NoisePrivateKey) FromHex(src string) (err error) {
+ err = loadExactHex(key[:], src)
+ key.clamp()
+ return
+}
+
+func (key *NoisePrivateKey) FromMaybeZeroHex(src string) (err error) {
+ err = loadExactHex(key[:], src)
+ if key.IsZero() {
+ return
+ }
+ key.clamp()
+ return
+}
+
+func (key NoisePrivateKey) ToHex() string {
+ return hex.EncodeToString(key[:])
+}
+
+func (key *NoisePublicKey) FromHex(src string) error {
+ return loadExactHex(key[:], src)
+}
+
+func (key NoisePublicKey) ToHex() string {
+ return hex.EncodeToString(key[:])
+}
+
+func (key NoisePublicKey) IsZero() bool {
+ var zero NoisePublicKey
+ return key.Equals(zero)
+}
+
+func (key NoisePublicKey) Equals(tar NoisePublicKey) bool {
+ return subtle.ConstantTimeCompare(key[:], tar[:]) == 1
+}
+
+func (key *NoiseSymmetricKey) FromHex(src string) error {
+ return loadExactHex(key[:], src)
+}
+
+func (key NoiseSymmetricKey) ToHex() string {
+ return hex.EncodeToString(key[:])
+}
"testing"
)
+func TestCurveWrappers(t *testing.T) {
+ sk1, err := newPrivateKey()
+ assertNil(t, err)
+
+ sk2, err := newPrivateKey()
+ assertNil(t, err)
+
+ pk1 := sk1.publicKey()
+ pk2 := sk2.publicKey()
+
+ ss1 := sk1.sharedSecret(pk2)
+ ss2 := sk2.sharedSecret(pk1)
+
+ if ss1 != ss2 {
+ t.Fatal("Failed to compute shared secet")
+ }
+}
+
func TestNoiseHandshake(t *testing.T) {
dev1 := randDevice(t)
dev2 := randDevice(t)
defer dev1.Close()
defer dev2.Close()
- peer1, err := dev2.NewPeer(dev1.staticIdentity.privateKey.Public())
- if err != nil {
- t.Fatal(err)
- }
- peer2, err := dev1.NewPeer(dev2.staticIdentity.privateKey.Public())
- if err != nil {
- t.Fatal(err)
- }
+ peer1, _ := dev2.NewPeer(dev1.staticIdentity.privateKey.publicKey())
+ peer2, _ := dev1.NewPeer(dev2.staticIdentity.privateKey.publicKey())
assertEqual(
t,
"time"
"golang.zx2c4.com/wireguard/conn"
- "golang.zx2c4.com/wireguard/wgcfg"
)
const (
cookieGenerator CookieGenerator
}
-func (device *Device) NewPeer(pk wgcfg.Key) (*Peer, error) {
-
+func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
if device.isClosed.Get() {
return nil, errors.New("device closed")
}
handshake := &peer.handshake
handshake.mutex.Lock()
- handshake.precomputedStaticStatic = device.staticIdentity.privateKey.SharedSecret(pk)
+ handshake.precomputedStaticStatic = device.staticIdentity.privateKey.sharedSecret(pk)
handshake.remoteStatic = pk
handshake.mutex.Unlock()
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/ipc"
- "golang.zx2c4.com/wireguard/wgcfg"
)
type IPCError struct {
// serialize device related values
if !device.staticIdentity.privateKey.IsZero() {
- send("private_key=" + device.staticIdentity.privateKey.HexString())
+ send("private_key=" + device.staticIdentity.privateKey.ToHex())
}
if device.net.port != 0 {
peer.RLock()
defer peer.RUnlock()
- send("public_key=" + peer.handshake.remoteStatic.HexString())
- send("preshared_key=" + peer.handshake.presharedKey.HexString())
+ send("public_key=" + peer.handshake.remoteStatic.ToHex())
+ send("preshared_key=" + peer.handshake.presharedKey.ToHex())
send("protocol_version=1")
if peer.endpoint != nil {
send("endpoint=" + peer.endpoint.DstToString())
switch key {
case "private_key":
- sk, err := wgcfg.ParsePrivateHexKey(value)
+ var sk NoisePrivateKey
+ err := sk.FromMaybeZeroHex(value)
if err != nil {
logError.Println("Failed to set private_key:", err)
return &IPCError{ipc.IpcErrorInvalid}
switch key {
case "public_key":
- publicKey, err := wgcfg.ParseHexKey(value)
+ var publicKey NoisePublicKey
+ err := publicKey.FromHex(value)
if err != nil {
logError.Println("Failed to get peer by public key:", err)
return &IPCError{ipc.IpcErrorInvalid}
// ignore peer with public key of device
device.staticIdentity.RLock()
- dummy = device.staticIdentity.publicKey.Equal(publicKey)
+ dummy = device.staticIdentity.publicKey.Equals(publicKey)
device.staticIdentity.RUnlock()
if dummy {
logDebug.Println(peer, "- UAPI: Updating preshared key")
peer.handshake.mutex.Lock()
- key, err := wgcfg.ParseSymmetricHexKey(value)
- peer.handshake.presharedKey = key
+ err := peer.handshake.presharedKey.FromHex(value)
peer.handshake.mutex.Unlock()
if err != nil {