From 5ced3852cedfd863d8736a1a441cc2c68ce48e3c Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sun, 9 Jun 2019 19:37:09 +0200 Subject: tunnel: generate GUIDs deterministically This allows NLA profiles to securely bind public keys to firewall profiles, a considerable improvement on the usual insecure Windows situation. --- tunnel/deterministicguid.go | 73 +++++++++++++++++++++++++++++++++++++++++++++ tunnel/service.go | 2 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tunnel/deterministicguid.go diff --git a/tunnel/deterministicguid.go b/tunnel/deterministicguid.go new file mode 100644 index 00000000..720972bf --- /dev/null +++ b/tunnel/deterministicguid.go @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package tunnel + +import ( + "bytes" + "encoding/binary" + "sort" + "unsafe" + + "golang.org/x/crypto/blake2s" + "golang.org/x/sys/windows" + + "golang.zx2c4.com/wireguard/windows/conf" +) + +const deterministicGUIDLabel = "Deterministic WireGuard Windows GUID for interface: " + +/* All peer public keys and allowed ips are sorted. Hash input is: + * + * label || interface name || zero padding to blake2s blocksize || + * interface public key || + * little endian 32-bit number of peers || + * peer public key || little endian 32-bit number of peer allowed ips || + * allowed ip in canonical string notation || '/' || cidr in decimal || '\n' || + * allowed ip in canonical string notation || '/' || cidr in decimal || '\n' || + * ... + * peer public key || little endian 32-bit number of peer allowed ips || + * allowed ip in canonical string notation || '/' || cidr in decimal || '\n' || + * allowed ip in canonical string notation || '/' || cidr in decimal || '\n' || + * ... + * ... + */ + +func deterministicGUID(conf *conf.Config) *windows.GUID { + b2, _ := blake2s.New256(nil) + u32 := func(i uint32) { + var bytes [4]byte + binary.LittleEndian.PutUint32(bytes[:], i) + b2.Write(bytes[:]) + } + header := []byte(deterministicGUIDLabel) + header = append(header, []byte(conf.Name)...) + b2.Write(header) + b2.Write(make([]byte, (((len(header)-1)|(blake2s.BlockSize-1))+1)-len(header))) + b2.Write(conf.Interface.PrivateKey.Public()[:]) + u32(uint32(len(conf.Peers))) + sortedPeers := conf.Peers + sort.Slice(sortedPeers, func(i, j int) bool { + return bytes.Compare(sortedPeers[i].PublicKey[:], sortedPeers[j].PublicKey[:]) < 0 + }) + for _, peer := range sortedPeers { + b2.Write(peer.PublicKey[:]) + u32(uint32(len(peer.AllowedIPs))) + sortedAllowedIPs := peer.AllowedIPs + sort.Slice(sortedAllowedIPs, func(i, j int) bool { + if bi, bj := sortedAllowedIPs[i].Bits(), sortedAllowedIPs[j].Bits(); bi != bj { + return bi < bj + } + if sortedAllowedIPs[i].Cidr != sortedAllowedIPs[j].Cidr { + return sortedAllowedIPs[i].Cidr < sortedAllowedIPs[j].Cidr + } + return bytes.Compare(sortedAllowedIPs[i].IP[:], sortedAllowedIPs[j].IP[:]) < 0 + }) + for _, allowedip := range sortedAllowedIPs { + b2.Write([]byte(allowedip.String() + "\n")) + } + } + return (*windows.GUID)(unsafe.Pointer(&b2.Sum(nil)[0])) +} diff --git a/tunnel/service.go b/tunnel/service.go index 8a045aab..1978cae0 100644 --- a/tunnel/service.go +++ b/tunnel/service.go @@ -148,7 +148,7 @@ func (service *Service) Execute(args []string, r <-chan svc.ChangeRequest, chang } log.Println("Creating Wintun device") - wintun, err := tun.CreateTUN(conf.Name) + wintun, err := tun.CreateTUNWithRequestedGUID(conf.Name, deterministicGUID(conf)) if err != nil { serviceError = services.ErrorCreateWintun return -- cgit v1.2.3-59-g8ed1b