版本封存

This commit is contained in:
zeaslity
2025-12-06 11:26:05 +08:00
parent 13949e1ba8
commit c0ae5e30c4
57 changed files with 2443 additions and 1428 deletions

View File

@@ -0,0 +1,169 @@
package totp_tier_one
import (
"cmii-uav-watchdog-common/models"
"cmii-uav-watchdog-common/wdd_log"
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"strconv"
"strings"
)
// Encrypt 加密字符串
// 保证使用相同的plaintext和key进行加密在不同时间、不同机器上输出的加密字符串都是固定的
func Encrypt(plaintext string, key string) (encryptedPlaintext string, encryptError error) {
// 创建hash
hasher := sha256.New()
hasher.Write([]byte(key))
keyBytes := hasher.Sum(nil)
// 创建cipher
block, err := aes.NewCipher(keyBytes)
if err != nil {
return "", err
}
// 创建gcm
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
// 生成确定性nonce - 使用plaintext和key的组合生成确保对相同输入产生相同nonce
nonceHasher := sha256.New()
nonceHasher.Write([]byte(key + "fixed_salt_for_deterministic_result" + plaintext[:min(10, len(plaintext))]))
nonceSource := nonceHasher.Sum(nil)
// 创建合适大小的nonce
nonce := nonceSource[:gcm.NonceSize()]
// 加密
ciphertext := gcm.Seal(nil, nonce, []byte(plaintext), nil)
// 将nonce和密文连接起来
result := append(nonce, ciphertext...)
// 编码为base64
return base64.StdEncoding.EncodeToString(result), nil
}
// Decrypt 解密字符串
// 可以使用加密字符串和key在不同时间、不同机器上解密出原始plaintext
func Decrypt(encrypted string, key string) (decryptedPlaintext string, decryptError error) {
// 解码base64
data, err := base64.StdEncoding.DecodeString(encrypted)
if err != nil {
return "", err
}
// 创建hash
hasher := sha256.New()
hasher.Write([]byte(key))
keyBytes := hasher.Sum(nil)
// 创建cipher
block, err := aes.NewCipher(keyBytes)
if err != nil {
return "", err
}
// 创建gcm
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
// 检查长度
if len(data) < gcm.NonceSize() {
return "", errors.New("密文太短")
}
// 分离nonce和密文
nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():]
// 解密
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}
// min 返回两个整数中较小的一个
func min(a, b int) int {
if a < b {
return a
}
return b
}
// EncryptHostInfo 加密主机信息
func EncryptHostInfo(hostInfo models.HostInfo, key string) (encryptedHostInfo string, encryptError error) {
// 生成主机唯一标识作为加密密钥
info := generateHostUniqueID(hostInfo)
// 加密主机信息
encrypted, err := Encrypt(info, key)
if err != nil {
return "", err
}
return encrypted, nil
}
// DecryptHostInfo 解密主机信息
func DecryptHostInfo(encrypted string, hostInfo models.HostInfo, key string) (decryptedOK bool, decryptError error) {
// 解密主机信息
decryptedStr, err := Decrypt(encrypted, key)
if err != nil {
wdd_log.Error("[DecryptHostInfo] - Decrypt失败: %v", err)
return false, err
}
// wdd_log.Info("[DecryptHostInfo] - Decrypt成功: %s", decryptedStr)
// 生成主机唯一标识作为加密密钥
hostUniqueID := generateHostUniqueID(hostInfo)
if hostUniqueID != decryptedStr {
wdd_log.Error("[DecryptHostInfo] - 主机信息篡改")
return false, errors.New("主机信息篡改")
}
return true, nil
}
// generateHostUniqueID 生成主机唯一标识作为加密密钥
func generateHostUniqueID(hostInfo models.HostInfo) string {
// hostinfo.SystemInfo.MachineID
machineID := hostInfo.SystemInfo.MachineID
// hostinfo.SystemInfo.KernelVersion
kernelVersion := hostInfo.SystemInfo.KernelVersion
// 将CPU信息中的ModelName Cores Hypervisor 拼接起来
cpuCores := hostInfo.CPUInfo.Cores
cpuHypervisor := hostInfo.CPUInfo.Hypervisor
cpuModelName := hostInfo.CPUInfo.ModelName
cpuInfo := fmt.Sprintf("%s-%d-%s", cpuModelName, cpuCores, cpuHypervisor)
// 不能使用 mac 地址,因为 每个Pod 的 mac 地址会变化
// hostinfo.SystemInfo.MachineSerial
machineSerial := hostInfo.SystemInfo.MachineSerial
// 将memory中的total 和 used 拼接起来
memoryTotal := hostInfo.MemoryInfo.Total
// 将上述的全部信息拼接起来
info := strings.Join([]string{machineID, kernelVersion, cpuInfo, machineSerial, strconv.FormatUint(memoryTotal, 10)}, "==")
return info
}

View File

@@ -0,0 +1,218 @@
package totp_tier_one
import (
"cmii-uav-watchdog-common/models"
"fmt"
"testing"
)
// TestEncryptDecrypt 测试加密和解密功能
func TestEncryptDecrypt(t *testing.T) {
tests := []struct {
name string
plaintext string
key string
}{
{
name: "正常文本加密解密",
plaintext: "这是一段测试文本",
key: "测试密钥",
},
{
name: "空文本加密解密",
plaintext: "",
key: "测试密钥",
},
{
name: "特殊字符加密解密",
plaintext: "!@#$%^&*()_+{}|:<>?",
key: "!@#$%^&*()_+{}|:<>?",
},
{
name: "长文本加密解密",
plaintext: "这是一段非常长的测试文本,用于测试加密和解密功能是否能够正确处理长文本。这是一段非常长的测试文本,用于测试加密和解密功能是否能够正确处理长文本。",
key: "测试密钥",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 加密
encrypted, err := Encrypt(tt.plaintext, tt.key)
if err != nil {
t.Fatalf("加密失败: %v", err)
}
// 确保加密后的文本与原始文本不同
if tt.plaintext != "" && encrypted == tt.plaintext {
t.Errorf("加密后的文本与原始文本相同,可能未正确加密")
}
// 解密
decrypted, err := Decrypt(encrypted, tt.key)
if err != nil {
t.Fatalf("解密失败: %v", err)
}
// 确保解密后的文本与原始文本相同
if decrypted != tt.plaintext {
t.Errorf("解密后的文本与原始文本不同,期望:%s实际%s", tt.plaintext, decrypted)
}
})
}
}
// TestDecryptWithWrongKey 测试使用错误密钥解密
func TestDecryptWithWrongKey(t *testing.T) {
plaintext := "这是一段测试文本"
correctKey := "正确密钥"
wrongKey := "错误密钥"
// 使用正确密钥加密
encrypted, err := Encrypt(plaintext, correctKey)
if err != nil {
t.Fatalf("加密失败: %v", err)
}
// 使用错误密钥解密,期望失败
_, err = Decrypt(encrypted, wrongKey)
fmt.Println("解密失败的 error :", err)
if err == nil {
t.Errorf("使用错误密钥解密应该失败,但成功了")
}
}
// TestDecryptInvalidData 测试解密无效的数据
func TestDecryptInvalidData(t *testing.T) {
invalidEncrypted := "这不是有效的加密数据"
key := "测试密钥"
_, err := Decrypt(invalidEncrypted, key)
if err == nil {
t.Errorf("解密无效数据应该失败,但成功了")
}
}
// createMockHostInfo 创建模拟的主机信息用于测试
func createMockHostInfo() models.HostInfo {
return models.HostInfo{
SystemInfo: models.SystemInfo{
MachineID: "test-machine-id",
KernelVersion: "5.10.0-test",
OS: models.OSInfo{
Name: "TestOS",
Version: "1.0",
ID: "test",
IDLike: "test",
VersionID: "1.0",
PrettyName: "Test OS 1.0",
},
},
CPUInfo: models.CPUInfo{
ModelName: "Test CPU",
Cores: 4,
Architecture: "x86_64",
Flags: []string{},
Hypervisor: "",
Virtualization: "",
Frequency: "",
},
DiskInfo: []models.DiskInfo{
{
Device: "/dev/sda1",
Filesystem: "ext4",
Type: "ext4",
Size: 1000000,
Used: 500000,
Available: 500000,
UsePercent: "50%",
MountPoint: "/",
PhysicalDevice: "/dev/sda",
PhysicalSize: 2000000,
},
},
MemoryInfo: models.MemoryInfo{
Total: 8000000,
Free: 4000000,
Available: 4000000,
Used: 4000000,
Buffers: 1000000,
Cached: 1000000,
Shared: 500000,
},
NetInfo: []models.NetworkInterfaceInfo{
{
Name: "eth0",
MACAddress: "00:11:22:33:44:55",
IPAddresses: []string{"192.168.1.100", "fe80::1234"},
},
},
MotherboardInfo: models.MotherboardInfo{
Manufacturer: "Test Manufacturer",
Product: "Test Product",
Version: "1.0",
Serial: "TEST123456",
},
}
}
// TestEncryptDecryptHostInfo 测试主机信息的加密和解密
func TestEncryptDecryptHostInfo(t *testing.T) {
hostInfo := createMockHostInfo()
key := "test-key"
// 加密主机信息
encrypted, err := EncryptHostInfo(hostInfo, key)
fmt.Println("加密后的主机信息:", encrypted)
if err != nil {
t.Fatalf("加密主机信息失败: %v", err)
}
// 确保加密后的文本不为空
if encrypted == "" {
t.Errorf("加密后的主机信息为空")
}
// 解密主机信息
decrypted, err := DecryptHostInfo(encrypted, hostInfo, key)
if err != nil {
t.Fatalf("解密主机信息失败: %v", err)
}
if !decrypted {
t.Errorf("解密后的主机信息与原始主机信息不同")
}
}
// TestEncryptDecryptHostInfoWithModifiedInfo 测试使用修改过的主机信息解密
func TestEncryptDecryptHostInfoWithModifiedInfo(t *testing.T) {
originalHostInfo := createMockHostInfo()
key := "test-key"
// 加密主机信息
encrypted, err := EncryptHostInfo(originalHostInfo, key)
if err != nil {
t.Fatalf("加密主机信息失败: %v", err)
}
t.Logf("加密后的主机信息: %s", encrypted)
// 创建修改过的主机信息
modifiedHostInfo := originalHostInfo
// 加密修改过的主机信息
encryptedModified, err := EncryptHostInfo(modifiedHostInfo, key)
if err != nil {
t.Fatalf("加密修改过的主机信息失败: %v", err)
}
t.Logf("加密修改后的主机信息: %s", encryptedModified)
// 使用修改过的主机信息尝试解密,期望失败
isOK, err := DecryptHostInfo(encryptedModified, originalHostInfo, key)
fmt.Println("解密后的主机信息 error :", err)
if isOK {
t.Errorf("解密后的主机信息与修改后的主机信息相同, 不应该相同")
}
}

View File

@@ -0,0 +1,39 @@
package totp_tier_one
import (
otp "cmii-uav-watchdog-otp"
"cmii-uav-watchdog-otp/totp"
"time"
)
var TierOneTOTPSecretOpts = totp.GenerateOpts{
SecretSize: 64,
Issuer: "cmii-uav-watchdog-center",
AccountName: "cmii-uav-watchdog-center",
Period: 30 * 2 * 30, // 30分钟
Digits: otp.DigitsEight,
Algorithm: otp.AlgorithmSHA256,
}
// GenerateTierOneTOTPCode 生成一级TOTP验证码
func GenerateTierOneTOTPCode(secret string) (string, error) {
validateOpts := totp.ValidateOpts{}
validateOpts.ConvertToValidateOpts(TierOneTOTPSecretOpts)
code, err := totp.GenerateCodeCustom(secret, time.Now(), validateOpts)
if err != nil {
return "", err
}
return code, nil
}
// VerifyTierOneTOTPCode 验证一级TOTP验证码
func VerifyTierOneTOTPCode(code string, secret string) bool {
validateOpts := totp.ValidateOpts{}
validateOpts.ConvertToValidateOpts(TierOneTOTPSecretOpts)
valid, err := totp.ValidateCustom(code, secret, time.Now(), validateOpts)
if err != nil {
return false
}
return valid
}