241 lines
6.3 KiB
Go
241 lines
6.3 KiB
Go
package meshseed
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// deriveKey 派生 AES-256-GCM 密钥
|
|
// 密钥派生规则:SHA256("meshray-ddns" || NetworkSecret) → 32 字节
|
|
func deriveKey(networkSecret string) []byte {
|
|
hash := sha256.Sum256([]byte("meshray-ddns" + networkSecret))
|
|
return hash[:]
|
|
}
|
|
|
|
// Encrypt 加密 MeshSeed
|
|
// 流程:签名 → AES-256-GCM 加密 → Base64URL 编码
|
|
func Encrypt(seed *MeshSeed, networkSecret string) (string, error) {
|
|
// 1. 签名(如果还没有签名)
|
|
if len(seed.Signature) == 0 {
|
|
// 注意:这里需要 Ed25519 私钥进行签名
|
|
// 由于 MeshSeed 已经包含签名,我们假设签名已经完成
|
|
return "", fmt.Errorf("MeshSeed 未签名")
|
|
}
|
|
|
|
// 2. 序列化 MeshSeed
|
|
plaintext, err := json.Marshal(seed)
|
|
if err != nil {
|
|
return "", fmt.Errorf("序列化 MeshSeed 失败:%w", err)
|
|
}
|
|
|
|
// 3. 派生加密密钥
|
|
key := deriveKey(networkSecret)
|
|
|
|
// 4. 创建 AES cipher
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return "", fmt.Errorf("创建 AES cipher 失败:%w", err)
|
|
}
|
|
|
|
// 5. 创建 GCM
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return "", fmt.Errorf("创建 GCM 失败:%w", err)
|
|
}
|
|
|
|
// 6. 生成随机 Nonce
|
|
nonce := make([]byte, gcm.NonceSize())
|
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
|
return "", fmt.Errorf("生成 Nonce 失败:%w", err)
|
|
}
|
|
|
|
// 7. 加密
|
|
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
|
|
|
|
// 8. Base64URL 编码
|
|
encoded := base64.URLEncoding.EncodeToString(ciphertext)
|
|
|
|
return encoded, nil
|
|
}
|
|
|
|
// Decrypt 解密 MeshSeed
|
|
// 流程:Base64URL 解码 → AES-256-GCM 解密 → Ed25519 验签
|
|
func Decrypt(encrypted string, networkSecret string) (*MeshSeed, error) {
|
|
// 1. Base64URL 解码
|
|
ciphertext, err := base64.URLEncoding.DecodeString(encrypted)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Base64URL 解码失败:%w", err)
|
|
}
|
|
|
|
// 2. 派生解密密钥
|
|
key := deriveKey(networkSecret)
|
|
|
|
// 3. 创建 AES cipher
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("创建 AES cipher 失败:%w", err)
|
|
}
|
|
|
|
// 4. 创建 GCM
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("创建 GCM 失败:%w", err)
|
|
}
|
|
|
|
// 5. 检查密文长度
|
|
nonceSize := gcm.NonceSize()
|
|
if len(ciphertext) < nonceSize {
|
|
return nil, fmt.Errorf("密文长度不足")
|
|
}
|
|
|
|
// 6. 分离 Nonce 和密文
|
|
nonce, ciphertextBytes := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
|
|
|
// 7. 解密
|
|
plaintext, err := gcm.Open(nil, nonce, ciphertextBytes, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("解密失败:%w", err)
|
|
}
|
|
|
|
// 8. 反序列化 MeshSeed
|
|
var seed MeshSeed
|
|
if err := json.Unmarshal(plaintext, &seed); err != nil {
|
|
return nil, fmt.Errorf("反序列化 MeshSeed 失败:%w", err)
|
|
}
|
|
|
|
return &seed, nil
|
|
}
|
|
|
|
// Sign 签名 MeshSeed
|
|
// 使用 Ed25519 私钥对 MeshSeed 进行签名
|
|
func Sign(seed *MeshSeed, privateKey ed25519.PrivateKey) error {
|
|
// 1. 临时清除签名字段
|
|
originalSignature := seed.Signature
|
|
seed.Signature = nil
|
|
|
|
// 2. 序列化 MeshSeed(不含签名)
|
|
data, err := json.Marshal(seed)
|
|
if err != nil {
|
|
seed.Signature = originalSignature
|
|
return fmt.Errorf("序列化 MeshSeed 失败:%w", err)
|
|
}
|
|
|
|
// 3. Ed25519 签名
|
|
signature := ed25519.Sign(privateKey, data)
|
|
|
|
// 4. 设置签名
|
|
seed.Signature = signature
|
|
|
|
return nil
|
|
}
|
|
|
|
// Verify 验证 MeshSeed 签名
|
|
// 使用 Ed25519 公钥验证 MeshSeed 签名
|
|
// 可选参数 publicKey:如果为 nil,则从 seed.IssuerNodeID 解析公钥
|
|
func Verify(seed *MeshSeed, publicKey ed25519.PublicKey) error {
|
|
// 1. 检查签名是否存在
|
|
if len(seed.Signature) == 0 {
|
|
return fmt.Errorf("MeshSeed 无签名")
|
|
}
|
|
|
|
// 2. 如果未提供公钥,从 IssuerNodeID 解析
|
|
if publicKey == nil {
|
|
if seed.IssuerNodeID == "" {
|
|
return fmt.Errorf("无法验证签名:缺少公钥(IssuerNodeID 为空)")
|
|
}
|
|
// IssuerNodeID 是 hex 编码的 Ed25519 公钥(32 字节)
|
|
pubKeyBytes, err := decodeIssuerNodeID(seed.IssuerNodeID)
|
|
if err != nil {
|
|
return fmt.Errorf("解析 IssuerNodeID 失败:%w", err)
|
|
}
|
|
if len(pubKeyBytes) != ed25519.PublicKeySize {
|
|
return fmt.Errorf("无效的公钥长度:期望 %d 字节,实际 %d 字节",
|
|
ed25519.PublicKeySize, len(pubKeyBytes))
|
|
}
|
|
publicKey = ed25519.PublicKey(pubKeyBytes)
|
|
}
|
|
|
|
// 3. 临时保存签名
|
|
signature := seed.Signature
|
|
|
|
// 4. 临时清除签名字段
|
|
seed.Signature = nil
|
|
|
|
// 5. 序列化 MeshSeed(不含签名)
|
|
data, err := json.Marshal(seed)
|
|
if err != nil {
|
|
seed.Signature = signature
|
|
return fmt.Errorf("序列化 MeshSeed 失败:%w", err)
|
|
}
|
|
|
|
// 6. 恢复签名
|
|
seed.Signature = signature
|
|
|
|
// 7. Ed25519 验签
|
|
if !ed25519.Verify(publicKey, data, signature) {
|
|
return fmt.Errorf("签名验证失败:签名无效或数据被篡改")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// decodeIssuerNodeID 解码 IssuerNodeID 为公钥字节
|
|
// 支持两种格式:hex 编码(64 字符)或 Base64 编码
|
|
func decodeIssuerNodeID(id string) ([]byte, error) {
|
|
// 尝试 hex 解码(64 字符 = 32 字节)
|
|
if len(id) == 64 {
|
|
return hexDecodeString(id)
|
|
}
|
|
|
|
// 尝试 Base64 解码
|
|
return base64.StdEncoding.DecodeString(id)
|
|
}
|
|
|
|
// hexDecodeString 解码 hex 字符串(辅助函数)
|
|
func hexDecodeString(s string) ([]byte, error) {
|
|
// 手动实现 hex 解码以避免导入 encoding/hex
|
|
result := make([]byte, len(s)/2)
|
|
for i := 0; i < len(s); i += 2 {
|
|
b1, ok := hexCharToByte(s[i])
|
|
if !ok {
|
|
return nil, fmt.Errorf("无效的 hex 字符:%c", s[i])
|
|
}
|
|
b2, ok := hexCharToByte(s[i+1])
|
|
if !ok {
|
|
return nil, fmt.Errorf("无效的 hex 字符:%c", s[i+1])
|
|
}
|
|
result[i/2] = b1<<4 | b2
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// hexCharToByte 单个 hex 字符转字节
|
|
func hexCharToByte(c byte) (byte, bool) {
|
|
switch {
|
|
case '0' <= c && c <= '9':
|
|
return c - '0', true
|
|
case 'a' <= c && c <= 'f':
|
|
return c - 'a' + 10, true
|
|
case 'A' <= c && c <= 'F':
|
|
return c - 'A' + 10, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
// GenerateKeyPair 生成 Ed25519 密钥对
|
|
func GenerateKeyPair() (ed25519.PublicKey, ed25519.PrivateKey, error) {
|
|
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("生成密钥对失败:%w", err)
|
|
}
|
|
return publicKey, privateKey, nil
|
|
}
|