ctr_test.go raw
1 package crypto
2
3 import (
4 "bytes"
5 "testing"
6 )
7
8 func TestCTRFromSecretRoundTrip(t *testing.T) {
9 secret := Hash([]byte("test-secret-key"))
10 nonce := []byte("test-nonce-001")
11 plaintext := []byte("Hello, hamadryad CTR encryption!")
12
13 stream := NewCTRStreamFromSecret(secret, nonce)
14 ciphertext := stream.Encrypt(plaintext)
15
16 if len(ciphertext) != len(plaintext) {
17 t.Errorf("ciphertext length = %d, want %d", len(ciphertext), len(plaintext))
18 }
19 if bytes.Equal(ciphertext, plaintext) {
20 t.Error("ciphertext should differ from plaintext")
21 }
22
23 stream2 := NewCTRStreamFromSecret(secret, nonce)
24 recovered := stream2.Decrypt(ciphertext)
25 if !bytes.Equal(recovered, plaintext) {
26 t.Errorf("recovered = %q, want %q", recovered, plaintext)
27 }
28 }
29
30 func TestCTRDeterministic(t *testing.T) {
31 secret := Hash([]byte("determinism-key"))
32 nonce := []byte("determinism-nonce")
33 plaintext := []byte("same message twice")
34
35 s1 := NewCTRStreamFromSecret(secret, nonce)
36 s2 := NewCTRStreamFromSecret(secret, nonce)
37
38 ct1 := s1.Encrypt(plaintext)
39 ct2 := s2.Encrypt(plaintext)
40 if !bytes.Equal(ct1, ct2) {
41 t.Error("CTR encryption should be deterministic with same key+nonce")
42 }
43 }
44
45 func TestCTRDifferentNonces(t *testing.T) {
46 secret := Hash([]byte("nonce-test-key"))
47 plaintext := []byte("same plaintext")
48
49 s1 := NewCTRStreamFromSecret(secret, []byte("nonce-1"))
50 s2 := NewCTRStreamFromSecret(secret, []byte("nonce-2"))
51
52 ct1 := s1.Encrypt(plaintext)
53 ct2 := s2.Encrypt(plaintext)
54 if bytes.Equal(ct1, ct2) {
55 t.Error("different nonces should produce different ciphertext")
56 }
57 }
58
59 func TestCTRDifferentSecrets(t *testing.T) {
60 secret1 := Hash([]byte("secret-1"))
61 secret2 := Hash([]byte("secret-2"))
62 nonce := []byte("same-nonce")
63 plaintext := []byte("same plaintext")
64
65 s1 := NewCTRStreamFromSecret(secret1, nonce)
66 s2 := NewCTRStreamFromSecret(secret2, nonce)
67
68 ct1 := s1.Encrypt(plaintext)
69 ct2 := s2.Encrypt(plaintext)
70 if bytes.Equal(ct1, ct2) {
71 t.Error("different secrets should produce different ciphertext")
72 }
73
74 // Cross-key decryption should fail.
75 s3 := NewCTRStreamFromSecret(secret2, nonce)
76 wrong := s3.Decrypt(ct1)
77 if bytes.Equal(wrong, plaintext) {
78 t.Error("wrong secret should not decrypt correctly")
79 }
80 }
81
82 func TestCTRRandomAccess(t *testing.T) {
83 secret := Hash([]byte("random-access-key"))
84 stream := NewCTRStreamFromSecret(secret, []byte("random-access"))
85
86 block0 := stream.KeystreamBlock(0)
87 block1 := stream.KeystreamBlock(1)
88 block2 := stream.KeystreamBlock(2)
89
90 // Order-independent.
91 block2r := stream.KeystreamBlock(2)
92 block1r := stream.KeystreamBlock(1)
93 block0r := stream.KeystreamBlock(0)
94
95 if !bytes.Equal(block0, block0r) {
96 t.Error("block 0 should be same regardless of generation order")
97 }
98 if !bytes.Equal(block1, block1r) {
99 t.Error("block 1 should be same regardless of generation order")
100 }
101 if !bytes.Equal(block2, block2r) {
102 t.Error("block 2 should be same regardless of generation order")
103 }
104 if bytes.Equal(block0, block1) {
105 t.Error("block 0 and 1 should differ")
106 }
107 if bytes.Equal(block1, block2) {
108 t.Error("block 1 and 2 should differ")
109 }
110 }
111
112 func TestCTRRandomAccessEncrypt(t *testing.T) {
113 secret := Hash([]byte("seek-test-key"))
114 nonce := []byte("seek-test")
115
116 plaintext := make([]byte, BlockSize*3)
117 for i := range plaintext {
118 plaintext[i] = byte(i % 256)
119 }
120
121 stream := NewCTRStreamFromSecret(secret, nonce)
122 ciphertext := stream.Encrypt(plaintext)
123
124 stream2 := NewCTRStreamFromSecret(secret, nonce)
125 blockSlice := ciphertext[BlockSize : BlockSize*2]
126 decrypted := stream2.EncryptCTRAt(blockSlice, uint64(BlockSize))
127
128 expected := plaintext[BlockSize : BlockSize*2]
129 if !bytes.Equal(decrypted, expected) {
130 t.Error("random-access decryption should recover correct block")
131 }
132 }
133
134 func TestCTREmptyPlaintext(t *testing.T) {
135 secret := Hash([]byte("empty-test"))
136 stream := NewCTRStreamFromSecret(secret, []byte("nonce"))
137 ct := stream.Encrypt([]byte{})
138 if len(ct) != 0 {
139 t.Errorf("ciphertext of empty plaintext should be empty, got %d bytes", len(ct))
140 }
141 }
142
143 func TestCTRBlockSizeIs64(t *testing.T) {
144 if BlockSize != 64 {
145 t.Errorf("BlockSize = %d, want 64", BlockSize)
146 }
147 }
148
149 func TestCTRKeystreamNonZero(t *testing.T) {
150 secret := Hash([]byte("nonzero-key"))
151 stream := NewCTRStreamFromSecret(secret, []byte("nonce"))
152 block := stream.KeystreamBlock(0)
153
154 allZero := true
155 for _, b := range block {
156 if b != 0 {
157 allZero = false
158 break
159 }
160 }
161 if allZero {
162 t.Error("keystream block should not be all zeros")
163 }
164 }
165