axe/internal/crypt/cipher_test.go
2026-10-18 22:07:25 +03:00

129 lines
3.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package crypt
import (
"encoding/base64"
"encoding/hex"
"errors"
"strings"
"testing"
)
// testKey — 32 байта в base64 (та же строка ниже и в hex).
var (
testKeyB64 = base64.StdEncoding.EncodeToString(make([]byte, 32))
testKeyHex = hex.EncodeToString(make([]byte, 32))
)
// TestNewKeyFormat — принимаются base64 и hex, отклоняется прочее:
// пустой ключ, короткий, мусор и неверный размер.
func TestNewKeyFormat(t *testing.T) {
for _, key := range []string{testKeyB64, testKeyHex, strings.TrimRight(testKeyB64, "=")} {
if _, err := New(key); err != nil {
t.Errorf("New(%q): %v", key[:12], err)
}
}
for _, key := range []string{
"",
"short",
base64.StdEncoding.EncodeToString(make([]byte, 16)), // 16 байт — AES-128, не наш формат
strings.Repeat("z", 44), // base64 от 33 байт
} {
if _, err := New(key); !errors.Is(err, ErrKeySize) {
t.Errorf("New(%q): err = %v, want ErrKeySize", key, err)
}
}
}
// TestSealOpen — основной путь: шифрование обратимо, ciphertext не
// содержит исходный текст и меняется от прогона к прогону (nonce).
func TestSealOpen(t *testing.T) {
c, err := New(testKeyB64)
if err != nil {
t.Fatalf("New: %v", err)
}
const secret = "123456789:AAFakeBotTokenForTests0123456789"
enc, err := c.Seal(secret)
if err != nil {
t.Fatalf("Seal: %v", err)
}
if strings.Contains(enc, secret) || strings.Contains(enc, "AAFake") {
t.Errorf("ciphertext leaks plaintext: %q", enc)
}
got, err := c.Open(enc)
if err != nil {
t.Fatalf("Open: %v", err)
}
if got != secret {
t.Errorf("roundtrip = %q, want %q", got, secret)
}
// Та же строка шифруется заново: nonce другой, результат другой —
// иначе два пользователя с одинаковым токеном дали бы одинаковый blob.
again, err := c.Seal(secret)
if err != nil {
t.Fatalf("Seal again: %v", err)
}
if again == enc {
t.Error("two seals of the same value are equal (nonce reuse?)")
}
}
// TestOpenRejects — битые и чужие данные: обрезанный результат, битый
// base64, подделанный байт (GCM поймает), другой ключ.
func TestOpenRejects(t *testing.T) {
c, err := New(testKeyB64)
if err != nil {
t.Fatalf("New: %v", err)
}
other, err := New(hex.EncodeToString(append(make([]byte, 31), 1)))
if err != nil {
t.Fatalf("New other: %v", err)
}
enc, err := c.Seal("topsecret")
if err != nil {
t.Fatalf("Seal: %v", err)
}
raw, err := base64.StdEncoding.DecodeString(enc)
if err != nil {
t.Fatalf("decode: %v", err)
}
raw[len(raw)-1] ^= 0xFF // битый тэг GCM
tampered := base64.StdEncoding.EncodeToString(raw)
for name, tc := range map[string]struct {
c *Cipher
in string
}{
"not base64": {c, "не base64"},
"too short": {c, base64.StdEncoding.EncodeToString([]byte("abc"))},
"tampered": {c, tampered},
"another key": {other, enc},
"empty": {c, ""},
} {
t.Run(name, func(t *testing.T) {
if _, err := tc.c.Open(tc.in); !errors.Is(err, ErrCorrupt) {
t.Errorf("err = %v, want ErrCorrupt", err)
}
})
}
}
// TestSealEmpty — пустая строка шифруется и расшифровывается (нужно,
// когда пустое значение хранят сознательно).
func TestSealEmpty(t *testing.T) {
c, err := New(testKeyHex)
if err != nil {
t.Fatalf("New: %v", err)
}
enc, err := c.Seal("")
if err != nil {
t.Fatalf("Seal: %v", err)
}
got, err := c.Open(enc)
if err != nil || got != "" {
t.Errorf("got %q, err %v, want empty", got, err)
}
}