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  }