...

Source file src/github.com/ThalesIgnite/crypto11/symmetric_test.go

Documentation: github.com/ThalesIgnite/crypto11

     1  // Copyright 2018 Thales e-Security, Inc
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining
     4  // a copy of this software and associated documentation files (the
     5  // "Software"), to deal in the Software without restriction, including
     6  // without limitation the rights to use, copy, modify, merge, publish,
     7  // distribute, sublicense, and/or sell copies of the Software, and to
     8  // permit persons to whom the Software is furnished to do so, subject to
     9  // the following conditions:
    10  //
    11  // The above copyright notice and this permission notice shall be
    12  // included in all copies or substantial portions of the Software.
    13  //
    14  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    15  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    16  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    17  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    18  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    19  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    20  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    21  
    22  package crypto11
    23  
    24  import (
    25  	"bytes"
    26  	"crypto/cipher"
    27  	"runtime"
    28  	"testing"
    29  
    30  	"github.com/miekg/pkcs11"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func TestHardSymmetric(t *testing.T) {
    35  	ctx, err := ConfigureFromFile("config")
    36  	require.NoError(t, err)
    37  
    38  	defer func() {
    39  		require.NoError(t, ctx.Close())
    40  	}()
    41  
    42  	t.Run("AES128", func(t *testing.T) { testHardSymmetric(t, ctx, pkcs11.CKK_AES, 128) })
    43  	t.Run("AES192", func(t *testing.T) { testHardSymmetric(t, ctx, pkcs11.CKK_AES, 192) })
    44  	t.Run("AES256", func(t *testing.T) { testHardSymmetric(t, ctx, pkcs11.CKK_AES, 256) })
    45  	t.Run("DES3", func(t *testing.T) { testHardSymmetric(t, ctx, pkcs11.CKK_DES3, 0) })
    46  }
    47  
    48  func testHardSymmetric(t *testing.T, ctx *Context, keytype int, bits int) {
    49  	for _, p := range Ciphers[keytype].GenParams {
    50  		skipIfMechUnsupported(t, ctx, p.GenMech)
    51  	}
    52  
    53  	id := randomBytes()
    54  	key, err := ctx.GenerateSecretKey(id, bits, Ciphers[keytype])
    55  	require.NoError(t, err)
    56  	require.NotNil(t, key)
    57  	defer key.Delete()
    58  
    59  	var key2 *SecretKey
    60  	t.Run("Find", func(t *testing.T) {
    61  		key2, err = ctx.FindKey(id, nil)
    62  		require.NoError(t, err)
    63  	})
    64  
    65  	t.Run("Block", func(t *testing.T) {
    66  		skipIfMechUnsupported(t, key.context, key.Cipher.ECBMech)
    67  		testSymmetricBlock(t, key, key2)
    68  	})
    69  
    70  	iv := make([]byte, key.BlockSize())
    71  	for i := range iv {
    72  		iv[i] = 0xF0
    73  	}
    74  
    75  	t.Run("CBC", func(t *testing.T) {
    76  		// By using cipher.NewCBCEncrypter, this test will actually use ECB mode on the key.
    77  		skipIfMechUnsupported(t, key2.context, key2.Cipher.ECBMech)
    78  		testSymmetricMode(t, cipher.NewCBCEncrypter(key2, iv), cipher.NewCBCDecrypter(key2, iv))
    79  	})
    80  
    81  	t.Run("CBCClose", func(t *testing.T) {
    82  		skipIfMechUnsupported(t, key2.context, key2.Cipher.CBCMech)
    83  		enc, err := key2.NewCBCEncrypterCloser(iv)
    84  		require.NoError(t, err)
    85  
    86  		dec, err := key2.NewCBCDecrypterCloser(iv)
    87  		require.NoError(t, err)
    88  
    89  		testSymmetricMode(t, enc, dec)
    90  		enc.Close()
    91  		dec.Close()
    92  	})
    93  
    94  	t.Run("CBCNoClose", func(t *testing.T) {
    95  		skipIfMechUnsupported(t, key2.context, key2.Cipher.CBCMech)
    96  		enc, err := key2.NewCBCEncrypter(iv)
    97  		require.NoError(t, err)
    98  
    99  		dec, err := key2.NewCBCDecrypter(iv)
   100  		require.NoError(t, err)
   101  		testSymmetricMode(t, enc, dec)
   102  
   103  		// See discussion at BlockModeCloser.
   104  		runtime.GC()
   105  	})
   106  
   107  	t.Run("CBCSealOpen", func(t *testing.T) {
   108  		aead, err := key2.NewCBC(PaddingNone)
   109  		require.NoError(t, err)
   110  		testAEADMode(t, aead, 128, 0)
   111  	})
   112  
   113  	t.Run("CBCPKCSSealOpen", func(t *testing.T) {
   114  		aead, err := key2.NewCBC(PaddingPKCS)
   115  		require.NoError(t, err)
   116  		testAEADMode(t, aead, 127, 0)
   117  	})
   118  	if bits == 128 {
   119  		t.Run("GCMSoft", func(t *testing.T) {
   120  			aead, err := cipher.NewGCM(key2)
   121  			require.NoError(t, err)
   122  			testAEADMode(t, aead, 127, 129)
   123  		})
   124  		t.Run("GCMHard", func(t *testing.T) {
   125  			aead, err := key2.NewGCM()
   126  			require.NoError(t, err)
   127  			skipIfMechUnsupported(t, key2.context, pkcs11.CKM_AES_GCM)
   128  			testAEADMode(t, aead, 127, 129)
   129  		})
   130  		// TODO check that hard/soft is consistent!
   131  	}
   132  	// TODO CFB
   133  	// TODO OFB
   134  	// TODO CTR
   135  
   136  }
   137  
   138  func testSymmetricBlock(t *testing.T, encryptKey cipher.Block, decryptKey cipher.Block) {
   139  	// The functions in cipher.Block have no error returns, so they panic if they encounter
   140  	// a problem. We catch these panics here, so the test can fail nicely
   141  	defer func() {
   142  		if cause := recover(); cause != nil {
   143  			t.Fatalf("Caught panic: %q", cause)
   144  		}
   145  	}()
   146  
   147  	b := encryptKey.BlockSize()
   148  	input := make([]byte, 3*b)
   149  	middle := make([]byte, 3*b)
   150  	output := make([]byte, 3*b)
   151  	// Set a recognizable pattern in the buffers
   152  	for i := 0; i < 3*b; i++ {
   153  		input[i] = byte(i)
   154  		middle[i] = byte(i + 3*b)
   155  		output[i] = byte(i + 6*b)
   156  	}
   157  	encryptKey.Encrypt(middle, input) // middle[:b] = encrypt(input[:b])
   158  	if bytes.Equal(input[:b], middle[:b]) {
   159  		t.Errorf("crypto11.PKCSSecretKey.Encrypt: identity transformation")
   160  		return
   161  	}
   162  	matches := 0
   163  	for i := 0; i < b; i++ {
   164  		if middle[i] == byte(i+3*b) {
   165  			matches++
   166  		}
   167  	}
   168  	if matches == b {
   169  		t.Errorf("crypto11.PKCSSecretKey.Encrypt: didn't modify destination")
   170  		return
   171  	}
   172  	for i := 0; i < 3*b; i++ {
   173  		if input[i] != byte(i) {
   174  			t.Errorf("crypto11.PKCSSecretKey.Encrypt: corrupted source")
   175  			return
   176  		}
   177  		if i >= b && middle[i] != byte(i+3*b) {
   178  			t.Errorf("crypto11.PKCSSecretKey.Encrypt: corrupted destination past blocksize")
   179  			return
   180  		}
   181  	}
   182  	decryptKey.Decrypt(output, middle) // output[:b] = decrypt(middle[:b])
   183  	if !bytes.Equal(input[:b], output[:b]) {
   184  		t.Errorf("crypto11.PKCSSecretKey.Decrypt: plaintext wrong")
   185  		return
   186  	}
   187  	for i := 0; i < 3*b; i++ {
   188  		if i >= b && output[i] != byte(i+6*b) {
   189  			t.Errorf("crypto11.PKCSSecretKey.Decrypt: corrupted destination past blocksize")
   190  			return
   191  		}
   192  	}
   193  }
   194  
   195  func testSymmetricMode(t *testing.T, encrypt cipher.BlockMode, decrypt cipher.BlockMode) {
   196  	// The functions in cipher.Block have no error returns, so they panic if they encounter
   197  	// a problem. We catch these panics here, so the test can fail nicely
   198  	defer func() {
   199  		if cause := recover(); cause != nil {
   200  			t.Fatalf("Caught panic: %q", cause)
   201  		}
   202  	}()
   203  
   204  	input := make([]byte, 256)
   205  	middle := make([]byte, 256)
   206  	output := make([]byte, 256)
   207  	// Set a recognizable pattern in the buffers
   208  	for i := 0; i < 256; i++ {
   209  		input[i] = byte(i)
   210  		middle[i] = byte(i + 32)
   211  		output[i] = byte(i + 64)
   212  	}
   213  	// Encrypt the first 128 bytes
   214  	encrypt.CryptBlocks(middle, input[:128])
   215  	if bytes.Equal(input[:128], middle[:128]) {
   216  		t.Errorf("BlockMode.Encrypt: did not modify destination")
   217  		return
   218  	}
   219  	for i := 0; i < 128; i++ {
   220  		if input[i] != byte(i) {
   221  			t.Errorf("BlockMode.Encrypt: corrupted source")
   222  			return
   223  		}
   224  	}
   225  	for i := 128; i < 256; i++ {
   226  		if middle[i] != byte(i+32) {
   227  			t.Errorf("BlockMode.Encrypt: corrupted destination past input size")
   228  			return
   229  		}
   230  	}
   231  	// Encrypt the rest
   232  	encrypt.CryptBlocks(middle[128:], input[128:])
   233  	// Decrypt in a single go
   234  	decrypt.CryptBlocks(output, middle)
   235  	if !bytes.Equal(input, output) {
   236  		t.Errorf("BlockMode.Decrypt: plaintext wrong")
   237  		return
   238  	}
   239  }
   240  
   241  func testAEADMode(t *testing.T, aead cipher.AEAD, ptlen int, adlen int) {
   242  	nonce := make([]byte, aead.NonceSize())
   243  	plaintext := make([]byte, ptlen)
   244  	for i := 0; i < len(plaintext); i++ {
   245  		plaintext[i] = byte(i)
   246  	}
   247  	additionalData := make([]byte, adlen)
   248  	for i := 0; i < len(additionalData); i++ {
   249  		additionalData[i] = byte(i + 16)
   250  	}
   251  	ciphertext := aead.Seal([]byte{}, nonce, plaintext, additionalData)
   252  
   253  	/*
   254  		In some HSM configurations the IV may be generated and appended to the ciphertext + tag
   255  		If this is the case (based on the ciphertext length), the test assumes that the IV has been appended
   256  	*/
   257  	if len(ciphertext) == aead.NonceSize()+len(plaintext)+aead.Overhead() {
   258  		nonce = ciphertext[len(ciphertext)-aead.NonceSize():]
   259  		ciphertext = ciphertext[:len(ciphertext)-aead.NonceSize()]
   260  	}
   261  
   262  	decrypted, err := aead.Open([]byte{}, nonce, ciphertext, additionalData)
   263  	if err != nil {
   264  		t.Errorf("aead.Open: %s", err)
   265  		return
   266  	}
   267  	if !bytes.Equal(plaintext, decrypted) {
   268  		t.Errorf("aead.Open: mismatch")
   269  		return
   270  	}
   271  }
   272  
   273  func BenchmarkCBC(b *testing.B) {
   274  	ctx, err := ConfigureFromFile("config")
   275  	require.NoError(b, err)
   276  
   277  	defer func() {
   278  		require.NoError(b, ctx.Close())
   279  	}()
   280  
   281  	id := randomBytes()
   282  	key, err := ctx.GenerateSecretKey(id, 128, Ciphers[pkcs11.CKK_AES])
   283  	require.NoError(b, err)
   284  	require.NotNil(b, key)
   285  	defer key.Delete()
   286  
   287  	iv := make([]byte, 16)
   288  	plaintext := make([]byte, 65536)
   289  	ciphertext := make([]byte, 65536)
   290  
   291  	b.Run("Native", func(b *testing.B) {
   292  		for i := 0; i < b.N; i++ {
   293  			mode := cipher.NewCBCEncrypter(key, iv)
   294  			mode.CryptBlocks(ciphertext, plaintext)
   295  		}
   296  	})
   297  
   298  	b.Run("IdiomaticClose", func(b *testing.B) {
   299  		for i := 0; i < b.N; i++ {
   300  			mode, err := key.NewCBCEncrypterCloser(iv)
   301  			if err != nil {
   302  				panic(err)
   303  			}
   304  			mode.CryptBlocks(ciphertext, plaintext)
   305  			mode.Close()
   306  		}
   307  	})
   308  
   309  	b.Run("Idiomatic", func(b *testing.B) {
   310  		for i := 0; i < b.N; i++ {
   311  			mode, err := key.NewCBCEncrypter(iv)
   312  			if err != nil {
   313  				panic(err)
   314  			}
   315  			mode.CryptBlocks(ciphertext, plaintext)
   316  		}
   317  		runtime.GC()
   318  	})
   319  }
   320  
   321  func TestSymmetricRequiredArgs(t *testing.T) {
   322  	ctx, err := ConfigureFromFile("config")
   323  	require.NoError(t, err)
   324  
   325  	defer func() {
   326  		require.NoError(t, ctx.Close())
   327  	}()
   328  
   329  	_, err = ctx.GenerateSecretKey(nil, 128, CipherAES)
   330  	require.Error(t, err)
   331  
   332  	val := randomBytes()
   333  
   334  	_, err = ctx.GenerateSecretKeyWithLabel(nil, val, 128, CipherAES)
   335  	require.Error(t, err)
   336  
   337  	_, err = ctx.GenerateSecretKeyWithLabel(val, nil, 128, CipherAES)
   338  	require.Error(t, err)
   339  }
   340  
   341  // TODO BenchmarkGCM along the same lines as above
   342  

View as plain text