Files
2026-03-27 17:55:57 +08:00

172 lines
4.4 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package security
import (
"bytes"
"testing"
)
func TestEncryptDecryptRoundTrip(t *testing.T) {
t.Parallel()
password := []byte("aa:bb:cc:dd:ee:ff")
plain := []byte(`{"access_token":"x"}`)
encrypted, err := Encrypt(plain, password)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
if len(encrypted) < SaltSize+NonceSize+16 {
t.Fatalf("ciphertext too short: %d bytes", len(encrypted))
}
decrypted, err := Decrypt(encrypted, password)
if err != nil {
t.Fatalf("Decrypt() error = %v", err)
}
if !bytes.Equal(decrypted, plain) {
t.Fatalf("round-trip: got %q want %q", decrypted, plain)
}
}
func TestDecryptWithWrongPassword(t *testing.T) {
t.Parallel()
password := []byte("aa:bb:cc:dd:ee:ff")
plain := []byte(`{"access_token":"x"}`)
encrypted, err := Encrypt(plain, password)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
_, err = Decrypt(encrypted, []byte("wrong-password"))
if err == nil {
t.Fatal("Decrypt with wrong password should fail")
}
}
func TestDecryptRejectsShortData(t *testing.T) {
t.Parallel()
_, err := Decrypt([]byte{0, 1, 2, 3}, []byte("password"))
if err == nil {
t.Fatal("Decrypt should fail with data shorter than salt+nonce+tag")
}
}
func TestDecryptRejectsTamperedCiphertext(t *testing.T) {
t.Parallel()
password := []byte("test-password-123456")
encrypted, err := Encrypt([]byte("secret data"), password)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
tampered := make([]byte, len(encrypted))
copy(tampered, encrypted)
tampered[len(tampered)-1] ^= 0xFF
_, err = Decrypt(tampered, password)
if err == nil {
t.Fatal("Decrypt should fail with tampered ciphertext")
}
}
func TestEncryptProducesDifferentCiphertexts(t *testing.T) {
t.Parallel()
password := []byte("deterministic-password")
plaintext := []byte("same plaintext")
enc1, err := Encrypt(plaintext, password)
if err != nil {
t.Fatalf("first Encrypt() error = %v", err)
}
enc2, err := Encrypt(plaintext, password)
if err != nil {
t.Fatalf("second Encrypt() error = %v", err)
}
if bytes.Equal(enc1, enc2) {
t.Fatal("two encryptions of same plaintext should differ (random salt/nonce)")
}
// Both should decrypt to the same plaintext.
dec1, _ := Decrypt(enc1, password)
dec2, _ := Decrypt(enc2, password)
if !bytes.Equal(dec1, dec2) || !bytes.Equal(dec1, plaintext) {
t.Fatal("both ciphertexts should decrypt to the same plaintext")
}
}
func TestGetMACAddress(t *testing.T) {
mac, err := GetMACAddress()
if err != nil {
t.Skipf("no suitable MAC in this environment: %v", err)
}
// MAC should be at least "xx:xx:xx:xx:xx:xx" (17 chars) or fallback format.
if len(mac) < 17 {
t.Fatalf("unexpected MAC: %q", mac)
}
}
func TestSecureTokenStorageRoundTrip(t *testing.T) {
configDir := t.TempDir()
mac := "aa:bb:cc:dd:ee:ff"
storage := NewSecureTokenStorage(configDir, "", mac)
if storage.Exists() {
t.Fatal("storage should not exist yet")
}
data := &TokenData{
AccessToken: "at_test",
RefreshToken: "rt_test",
CorpID: "dingcorp",
}
if err := storage.SaveToken(data); err != nil {
t.Fatalf("SaveToken() error = %v", err)
}
if !storage.Exists() {
t.Fatal("storage should exist after save")
}
loaded, err := storage.LoadToken()
if err != nil {
t.Fatalf("LoadToken() error = %v", err)
}
if loaded.AccessToken != data.AccessToken {
t.Fatalf("access_token = %q, want %q", loaded.AccessToken, data.AccessToken)
}
if loaded.CorpID != data.CorpID {
t.Fatalf("corp_id = %q, want %q", loaded.CorpID, data.CorpID)
}
// Wrong MAC should fail.
wrongStorage := NewSecureTokenStorage(configDir, "", "11:22:33:44:55:66")
if _, err := wrongStorage.LoadToken(); err == nil {
t.Fatal("LoadToken with wrong MAC should fail")
}
if err := storage.DeleteToken(); err != nil {
t.Fatalf("DeleteToken() error = %v", err)
}
if storage.Exists() {
t.Fatal("storage should not exist after delete")
}
}