...

Source file src/github.com/ProtonMail/go-crypto/ocb/ocb_test.go

Documentation: github.com/ProtonMail/go-crypto/ocb

     1  // Copyright 2019 ProtonTech AG.
     2  
     3  package ocb
     4  
     5  import (
     6  	"bytes"
     7  	"crypto/aes"
     8  	"crypto/cipher"
     9  	"crypto/rand"
    10  	"encoding/binary"
    11  	"encoding/hex"
    12  	mathrand "math/rand"
    13  	"testing"
    14  	"time"
    15  )
    16  
    17  const (
    18  	blockLength = 16
    19  	maxLength   = 1 << 12
    20  )
    21  
    22  func TestOCBImplementsAEADInterface(t *testing.T) {
    23  	var ocbInstance ocb
    24  	var aux interface{} = &ocbInstance
    25  	_, ok := aux.(cipher.AEAD)
    26  	if !ok {
    27  		t.Errorf("Error: OCB can't implement AEAD interface")
    28  	}
    29  }
    30  
    31  func TestZeroHash(t *testing.T) {
    32  	// Key is shared by all test vectors
    33  	aesCipher, err := aes.NewCipher(testKey)
    34  	if err != nil {
    35  		t.Fatal(err)
    36  	}
    37  	o := ocb{
    38  		block:     aesCipher,
    39  		tagSize:   defaultTagSize,
    40  		nonceSize: defaultNonceSize,
    41  	}
    42  
    43  	blockSize := o.block.BlockSize()
    44  	if !bytes.Equal(o.hash(make([]byte, 0)), make([]byte, blockSize)) {
    45  		t.Errorf("Error: Hash() did not return a correct amount of zero bytes")
    46  	}
    47  }
    48  
    49  func TestNewOCBIncorrectNonceLength(t *testing.T) {
    50  	aesCipher, err := aes.NewCipher(make([]byte, 16))
    51  	if err != nil {
    52  		t.Fatal(err)
    53  	}
    54  	e, err := NewOCBWithNonceAndTagSize(aesCipher, 0, 16)
    55  	if err == nil || e != nil {
    56  		t.Errorf("OCB with nonceLength 0 was not rejected")
    57  	}
    58  }
    59  
    60  func TestSealIncorrectNonceLength(t *testing.T) {
    61  	aesCipher, err := aes.NewCipher(make([]byte, 16))
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	o, err := NewOCBWithNonceAndTagSize(aesCipher, 15, 16)
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	defer func() {
    70  		if r := recover(); r == nil {
    71  			t.Errorf("Ocb.Seal didn't panic on exceedingly long nonce")
    72  		}
    73  	}()
    74  	longNonce := make([]byte, o.NonceSize()+1)
    75  	o.Seal(nil, longNonce, nil, nil)
    76  }
    77  
    78  func TestOpenIncorrectNonceLength(t *testing.T) {
    79  	aesCipher, err := aes.NewCipher(make([]byte, 16))
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	o, err := NewOCBWithNonceAndTagSize(aesCipher, 15, 16)
    84  	if err != nil {
    85  		t.Fatal(err)
    86  	}
    87  	defer func() {
    88  		if r := recover(); r == nil {
    89  			t.Errorf("Ocb.Open didn't panic on exceedingly long nonce")
    90  		}
    91  	}()
    92  	longNonce := make([]byte, o.NonceSize()+1)
    93  	_, err = o.Open(nil, longNonce, nil, nil)
    94  	// Let the Open procedure panic
    95  	if err != nil {
    96  	}
    97  }
    98  
    99  func TestOpenShortCiphertext(t *testing.T) {
   100  	aesCipher, err := aes.NewCipher(make([]byte, 16))
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  	o, err := NewOCBWithNonceAndTagSize(aesCipher, 15, 16)
   105  	if err != nil {
   106  		t.Fatal(err)
   107  	}
   108  	shortCt := make([]byte, o.Overhead()-1)
   109  	pt, err := o.Open(nil, nil, nil, shortCt)
   110  	if pt != nil || err == nil {
   111  		t.Errorf("Ocb.Open processed an exceedingly short ciphertext")
   112  	}
   113  }
   114  
   115  func TestEncryptDecryptRFC7253TestVectors(t *testing.T) {
   116  	// Key is shared by all test vectors
   117  	aesCipher, err := aes.NewCipher(testKey)
   118  	if err != nil {
   119  		t.Fatal(err)
   120  	}
   121  	ocbInstance, errO := NewOCB(aesCipher)
   122  	if errO != nil {
   123  		t.Fatal(err)
   124  	}
   125  	for _, test := range rfc7253testVectors {
   126  		nonce, _ := hex.DecodeString(test.nonce)
   127  		adata, _ := hex.DecodeString(test.header)
   128  		targetPt, _ := hex.DecodeString(test.plaintext)
   129  		targetCt, _ := hex.DecodeString(test.ciphertext)
   130  		ct := ocbInstance.Seal(nil, nonce, targetPt, adata)
   131  		// Encrypt
   132  		if !bytes.Equal(ct, targetCt) {
   133  			t.Errorf(
   134  				`RFC7253 Test vectors Encrypt error (ciphertexts don't match):
   135  			Got:
   136  			%X
   137  			Want:
   138  			%X`, ct, targetCt)
   139  		}
   140  		// Decrypt
   141  		pt, err := ocbInstance.Open(nil, nonce, targetCt, adata)
   142  		if err != nil {
   143  			t.Errorf(
   144  				`RFC7253 Valid ciphertext was refused decryption:
   145  				plaintext %X
   146  				nonce %X
   147  				header %X
   148  				ciphertext %X`, targetPt, nonce, adata, targetCt)
   149  		}
   150  		if !bytes.Equal(pt, targetPt) {
   151  			t.Errorf(
   152  				`RFC7253 test vectors Decrypt error (plaintexts don't match):
   153  			Got:
   154  			%X
   155  			Want:
   156  			%X`, pt, targetPt)
   157  		}
   158  	}
   159  }
   160  
   161  func TestEncryptDecryptRFC7253TagLen96(t *testing.T) {
   162  	test := rfc7253TestVectorTaglen96
   163  	key, _ := hex.DecodeString(test.key)
   164  	nonce, _ := hex.DecodeString(test.nonce)
   165  	adata, _ := hex.DecodeString(test.header)
   166  	targetPt, _ := hex.DecodeString(test.plaintext)
   167  	targetCt, _ := hex.DecodeString(test.ciphertext)
   168  	aesCipher, err := aes.NewCipher(key)
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  	ocbInstance, err := NewOCBWithNonceAndTagSize(aesCipher, len(nonce), 96/8)
   173  	if err != nil {
   174  		t.Fatal(err)
   175  	}
   176  	ct := ocbInstance.Seal(nil, nonce, targetPt, adata)
   177  	if !bytes.Equal(ct, targetCt) {
   178  		t.Errorf(
   179  			`RFC7253 test tagLen96 error (ciphertexts don't match):
   180  		Got:
   181  		%X
   182  		Want:
   183  		%X`, ct, targetCt)
   184  	}
   185  	pt, err := ocbInstance.Open(nil, nonce, targetCt, adata)
   186  	if err != nil {
   187  		t.Errorf(`RFC7253 test tagLen96 was refused decryption`)
   188  	}
   189  	if !bytes.Equal(pt, targetPt) {
   190  		t.Errorf(
   191  			`RFC7253 test tagLen96 error (plaintexts don't match):
   192  		Got:
   193  		%X
   194  		Want:
   195  		%X`, pt, targetPt)
   196  	}
   197  }
   198  
   199  // This test algorithm is defined in RFC7253, Appendix A
   200  func TestEncryptDecryptRFC7253DifferentKeySizes(t *testing.T) {
   201  	for _, testCase := range rfc7253AlgorithmTest {
   202  		keyLen := testCase.KEYLEN
   203  		tagLen := testCase.TAGLEN
   204  		key := make([]byte, keyLen/8)
   205  		key[len(key)-1] = byte(tagLen)
   206  
   207  		aesCipher, err := aes.NewCipher(key)
   208  		if err != nil {
   209  			t.Fatal(err)
   210  		}
   211  		ocbInstance, err := NewOCBWithNonceAndTagSize(aesCipher, 12, tagLen/8)
   212  		if err != nil {
   213  			t.Fatal(err)
   214  		}
   215  		C := make([]byte, 0)
   216  		ending := make([]byte, 4)
   217  		var N, S []byte
   218  		for i := 0; i < 128; i++ {
   219  			S = make([]byte, i)
   220  			binary.BigEndian.PutUint32(ending, uint32(3*i+1))
   221  			N = append(make([]byte, 8), ending...)
   222  			// C ||= ENC(S, N, S)
   223  			C = append(C, ocbInstance.Seal(nil, N, S, S)...)
   224  			binary.BigEndian.PutUint32(ending, uint32(3*i+2))
   225  			N = append(make([]byte, 8), ending...)
   226  			// C ||= ENC(S, N, <empty>)
   227  			C = append(C, ocbInstance.Seal(nil, N, S, make([]byte, 0))...)
   228  			binary.BigEndian.PutUint32(ending, uint32(3*i+3))
   229  			N = append(make([]byte, 8), ending...)
   230  			// C ||= ENC(<empty>, N, S)
   231  			C = append(C, ocbInstance.Seal(nil, N, make([]byte, 0), S)...)
   232  		}
   233  		binary.BigEndian.PutUint32(ending, uint32(385))
   234  		N = append(make([]byte, 8), ending...)
   235  		// output = Enc(<empty>, N, C)
   236  		output := ocbInstance.Seal(nil, N, make([]byte, 0), C)
   237  		targetOutput, _ := hex.DecodeString(testCase.OUTPUT)
   238  		if !bytes.Equal(output, targetOutput) {
   239  			t.Errorf(
   240  				`RFC7253 Test algorithm error (outputs do not match):
   241  		AES_%d_OCB_TAGLEN%d
   242  		Got:
   243  		%X
   244  		Want:
   245  		%X`, keyLen, tagLen, output, targetOutput)
   246  		}
   247  	}
   248  }
   249  
   250  func TestEncryptDecryptGoTestVectors(t *testing.T) {
   251  	for _, test := range randomVectors {
   252  		key, _ := hex.DecodeString(test.key)
   253  		aesCipher, err := aes.NewCipher(key)
   254  		if err != nil {
   255  			t.Fatal(err)
   256  		}
   257  		nonce, _ := hex.DecodeString(test.nonce)
   258  		adata, _ := hex.DecodeString(test.header)
   259  		targetPt, _ := hex.DecodeString(test.plaintext)
   260  		targetCt, _ := hex.DecodeString(test.ciphertext)
   261  		tagSize := len(targetCt) - len(targetPt)
   262  		ocbInstance, err := NewOCBWithNonceAndTagSize(aesCipher, len(nonce), tagSize)
   263  		if err != nil {
   264  			t.Fatal(err)
   265  		}
   266  		// Encrypt
   267  		ct := ocbInstance.Seal(nil, nonce, targetPt, adata)
   268  		if !bytes.Equal(ct, targetCt) {
   269  			t.Errorf(
   270  				`Go Test vectors Encrypt error (ciphertexts don't match):
   271  			Got:
   272  			%X
   273  			Want:
   274  			%X`, ct, targetCt)
   275  		}
   276  
   277  		// Decrypt
   278  		pt, err := ocbInstance.Open(nil, nonce, targetCt, adata)
   279  		if err != nil {
   280  			t.Errorf(
   281  				`Valid Go ciphertext was refused decryption:
   282  			plaintext %X
   283  			nonce %X
   284  			header %X
   285  			ciphertext %X`, targetPt, nonce, adata, targetCt)
   286  		}
   287  		if !bytes.Equal(pt, targetPt) {
   288  			t.Errorf(
   289  				`Go Test vectors Decrypt error (plaintexts don't match):
   290  			Got:
   291  			%X
   292  			Want:
   293  			%X`, pt, targetPt)
   294  		}
   295  	}
   296  }
   297  
   298  func TestEncryptDecryptVectorsWithPreviousDataRandomizeSlow(t *testing.T) {
   299  	mathrand.Seed(time.Now().UnixNano())
   300  	allowedKeyLengths := []int{16, 24, 32}
   301  	for _, keyLength := range allowedKeyLengths {
   302  		pt := make([]byte, mathrand.Intn(maxLength))
   303  		header := make([]byte, mathrand.Intn(maxLength))
   304  		key := make([]byte, keyLength)
   305  		// Testing for short nonces but take notice they are not recommended
   306  		nonce := make([]byte, 1+mathrand.Intn(blockLength-1))
   307  		previousData := make([]byte, mathrand.Intn(maxLength))
   308  		// Populate items with crypto/rand
   309  		itemsToPopulate := [][]byte{pt, header, key, nonce, previousData}
   310  		for _, item := range itemsToPopulate {
   311  			_, err := rand.Read(item)
   312  			if err != nil {
   313  			}
   314  		}
   315  		aesCipher, err := aes.NewCipher(key)
   316  		if err != nil {
   317  			t.Fatal(err)
   318  		}
   319  		ocb, err := NewOCB(aesCipher)
   320  		if err != nil {
   321  			t.Fatal(err)
   322  		}
   323  		newData := ocb.Seal(previousData, nonce, pt, header)
   324  		ct := newData[len(previousData):]
   325  		decrypted, err := ocb.Open(nil, nonce, ct, header)
   326  		if err != nil {
   327  			t.Errorf(
   328  				`Decrypt refused valid tag (not displaying long output)`)
   329  			break
   330  		}
   331  		if !bytes.Equal(pt, decrypted) {
   332  			t.Errorf(
   333  				`Random Encrypt/Decrypt error (plaintexts don't match)`)
   334  			break
   335  		}
   336  	}
   337  }
   338  
   339  func TestRejectTamperedCiphertextRandomizeSlow(t *testing.T) {
   340  	pt := make([]byte, mathrand.Intn(maxLength))
   341  	header := make([]byte, mathrand.Intn(maxLength))
   342  	key := make([]byte, blockLength)
   343  	// Note: Nonce cannot equal blockLength
   344  	nonce := make([]byte, blockLength-1)
   345  	itemsToPopulate := [][]byte{pt, header, key, nonce}
   346  	for _, item := range itemsToPopulate {
   347  		_, err := rand.Read(item)
   348  		if err != nil {
   349  		}
   350  	}
   351  	aesCipher, err := aes.NewCipher(key)
   352  	if err != nil {
   353  		t.Fatal(err)
   354  	}
   355  	ocb, errO := NewOCB(aesCipher)
   356  	if errO != nil {
   357  		t.Fatal(err)
   358  	}
   359  	ct := ocb.Seal(nil, nonce, pt, header)
   360  	// Change one byte of ct (could affect either the tag or the ciphertext)
   361  	tampered := make([]byte, len(ct))
   362  	copy(tampered, ct)
   363  	for bytes.Equal(tampered, ct) {
   364  		tampered[mathrand.Intn(len(ct))] = byte(mathrand.Intn(len(ct)))
   365  	}
   366  	_, err = ocb.Open(nil, nonce, tampered, header)
   367  	if err == nil {
   368  		t.Errorf(
   369  			"Tampered ciphertext was not refused decryption (OCB did not return an error)")
   370  		return
   371  	}
   372  }
   373  
   374  func TestParameters(t *testing.T) {
   375  	blockLength := 16
   376  	key := make([]byte, blockLength)
   377  	aesCipher, err := aes.NewCipher(key)
   378  	if err != nil {
   379  		t.Fatal(err)
   380  	}
   381  	t.Run("Should return error on too long tagSize", func(st *testing.T) {
   382  		tagSize := blockLength + 1 + mathrand.Intn(12)
   383  		nonceSize := 1 + mathrand.Intn(16)
   384  		_, err := NewOCBWithNonceAndTagSize(aesCipher, nonceSize, tagSize)
   385  		if err == nil {
   386  			st.Errorf("No error was returned")
   387  		}
   388  	})
   389  	t.Run("Should return error on too long nonceSize", func(st *testing.T) {
   390  		tagSize := 12
   391  		nonceSize := blockLength + mathrand.Intn(16)
   392  		_, err := NewOCBWithNonceAndTagSize(aesCipher, nonceSize, tagSize)
   393  		if err == nil {
   394  			st.Errorf("No error was returned")
   395  		}
   396  	})
   397  	t.Run(
   398  		"Should not give error with allowed parameters", func(st *testing.T) {
   399  			// Noncesize ∈  12,...,blocklength - 1
   400  			// Shorter values of nonceSize are not recommended.
   401  			nonceSize := 12 + mathrand.Intn(blockLength-12)
   402  			tagSize := 12 + mathrand.Intn(blockLength-11)
   403  			_, err := NewOCBWithNonceAndTagSize(aesCipher, nonceSize, tagSize)
   404  			if err != nil {
   405  				st.Errorf("An error was returned")
   406  			}
   407  		})
   408  }
   409  
   410  func BenchmarkEncrypt(b *testing.B) {
   411  	plaintextLength := maxLength
   412  	headerLength := 16
   413  	pt := make([]byte, plaintextLength)
   414  	header := make([]byte, headerLength)
   415  	key := make([]byte, blockLength)
   416  	nonce := make([]byte, blockLength-1)
   417  	itemsToPopulate := [][]byte{pt, header, key, nonce}
   418  	for _, item := range itemsToPopulate {
   419  		_, err := rand.Read(item)
   420  		if err != nil {
   421  		}
   422  	}
   423  	aesCipher, err := aes.NewCipher(key)
   424  	if err != nil {
   425  		b.Fatal(err)
   426  	}
   427  	ocb, err := NewOCB(aesCipher)
   428  	if err != nil {
   429  		b.Fatal(err)
   430  	}
   431  	for i := 0; i < b.N; i++ {
   432  		ocb.Seal(nil, nonce, pt, header)
   433  	}
   434  }
   435  
   436  func BenchmarkDecrypt(b *testing.B) {
   437  	plaintextLength := maxLength
   438  	headerLength := 16
   439  	pt := make([]byte, plaintextLength)
   440  	header := make([]byte, headerLength)
   441  	key := make([]byte, blockLength)
   442  	nonce := make([]byte, blockLength-1)
   443  	itemsToPopulate := [][]byte{pt, header, key, nonce}
   444  	for _, item := range itemsToPopulate {
   445  		_, err := rand.Read(item)
   446  		if err != nil {
   447  		}
   448  	}
   449  	aesCipher, err := aes.NewCipher(key)
   450  	if err != nil {
   451  		b.Fatal(err)
   452  	}
   453  	ocb, errO := NewOCB(aesCipher)
   454  	if errO != nil {
   455  		b.Fatal(err)
   456  	}
   457  	ct := ocb.Seal(nil, nonce, pt, header)
   458  	for i := 0; i < b.N; i++ {
   459  		_, err := ocb.Open(nil, nonce, ct, header)
   460  		if err != nil {
   461  			b.Fatal(err)
   462  		}
   463  	}
   464  }
   465  

View as plain text