1 package ascon_test
2
3 import (
4 "bytes"
5 "crypto/cipher"
6 hexa "encoding/hex"
7 "encoding/json"
8 "io"
9 "os"
10 "strconv"
11 "sync"
12 "testing"
13
14 "github.com/cloudflare/circl/cipher/ascon"
15 "github.com/cloudflare/circl/internal/test"
16 )
17
18 type vector struct {
19 Count int `json:"Count"`
20 Key hex `json:"Key"`
21 Nonce hex `json:"Nonce"`
22 PT hex `json:"PT"`
23 AD hex `json:"AD"`
24 CT hex `json:"CT"`
25 }
26
27 type hex []byte
28
29 func (h *hex) UnmarshalJSON(data []byte) error {
30 var s string
31 if err := json.Unmarshal(data, &s); err != nil {
32 return err
33 }
34 decoded, err := hexa.DecodeString(s)
35 if err != nil {
36 return err
37 }
38 *h = hex(decoded)
39 return nil
40 }
41
42 func readFile(t *testing.T, fileName string) []vector {
43 jsonFile, err := os.Open(fileName)
44 if err != nil {
45 t.Fatalf("File %v can not be opened. Error: %v", fileName, err)
46 }
47 defer jsonFile.Close()
48 input, err := io.ReadAll(jsonFile)
49 if err != nil {
50 t.Fatalf("File %v can not be read. Error: %v", fileName, err)
51 }
52 var v []vector
53 err = json.Unmarshal(input, &v)
54 if err != nil {
55 t.Fatalf("File %v can not be loaded. Error: %v", fileName, err)
56 }
57 return v
58 }
59
60 func TestAscon(t *testing.T) {
61
62
63 for _, mode := range []ascon.Mode{ascon.Ascon128, ascon.Ascon128a, ascon.Ascon80pq} {
64 name := mode.String()
65 t.Run(name, func(t *testing.T) {
66 vectors := readFile(t, "testdata/"+name+".json")
67 for _, v := range vectors {
68 a, err := ascon.New(v.Key, mode)
69 test.CheckNoErr(t, err, "failed to create cipher")
70
71 var aead cipher.AEAD = a
72 test.CheckOk(len(v.Nonce) == aead.NonceSize(), "bad nonce size", t)
73 got := aead.Seal(nil, v.Nonce, v.PT, v.AD)
74 want := v.CT
75 if !bytes.Equal(got, want) {
76 test.ReportError(t, got, want, name, v.Count)
77 }
78
79 got, err = aead.Open(nil, v.Nonce, v.CT, v.AD)
80 if err != nil {
81 t.Fatal(err)
82 }
83 want = v.PT
84 if !bytes.Equal(got, want) {
85 test.ReportError(t, got, want, name, v.Count)
86 }
87 test.CheckOk(len(v.PT)+aead.Overhead() == len(v.CT), "bad overhead size", t)
88 }
89 })
90 }
91 }
92
93 func TestBadInputs(t *testing.T) {
94 var key [ascon.KeySize]byte
95 var m ascon.Mode = 0
96
97 _, err := ascon.New(key[:], m)
98 test.CheckIsErr(t, err, "should fail due to bad mode")
99
100 err = test.CheckPanic(func() { _ = m.String() })
101 test.CheckNoErr(t, err, "should panic due to bad mode")
102
103 _, err = ascon.New(nil, ascon.Ascon128)
104 test.CheckIsErr(t, err, "should fail due to nil key")
105
106 _, err = ascon.New(key[:4], ascon.Ascon128)
107 test.CheckIsErr(t, err, "should fail due to short key")
108
109 _, err = ascon.New(key[:], ascon.Ascon80pq)
110 test.CheckIsErr(t, err, "should fail due to short key")
111
112 a, _ := ascon.New(key[:], ascon.Ascon128)
113 err = test.CheckPanic(func() { _ = a.Seal(nil, nil, nil, nil) })
114 test.CheckNoErr(t, err, "should panic due to bad nonce")
115
116 err = test.CheckPanic(func() { _, _ = a.Open(nil, nil, nil, nil) })
117 test.CheckNoErr(t, err, "should panic due to bad nonce")
118
119 var nonce [ascon.NonceSize]byte
120 _ = a.Seal(nil, nonce[:], nil, nil)
121 _, err = a.Open(nil, nonce[:], nil, nil)
122 test.CheckIsErr(t, err, "should panic due to empty ciphertext")
123
124 pt := []byte("")
125 ct := a.Seal(nil, nonce[:], pt, nil)
126 ct[0] ^= 0xFF
127 _, err = a.Open(nil, nonce[:], ct, nil)
128 test.CheckIsErr(t, err, "should panic due to bad ciphertext")
129 }
130
131 func TestAPI(t *testing.T) {
132 key, _ := hexa.DecodeString("000102030405060708090A0B0C0D0E0F")
133 nonce, _ := hexa.DecodeString("000102030405060708090A0B0C0D0E0F")
134 c, _ := ascon.New(key, ascon.Ascon128)
135 pt := []byte("helloworld")
136 ct, _ := hexa.DecodeString("d4e663d29cd60a693c20f890982e167d266f940b93b586945065")
137
138 t.Run("append", func(t *testing.T) {
139 prefix := [5]byte{0x1F, 0x2F, 0x3F, 0x4F, 0x5F}
140 prefixAndCt := c.Seal(prefix[:], nonce, pt, nil)
141 got := prefixAndCt
142 want := append(append([]byte{}, prefix[:]...), ct...)
143 if !bytes.Equal(got, want) {
144 test.ReportError(t, got, want)
145 }
146
147 ciphertext := prefixAndCt[len(prefix):]
148 prefix = [5]byte{0x11, 0x22, 0x33, 0x44, 0x55}
149 prefixAndPt, err := c.Open(prefix[:], nonce, ciphertext, nil)
150 if err != nil {
151 t.Fatal(err)
152 }
153 got = prefixAndPt
154 want = append(append([]byte{}, prefix[:]...), pt...)
155 if !bytes.Equal(got, want) {
156 test.ReportError(t, got, want)
157 }
158 })
159 t.Run("reuse", func(t *testing.T) {
160 ptWithCap := make([]byte, len(pt), len(pt)+100)
161 copy(ptWithCap, pt)
162
163 ciphertext := c.Seal(ptWithCap[:0], nonce, ptWithCap, nil)
164 got := ciphertext
165 want := ct
166 if !bytes.Equal(got, want) {
167 test.ReportError(t, got, want)
168 }
169 test.CheckOk(&ptWithCap[0] == &ciphertext[0], "should have same address", t)
170
171 ctWithCap := make([]byte, len(ct), len(ct)+100)
172 copy(ctWithCap, ct)
173
174 plaintext, err := c.Open(ctWithCap[:0], nonce, ctWithCap, nil)
175 if err != nil {
176 t.Fatal(err)
177 }
178 got = plaintext
179 want = pt
180 if !bytes.Equal(got, want) {
181 test.ReportError(t, got, want)
182 }
183 test.CheckOk(&ctWithCap[0] == &plaintext[0], "should have same address", t)
184 })
185 t.Run("parallel", func(t *testing.T) {
186 var wg sync.WaitGroup
187 for i := 0; i < 1_000; i++ {
188 wg.Add(1)
189 go func() {
190 defer wg.Done()
191 ciphertext := c.Seal(nil, nonce, pt, nil)
192 plaintext, err := c.Open(nil, nonce, ciphertext, nil)
193 if err != nil {
194 t.Error(err)
195 }
196 got := plaintext
197 want := pt
198 if !bytes.Equal(got, want) {
199 test.ReportError(t, got, want)
200 }
201 }()
202 }
203 wg.Wait()
204 })
205 }
206
207 func BenchmarkAscon(b *testing.B) {
208 for _, mode := range []ascon.Mode{ascon.Ascon128, ascon.Ascon128a, ascon.Ascon80pq} {
209 for _, length := range []int{64, 1350, 8 * 1024} {
210 b.Run(mode.String()+"/Open-"+strconv.Itoa(length), func(b *testing.B) { benchmarkOpen(b, make([]byte, length), mode) })
211 b.Run(mode.String()+"/Seal-"+strconv.Itoa(length), func(b *testing.B) { benchmarkSeal(b, make([]byte, length), mode) })
212 }
213 }
214 }
215
216 func benchmarkSeal(b *testing.B, buf []byte, mode ascon.Mode) {
217 b.ReportAllocs()
218 b.SetBytes(int64(len(buf)))
219
220 var key []byte
221 switch mode {
222 case ascon.Ascon128, ascon.Ascon128a:
223 key = make([]byte, ascon.KeySize)
224 case ascon.Ascon80pq:
225 key = make([]byte, ascon.KeySize80pq)
226 }
227
228 var nonce [ascon.NonceSize]byte
229 var ad [13]byte
230 a, err := ascon.New(key[:], mode)
231 if err != nil {
232 b.Fatal(err)
233 }
234 var out []byte
235
236 b.ResetTimer()
237 for i := 0; i < b.N; i++ {
238 out = a.Seal(out[:0], nonce[:], buf, ad[:])
239 }
240 }
241
242 func benchmarkOpen(b *testing.B, buf []byte, mode ascon.Mode) {
243 b.ReportAllocs()
244 b.SetBytes(int64(len(buf)))
245
246 var key []byte
247 switch mode {
248 case ascon.Ascon128, ascon.Ascon128a:
249 key = make([]byte, ascon.KeySize)
250 case ascon.Ascon80pq:
251 key = make([]byte, ascon.KeySize80pq)
252 }
253
254 var nonce [ascon.NonceSize]byte
255 var ad [13]byte
256 a, err := ascon.New(key[:], mode)
257 if err != nil {
258 b.Fatal(err)
259 }
260 var out []byte
261
262 ct := a.Seal(nil, nonce[:], buf, ad[:])
263
264 b.ResetTimer()
265 for i := 0; i < b.N; i++ {
266 out, _ = a.Open(out[:0], nonce[:], ct, ad[:])
267 }
268 }
269
View as plain text