wireguard源码分析(七)

这段Go代码主要用于根据WireGuard配置文件生成一个确定性的GUID(全局唯一标识符)。GUID是在Windows系统中广泛使用的标识符格式,用于标识对象或实体。这段代码使用BLAKE2s哈希函数来生成GUID,并通过特定的规则和输入数据来确保其唯一性和确定性。

导入包

import (
	"bytes"
	"encoding/binary"
	"sort"
	"unsafe"

	"golang.org/x/crypto/blake2s"
	"golang.org/x/sys/windows"
	"golang.org/x/text/unicode/norm"

	"golang.zx2c4.com/wireguard/windows/conf"
)

分析

  • bytes 提供了字节缓冲区和常用的字节操作功能。
  • encoding/binary 提供了在不同字节序(大端、小端)之间转换数据的能力。
  • sort 提供了切片排序功能。
  • unsafe 允许进行不安全的内存操作,如直接转换指针类型。
  • blake2s 是一种加密哈希函数,速度快且安全性高。
  • windows 提供了与Windows操作系统的交互功能,包括GUID的定义。
  • norm 提供了Unicode规范化功能,确保字符串在比较时是一致的。
  • conf 是WireGuard的配置包,用于获取配置数据。

常量定义

const (
	deterministicGUIDLabel = "Deterministic WireGuard Windows GUID v1 jason@zx2c4.com"
	fixedGUIDLabel         = "Fixed WireGuard Windows GUID v1 jason@zx2c4.com"
)

分析

  • 定义了两个字符串常量,用于在生成GUID时作为标签。一个用于确定性GUID (deterministicGUIDLabel),另一个用于固定GUID (fixedGUIDLabel)。

变量定义

var UseFixedGUIDInsteadOfDeterministic = false

分析

  • 这是一个全局变量,默认值为 false。它是一个“逃生舱口”变量,用于控制是否使用固定GUID而不是确定性GUID。

注释解释

/* All peer public keys and allowed ips are sorted. Length/number fields are
 * little endian 32-bit. Hash input is:
 *
 * label || len(interface name) || interface name ||
 * interface public key || number of peers ||
 * peer public key || number of peer allowed ips ||
 * len(allowed ip string) || allowed ip/cidr in canonical string notation ||
 * len(allowed ip string) || allowed ip/cidr in canonical string notation ||
 * len(allowed ip string) || allowed ip/cidr in canonical string notation ||
 * ...
 * peer public key || number of peer allowed ips ||
 * len(allowed ip string) || allowed ip/cidr in canonical string notation ||
 * len(allowed ip string) || allowed ip/cidr in canonical string notation ||
 * len(allowed ip string) || allowed ip/cidr in canonical string notation ||
 * ...
 * ...
 */

分析

  • 这段注释解释了生成GUID时,哈希函数的输入是如何构造的。输入包含标签、接口名称、接口公钥、Peer的公钥、以及允许的IP地址,所有这些信息都经过规范化处理并按特定顺序排序,以确保GUID的确定性。

deterministicGUID 函数

func deterministicGUID(c *conf.Config) *windows.GUID {
	b2, _ := blake2s.New256(nil)
	if !UseFixedGUIDInsteadOfDeterministic {
		b2.Write([]byte(deterministicGUIDLabel))
	} else {
		b2.Write([]byte(fixedGUIDLabel))
	}
	// 省略部分代码...

分析

  • 函数 deterministicGUID 用于根据给定的配置 c 生成一个确定性的GUID。
  • b2 是一个BLAKE2s哈希函数的实例,256位输出。
  • 根据 UseFixedGUIDInsteadOfDeterministic 的值,决定是使用确定性GUID的标签还是固定GUID的标签。
内嵌函数 b2Number
	b2Number := func(i int) {
		if uint(i) > uint(^uint32(0)) {
			panic("length out of bounds")
		}
		var bytes [4]byte
		binary.LittleEndian.PutUint32(bytes[:], uint32(i))
		b2.Write(bytes[:])
	}

分析

  • b2Number 将一个整数转换为小端字节序的4字节表示,并将其写入到哈希输入中。
内嵌函数 b2String
	b2String := func(s string) {
		bytes := []byte(s)
		bytes = norm.NFC.Bytes(bytes)
		b2Number(len(bytes))
		b2.Write(bytes)
	}

分析

  • b2String 将一个字符串转换为规范化的字节数组,将其长度写入哈希函数,然后写入字节数组内容。
内嵌函数 b2Key
	b2Key := func(k *conf.Key) {
		b2.Write(k[:])
	}

分析

  • b2Key 将一个公钥直接写入哈希函数。
哈希输入构造
	b2String(c.Name)
	if !UseFixedGUIDInsteadOfDeterministic {
		b2Key(c.Interface.PrivateKey.Public())
		b2Number(len(c.Peers))
		sortedPeers := c.Peers
		sort.Slice(sortedPeers, func(i, j int) bool {
			return bytes.Compare(sortedPeers[i].PublicKey[:], sortedPeers[j].PublicKey[:]) < 0
		})
		// 省略部分代码...
	}

分析

  • 首先将接口名称写入哈希输入。
  • 如果不使用固定GUID,则进一步将接口的公钥和Peer的数量写入哈希。
  • 对Peers进行排序,确保GUID的确定性。

GUID生成

	return (*windows.GUID)(unsafe.Pointer(&b2.Sum(nil)[0]))

分析

  • 最后,将哈希结果转换为GUID并返回。使用 unsafe.Pointer 进行类型转换,这是Go语言中直接操作内存的方式。

总结

这段代码的核心功能是根据WireGuard的配置文件生成一个确定性GUID,确保同样的配置文件在不同时间或不同机器上生成的GUID是相同的。通过使用BLAKE2s哈希函数和一系列严格排序和规范化的输入,确保GUID的唯一性和一致性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值