129 lines
3.8 KiB
Go
129 lines
3.8 KiB
Go
|
|
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)
|
|||
|
|
}
|
|||
|
|
}
|