github.com/cloudflare/circl@v1.5.0/cipher/ascon/ascon_test.go (about) 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 // Test vectors generated with pyascon 62 // https://github.com/meichlseder/pyascon/ 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 // tamper ciphertext 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 // reusing the input to store the ciphertext. 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 // reusing the input to store the plaintext. 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 }