Source file src/crypto/internal/fips140test/xaes_test.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fipstest
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/internal/cryptotest"
    10  	"crypto/internal/fips140/aes"
    11  	"crypto/internal/fips140/aes/gcm"
    12  	"crypto/internal/fips140/drbg"
    13  	"crypto/internal/fips140/sha3"
    14  	"encoding/hex"
    15  	"runtime"
    16  	"testing"
    17  )
    18  
    19  func TestXAESAllocations(t *testing.T) {
    20  	if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
    21  		t.Skip("Test reports non-zero allocation count. See issue #70448")
    22  	}
    23  	cryptotest.SkipTestAllocations(t)
    24  	if allocs := testing.AllocsPerRun(10, func() {
    25  		key := make([]byte, 32)
    26  		nonce := make([]byte, 24)
    27  		plaintext := make([]byte, 16)
    28  		aad := make([]byte, 16)
    29  		ciphertext := make([]byte, 0, 16+16)
    30  		ciphertext = xaesSeal(ciphertext, key, nonce, plaintext, aad)
    31  		if _, err := xaesOpen(plaintext[:0], key, nonce, ciphertext, aad); err != nil {
    32  			t.Fatal(err)
    33  		}
    34  	}); allocs > 0 {
    35  		t.Errorf("expected zero allocations, got %0.1f", allocs)
    36  	}
    37  }
    38  
    39  func TestXAES(t *testing.T) {
    40  	key := bytes.Repeat([]byte{0x01}, 32)
    41  	plaintext := []byte("XAES-256-GCM")
    42  	additionalData := []byte("c2sp.org/XAES-256-GCM")
    43  
    44  	nonce := make([]byte, 24)
    45  	ciphertext := make([]byte, len(plaintext)+16)
    46  
    47  	drbg.Read(nonce[:12])
    48  	c, _ := aes.New(key)
    49  	k := gcm.NewCounterKDF(c).DeriveKey(0x58, [12]byte(nonce))
    50  	a, _ := aes.New(k[:])
    51  	g, _ := gcm.New(a, 12, 16)
    52  	gcm.SealWithRandomNonce(g, nonce[12:], ciphertext, plaintext, additionalData)
    53  
    54  	got, err := xaesOpen(nil, key, nonce, ciphertext, additionalData)
    55  	if err != nil {
    56  		t.Fatal(err)
    57  	}
    58  	if !bytes.Equal(plaintext, got) {
    59  		t.Errorf("plaintext and got are not equal")
    60  	}
    61  }
    62  
    63  // ACVP tests consider fixed data part of the output, not part of the input, and
    64  // all the pre-generated vectors at
    65  // https://github.com/usnistgov/ACVP-Server/blob/3a7333f6/gen-val/json-files/KDF-1.0/expectedResults.json
    66  // have a 32-byte fixed data, while ours is always 14 bytes. Instead, test
    67  // against the XAES-256-GCM vectors, which were tested against OpenSSL's Counter
    68  // KDF. This also ensures the KDF will work for XAES-256-GCM.
    69  
    70  func xaesSeal(dst, key, nonce, plaintext, additionalData []byte) []byte {
    71  	c, _ := aes.New(key)
    72  	k := gcm.NewCounterKDF(c).DeriveKey(0x58, [12]byte(nonce))
    73  	n := nonce[12:]
    74  	a, _ := aes.New(k[:])
    75  	g, _ := gcm.New(a, 12, 16)
    76  	return g.Seal(dst, n, plaintext, additionalData)
    77  }
    78  
    79  func xaesOpen(dst, key, nonce, ciphertext, additionalData []byte) ([]byte, error) {
    80  	c, _ := aes.New(key)
    81  	k := gcm.NewCounterKDF(c).DeriveKey(0x58, [12]byte(nonce))
    82  	n := nonce[12:]
    83  	a, _ := aes.New(k[:])
    84  	g, _ := gcm.New(a, 12, 16)
    85  	return g.Open(dst, n, ciphertext, additionalData)
    86  }
    87  
    88  func TestXAESVectors(t *testing.T) {
    89  	key := bytes.Repeat([]byte{0x01}, 32)
    90  	nonce := []byte("ABCDEFGHIJKLMNOPQRSTUVWX")
    91  	plaintext := []byte("XAES-256-GCM")
    92  	ciphertext := xaesSeal(nil, key, nonce, plaintext, nil)
    93  	expected := "ce546ef63c9cc60765923609b33a9a1974e96e52daf2fcf7075e2271"
    94  	if got := hex.EncodeToString(ciphertext); got != expected {
    95  		t.Errorf("got: %s", got)
    96  	}
    97  	if decrypted, err := xaesOpen(nil, key, nonce, ciphertext, nil); err != nil {
    98  		t.Fatal(err)
    99  	} else if !bytes.Equal(plaintext, decrypted) {
   100  		t.Errorf("plaintext and decrypted are not equal")
   101  	}
   102  
   103  	key = bytes.Repeat([]byte{0x03}, 32)
   104  	aad := []byte("c2sp.org/XAES-256-GCM")
   105  	ciphertext = xaesSeal(nil, key, nonce, plaintext, aad)
   106  	expected = "986ec1832593df5443a179437fd083bf3fdb41abd740a21f71eb769d"
   107  	if got := hex.EncodeToString(ciphertext); got != expected {
   108  		t.Errorf("got: %s", got)
   109  	}
   110  	if decrypted, err := xaesOpen(nil, key, nonce, ciphertext, aad); err != nil {
   111  		t.Fatal(err)
   112  	} else if !bytes.Equal(plaintext, decrypted) {
   113  		t.Errorf("plaintext and decrypted are not equal")
   114  	}
   115  }
   116  
   117  func TestXAESAccumulated(t *testing.T) {
   118  	iterations := 10_000
   119  	expected := "e6b9edf2df6cec60c8cbd864e2211b597fb69a529160cd040d56c0c210081939"
   120  
   121  	s, d := sha3.NewShake128(), sha3.NewShake128()
   122  	for i := 0; i < iterations; i++ {
   123  		key := make([]byte, 32)
   124  		s.Read(key)
   125  		nonce := make([]byte, 24)
   126  		s.Read(nonce)
   127  		lenByte := make([]byte, 1)
   128  		s.Read(lenByte)
   129  		plaintext := make([]byte, int(lenByte[0]))
   130  		s.Read(plaintext)
   131  		s.Read(lenByte)
   132  		aad := make([]byte, int(lenByte[0]))
   133  		s.Read(aad)
   134  
   135  		ciphertext := xaesSeal(nil, key, nonce, plaintext, aad)
   136  		decrypted, err := xaesOpen(nil, key, nonce, ciphertext, aad)
   137  		if err != nil {
   138  			t.Fatal(err)
   139  		}
   140  		if !bytes.Equal(plaintext, decrypted) {
   141  			t.Errorf("plaintext and decrypted are not equal")
   142  		}
   143  
   144  		d.Write(ciphertext)
   145  	}
   146  	if got := hex.EncodeToString(d.Sum(nil)); got != expected {
   147  		t.Errorf("got: %s", got)
   148  	}
   149  }
   150  

View as plain text