...

Source file src/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_test.go

Documentation: golang.org/x/crypto/chacha20poly1305

     1  // Copyright 2016 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 chacha20poly1305
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/cipher"
    10  	cryptorand "crypto/rand"
    11  	"encoding/hex"
    12  	"fmt"
    13  	mathrand "math/rand"
    14  	"strconv"
    15  	"testing"
    16  )
    17  
    18  func TestVectors(t *testing.T) {
    19  	for i, test := range chacha20Poly1305Tests {
    20  		key, _ := hex.DecodeString(test.key)
    21  		nonce, _ := hex.DecodeString(test.nonce)
    22  		ad, _ := hex.DecodeString(test.aad)
    23  		plaintext, _ := hex.DecodeString(test.plaintext)
    24  
    25  		var (
    26  			aead cipher.AEAD
    27  			err  error
    28  		)
    29  		switch len(nonce) {
    30  		case NonceSize:
    31  			aead, err = New(key)
    32  		case NonceSizeX:
    33  			aead, err = NewX(key)
    34  		default:
    35  			t.Fatalf("#%d: wrong nonce length: %d", i, len(nonce))
    36  		}
    37  		if err != nil {
    38  			t.Fatal(err)
    39  		}
    40  
    41  		ct := aead.Seal(nil, nonce, plaintext, ad)
    42  		if ctHex := hex.EncodeToString(ct); ctHex != test.out {
    43  			t.Errorf("#%d: got %s, want %s", i, ctHex, test.out)
    44  			continue
    45  		}
    46  
    47  		plaintext2, err := aead.Open(nil, nonce, ct, ad)
    48  		if err != nil {
    49  			t.Errorf("#%d: Open failed", i)
    50  			continue
    51  		}
    52  
    53  		if !bytes.Equal(plaintext, plaintext2) {
    54  			t.Errorf("#%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
    55  			continue
    56  		}
    57  
    58  		if len(ad) > 0 {
    59  			alterAdIdx := mathrand.Intn(len(ad))
    60  			ad[alterAdIdx] ^= 0x80
    61  			if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
    62  				t.Errorf("#%d: Open was successful after altering additional data", i)
    63  			}
    64  			ad[alterAdIdx] ^= 0x80
    65  		}
    66  
    67  		alterNonceIdx := mathrand.Intn(aead.NonceSize())
    68  		nonce[alterNonceIdx] ^= 0x80
    69  		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
    70  			t.Errorf("#%d: Open was successful after altering nonce", i)
    71  		}
    72  		nonce[alterNonceIdx] ^= 0x80
    73  
    74  		alterCtIdx := mathrand.Intn(len(ct))
    75  		ct[alterCtIdx] ^= 0x80
    76  		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
    77  			t.Errorf("#%d: Open was successful after altering ciphertext", i)
    78  		}
    79  		ct[alterCtIdx] ^= 0x80
    80  	}
    81  }
    82  
    83  func TestRandom(t *testing.T) {
    84  	// Some random tests to verify Open(Seal) == Plaintext
    85  	f := func(t *testing.T, nonceSize int) {
    86  		for i := 0; i < 256; i++ {
    87  			var nonce = make([]byte, nonceSize)
    88  			var key [32]byte
    89  
    90  			al := mathrand.Intn(128)
    91  			pl := mathrand.Intn(16384)
    92  			ad := make([]byte, al)
    93  			plaintext := make([]byte, pl)
    94  			cryptorand.Read(key[:])
    95  			cryptorand.Read(nonce[:])
    96  			cryptorand.Read(ad)
    97  			cryptorand.Read(plaintext)
    98  
    99  			var (
   100  				aead cipher.AEAD
   101  				err  error
   102  			)
   103  			switch len(nonce) {
   104  			case NonceSize:
   105  				aead, err = New(key[:])
   106  			case NonceSizeX:
   107  				aead, err = NewX(key[:])
   108  			default:
   109  				t.Fatalf("#%d: wrong nonce length: %d", i, len(nonce))
   110  			}
   111  			if err != nil {
   112  				t.Fatal(err)
   113  			}
   114  
   115  			ct := aead.Seal(nil, nonce[:], plaintext, ad)
   116  
   117  			plaintext2, err := aead.Open(nil, nonce[:], ct, ad)
   118  			if err != nil {
   119  				t.Errorf("Random #%d: Open failed", i)
   120  				continue
   121  			}
   122  
   123  			if !bytes.Equal(plaintext, plaintext2) {
   124  				t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
   125  				continue
   126  			}
   127  
   128  			if len(ad) > 0 {
   129  				alterAdIdx := mathrand.Intn(len(ad))
   130  				ad[alterAdIdx] ^= 0x80
   131  				if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
   132  					t.Errorf("Random #%d: Open was successful after altering additional data", i)
   133  				}
   134  				ad[alterAdIdx] ^= 0x80
   135  			}
   136  
   137  			alterNonceIdx := mathrand.Intn(aead.NonceSize())
   138  			nonce[alterNonceIdx] ^= 0x80
   139  			if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
   140  				t.Errorf("Random #%d: Open was successful after altering nonce", i)
   141  			}
   142  			nonce[alterNonceIdx] ^= 0x80
   143  
   144  			alterCtIdx := mathrand.Intn(len(ct))
   145  			ct[alterCtIdx] ^= 0x80
   146  			if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
   147  				t.Errorf("Random #%d: Open was successful after altering ciphertext", i)
   148  			}
   149  			ct[alterCtIdx] ^= 0x80
   150  		}
   151  	}
   152  	t.Run("Standard", func(t *testing.T) { f(t, NonceSize) })
   153  	t.Run("X", func(t *testing.T) { f(t, NonceSizeX) })
   154  }
   155  
   156  func benchamarkChaCha20Poly1305Seal(b *testing.B, buf []byte, nonceSize int) {
   157  	b.ReportAllocs()
   158  	b.SetBytes(int64(len(buf)))
   159  
   160  	var key [32]byte
   161  	var nonce = make([]byte, nonceSize)
   162  	var ad [13]byte
   163  	var out []byte
   164  
   165  	var aead cipher.AEAD
   166  	switch len(nonce) {
   167  	case NonceSize:
   168  		aead, _ = New(key[:])
   169  	case NonceSizeX:
   170  		aead, _ = NewX(key[:])
   171  	}
   172  
   173  	b.ResetTimer()
   174  	for i := 0; i < b.N; i++ {
   175  		out = aead.Seal(out[:0], nonce[:], buf[:], ad[:])
   176  	}
   177  }
   178  
   179  func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte, nonceSize int) {
   180  	b.ReportAllocs()
   181  	b.SetBytes(int64(len(buf)))
   182  
   183  	var key [32]byte
   184  	var nonce = make([]byte, nonceSize)
   185  	var ad [13]byte
   186  	var ct []byte
   187  	var out []byte
   188  
   189  	var aead cipher.AEAD
   190  	switch len(nonce) {
   191  	case NonceSize:
   192  		aead, _ = New(key[:])
   193  	case NonceSizeX:
   194  		aead, _ = NewX(key[:])
   195  	}
   196  	ct = aead.Seal(ct[:0], nonce[:], buf[:], ad[:])
   197  
   198  	b.ResetTimer()
   199  	for i := 0; i < b.N; i++ {
   200  		out, _ = aead.Open(out[:0], nonce[:], ct[:], ad[:])
   201  	}
   202  }
   203  
   204  func BenchmarkChacha20Poly1305(b *testing.B) {
   205  	for _, length := range []int{64, 1350, 8 * 1024} {
   206  		b.Run("Open-"+strconv.Itoa(length), func(b *testing.B) {
   207  			benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSize)
   208  		})
   209  		b.Run("Seal-"+strconv.Itoa(length), func(b *testing.B) {
   210  			benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSize)
   211  		})
   212  
   213  		b.Run("Open-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
   214  			benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSizeX)
   215  		})
   216  		b.Run("Seal-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
   217  			benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSizeX)
   218  		})
   219  	}
   220  }
   221  
   222  func ExampleNewX() {
   223  	// key should be randomly generated or derived from a function like Argon2.
   224  	key := make([]byte, KeySize)
   225  	if _, err := cryptorand.Read(key); err != nil {
   226  		panic(err)
   227  	}
   228  
   229  	aead, err := NewX(key)
   230  	if err != nil {
   231  		panic(err)
   232  	}
   233  
   234  	// Encryption.
   235  	var encryptedMsg []byte
   236  	{
   237  		msg := []byte("Gophers, gophers, gophers everywhere!")
   238  
   239  		// Select a random nonce, and leave capacity for the ciphertext.
   240  		nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(msg)+aead.Overhead())
   241  		if _, err := cryptorand.Read(nonce); err != nil {
   242  			panic(err)
   243  		}
   244  
   245  		// Encrypt the message and append the ciphertext to the nonce.
   246  		encryptedMsg = aead.Seal(nonce, nonce, msg, nil)
   247  	}
   248  
   249  	// Decryption.
   250  	{
   251  		if len(encryptedMsg) < aead.NonceSize() {
   252  			panic("ciphertext too short")
   253  		}
   254  
   255  		// Split nonce and ciphertext.
   256  		nonce, ciphertext := encryptedMsg[:aead.NonceSize()], encryptedMsg[aead.NonceSize():]
   257  
   258  		// Decrypt the message and check it wasn't tampered with.
   259  		plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
   260  		if err != nil {
   261  			panic(err)
   262  		}
   263  
   264  		fmt.Printf("%s\n", plaintext)
   265  	}
   266  
   267  	// Output: Gophers, gophers, gophers everywhere!
   268  }
   269  

View as plain text