From 8d646ca06fbbcfbcba6070e9aa1cac58709557a4 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 23 Aug 2023 13:09:24 +0200 Subject: [PATCH 1/8] refactor aes_test.go to only use the public openssl api --- aes_test.go | 121 ++++++++++++++++++------------------------------- export_test.go | 8 ++++ 2 files changed, 52 insertions(+), 77 deletions(-) create mode 100644 export_test.go diff --git a/aes_test.go b/aes_test.go index a5add071..c00c1944 100644 --- a/aes_test.go +++ b/aes_test.go @@ -5,21 +5,29 @@ import ( "crypto/cipher" "math" "testing" + + "github.com/golang-fips/openssl/v2" ) func TestNewGCMNonce(t *testing.T) { key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D") - ci, err := NewAESCipher(key) + ci, err := openssl.NewAESCipher(key) if err != nil { t.Fatal(err) } - c := ci.(*aesCipher) - gi, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize) + const ( + gcmTagSize = 16 + gcmStandardNonceSize = 12 + ) + + c := ci.(interface { + NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) + }) + g, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize) if err != nil { t.Errorf("expected no error for standard nonce size with standard tag size, got: %#v", err) } - g := gi.(*aesGCM) if g.NonceSize() != gcmStandardNonceSize { t.Errorf("unexpected nonce size\ngot: %#v\nexp: %#v", g.NonceSize(), gcmStandardNonceSize) @@ -49,12 +57,11 @@ func TestNewGCMNonce(t *testing.T) { func TestSealAndOpen(t *testing.T) { key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D") - ci, err := NewAESCipher(key) + ci, err := openssl.NewAESCipher(key) if err != nil { t.Fatal(err) } - c := ci.(*aesCipher) - gcm, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize) + gcm, err := cipher.NewGCM(ci) if err != nil { t.Fatal(err) } @@ -73,12 +80,11 @@ func TestSealAndOpen(t *testing.T) { func TestSealAndOpen_Empty(t *testing.T) { key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D") - ci, err := NewAESCipher(key) + ci, err := openssl.NewAESCipher(key) if err != nil { t.Fatal(err) } - c := ci.(*aesCipher) - gcm, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize) + gcm, err := cipher.NewGCM(ci) if err != nil { t.Fatal(err) } @@ -95,11 +101,11 @@ func TestSealAndOpen_Empty(t *testing.T) { func TestSealAndOpenTLS(t *testing.T) { key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D") - ci, err := NewAESCipher(key) + ci, err := openssl.NewAESCipher(key) if err != nil { t.Fatal(err) } - gcm, err := NewGCMTLS(ci) + gcm, err := openssl.NewGCMTLS(ci) if err != nil { t.Fatal(err) } @@ -145,12 +151,11 @@ func TestSealAndOpenTLS(t *testing.T) { func TestSealAndOpenAuthenticationError(t *testing.T) { key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D") - ci, err := NewAESCipher(key) + ci, err := openssl.NewAESCipher(key) if err != nil { t.Fatal(err) } - c := ci.(*aesCipher) - gcm, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize) + gcm, err := cipher.NewGCM(ci) if err != nil { t.Fatal(err) } @@ -159,7 +164,7 @@ func TestSealAndOpenAuthenticationError(t *testing.T) { additionalData := []byte{0x05, 0x05, 0x07} sealed := gcm.Seal(nil, nonce, plainText, additionalData) _, err = gcm.Open(nil, nonce, sealed, nil) - if err != errOpen { + if err != openssl.ErrOpen { t.Errorf("expected authentication error, got: %#v", err) } } @@ -175,20 +180,19 @@ func assertPanic(t *testing.T, f func()) { } func TestSealPanic(t *testing.T) { - ci, err := NewAESCipher([]byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D")) + ci, err := openssl.NewAESCipher([]byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D")) if err != nil { t.Fatal(err) } - c := ci.(*aesCipher) - gcm, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize) + gcm, err := cipher.NewGCM(ci) if err != nil { t.Fatal(err) } assertPanic(t, func() { - gcm.Seal(nil, make([]byte, gcmStandardNonceSize-1), []byte{0x01, 0x02, 0x03}, nil) + gcm.Seal(nil, make([]byte, gcm.NonceSize()-1), []byte{0x01, 0x02, 0x03}, nil) }) assertPanic(t, func() { - gcm.Seal(nil, make([]byte, gcmStandardNonceSize), make([]byte, math.MaxInt), nil) + gcm.Seal(nil, make([]byte, gcm.NonceSize()), make([]byte, math.MaxInt), nil) }) } @@ -196,7 +200,7 @@ func TestBlobEncryptBasicBlockEncryption(t *testing.T) { key := []byte{0x24, 0xcd, 0x8b, 0x13, 0x37, 0xc5, 0xc1, 0xb1, 0x0, 0xbb, 0x27, 0x40, 0x4f, 0xab, 0x5f, 0x7b, 0x2d, 0x0, 0x20, 0xf5, 0x1, 0x84, 0x4, 0xbf, 0xe3, 0xbd, 0xa1, 0xc4, 0xbf, 0x61, 0x2f, 0xc5} iv := []byte{0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb, 0x91, 0xa8, 0x6c, 0xf9, 0x79, 0xd5, 0xac, 0x74} - block, err := NewAESCipher(key) + block, err := openssl.NewAESCipher(key) if err != nil { t.Errorf("expected no error for aes.NewCipher, got: %s", err) } @@ -205,17 +209,7 @@ func TestBlobEncryptBasicBlockEncryption(t *testing.T) { if blockSize != 16 { t.Errorf("unexpected block size, expected 16 got: %d", blockSize) } - var encryptor cipher.BlockMode - if c, ok := block.(*aesCipher); ok { - encryptor = c.NewCBCEncrypter(iv) - if encryptor == nil { - t.Error("unable to create new CBC encrypter") - } - } - - cbc := encryptor.(*aesCBC) - cbc.SetIV(iv) - + encryptor := cipher.NewCBCEncrypter(block, iv) encrypted := make([]byte, 32) // First block. 16 bytes. @@ -238,13 +232,7 @@ func TestBlobEncryptBasicBlockEncryption(t *testing.T) { t.Error("unexpected CryptBlocks result for second block") } - var decrypter cipher.BlockMode - if c, ok := block.(*aesCipher); ok { - decrypter = c.NewCBCDecrypter(iv) - if decrypter == nil { - t.Error("unable to create new CBC decrypter") - } - } + decrypter := cipher.NewCBCDecrypter(block, iv) plainText := append(srcBlock1, srcBlock2...) decrypted := make([]byte, len(plainText)) decrypter.CryptBlocks(decrypted, encrypted[:16]) @@ -262,7 +250,7 @@ func testDecrypt(t *testing.T, resetNonce bool) { 0xe3, 0xbd, 0xa1, 0xc4, 0xbf, 0x61, 0x2f, 0xc5, } - block, err := NewAESCipher(key) + block, err := openssl.NewAESCipher(key) if err != nil { panic(err) } @@ -271,20 +259,11 @@ func testDecrypt(t *testing.T, resetNonce bool) { 0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb, 0x91, 0xa8, 0x6c, 0xf9, 0x79, 0xd5, 0xac, 0x74, } - var encrypter, decrypter cipher.BlockMode - if c, ok := block.(*aesCipher); ok { - encrypter = c.NewCBCEncrypter(iv) - if encrypter == nil { - t.Error("unable to create new CBC encrypter") - } - decrypter = c.NewCBCDecrypter(iv) - if decrypter == nil { - t.Error("unable to create new CBC decrypter") - } - if resetNonce { - for i := range iv { - iv[i] = 0 - } + encrypter := cipher.NewCBCEncrypter(block, iv) + decrypter := cipher.NewCBCDecrypter(block, iv) + if resetNonce { + for i := range iv { + iv[i] = 0 } } @@ -358,25 +337,13 @@ func Test_aesCipher_finalize(t *testing.T) { // This test is important because aesCipher.finalize contains logic that is normally not exercided while testing. // We can't used NewAESCipher here because the returned object will be automatically finalized by the GC // in case test execution takes long enough, and it can't be finalized twice. - new(aesCipher).finalize() -} - -func Test_aesCBC_finalize(t *testing.T) { - new(aesCBC).finalize() -} - -func Test_aesGCM_finalize(t *testing.T) { - new(aesGCM).finalize() -} - -func Test_aesCTR_finalize(t *testing.T) { - new(aesCTR).finalize() + openssl.EVPCipherFinalize() } func TestCipherEncryptDecrypt(t *testing.T) { key := []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} pt := []byte{0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34} - c, err := NewAESCipher(key) + c, err := openssl.NewAESCipher(key) if err != nil { t.Fatal(err) } @@ -412,7 +379,7 @@ func TestNewCTR(t *testing.T) { 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab, 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee, } - c, err := NewAESCipher(key) + c, err := openssl.NewAESCipher(key) if err != nil { t.Fatal(err) } @@ -430,7 +397,7 @@ func TestNewCTR(t *testing.T) { func TestCipherEncryptDecryptSharedBuffer(t *testing.T) { key := []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} pt := []byte{0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34} - c, err := NewAESCipher(key) + c, err := openssl.NewAESCipher(key) if err != nil { t.Fatal(err) } @@ -448,7 +415,7 @@ func TestCipherEncryptDecryptSharedBuffer(t *testing.T) { func BenchmarkAES_Encrypt(b *testing.B) { key := []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} in := []byte{0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34} - c, err := NewAESCipher(key) + c, err := openssl.NewAESCipher(key) if err != nil { b.Fatal("NewCipher:", err) } @@ -464,7 +431,7 @@ func BenchmarkAES_Encrypt(b *testing.B) { func BenchmarkAES_Decrypt(b *testing.B) { key := []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} in := []byte{0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32} - c, err := NewAESCipher(key) + c, err := openssl.NewAESCipher(key) if err != nil { b.Fatal("NewCipher:", err) } @@ -488,8 +455,8 @@ func BenchmarkAESGCM_Open(b *testing.B) { var key = make([]byte, keySize) var nonce [12]byte var ad [13]byte - c, _ := NewAESCipher(key) - aesgcm, _ := c.(extraModes).NewGCM(gcmStandardNonceSize, gcmTagSize) + c, _ := openssl.NewAESCipher(key) + aesgcm, _ := cipher.NewGCM(c) var out []byte ct := aesgcm.Seal(nil, nonce[:], buf[:], ad[:]) @@ -511,8 +478,8 @@ func BenchmarkAESGCM_Seal(b *testing.B) { var key = make([]byte, keySize) var nonce [12]byte var ad [13]byte - c, _ := NewAESCipher(key) - aesgcm, _ := c.(extraModes).NewGCM(gcmStandardNonceSize, gcmTagSize) + c, _ := openssl.NewAESCipher(key) + aesgcm, _ := cipher.NewGCM(c) var out []byte b.ResetTimer() diff --git a/export_test.go b/export_test.go new file mode 100644 index 00000000..00dd9374 --- /dev/null +++ b/export_test.go @@ -0,0 +1,8 @@ +//go:build linux + +package openssl + +var ( + ErrOpen = errOpen + EVPCipherFinalize = new(evpCipher).finalize +) From cd5f74f8342f348fd8dd06d9e07600141797dba9 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 23 Aug 2023 13:10:20 +0200 Subject: [PATCH 2/8] decouple cipher modes from AES cipher --- aes.go | 367 +++++++++++++++++++++++++--------------------------- goopenssl.h | 4 +- shims.h | 6 - 3 files changed, 180 insertions(+), 197 deletions(-) diff --git a/aes.go b/aes.go index a7c3aea6..870305dc 100644 --- a/aes.go +++ b/aes.go @@ -17,26 +17,31 @@ import ( type cipherKind int8 const ( - cipherAES128_ECB cipherKind = iota - cipherAES192_ECB - cipherAES256_ECB - cipherAES128_CBC - cipherAES192_CBC - cipherAES256_CBC - cipherAES128_CTR - cipherAES192_CTR - cipherAES256_CTR - cipherAES128_GCM - cipherAES192_GCM - cipherAES256_GCM + cipherAES128 cipherKind = iota + cipherAES192 + cipherAES256 +) + +type cipherMode int8 + +const ( + cipherModeECB cipherMode = iota + cipherModeCBC + cipherModeCTR + cipherModeGCM ) // cacheCipher is a cache of cipherKind to GO_EVP_CIPHER_PTR. var cacheCipher sync.Map -// newCipher returns a cipher object for the given k. -func newCipher(k cipherKind) (cipher C.GO_EVP_CIPHER_PTR) { - if v, ok := cacheCipher.Load(k); ok { +type cacheCipherKey struct { + kind cipherKind + mode cipherMode +} + +// loadCipher returns a cipher object for the given k. +func loadCipher(k cipherKind, mode cipherMode) (cipher C.GO_EVP_CIPHER_PTR) { + if v, ok := cacheCipher.Load(cacheCipherKey{k, mode}); ok { return v.(C.GO_EVP_CIPHER_PTR) } defer func() { @@ -47,46 +52,49 @@ func newCipher(k cipherKind) (cipher C.GO_EVP_CIPHER_PTR) { // to fetch it on every call. Better to just fetch it once here. cipher = C.go_openssl_EVP_CIPHER_fetch(nil, C.go_openssl_EVP_CIPHER_get0_name(cipher), nil) } - cacheCipher.Store(k, cipher) + cacheCipher.Store(cacheCipherKey{k, mode}, cipher) }() switch k { - case cipherAES128_CBC: - cipher = C.go_openssl_EVP_aes_128_cbc() - case cipherAES192_CBC: - cipher = C.go_openssl_EVP_aes_192_cbc() - case cipherAES256_CBC: - cipher = C.go_openssl_EVP_aes_256_cbc() - case cipherAES128_ECB: - cipher = C.go_openssl_EVP_aes_128_ecb() - case cipherAES192_ECB: - cipher = C.go_openssl_EVP_aes_192_ecb() - case cipherAES256_ECB: - cipher = C.go_openssl_EVP_aes_256_ecb() - case cipherAES128_CTR: - cipher = C.go_openssl_EVP_aes_128_ctr() - case cipherAES192_CTR: - cipher = C.go_openssl_EVP_aes_192_ctr() - case cipherAES256_CTR: - cipher = C.go_openssl_EVP_aes_256_ctr() - case cipherAES128_GCM: - cipher = C.go_openssl_EVP_aes_128_gcm() - case cipherAES192_GCM: - cipher = C.go_openssl_EVP_aes_192_gcm() - case cipherAES256_GCM: - cipher = C.go_openssl_EVP_aes_256_gcm() + case cipherAES128: + switch mode { + case cipherModeECB: + cipher = C.go_openssl_EVP_aes_128_ecb() + case cipherModeCBC: + cipher = C.go_openssl_EVP_aes_128_cbc() + case cipherModeCTR: + cipher = C.go_openssl_EVP_aes_128_ctr() + case cipherModeGCM: + cipher = C.go_openssl_EVP_aes_128_gcm() + } + case cipherAES192: + switch mode { + case cipherModeECB: + cipher = C.go_openssl_EVP_aes_192_ecb() + case cipherModeCBC: + cipher = C.go_openssl_EVP_aes_192_cbc() + case cipherModeCTR: + cipher = C.go_openssl_EVP_aes_192_ctr() + case cipherModeGCM: + cipher = C.go_openssl_EVP_aes_192_gcm() + } + case cipherAES256: + switch mode { + case cipherModeECB: + cipher = C.go_openssl_EVP_aes_256_ecb() + case cipherModeCBC: + cipher = C.go_openssl_EVP_aes_256_cbc() + case cipherModeCTR: + cipher = C.go_openssl_EVP_aes_256_ctr() + case cipherModeGCM: + cipher = C.go_openssl_EVP_aes_256_gcm() + } } return cipher } -type aesKeySizeError int - -func (k aesKeySizeError) Error() string { - return "crypto/aes: invalid key size " + strconv.Itoa(int(k)) -} - const aesBlockSize = 16 -type aesCipher struct { +type evpCipher struct { key []byte enc_ctx C.GO_EVP_CIPHER_CTX_PTR dec_ctx C.GO_EVP_CIPHER_CTX_PTR @@ -107,26 +115,60 @@ type extraModes interface { var _ extraModes = (*aesCipher)(nil) func NewAESCipher(key []byte) (cipher.Block, error) { - c := &aesCipher{key: make([]byte, len(key))} + c := &evpCipher{key: make([]byte, len(key))} copy(c.key, key) switch len(c.key) * 8 { case 128: - c.kind = cipherAES128_ECB + c.kind = cipherAES128 case 192: - c.kind = cipherAES192_ECB + c.kind = cipherAES192 case 256: - c.kind = cipherAES256_ECB + c.kind = cipherAES256 default: - return nil, errors.New("crypto/cipher: Invalid key size") + return nil, errors.New("crypto/aes: invalid key size") } - runtime.SetFinalizer(c, (*aesCipher).finalize) + runtime.SetFinalizer(c, (*evpCipher).finalize) + + return &aesCipher{c}, nil +} + +type aesCipher struct { + *evpCipher +} + +func (c *aesCipher) BlockSize() int { return aesBlockSize } + +func (c *aesCipher) Encrypt(dst, src []byte) { + c.encrypt(dst, src) +} + +func (c *aesCipher) Decrypt(dst, src []byte) { + c.decrypt(dst, src) +} + +func (c *aesCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { + return c.newCBC(iv, true) +} + +func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { + return c.newCBC(iv, false) +} + +func (c *aesCipher) NewCTR(iv []byte) cipher.Stream { + return c.newCTR(iv) +} - return c, nil +func (c *aesCipher) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) { + return c.newGCMChecked(nonceSize, tagSize) } -func (c *aesCipher) finalize() { +func (c *aesCipher) NewGCMTLS() (cipher.AEAD, error) { + return c.newGCM(true) +} + +func (c *evpCipher) finalize() { if c.enc_ctx != nil { C.go_openssl_EVP_CIPHER_CTX_free(c.enc_ctx) } @@ -135,49 +177,58 @@ func (c *aesCipher) finalize() { } } -func (c *aesCipher) BlockSize() int { return aesBlockSize } +func (c *evpCipher) blockSize() int { + switch c.kind { + case cipherAES128, cipherAES192, cipherAES256: + return aesBlockSize + default: + panic("openssl: unsupported cipher: " + strconv.Itoa(int(c.kind))) + } +} -func (c *aesCipher) Encrypt(dst, src []byte) { - if len(src) < aesBlockSize { - panic("crypto/aes: input not full block") +func (c *evpCipher) encrypt(dst, src []byte) { + blockSize := c.blockSize() + if len(src) < blockSize { + panic("crypto/cipher: input not full block") } - if len(dst) < aesBlockSize { - panic("crypto/aes: output not full block") + if len(dst) < blockSize { + panic("crypto/cipher: output not full block") } // Only check for overlap between the parts of src and dst that will actually be used. // This matches Go standard library behavior. - if inexactOverlap(dst[:aesBlockSize], src[:aesBlockSize]) { + if inexactOverlap(dst[:blockSize], src[:blockSize]) { panic("crypto/cipher: invalid buffer overlap") } if c.enc_ctx == nil { var err error - c.enc_ctx, err = newCipherCtx(c.kind, C.GO_AES_ENCRYPT, c.key, nil) + c.enc_ctx, err = newCipherCtx(c.kind, cipherModeECB, 1, c.key, nil) if err != nil { panic(err) } } - if C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), aesBlockSize) != 1 { + if C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), C.int(blockSize)) != 1 { panic("crypto/cipher: EncryptUpdate failed") } runtime.KeepAlive(c) } -func (c *aesCipher) Decrypt(dst, src []byte) { - if len(src) < aesBlockSize { - panic("crypto/aes: input not full block") +func (c *evpCipher) decrypt(dst, src []byte) { + blockSize := c.blockSize() + if len(src) < blockSize { + panic("crypto/cipher: input not full block") } - if len(dst) < aesBlockSize { - panic("crypto/aes: output not full block") + if len(dst) < blockSize { + panic("crypto/cipher: output not full block") } // Only check for overlap between the parts of src and dst that will actually be used. // This matches Go standard library behavior. - if inexactOverlap(dst[:aesBlockSize], src[:aesBlockSize]) { + if inexactOverlap(dst[:blockSize], src[:blockSize]) { panic("crypto/cipher: invalid buffer overlap") } if c.dec_ctx == nil { var err error - c.dec_ctx, err = newCipherCtx(c.kind, C.GO_AES_DECRYPT, c.key, nil) + c.dec_ctx, err = newCipherCtx(c.kind, cipherModeECB, 0, c.key, nil) if err != nil { panic(err) } @@ -186,21 +237,26 @@ func (c *aesCipher) Decrypt(dst, src []byte) { } } - C.go_openssl_EVP_DecryptUpdate_wrapper(c.dec_ctx, base(dst), base(src), aesBlockSize) + C.go_openssl_EVP_DecryptUpdate_wrapper(c.dec_ctx, base(dst), base(src), C.int(blockSize)) runtime.KeepAlive(c) } -type aesCBC struct { - ctx C.GO_EVP_CIPHER_CTX_PTR +type cipherCBC struct { + ctx C.GO_EVP_CIPHER_CTX_PTR + blockSize int } -func (x *aesCBC) BlockSize() int { return aesBlockSize } +func (c *cipherCBC) finalize() { + C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) +} + +func (x *cipherCBC) BlockSize() int { return x.blockSize } -func (x *aesCBC) CryptBlocks(dst, src []byte) { +func (x *cipherCBC) CryptBlocks(dst, src []byte) { if inexactOverlap(dst, src) { panic("crypto/cipher: invalid buffer overlap") } - if len(src)%aesBlockSize != 0 { + if len(src)%x.blockSize != 0 { panic("crypto/cipher: input not full blocks") } if len(dst) < len(src) { @@ -214,8 +270,8 @@ func (x *aesCBC) CryptBlocks(dst, src []byte) { } } -func (x *aesCBC) SetIV(iv []byte) { - if len(iv) != aesBlockSize { +func (x *cipherCBC) SetIV(iv []byte) { + if len(iv) != x.blockSize { panic("cipher: incorrect length IV") } if C.go_openssl_EVP_CipherInit_ex(x.ctx, nil, nil, nil, base(iv), -1) != 1 { @@ -223,72 +279,28 @@ func (x *aesCBC) SetIV(iv []byte) { } } -func (c *aesCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { - x := new(aesCBC) - - var cipher cipherKind - switch len(c.key) * 8 { - case 128: - cipher = cipherAES128_CBC - case 192: - cipher = cipherAES192_CBC - case 256: - cipher = cipherAES256_CBC - default: - panic("openssl: unsupported key length") - } - var err error - x.ctx, err = newCipherCtx(cipher, C.GO_AES_ENCRYPT, c.key, iv) - if err != nil { - panic(err) - } - - runtime.SetFinalizer(x, (*aesCBC).finalize) - - if C.go_openssl_EVP_CIPHER_CTX_set_padding(x.ctx, 0) != 1 { - panic("cipher: unable to set padding") - } - return x -} - -func (c *aesCBC) finalize() { - C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) -} - -func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { - x := new(aesCBC) - - var cipher cipherKind - switch len(c.key) * 8 { - case 128: - cipher = cipherAES128_CBC - case 192: - cipher = cipherAES192_CBC - case 256: - cipher = cipherAES256_CBC - default: - panic("openssl: unsupported key length") +func (c *evpCipher) newCBC(iv []byte, encrypt bool) cipher.BlockMode { + enc := 1 + if !encrypt { + enc = 0 } - - var err error - x.ctx, err = newCipherCtx(cipher, C.GO_AES_DECRYPT, c.key, iv) + ctx, err := newCipherCtx(c.kind, cipherModeCBC, enc, c.key, iv) if err != nil { panic(err) } - - runtime.SetFinalizer(x, (*aesCBC).finalize) - + x := &cipherCBC{ctx: ctx, blockSize: c.blockSize()} + runtime.SetFinalizer(x, (*cipherCBC).finalize) if C.go_openssl_EVP_CIPHER_CTX_set_padding(x.ctx, 0) != 1 { panic("cipher: unable to set padding") } return x } -type aesCTR struct { +type cipherCTR struct { ctx C.GO_EVP_CIPHER_CTX_PTR } -func (x *aesCTR) XORKeyStream(dst, src []byte) { +func (x *cipherCTR) XORKeyStream(dst, src []byte) { if inexactOverlap(dst, src) { panic("crypto/cipher: invalid buffer overlap") } @@ -304,39 +316,25 @@ func (x *aesCTR) XORKeyStream(dst, src []byte) { runtime.KeepAlive(x) } -func (c *aesCipher) NewCTR(iv []byte) cipher.Stream { - x := new(aesCTR) - - var cipher cipherKind - switch len(c.key) * 8 { - case 128: - cipher = cipherAES128_CTR - case 192: - cipher = cipherAES192_CTR - case 256: - cipher = cipherAES256_CTR - default: - panic("openssl: unsupported key length") - } - var err error - x.ctx, err = newCipherCtx(cipher, C.GO_AES_ENCRYPT, c.key, iv) +func (c *evpCipher) newCTR(iv []byte) cipher.Stream { + ctx, err := newCipherCtx(c.kind, cipherModeCTR, 1, c.key, iv) if err != nil { panic(err) } - - runtime.SetFinalizer(x, (*aesCTR).finalize) - + x := &cipherCTR{ctx: ctx} + runtime.SetFinalizer(x, (*cipherCTR).finalize) return x } -func (c *aesCTR) finalize() { +func (c *cipherCTR) finalize() { C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) } -type aesGCM struct { +type cipherGCM struct { ctx C.GO_EVP_CIPHER_CTX_PTR tls bool minNextNonce uint64 + blockSize int } const ( @@ -346,19 +344,25 @@ const ( gcmTlsFixedNonceSize = 4 ) -type aesNonceSizeError int +type noGCM struct { + *evpCipher +} -func (n aesNonceSizeError) Error() string { - return "crypto/aes: invalid GCM nonce size " + strconv.Itoa(int(n)) +func (g *noGCM) BlockSize() int { + return g.blockSize() } -type noGCM struct { - cipher.Block +func (g *noGCM) Encrypt(dst, src []byte) { + g.encrypt(dst, src) } -func (c *aesCipher) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) { +func (g *noGCM) Decrypt(dst, src []byte) { + g.decrypt(dst, src) +} + +func (c *evpCipher) newGCMChecked(nonceSize, tagSize int) (cipher.AEAD, error) { if nonceSize != gcmStandardNonceSize && tagSize != gcmTagSize { - return nil, errors.New("crypto/aes: GCM tag and nonce sizes can't be non-standard at the same time") + return nil, errors.New("crypto/cipher: GCM tag and nonce sizes can't be non-standard at the same time") } // Fall back to standard library for GCM with non-standard nonce or tag size. if nonceSize != gcmStandardNonceSize { @@ -376,48 +380,33 @@ func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { return c.(*aesCipher).NewGCMTLS() } -func (c *aesCipher) NewGCMTLS() (cipher.AEAD, error) { - return c.newGCM(true) -} - -func (c *aesCipher) newGCM(tls bool) (cipher.AEAD, error) { - var cipher cipherKind - switch len(c.key) * 8 { - case 128: - cipher = cipherAES128_GCM - case 192: - cipher = cipherAES192_GCM - case 256: - cipher = cipherAES256_GCM - default: - panic("openssl: unsupported key length") - } - ctx, err := newCipherCtx(cipher, -1, c.key, nil) +func (c *evpCipher) newGCM(tls bool) (cipher.AEAD, error) { + ctx, err := newCipherCtx(c.kind, cipherModeGCM, -1, c.key, nil) if err != nil { return nil, err } - g := &aesGCM{ctx: ctx, tls: tls} - runtime.SetFinalizer(g, (*aesGCM).finalize) + g := &cipherGCM{ctx: ctx, tls: tls, blockSize: c.blockSize()} + runtime.SetFinalizer(g, (*cipherGCM).finalize) return g, nil } -func (g *aesGCM) finalize() { +func (g *cipherGCM) finalize() { C.go_openssl_EVP_CIPHER_CTX_free(g.ctx) } -func (g *aesGCM) NonceSize() int { +func (g *cipherGCM) NonceSize() int { return gcmStandardNonceSize } -func (g *aesGCM) Overhead() int { +func (g *cipherGCM) Overhead() int { return gcmTagSize } -func (g *aesGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte { +func (g *cipherGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte { if len(nonce) != gcmStandardNonceSize { panic("cipher: incorrect nonce length given to GCM") } - if uint64(len(plaintext)) > ((1<<32)-2)*aesBlockSize || len(plaintext)+gcmTagSize < len(plaintext) { + if uint64(len(plaintext)) > ((1<<32)-2)*uint64(g.blockSize) || len(plaintext)+gcmTagSize < len(plaintext) { panic("cipher: message too large for GCM") } if len(dst)+len(plaintext)+gcmTagSize < len(dst) { @@ -470,14 +459,14 @@ func (g *aesGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte { var errOpen = errors.New("cipher: message authentication failed") -func (g *aesGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { +func (g *cipherGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { if len(nonce) != gcmStandardNonceSize { panic("cipher: incorrect nonce length given to GCM") } if len(ciphertext) < gcmTagSize { return nil, errOpen } - if uint64(len(ciphertext)) > ((1<<32)-2)*aesBlockSize+gcmTagSize { + if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(g.blockSize)+gcmTagSize { return nil, errOpen } // BoringCrypto does not do any TLS check when decrypting, neither do we. @@ -520,8 +509,8 @@ func sliceForAppend(in []byte, n int) (head, tail []byte) { return } -func newCipherCtx(kind cipherKind, mode C.int, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) { - cipher := newCipher(kind) +func newCipherCtx(kind cipherKind, mode cipherMode, encrypt int, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) { + cipher := loadCipher(kind, mode) if cipher == nil { panic("openssl: unsupported cipher: " + strconv.Itoa(int(kind))) } @@ -529,7 +518,7 @@ func newCipherCtx(kind cipherKind, mode C.int, key, iv []byte) (C.GO_EVP_CIPHER_ if ctx == nil { return nil, fail("unable to create EVP cipher ctx") } - if C.go_openssl_EVP_CipherInit_ex(ctx, cipher, nil, base(key), base(iv), mode) != 1 { + if C.go_openssl_EVP_CipherInit_ex(ctx, cipher, nil, base(key), base(iv), C.int(encrypt)) != 1 { C.go_openssl_EVP_CIPHER_CTX_free(ctx) return nil, fail("unable to initialize EVP cipher ctx") } diff --git a/goopenssl.h b/goopenssl.h index dc2ce35c..a523ec61 100644 --- a/goopenssl.h +++ b/goopenssl.h @@ -119,7 +119,7 @@ go_openssl_EVP_CIPHER_CTX_seal_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx, if (in_len == 0) in = (const unsigned char *)""; if (aad_len == 0) aad = (const unsigned char *)""; - if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, GO_AES_ENCRYPT) != 1) + if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, 1) != 1) return 0; int discard_len, out_len; @@ -147,7 +147,7 @@ go_openssl_EVP_CIPHER_CTX_open_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx, if (in_len == 0) in = (const unsigned char *)""; if (aad_len == 0) aad = (const unsigned char *)""; - if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, GO_AES_DECRYPT) != 1) + if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, 0) != 1) return 0; int discard_len, out_len; diff --git a/shims.h b/shims.h index 47d8724e..b93529eb 100644 --- a/shims.h +++ b/shims.h @@ -9,12 +9,6 @@ enum { GO_OPENSSL_INIT_LOAD_CONFIG = 0x00000040L }; -// #include -enum { - GO_AES_ENCRYPT = 1, - GO_AES_DECRYPT = 0 -}; - // #include enum { GO_EVP_CTRL_GCM_GET_TAG = 0x10, From bb85915fa618341c6458e4c2ac70b4163e338f67 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 23 Aug 2023 13:16:00 +0200 Subject: [PATCH 3/8] move cipher-agnostic code to cipher.go --- aes.go | 467 +----------------------------------------------------- cipher.go | 466 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 472 insertions(+), 461 deletions(-) create mode 100644 cipher.go diff --git a/aes.go b/aes.go index 870305dc..a8759480 100644 --- a/aes.go +++ b/aes.go @@ -6,101 +6,12 @@ package openssl import "C" import ( "crypto/cipher" - "encoding/binary" "errors" "runtime" - "strconv" - "sync" - "unsafe" ) -type cipherKind int8 - -const ( - cipherAES128 cipherKind = iota - cipherAES192 - cipherAES256 -) - -type cipherMode int8 - -const ( - cipherModeECB cipherMode = iota - cipherModeCBC - cipherModeCTR - cipherModeGCM -) - -// cacheCipher is a cache of cipherKind to GO_EVP_CIPHER_PTR. -var cacheCipher sync.Map - -type cacheCipherKey struct { - kind cipherKind - mode cipherMode -} - -// loadCipher returns a cipher object for the given k. -func loadCipher(k cipherKind, mode cipherMode) (cipher C.GO_EVP_CIPHER_PTR) { - if v, ok := cacheCipher.Load(cacheCipherKey{k, mode}); ok { - return v.(C.GO_EVP_CIPHER_PTR) - } - defer func() { - if cipher != nil && vMajor == 3 { - // On OpenSSL 3, directly operating on a EVP_CIPHER object - // not created by EVP_CIPHER has negative performance - // implications, as cipher operations will have - // to fetch it on every call. Better to just fetch it once here. - cipher = C.go_openssl_EVP_CIPHER_fetch(nil, C.go_openssl_EVP_CIPHER_get0_name(cipher), nil) - } - cacheCipher.Store(cacheCipherKey{k, mode}, cipher) - }() - switch k { - case cipherAES128: - switch mode { - case cipherModeECB: - cipher = C.go_openssl_EVP_aes_128_ecb() - case cipherModeCBC: - cipher = C.go_openssl_EVP_aes_128_cbc() - case cipherModeCTR: - cipher = C.go_openssl_EVP_aes_128_ctr() - case cipherModeGCM: - cipher = C.go_openssl_EVP_aes_128_gcm() - } - case cipherAES192: - switch mode { - case cipherModeECB: - cipher = C.go_openssl_EVP_aes_192_ecb() - case cipherModeCBC: - cipher = C.go_openssl_EVP_aes_192_cbc() - case cipherModeCTR: - cipher = C.go_openssl_EVP_aes_192_ctr() - case cipherModeGCM: - cipher = C.go_openssl_EVP_aes_192_gcm() - } - case cipherAES256: - switch mode { - case cipherModeECB: - cipher = C.go_openssl_EVP_aes_256_ecb() - case cipherModeCBC: - cipher = C.go_openssl_EVP_aes_256_cbc() - case cipherModeCTR: - cipher = C.go_openssl_EVP_aes_256_ctr() - case cipherModeGCM: - cipher = C.go_openssl_EVP_aes_256_gcm() - } - } - return cipher -} - const aesBlockSize = 16 -type evpCipher struct { - key []byte - enc_ctx C.GO_EVP_CIPHER_CTX_PTR - dec_ctx C.GO_EVP_CIPHER_CTX_PTR - kind cipherKind -} - type extraModes interface { // Copied out of crypto/aes/modes.go. NewCBCEncrypter(iv []byte) cipher.BlockMode @@ -134,6 +45,12 @@ func NewAESCipher(key []byte) (cipher.Block, error) { return &aesCipher{c}, nil } +// NewGCMTLS returns a GCM cipher specific to TLS +// and should not be used for non-TLS purposes. +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { + return c.(*aesCipher).NewGCMTLS() +} + type aesCipher struct { *evpCipher } @@ -167,375 +84,3 @@ func (c *aesCipher) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) { func (c *aesCipher) NewGCMTLS() (cipher.AEAD, error) { return c.newGCM(true) } - -func (c *evpCipher) finalize() { - if c.enc_ctx != nil { - C.go_openssl_EVP_CIPHER_CTX_free(c.enc_ctx) - } - if c.dec_ctx != nil { - C.go_openssl_EVP_CIPHER_CTX_free(c.dec_ctx) - } -} - -func (c *evpCipher) blockSize() int { - switch c.kind { - case cipherAES128, cipherAES192, cipherAES256: - return aesBlockSize - default: - panic("openssl: unsupported cipher: " + strconv.Itoa(int(c.kind))) - } -} - -func (c *evpCipher) encrypt(dst, src []byte) { - blockSize := c.blockSize() - if len(src) < blockSize { - panic("crypto/cipher: input not full block") - } - if len(dst) < blockSize { - panic("crypto/cipher: output not full block") - } - // Only check for overlap between the parts of src and dst that will actually be used. - // This matches Go standard library behavior. - if inexactOverlap(dst[:blockSize], src[:blockSize]) { - panic("crypto/cipher: invalid buffer overlap") - } - if c.enc_ctx == nil { - var err error - c.enc_ctx, err = newCipherCtx(c.kind, cipherModeECB, 1, c.key, nil) - if err != nil { - panic(err) - } - } - - if C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), C.int(blockSize)) != 1 { - panic("crypto/cipher: EncryptUpdate failed") - } - runtime.KeepAlive(c) -} - -func (c *evpCipher) decrypt(dst, src []byte) { - blockSize := c.blockSize() - if len(src) < blockSize { - panic("crypto/cipher: input not full block") - } - if len(dst) < blockSize { - panic("crypto/cipher: output not full block") - } - // Only check for overlap between the parts of src and dst that will actually be used. - // This matches Go standard library behavior. - if inexactOverlap(dst[:blockSize], src[:blockSize]) { - panic("crypto/cipher: invalid buffer overlap") - } - if c.dec_ctx == nil { - var err error - c.dec_ctx, err = newCipherCtx(c.kind, cipherModeECB, 0, c.key, nil) - if err != nil { - panic(err) - } - if C.go_openssl_EVP_CIPHER_CTX_set_padding(c.dec_ctx, 0) != 1 { - panic("crypto/cipher: could not disable cipher padding") - } - } - - C.go_openssl_EVP_DecryptUpdate_wrapper(c.dec_ctx, base(dst), base(src), C.int(blockSize)) - runtime.KeepAlive(c) -} - -type cipherCBC struct { - ctx C.GO_EVP_CIPHER_CTX_PTR - blockSize int -} - -func (c *cipherCBC) finalize() { - C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) -} - -func (x *cipherCBC) BlockSize() int { return x.blockSize } - -func (x *cipherCBC) CryptBlocks(dst, src []byte) { - if inexactOverlap(dst, src) { - panic("crypto/cipher: invalid buffer overlap") - } - if len(src)%x.blockSize != 0 { - panic("crypto/cipher: input not full blocks") - } - if len(dst) < len(src) { - panic("crypto/cipher: output smaller than input") - } - if len(src) > 0 { - if C.go_openssl_EVP_CipherUpdate_wrapper(x.ctx, base(dst), base(src), C.int(len(src))) != 1 { - panic("crypto/cipher: CipherUpdate failed") - } - runtime.KeepAlive(x) - } -} - -func (x *cipherCBC) SetIV(iv []byte) { - if len(iv) != x.blockSize { - panic("cipher: incorrect length IV") - } - if C.go_openssl_EVP_CipherInit_ex(x.ctx, nil, nil, nil, base(iv), -1) != 1 { - panic("cipher: unable to initialize EVP cipher ctx") - } -} - -func (c *evpCipher) newCBC(iv []byte, encrypt bool) cipher.BlockMode { - enc := 1 - if !encrypt { - enc = 0 - } - ctx, err := newCipherCtx(c.kind, cipherModeCBC, enc, c.key, iv) - if err != nil { - panic(err) - } - x := &cipherCBC{ctx: ctx, blockSize: c.blockSize()} - runtime.SetFinalizer(x, (*cipherCBC).finalize) - if C.go_openssl_EVP_CIPHER_CTX_set_padding(x.ctx, 0) != 1 { - panic("cipher: unable to set padding") - } - return x -} - -type cipherCTR struct { - ctx C.GO_EVP_CIPHER_CTX_PTR -} - -func (x *cipherCTR) XORKeyStream(dst, src []byte) { - if inexactOverlap(dst, src) { - panic("crypto/cipher: invalid buffer overlap") - } - if len(dst) < len(src) { - panic("crypto/cipher: output smaller than input") - } - if len(src) == 0 { - return - } - if C.go_openssl_EVP_EncryptUpdate_wrapper(x.ctx, base(dst), base(src), C.int(len(src))) != 1 { - panic("crypto/cipher: EncryptUpdate failed") - } - runtime.KeepAlive(x) -} - -func (c *evpCipher) newCTR(iv []byte) cipher.Stream { - ctx, err := newCipherCtx(c.kind, cipherModeCTR, 1, c.key, iv) - if err != nil { - panic(err) - } - x := &cipherCTR{ctx: ctx} - runtime.SetFinalizer(x, (*cipherCTR).finalize) - return x -} - -func (c *cipherCTR) finalize() { - C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) -} - -type cipherGCM struct { - ctx C.GO_EVP_CIPHER_CTX_PTR - tls bool - minNextNonce uint64 - blockSize int -} - -const ( - gcmTagSize = 16 - gcmStandardNonceSize = 12 - gcmTlsAddSize = 13 - gcmTlsFixedNonceSize = 4 -) - -type noGCM struct { - *evpCipher -} - -func (g *noGCM) BlockSize() int { - return g.blockSize() -} - -func (g *noGCM) Encrypt(dst, src []byte) { - g.encrypt(dst, src) -} - -func (g *noGCM) Decrypt(dst, src []byte) { - g.decrypt(dst, src) -} - -func (c *evpCipher) newGCMChecked(nonceSize, tagSize int) (cipher.AEAD, error) { - if nonceSize != gcmStandardNonceSize && tagSize != gcmTagSize { - return nil, errors.New("crypto/cipher: GCM tag and nonce sizes can't be non-standard at the same time") - } - // Fall back to standard library for GCM with non-standard nonce or tag size. - if nonceSize != gcmStandardNonceSize { - return cipher.NewGCMWithNonceSize(&noGCM{c}, nonceSize) - } - if tagSize != gcmTagSize { - return cipher.NewGCMWithTagSize(&noGCM{c}, tagSize) - } - return c.newGCM(false) -} - -// NewGCMTLS returns a GCM cipher specific to TLS -// and should not be used for non-TLS purposes. -func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { - return c.(*aesCipher).NewGCMTLS() -} - -func (c *evpCipher) newGCM(tls bool) (cipher.AEAD, error) { - ctx, err := newCipherCtx(c.kind, cipherModeGCM, -1, c.key, nil) - if err != nil { - return nil, err - } - g := &cipherGCM{ctx: ctx, tls: tls, blockSize: c.blockSize()} - runtime.SetFinalizer(g, (*cipherGCM).finalize) - return g, nil -} - -func (g *cipherGCM) finalize() { - C.go_openssl_EVP_CIPHER_CTX_free(g.ctx) -} - -func (g *cipherGCM) NonceSize() int { - return gcmStandardNonceSize -} - -func (g *cipherGCM) Overhead() int { - return gcmTagSize -} - -func (g *cipherGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte { - if len(nonce) != gcmStandardNonceSize { - panic("cipher: incorrect nonce length given to GCM") - } - if uint64(len(plaintext)) > ((1<<32)-2)*uint64(g.blockSize) || len(plaintext)+gcmTagSize < len(plaintext) { - panic("cipher: message too large for GCM") - } - if len(dst)+len(plaintext)+gcmTagSize < len(dst) { - panic("cipher: message too large for buffer") - } - if g.tls { - if len(additionalData) != gcmTlsAddSize { - panic("cipher: incorrect additional data length given to GCM TLS") - } - // BoringCrypto enforces strictly monotonically increasing explicit nonces - // and to fail after 2^64 - 1 keys as per FIPS 140-2 IG A.5, - // but OpenSSL does not perform this check, so it is implemented here. - const maxUint64 = 1<<64 - 1 - counter := binary.BigEndian.Uint64(nonce[gcmTlsFixedNonceSize:]) - if counter == maxUint64 { - panic("cipher: nonce counter must be less than 2^64 - 1") - } - if counter < g.minNextNonce { - panic("cipher: nonce counter must be strictly monotonically increasing") - } - defer func() { - g.minNextNonce = counter + 1 - }() - } - - // Make room in dst to append plaintext+overhead. - ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize) - - // Check delayed until now to make sure len(dst) is accurate. - if inexactOverlap(out, plaintext) { - panic("cipher: invalid buffer overlap") - } - - // Encrypt additional data. - // When sealing a TLS payload, OpenSSL app sets the additional data using - // 'EVP_CIPHER_CTX_ctrl(g.ctx, C.EVP_CTRL_AEAD_TLS1_AAD, C.EVP_AEAD_TLS1_AAD_LEN, base(additionalData))'. - // This makes the explicit nonce component to monotonically increase on every Seal operation without - // relying in the explicit nonce being securely set externally, - // and it also gives some interesting speed gains. - // Unfortunately we can't use it because Go expects AEAD.Seal to honor the provided nonce. - if C.go_openssl_EVP_CIPHER_CTX_seal_wrapper(g.ctx, base(out), base(nonce), - base(plaintext), C.int(len(plaintext)), - base(additionalData), C.int(len(additionalData))) != 1 { - - panic(fail("EVP_CIPHER_CTX_seal")) - } - runtime.KeepAlive(g) - return ret -} - -var errOpen = errors.New("cipher: message authentication failed") - -func (g *cipherGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { - if len(nonce) != gcmStandardNonceSize { - panic("cipher: incorrect nonce length given to GCM") - } - if len(ciphertext) < gcmTagSize { - return nil, errOpen - } - if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(g.blockSize)+gcmTagSize { - return nil, errOpen - } - // BoringCrypto does not do any TLS check when decrypting, neither do we. - - tag := ciphertext[len(ciphertext)-gcmTagSize:] - ciphertext = ciphertext[:len(ciphertext)-gcmTagSize] - - // Make room in dst to append ciphertext without tag. - ret, out := sliceForAppend(dst, len(ciphertext)) - - // Check delayed until now to make sure len(dst) is accurate. - if inexactOverlap(out, ciphertext) { - panic("cipher: invalid buffer overlap") - } - - ok := C.go_openssl_EVP_CIPHER_CTX_open_wrapper( - g.ctx, base(out), base(nonce), - base(ciphertext), C.int(len(ciphertext)), - base(additionalData), C.int(len(additionalData)), base(tag)) - runtime.KeepAlive(g) - if ok == 0 { - // Zero output buffer on error. - for i := range out { - out[i] = 0 - } - return nil, errOpen - } - return ret, nil -} - -// sliceForAppend is a mirror of crypto/cipher.sliceForAppend. -func sliceForAppend(in []byte, n int) (head, tail []byte) { - if total := len(in) + n; cap(in) >= total { - head = in[:total] - } else { - head = make([]byte, total) - copy(head, in) - } - tail = head[len(in):] - return -} - -func newCipherCtx(kind cipherKind, mode cipherMode, encrypt int, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) { - cipher := loadCipher(kind, mode) - if cipher == nil { - panic("openssl: unsupported cipher: " + strconv.Itoa(int(kind))) - } - ctx := C.go_openssl_EVP_CIPHER_CTX_new() - if ctx == nil { - return nil, fail("unable to create EVP cipher ctx") - } - if C.go_openssl_EVP_CipherInit_ex(ctx, cipher, nil, base(key), base(iv), C.int(encrypt)) != 1 { - C.go_openssl_EVP_CIPHER_CTX_free(ctx) - return nil, fail("unable to initialize EVP cipher ctx") - } - return ctx, nil -} - -// The following two functions are a mirror of golang.org/x/crypto/internal/subtle. - -func anyOverlap(x, y []byte) bool { - return len(x) > 0 && len(y) > 0 && - uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) && - uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1])) -} - -func inexactOverlap(x, y []byte) bool { - if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { - return false - } - return anyOverlap(x, y) -} diff --git a/cipher.go b/cipher.go new file mode 100644 index 00000000..aa7ed20f --- /dev/null +++ b/cipher.go @@ -0,0 +1,466 @@ +//go:build linux && !cmd_go_bootstrap + +package openssl + +// #include "goopenssl.h" +import "C" +import ( + "crypto/cipher" + "encoding/binary" + "errors" + "runtime" + "strconv" + "sync" + "unsafe" +) + +type cipherKind int8 + +const ( + cipherAES128 cipherKind = iota + cipherAES192 + cipherAES256 +) + +type cipherMode int8 + +const ( + cipherModeECB cipherMode = iota + cipherModeCBC + cipherModeCTR + cipherModeGCM +) + +// cacheCipher is a cache of cipherKind to GO_EVP_CIPHER_PTR. +var cacheCipher sync.Map + +type cacheCipherKey struct { + kind cipherKind + mode cipherMode +} + +// loadCipher returns a cipher object for the given k. +func loadCipher(k cipherKind, mode cipherMode) (cipher C.GO_EVP_CIPHER_PTR) { + if v, ok := cacheCipher.Load(cacheCipherKey{k, mode}); ok { + return v.(C.GO_EVP_CIPHER_PTR) + } + defer func() { + if cipher != nil && vMajor == 3 { + // On OpenSSL 3, directly operating on a EVP_CIPHER object + // not created by EVP_CIPHER has negative performance + // implications, as cipher operations will have + // to fetch it on every call. Better to just fetch it once here. + cipher = C.go_openssl_EVP_CIPHER_fetch(nil, C.go_openssl_EVP_CIPHER_get0_name(cipher), nil) + } + cacheCipher.Store(cacheCipherKey{k, mode}, cipher) + }() + switch k { + case cipherAES128: + switch mode { + case cipherModeECB: + cipher = C.go_openssl_EVP_aes_128_ecb() + case cipherModeCBC: + cipher = C.go_openssl_EVP_aes_128_cbc() + case cipherModeCTR: + cipher = C.go_openssl_EVP_aes_128_ctr() + case cipherModeGCM: + cipher = C.go_openssl_EVP_aes_128_gcm() + } + case cipherAES192: + switch mode { + case cipherModeECB: + cipher = C.go_openssl_EVP_aes_192_ecb() + case cipherModeCBC: + cipher = C.go_openssl_EVP_aes_192_cbc() + case cipherModeCTR: + cipher = C.go_openssl_EVP_aes_192_ctr() + case cipherModeGCM: + cipher = C.go_openssl_EVP_aes_192_gcm() + } + case cipherAES256: + switch mode { + case cipherModeECB: + cipher = C.go_openssl_EVP_aes_256_ecb() + case cipherModeCBC: + cipher = C.go_openssl_EVP_aes_256_cbc() + case cipherModeCTR: + cipher = C.go_openssl_EVP_aes_256_ctr() + case cipherModeGCM: + cipher = C.go_openssl_EVP_aes_256_gcm() + } + } + return cipher +} + +type evpCipher struct { + key []byte + enc_ctx C.GO_EVP_CIPHER_CTX_PTR + dec_ctx C.GO_EVP_CIPHER_CTX_PTR + kind cipherKind +} + +func (c *evpCipher) finalize() { + if c.enc_ctx != nil { + C.go_openssl_EVP_CIPHER_CTX_free(c.enc_ctx) + } + if c.dec_ctx != nil { + C.go_openssl_EVP_CIPHER_CTX_free(c.dec_ctx) + } +} + +func (c *evpCipher) blockSize() int { + switch c.kind { + case cipherAES128, cipherAES192, cipherAES256: + return aesBlockSize + default: + panic("openssl: unsupported cipher: " + strconv.Itoa(int(c.kind))) + } +} + +func (c *evpCipher) encrypt(dst, src []byte) { + blockSize := c.blockSize() + if len(src) < blockSize { + panic("crypto/cipher: input not full block") + } + if len(dst) < blockSize { + panic("crypto/cipher: output not full block") + } + // Only check for overlap between the parts of src and dst that will actually be used. + // This matches Go standard library behavior. + if inexactOverlap(dst[:blockSize], src[:blockSize]) { + panic("crypto/cipher: invalid buffer overlap") + } + if c.enc_ctx == nil { + var err error + c.enc_ctx, err = newCipherCtx(c.kind, cipherModeECB, 1, c.key, nil) + if err != nil { + panic(err) + } + } + + if C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), C.int(blockSize)) != 1 { + panic("crypto/cipher: EncryptUpdate failed") + } + runtime.KeepAlive(c) +} + +func (c *evpCipher) decrypt(dst, src []byte) { + blockSize := c.blockSize() + if len(src) < blockSize { + panic("crypto/cipher: input not full block") + } + if len(dst) < blockSize { + panic("crypto/cipher: output not full block") + } + // Only check for overlap between the parts of src and dst that will actually be used. + // This matches Go standard library behavior. + if inexactOverlap(dst[:blockSize], src[:blockSize]) { + panic("crypto/cipher: invalid buffer overlap") + } + if c.dec_ctx == nil { + var err error + c.dec_ctx, err = newCipherCtx(c.kind, cipherModeECB, 0, c.key, nil) + if err != nil { + panic(err) + } + if C.go_openssl_EVP_CIPHER_CTX_set_padding(c.dec_ctx, 0) != 1 { + panic("crypto/cipher: could not disable cipher padding") + } + } + + C.go_openssl_EVP_DecryptUpdate_wrapper(c.dec_ctx, base(dst), base(src), C.int(blockSize)) + runtime.KeepAlive(c) +} + +type cipherCBC struct { + ctx C.GO_EVP_CIPHER_CTX_PTR + blockSize int +} + +func (c *cipherCBC) finalize() { + C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) +} + +func (x *cipherCBC) BlockSize() int { return x.blockSize } + +func (x *cipherCBC) CryptBlocks(dst, src []byte) { + if inexactOverlap(dst, src) { + panic("crypto/cipher: invalid buffer overlap") + } + if len(src)%x.blockSize != 0 { + panic("crypto/cipher: input not full blocks") + } + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if len(src) > 0 { + if C.go_openssl_EVP_CipherUpdate_wrapper(x.ctx, base(dst), base(src), C.int(len(src))) != 1 { + panic("crypto/cipher: CipherUpdate failed") + } + runtime.KeepAlive(x) + } +} + +func (x *cipherCBC) SetIV(iv []byte) { + if len(iv) != x.blockSize { + panic("cipher: incorrect length IV") + } + if C.go_openssl_EVP_CipherInit_ex(x.ctx, nil, nil, nil, base(iv), -1) != 1 { + panic("cipher: unable to initialize EVP cipher ctx") + } +} + +func (c *evpCipher) newCBC(iv []byte, encrypt bool) cipher.BlockMode { + enc := 1 + if !encrypt { + enc = 0 + } + ctx, err := newCipherCtx(c.kind, cipherModeCBC, enc, c.key, iv) + if err != nil { + panic(err) + } + x := &cipherCBC{ctx: ctx, blockSize: c.blockSize()} + runtime.SetFinalizer(x, (*cipherCBC).finalize) + if C.go_openssl_EVP_CIPHER_CTX_set_padding(x.ctx, 0) != 1 { + panic("cipher: unable to set padding") + } + return x +} + +type cipherCTR struct { + ctx C.GO_EVP_CIPHER_CTX_PTR +} + +func (x *cipherCTR) XORKeyStream(dst, src []byte) { + if inexactOverlap(dst, src) { + panic("crypto/cipher: invalid buffer overlap") + } + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if len(src) == 0 { + return + } + if C.go_openssl_EVP_EncryptUpdate_wrapper(x.ctx, base(dst), base(src), C.int(len(src))) != 1 { + panic("crypto/cipher: EncryptUpdate failed") + } + runtime.KeepAlive(x) +} + +func (c *evpCipher) newCTR(iv []byte) cipher.Stream { + ctx, err := newCipherCtx(c.kind, cipherModeCTR, 1, c.key, iv) + if err != nil { + panic(err) + } + x := &cipherCTR{ctx: ctx} + runtime.SetFinalizer(x, (*cipherCTR).finalize) + return x +} + +func (c *cipherCTR) finalize() { + C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) +} + +type cipherGCM struct { + ctx C.GO_EVP_CIPHER_CTX_PTR + tls bool + minNextNonce uint64 + blockSize int +} + +const ( + gcmTagSize = 16 + gcmStandardNonceSize = 12 + gcmTlsAddSize = 13 + gcmTlsFixedNonceSize = 4 +) + +type noGCM struct { + *evpCipher +} + +func (g *noGCM) BlockSize() int { + return g.blockSize() +} + +func (g *noGCM) Encrypt(dst, src []byte) { + g.encrypt(dst, src) +} + +func (g *noGCM) Decrypt(dst, src []byte) { + g.decrypt(dst, src) +} + +func (c *evpCipher) newGCMChecked(nonceSize, tagSize int) (cipher.AEAD, error) { + if nonceSize != gcmStandardNonceSize && tagSize != gcmTagSize { + return nil, errors.New("crypto/cipher: GCM tag and nonce sizes can't be non-standard at the same time") + } + // Fall back to standard library for GCM with non-standard nonce or tag size. + if nonceSize != gcmStandardNonceSize { + return cipher.NewGCMWithNonceSize(&noGCM{c}, nonceSize) + } + if tagSize != gcmTagSize { + return cipher.NewGCMWithTagSize(&noGCM{c}, tagSize) + } + return c.newGCM(false) +} + +func (c *evpCipher) newGCM(tls bool) (cipher.AEAD, error) { + ctx, err := newCipherCtx(c.kind, cipherModeGCM, -1, c.key, nil) + if err != nil { + return nil, err + } + g := &cipherGCM{ctx: ctx, tls: tls, blockSize: c.blockSize()} + runtime.SetFinalizer(g, (*cipherGCM).finalize) + return g, nil +} + +func (g *cipherGCM) finalize() { + C.go_openssl_EVP_CIPHER_CTX_free(g.ctx) +} + +func (g *cipherGCM) NonceSize() int { + return gcmStandardNonceSize +} + +func (g *cipherGCM) Overhead() int { + return gcmTagSize +} + +func (g *cipherGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte { + if len(nonce) != gcmStandardNonceSize { + panic("cipher: incorrect nonce length given to GCM") + } + if uint64(len(plaintext)) > ((1<<32)-2)*uint64(g.blockSize) || len(plaintext)+gcmTagSize < len(plaintext) { + panic("cipher: message too large for GCM") + } + if len(dst)+len(plaintext)+gcmTagSize < len(dst) { + panic("cipher: message too large for buffer") + } + if g.tls { + if len(additionalData) != gcmTlsAddSize { + panic("cipher: incorrect additional data length given to GCM TLS") + } + // BoringCrypto enforces strictly monotonically increasing explicit nonces + // and to fail after 2^64 - 1 keys as per FIPS 140-2 IG A.5, + // but OpenSSL does not perform this check, so it is implemented here. + const maxUint64 = 1<<64 - 1 + counter := binary.BigEndian.Uint64(nonce[gcmTlsFixedNonceSize:]) + if counter == maxUint64 { + panic("cipher: nonce counter must be less than 2^64 - 1") + } + if counter < g.minNextNonce { + panic("cipher: nonce counter must be strictly monotonically increasing") + } + defer func() { + g.minNextNonce = counter + 1 + }() + } + + // Make room in dst to append plaintext+overhead. + ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize) + + // Check delayed until now to make sure len(dst) is accurate. + if inexactOverlap(out, plaintext) { + panic("cipher: invalid buffer overlap") + } + + // Encrypt additional data. + // When sealing a TLS payload, OpenSSL app sets the additional data using + // 'EVP_CIPHER_CTX_ctrl(g.ctx, C.EVP_CTRL_AEAD_TLS1_AAD, C.EVP_AEAD_TLS1_AAD_LEN, base(additionalData))'. + // This makes the explicit nonce component to monotonically increase on every Seal operation without + // relying in the explicit nonce being securely set externally, + // and it also gives some interesting speed gains. + // Unfortunately we can't use it because Go expects AEAD.Seal to honor the provided nonce. + if C.go_openssl_EVP_CIPHER_CTX_seal_wrapper(g.ctx, base(out), base(nonce), + base(plaintext), C.int(len(plaintext)), + base(additionalData), C.int(len(additionalData))) != 1 { + + panic(fail("EVP_CIPHER_CTX_seal")) + } + runtime.KeepAlive(g) + return ret +} + +var errOpen = errors.New("cipher: message authentication failed") + +func (g *cipherGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + if len(nonce) != gcmStandardNonceSize { + panic("cipher: incorrect nonce length given to GCM") + } + if len(ciphertext) < gcmTagSize { + return nil, errOpen + } + if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(g.blockSize)+gcmTagSize { + return nil, errOpen + } + // BoringCrypto does not do any TLS check when decrypting, neither do we. + + tag := ciphertext[len(ciphertext)-gcmTagSize:] + ciphertext = ciphertext[:len(ciphertext)-gcmTagSize] + + // Make room in dst to append ciphertext without tag. + ret, out := sliceForAppend(dst, len(ciphertext)) + + // Check delayed until now to make sure len(dst) is accurate. + if inexactOverlap(out, ciphertext) { + panic("cipher: invalid buffer overlap") + } + + ok := C.go_openssl_EVP_CIPHER_CTX_open_wrapper( + g.ctx, base(out), base(nonce), + base(ciphertext), C.int(len(ciphertext)), + base(additionalData), C.int(len(additionalData)), base(tag)) + runtime.KeepAlive(g) + if ok == 0 { + // Zero output buffer on error. + for i := range out { + out[i] = 0 + } + return nil, errOpen + } + return ret, nil +} + +// sliceForAppend is a mirror of crypto/cipher.sliceForAppend. +func sliceForAppend(in []byte, n int) (head, tail []byte) { + if total := len(in) + n; cap(in) >= total { + head = in[:total] + } else { + head = make([]byte, total) + copy(head, in) + } + tail = head[len(in):] + return +} + +func newCipherCtx(kind cipherKind, mode cipherMode, encrypt int, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) { + cipher := loadCipher(kind, mode) + if cipher == nil { + panic("openssl: unsupported cipher: " + strconv.Itoa(int(kind))) + } + ctx := C.go_openssl_EVP_CIPHER_CTX_new() + if ctx == nil { + return nil, fail("unable to create EVP cipher ctx") + } + if C.go_openssl_EVP_CipherInit_ex(ctx, cipher, nil, base(key), base(iv), C.int(encrypt)) != 1 { + C.go_openssl_EVP_CIPHER_CTX_free(ctx) + return nil, fail("unable to initialize EVP cipher ctx") + } + return ctx, nil +} + +// The following two functions are a mirror of golang.org/x/crypto/internal/subtle. + +func anyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) && + uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1])) +} + +func inexactOverlap(x, y []byte) bool { + if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { + return false + } + return anyOverlap(x, y) +} From 00cfa2300ebcf165f1de2c80f2d7682f1ae3997d Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 23 Aug 2023 13:24:01 +0200 Subject: [PATCH 4/8] implement DES --- cipher.go | 18 + des.go | 82 +++ des_test.go | 1736 +++++++++++++++++++++++++++++++++++++++++++++++++++ shims.h | 4 + 4 files changed, 1840 insertions(+) create mode 100644 des.go create mode 100644 des_test.go diff --git a/cipher.go b/cipher.go index aa7ed20f..a1edcb9e 100644 --- a/cipher.go +++ b/cipher.go @@ -20,6 +20,8 @@ const ( cipherAES128 cipherKind = iota cipherAES192 cipherAES256 + cipherDES + cipherDES3 ) type cipherMode int8 @@ -88,6 +90,20 @@ func loadCipher(k cipherKind, mode cipherMode) (cipher C.GO_EVP_CIPHER_PTR) { case cipherModeGCM: cipher = C.go_openssl_EVP_aes_256_gcm() } + case cipherDES: + switch mode { + case cipherModeECB: + cipher = C.go_openssl_EVP_des_ecb() + case cipherModeCBC: + cipher = C.go_openssl_EVP_des_cbc() + } + case cipherDES3: + switch mode { + case cipherModeECB: + cipher = C.go_openssl_EVP_des_ede3_ecb() + case cipherModeCBC: + cipher = C.go_openssl_EVP_des_ede3_cbc() + } } return cipher } @@ -112,6 +128,8 @@ func (c *evpCipher) blockSize() int { switch c.kind { case cipherAES128, cipherAES192, cipherAES256: return aesBlockSize + case cipherDES, cipherDES3: + return desBlockSize default: panic("openssl: unsupported cipher: " + strconv.Itoa(int(c.kind))) } diff --git a/des.go b/des.go new file mode 100644 index 00000000..f2c69607 --- /dev/null +++ b/des.go @@ -0,0 +1,82 @@ +//go:build linux && !cmd_go_bootstrap + +package openssl + +// #include "goopenssl.h" +import "C" +import ( + "crypto/cipher" + "errors" + "runtime" +) + +const desBlockSize = 8 + +// SupportsDESCipher returns true if NewDESCipher is supported. +func SupportsDESCipher() bool { + // True for stock OpenSSL 1. + // False for stock OpenSSL 3 unless the legacy provider is available. + return loadCipher(cipherDES, cipherModeECB) != nil +} + +// SupportsTripleDESCipher returns true if NewTripleDESCipher is supported. +func SupportsTripleDESCipher() bool { + // Should always be true for stock OpenSSL, + // even when using the FIPS provider. + return loadCipher(cipherDES3, cipherModeECB) != nil +} + +func NewDESCipher(key []byte) (cipher.Block, error) { + if !SupportsDESCipher() { + return nil, errors.New("crypto/des: not supported") + } + if len(key) != 8 { + return nil, errors.New("crypto/des: invalid key size") + } + c := &evpCipher{key: make([]byte, len(key)), kind: cipherDES} + copy(c.key, key) + runtime.SetFinalizer(c, (*evpCipher).finalize) + return &desCipher{c}, nil +} + +func NewTripleDESCipher(key []byte) (cipher.Block, error) { + if !SupportsTripleDESCipher() { + return nil, errors.New("crypto/des: not supported") + } + if len(key) != 24 { + return nil, errors.New("crypto/des: invalid key size") + } + c := &evpCipher{key: make([]byte, len(key)), kind: cipherDES3} + copy(c.key, key) + runtime.SetFinalizer(c, (*evpCipher).finalize) + return &desCipher{c}, nil +} + +type desExtraModes interface { + NewCBCEncrypter(iv []byte) cipher.BlockMode + NewCBCDecrypter(iv []byte) cipher.BlockMode +} + +var _ desExtraModes = (*desCipher)(nil) + +type desCipher struct { + *evpCipher +} + +func (c *desCipher) BlockSize() int { return desBlockSize } + +func (c *desCipher) Encrypt(dst, src []byte) { + c.encrypt(dst, src) +} + +func (c *desCipher) Decrypt(dst, src []byte) { + c.decrypt(dst, src) +} + +func (c *desCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { + return c.newCBC(iv, true) +} + +func (c *desCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { + return c.newCBC(iv, false) +} diff --git a/des_test.go b/des_test.go new file mode 100644 index 00000000..32eaee41 --- /dev/null +++ b/des_test.go @@ -0,0 +1,1736 @@ +//go:build linux + +package openssl_test + +import ( + "bytes" + "crypto/cipher" + "testing" + + "github.com/golang-fips/openssl/v2" +) + +type CryptTest struct { + key []byte + in []byte + out []byte +} + +// some custom tests for DES +var encryptDESTests = []CryptTest{ + { + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x8c, 0xa6, 0x4d, 0xe9, 0xc1, 0xb1, 0x23, 0xa7}}, + { + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0x35, 0x55, 0x50, 0xb2, 0x15, 0x0e, 0x24, 0x51}}, + { + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0x61, 0x7b, 0x3a, 0x0c, 0xe8, 0xf0, 0x71, 0x00}}, + { + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, + []byte{0x92, 0x31, 0xf2, 0x36, 0xff, 0x9a, 0xa9, 0x5c}}, + { + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xca, 0xaa, 0xaf, 0x4d, 0xea, 0xf1, 0xdb, 0xae}}, + { + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0x73, 0x59, 0xb2, 0x16, 0x3e, 0x4e, 0xdc, 0x58}}, + { + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0x6d, 0xce, 0x0d, 0xc9, 0x00, 0x65, 0x56, 0xa3}}, + { + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, + []byte{0x9e, 0x84, 0xc5, 0xf3, 0x17, 0x0f, 0x8e, 0xff}}, + { + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xd5, 0xd4, 0x4f, 0xf7, 0x20, 0x68, 0x3d, 0x0d}}, + { + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0x59, 0x73, 0x23, 0x56, 0xf3, 0x6f, 0xde, 0x06}}, + { + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0x56, 0xcc, 0x09, 0xe7, 0xcf, 0xdc, 0x4c, 0xef}}, + { + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, + []byte{0x12, 0xc6, 0x26, 0xaf, 0x05, 0x8b, 0x43, 0x3b}}, + { + []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xa6, 0x8c, 0xdc, 0xa9, 0x0c, 0x90, 0x21, 0xf9}}, + { + []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0x2a, 0x2b, 0xb0, 0x08, 0xdf, 0x97, 0xc2, 0xf2}}, + { + []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0xed, 0x39, 0xd9, 0x50, 0xfa, 0x74, 0xbc, 0xc4}}, + { + []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, + []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, + []byte{0xa9, 0x33, 0xf6, 0x18, 0x30, 0x23, 0xb3, 0x10}}, + { + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, + []byte{0x17, 0x66, 0x8d, 0xfc, 0x72, 0x92, 0x53, 0x2d}}, + { + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + []byte{0xb4, 0xfd, 0x23, 0x16, 0x47, 0xa5, 0xbe, 0xc0}}, + { + []byte{0x0e, 0x32, 0x92, 0x32, 0xea, 0x6d, 0x0d, 0x73}, + []byte{0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + { + []byte{0x73, 0x65, 0x63, 0x52, 0x33, 0x74, 0x24, 0x3b}, // "secR3t$;" + []byte{0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32}, // "a test12" + []byte{0x37, 0x0d, 0xee, 0x2c, 0x1f, 0xb4, 0xf7, 0xa5}}, + { + []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68}, // "abcdefgh" + []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68}, // "abcdefgh" + []byte{0x2a, 0x8d, 0x69, 0xde, 0x9d, 0x5f, 0xdf, 0xf9}}, + { + []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68}, // "abcdefgh" + []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}, // "12345678" + []byte{0x21, 0xc6, 0x0d, 0xa5, 0x34, 0x24, 0x8b, 0xce}}, + { + []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}, // "12345678" + []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68}, // "abcdefgh" + []byte{0x94, 0xd4, 0x43, 0x6b, 0xc3, 0xb5, 0xb6, 0x93}}, + { + []byte{0x1f, 0x79, 0x90, 0x5f, 0x88, 0x01, 0xc8, 0x88}, // random + []byte{0xc7, 0x46, 0x18, 0x73, 0xaf, 0x48, 0x5f, 0xb3}, // random + []byte{0xb0, 0x93, 0x50, 0x88, 0xf9, 0x92, 0x44, 0x6a}}, + { + []byte{0xe6, 0xf4, 0xf2, 0xdb, 0x31, 0x42, 0x53, 0x01}, // random + []byte{0xff, 0x3d, 0x25, 0x50, 0x12, 0xe3, 0x4a, 0xc5}, // random + []byte{0x86, 0x08, 0xd3, 0xd1, 0x6c, 0x2f, 0xd2, 0x55}}, + { + []byte{0x69, 0xc1, 0x9d, 0xc1, 0x15, 0xc5, 0xfb, 0x2b}, // random + []byte{0x1a, 0x22, 0x5c, 0xaf, 0x1f, 0x1d, 0xa3, 0xf9}, // random + []byte{0x64, 0xba, 0x31, 0x67, 0x56, 0x91, 0x1e, 0xa7}}, + { + []byte{0x6e, 0x5e, 0xe2, 0x47, 0xc4, 0xbf, 0xf6, 0x51}, // random + []byte{0x11, 0xc9, 0x57, 0xff, 0x66, 0x89, 0x0e, 0xf0}, // random + []byte{0x94, 0xc5, 0x35, 0xb2, 0xc5, 0x8b, 0x39, 0x72}}, +} + +var weakKeyTests = []CryptTest{ + { + []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + []byte{0x55, 0x74, 0xc0, 0xbd, 0x7c, 0xdf, 0xf7, 0x39}, // random + nil}, + { + []byte{0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe}, + []byte{0xe8, 0xe1, 0xa7, 0xc1, 0xde, 0x11, 0x89, 0xaa}, // random + nil}, + { + []byte{0xe0, 0xe0, 0xe0, 0xe0, 0xf1, 0xf1, 0xf1, 0xf1}, + []byte{0x50, 0x6a, 0x4b, 0x94, 0x3b, 0xed, 0x7d, 0xdc}, // random + nil}, + { + []byte{0x1f, 0x1f, 0x1f, 0x1f, 0x0e, 0x0e, 0x0e, 0x0e}, + []byte{0x88, 0x81, 0x56, 0x38, 0xec, 0x3b, 0x1c, 0x97}, // random + nil}, + { + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x17, 0xa0, 0x83, 0x62, 0x32, 0xfe, 0x9a, 0x0b}, // random + nil}, + { + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0xca, 0x8f, 0xca, 0x1f, 0x50, 0xc5, 0x7b, 0x49}, // random + nil}, + { + []byte{0xe1, 0xe1, 0xe1, 0xe1, 0xf0, 0xf0, 0xf0, 0xf0}, + []byte{0xb1, 0xea, 0xad, 0x7d, 0xe7, 0xc3, 0x7a, 0x43}, // random + nil}, + { + []byte{0x1e, 0x1e, 0x1e, 0x1e, 0x0f, 0x0f, 0x0f, 0x0f}, + []byte{0xae, 0x74, 0x7d, 0x6f, 0xef, 0x16, 0xbb, 0x81}, // random + nil}, +} + +var semiWeakKeyTests = []CryptTest{ + // key and out contain the semi-weak key pair + { + []byte{0x01, 0x1f, 0x01, 0x1f, 0x01, 0x0e, 0x01, 0x0e}, + []byte{0x12, 0xfa, 0x31, 0x16, 0xf9, 0xc5, 0x0a, 0xe4}, // random + []byte{0x1f, 0x01, 0x1f, 0x01, 0x0e, 0x01, 0x0e, 0x01}}, + { + []byte{0x01, 0xe0, 0x01, 0xe0, 0x01, 0xf1, 0x01, 0xf1}, + []byte{0xb0, 0x4c, 0x7a, 0xee, 0xd2, 0xe5, 0x4d, 0xb7}, // random + []byte{0xe0, 0x01, 0xe0, 0x01, 0xf1, 0x01, 0xf1, 0x01}}, + { + []byte{0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe}, + []byte{0xa4, 0x81, 0xcd, 0xb1, 0x64, 0x6f, 0xd3, 0xbc}, // random + []byte{0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01}}, + { + []byte{0x1f, 0xe0, 0x1f, 0xe0, 0x0e, 0xf1, 0x0e, 0xf1}, + []byte{0xee, 0x27, 0xdd, 0x88, 0x4c, 0x22, 0xcd, 0xce}, // random + []byte{0xe0, 0x1f, 0xe0, 0x1f, 0xf1, 0x0e, 0xf1, 0x0e}}, + { + []byte{0x1f, 0xfe, 0x1f, 0xfe, 0x0e, 0xfe, 0x0e, 0xfe}, + []byte{0x19, 0x3d, 0xcf, 0x97, 0x70, 0xfb, 0xab, 0xe1}, // random + []byte{0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x0e, 0xfe, 0x0e}}, + { + []byte{0xe0, 0xfe, 0xe0, 0xfe, 0xf1, 0xfe, 0xf1, 0xfe}, + []byte{0x7c, 0x82, 0x69, 0xe4, 0x1e, 0x86, 0x99, 0xd7}, // random + []byte{0xfe, 0xe0, 0xfe, 0xe0, 0xfe, 0xf1, 0xfe, 0xf1}}, +} + +// some custom tests for TripleDES +var encryptTripleDESTests = []CryptTest{ + { + []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x92, 0x95, 0xb5, 0x9b, 0xb3, 0x84, 0x73, 0x6e}}, + { + []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0xc1, 0x97, 0xf5, 0x58, 0x74, 0x8a, 0x20, 0xe7}}, + { + []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x3e, 0x68, 0x0a, 0xa7, 0x8b, 0x75, 0xdf, 0x18}}, + { + []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + []byte{0x6d, 0x6a, 0x4a, 0x64, 0x4c, 0x7b, 0x8c, 0x91}}, + { + []byte{ // "abcdefgh12345678ABCDEFGH" + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, + []byte{0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30}, // "00000000" + []byte{0xe4, 0x61, 0xb7, 0x59, 0x68, 0x8b, 0xff, 0x66}}, + { + []byte{ // "abcdefgh12345678ABCDEFGH" + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, + []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}, // "12345678" + []byte{0xdb, 0xd0, 0x92, 0xde, 0xf8, 0x34, 0xff, 0x58}}, + { + []byte{ // "abcdefgh12345678ABCDEFGH" + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, + []byte{0xf0, 0xc5, 0x82, 0x22, 0xd3, 0xe6, 0x12, 0xd2}, // random + []byte{0xba, 0xe4, 0x41, 0xb1, 0x3c, 0x37, 0x4d, 0xf4}}, + { + []byte{ // random + 0xd3, 0x7d, 0x45, 0xee, 0x22, 0xe9, 0xcf, 0x52, + 0xf4, 0x65, 0xa2, 0x4f, 0x70, 0xd1, 0x81, 0x8a, + 0x3d, 0xbe, 0x2f, 0x39, 0xc7, 0x71, 0xd2, 0xe9}, + []byte{0x49, 0x53, 0xc3, 0xe9, 0x78, 0xdf, 0x9f, 0xaf}, // random + []byte{0x53, 0x40, 0x51, 0x24, 0xd8, 0x3c, 0xf9, 0x88}}, + { + []byte{ // random + 0xcb, 0x10, 0x7d, 0xda, 0x7e, 0x96, 0x57, 0x0a, + 0xe8, 0xeb, 0xe8, 0x07, 0x8e, 0x87, 0xd3, 0x57, + 0xb2, 0x61, 0x12, 0xb8, 0x2a, 0x90, 0xb7, 0x2f}, + []byte{0xa3, 0xc2, 0x60, 0xb1, 0x0b, 0xb7, 0x28, 0x6e}, // random + []byte{0x56, 0x73, 0x7d, 0xfb, 0xb5, 0xa1, 0xc3, 0xde}}, +} + +// NIST Special Publication 800-20, Appendix A +// Key for use with Table A.1 tests +var tableA1Key = []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +} + +// Table A.1 Resulting Ciphertext from the Variable Plaintext Known Answer Test +var tableA1Tests = []CryptTest{ + {nil, // 0 + []byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x95, 0xf8, 0xa5, 0xe5, 0xdd, 0x31, 0xd9, 0x00}}, + {nil, // 1 + []byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xdd, 0x7f, 0x12, 0x1c, 0xa5, 0x01, 0x56, 0x19}}, + {nil, // 2 + []byte{0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x2e, 0x86, 0x53, 0x10, 0x4f, 0x38, 0x34, 0xea}}, + {nil, // 3 + []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x4b, 0xd3, 0x88, 0xff, 0x6c, 0xd8, 0x1d, 0x4f}}, + {nil, // 4 + []byte{0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x20, 0xb9, 0xe7, 0x67, 0xb2, 0xfb, 0x14, 0x56}}, + {nil, // 5 + []byte{0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x55, 0x57, 0x93, 0x80, 0xd7, 0x71, 0x38, 0xef}}, + {nil, // 6 + []byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x6c, 0xc5, 0xde, 0xfa, 0xaf, 0x04, 0x51, 0x2f}}, + {nil, // 7 + []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x0d, 0x9f, 0x27, 0x9b, 0xa5, 0xd8, 0x72, 0x60}}, + {nil, // 8 + []byte{0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xd9, 0x03, 0x1b, 0x02, 0x71, 0xbd, 0x5a, 0x0a}}, + {nil, // 9 + []byte{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x42, 0x42, 0x50, 0xb3, 0x7c, 0x3d, 0xd9, 0x51}}, + {nil, // 10 + []byte{0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xb8, 0x06, 0x1b, 0x7e, 0xcd, 0x9a, 0x21, 0xe5}}, + {nil, // 11 + []byte{0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xf1, 0x5d, 0x0f, 0x28, 0x6b, 0x65, 0xbd, 0x28}}, + {nil, // 12 + []byte{0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xad, 0xd0, 0xcc, 0x8d, 0x6e, 0x5d, 0xeb, 0xa1}}, + {nil, // 13 + []byte{0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xe6, 0xd5, 0xf8, 0x27, 0x52, 0xad, 0x63, 0xd1}}, + {nil, // 14 + []byte{0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xec, 0xbf, 0xe3, 0xbd, 0x3f, 0x59, 0x1a, 0x5e}}, + {nil, // 15 + []byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xf3, 0x56, 0x83, 0x43, 0x79, 0xd1, 0x65, 0xcd}}, + {nil, // 16 + []byte{0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x2b, 0x9f, 0x98, 0x2f, 0x20, 0x03, 0x7f, 0xa9}}, + {nil, // 17 + []byte{0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x88, 0x9d, 0xe0, 0x68, 0xa1, 0x6f, 0x0b, 0xe6}}, + {nil, // 18 + []byte{0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xe1, 0x9e, 0x27, 0x5d, 0x84, 0x6a, 0x12, 0x98}}, + {nil, // 19 + []byte{0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x32, 0x9a, 0x8e, 0xd5, 0x23, 0xd7, 0x1a, 0xec}}, + {nil, // 20 + []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xe7, 0xfc, 0xe2, 0x25, 0x57, 0xd2, 0x3c, 0x97}}, + {nil, // 21 + []byte{0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x12, 0xa9, 0xf5, 0x81, 0x7f, 0xf2, 0xd6, 0x5d}}, + {nil, // 22 + []byte{0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xa4, 0x84, 0xc3, 0xad, 0x38, 0xdc, 0x9c, 0x19}}, + {nil, // 23 + []byte{0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xfb, 0xe0, 0x0a, 0x8a, 0x1e, 0xf8, 0xad, 0x72}}, + {nil, // 24 + []byte{0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00}, + []byte{0x75, 0x0d, 0x07, 0x94, 0x07, 0x52, 0x13, 0x63}}, + {nil, // 25 + []byte{0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00}, + []byte{0x64, 0xfe, 0xed, 0x9c, 0x72, 0x4c, 0x2f, 0xaf}}, + {nil, // 26 + []byte{0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00}, + []byte{0xf0, 0x2b, 0x26, 0x3b, 0x32, 0x8e, 0x2b, 0x60}}, + {nil, // 27 + []byte{0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00}, + []byte{0x9d, 0x64, 0x55, 0x5a, 0x9a, 0x10, 0xb8, 0x52}}, + {nil, // 28 + []byte{0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00}, + []byte{0xd1, 0x06, 0xff, 0x0b, 0xed, 0x52, 0x55, 0xd7}}, + {nil, // 29 + []byte{0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}, + []byte{0xe1, 0x65, 0x2c, 0x6b, 0x13, 0x8c, 0x64, 0xa5}}, + {nil, // 30 + []byte{0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00}, + []byte{0xe4, 0x28, 0x58, 0x11, 0x86, 0xec, 0x8f, 0x46}}, + {nil, // 31 + []byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}, + []byte{0xae, 0xb5, 0xf5, 0xed, 0xe2, 0x2d, 0x1a, 0x36}}, + {nil, // 32 + []byte{0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}, + []byte{0xe9, 0x43, 0xd7, 0x56, 0x8a, 0xec, 0x0c, 0x5c}}, + {nil, // 33 + []byte{0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00}, + []byte{0xdf, 0x98, 0xc8, 0x27, 0x6f, 0x54, 0xb0, 0x4b}}, + {nil, // 34 + []byte{0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}, + []byte{0xb1, 0x60, 0xe4, 0x68, 0x0f, 0x6c, 0x69, 0x6f}}, + {nil, // 35 + []byte{0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00}, + []byte{0xfa, 0x07, 0x52, 0xb0, 0x7d, 0x9c, 0x4a, 0xb8}}, + {nil, // 36 + []byte{0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00}, + []byte{0xca, 0x3a, 0x2b, 0x03, 0x6d, 0xbc, 0x85, 0x02}}, + {nil, // 37 + []byte{0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00}, + []byte{0x5e, 0x09, 0x05, 0x51, 0x7b, 0xb5, 0x9b, 0xcf}}, + {nil, // 38 + []byte{0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}, + []byte{0x81, 0x4e, 0xeb, 0x3b, 0x91, 0xd9, 0x07, 0x26}}, + {nil, // 39 + []byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, + []byte{0x4d, 0x49, 0xdb, 0x15, 0x32, 0x91, 0x9c, 0x9f}}, + {nil, // 40 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00}, + []byte{0x25, 0xeb, 0x5f, 0xc3, 0xf8, 0xcf, 0x06, 0x21}}, + {nil, // 41 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00}, + []byte{0xab, 0x6a, 0x20, 0xc0, 0x62, 0x0d, 0x1c, 0x6f}}, + {nil, // 42 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00}, + []byte{0x79, 0xe9, 0x0d, 0xbc, 0x98, 0xf9, 0x2c, 0xca}}, + {nil, // 43 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00}, + []byte{0x86, 0x6e, 0xce, 0xdd, 0x80, 0x72, 0xbb, 0x0e}}, + {nil, // 44 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00}, + []byte{0x8b, 0x54, 0x53, 0x6f, 0x2f, 0x3e, 0x64, 0xa8}}, + {nil, // 45 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00}, + []byte{0xea, 0x51, 0xd3, 0x97, 0x55, 0x95, 0xb8, 0x6b}}, + {nil, // 46 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00}, + []byte{0xca, 0xff, 0xc6, 0xac, 0x45, 0x42, 0xde, 0x31}}, + {nil, // 47 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, + []byte{0x8d, 0xd4, 0x5a, 0x2d, 0xdf, 0x90, 0x79, 0x6c}}, + {nil, // 48 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00}, + []byte{0x10, 0x29, 0xd5, 0x5e, 0x88, 0x0e, 0xc2, 0xd0}}, + {nil, // 49 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00}, + []byte{0x5d, 0x86, 0xcb, 0x23, 0x63, 0x9d, 0xbe, 0xa9}}, + {nil, // 50 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00}, + []byte{0x1d, 0x1c, 0xa8, 0x53, 0xae, 0x7c, 0x0c, 0x5f}}, + {nil, // 51 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00}, + []byte{0xce, 0x33, 0x23, 0x29, 0x24, 0x8f, 0x32, 0x28}}, + {nil, // 52 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00}, + []byte{0x84, 0x05, 0xd1, 0xab, 0xe2, 0x4f, 0xb9, 0x42}}, + {nil, // 53 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00}, + []byte{0xe6, 0x43, 0xd7, 0x80, 0x90, 0xca, 0x42, 0x07}}, + {nil, // 54 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00}, + []byte{0x48, 0x22, 0x1b, 0x99, 0x37, 0x74, 0x8a, 0x23}}, + {nil, // 55 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}, + []byte{0xdd, 0x7c, 0x0b, 0xbd, 0x61, 0xfa, 0xfd, 0x54}}, + {nil, // 56 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, + []byte{0x2f, 0xbc, 0x29, 0x1a, 0x57, 0x0d, 0xb5, 0xc4}}, + {nil, // 57 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40}, + []byte{0xe0, 0x7c, 0x30, 0xd7, 0xe4, 0xe2, 0x6e, 0x12}}, + {nil, // 58 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20}, + []byte{0x09, 0x53, 0xe2, 0x25, 0x8e, 0x8e, 0x90, 0xa1}}, + {nil, // 59 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + []byte{0x5b, 0x71, 0x1b, 0xc4, 0xce, 0xeb, 0xf2, 0xee}}, + {nil, // 60 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + []byte{0xcc, 0x08, 0x3f, 0x1e, 0x6d, 0x9e, 0x85, 0xf6}}, + {nil, // 61 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + []byte{0xd2, 0xfd, 0x88, 0x67, 0xd5, 0x0d, 0x2d, 0xfe}}, + {nil, // 62 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + []byte{0x06, 0xe7, 0xea, 0x22, 0xce, 0x92, 0x70, 0x8f}}, + {nil, // 63 + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + []byte{0x16, 0x6b, 0x40, 0xb4, 0x4a, 0xba, 0x4b, 0xd6}}, +} + +// Plaintext for use with Table A.2 tests +var tableA2Plaintext = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + +// Table A.2 Resulting Ciphertext from the Variable Key Known Answer Test +var tableA2Tests = []CryptTest{ + { // 0 + []byte{ + 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x95, 0xa8, 0xd7, 0x28, 0x13, 0xda, 0xa9, 0x4d}}, + { // 1 + []byte{ + 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x0e, 0xec, 0x14, 0x87, 0xdd, 0x8c, 0x26, 0xd5}}, + { // 2 + []byte{ + 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x7a, 0xd1, 0x6f, 0xfb, 0x79, 0xc4, 0x59, 0x26}}, + { // 3 + []byte{ + 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xd3, 0x74, 0x62, 0x94, 0xca, 0x6a, 0x6c, 0xf3}}, + { // 4 + []byte{ + 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x80, 0x9f, 0x5f, 0x87, 0x3c, 0x1f, 0xd7, 0x61}}, + { // 5 + []byte{ + 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xc0, 0x2f, 0xaf, 0xfe, 0xc9, 0x89, 0xd1, 0xfc}}, + { // 6 + []byte{ + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x46, 0x15, 0xaa, 0x1d, 0x33, 0xe7, 0x2f, 0x10}}, + { // 7 + []byte{ + 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x20, 0x55, 0x12, 0x33, 0x50, 0xc0, 0x08, 0x58}}, + { // 8 + []byte{ + 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xdf, 0x3b, 0x99, 0xd6, 0x57, 0x73, 0x97, 0xc8}}, + { // 9 + []byte{ + 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x31, 0xfe, 0x17, 0x36, 0x9b, 0x52, 0x88, 0xc9}}, + { // 10 + []byte{ + 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xdf, 0xdd, 0x3c, 0xc6, 0x4d, 0xae, 0x16, 0x42}}, + { // 11 + []byte{ + 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x17, 0x8c, 0x83, 0xce, 0x2b, 0x39, 0x9d, 0x94}}, + { // 12 + []byte{ + 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x50, 0xf6, 0x36, 0x32, 0x4a, 0x9b, 0x7f, 0x80}}, + { // 13 + []byte{ + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xa8, 0x46, 0x8e, 0xe3, 0xbc, 0x18, 0xf0, 0x6d}}, + { // 14 + []byte{ + 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xa2, 0xdc, 0x9e, 0x92, 0xfd, 0x3c, 0xde, 0x92}}, + { // 15 + []byte{ + 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xca, 0xc0, 0x9f, 0x79, 0x7d, 0x03, 0x12, 0x87}}, + { // 16 + []byte{ + 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x90, 0xba, 0x68, 0x0b, 0x22, 0xae, 0xb5, 0x25}}, + { // 17 + []byte{ + 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xce, 0x7a, 0x24, 0xf3, 0x50, 0xe2, 0x80, 0xb6}}, + { // 18 + []byte{ + 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x88, 0x2b, 0xff, 0x0a, 0xa0, 0x1a, 0x0b, 0x87}}, + { // 19 + []byte{ + 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x25, 0x61, 0x02, 0x88, 0x92, 0x45, 0x11, 0xc2}}, + { // 20 + []byte{ + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xc7, 0x15, 0x16, 0xc2, 0x9c, 0x75, 0xd1, 0x70}}, + { // 21 + []byte{ + 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x51, 0x99, 0xc2, 0x9a, 0x52, 0xc9, 0xf0, 0x59}}, + { // 22 + []byte{ + 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xc2, 0x2f, 0x0a, 0x29, 0x4a, 0x71, 0xf2, 0x9f}}, + { // 23 + []byte{ + 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xee, 0x37, 0x14, 0x83, 0x71, 0x4c, 0x02, 0xea}}, + { // 24 + []byte{ + 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xa8, 0x1f, 0xbd, 0x44, 0x8f, 0x9e, 0x52, 0x2f}}, + { // 25 + []byte{ + 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x4f, 0x64, 0x4c, 0x92, 0xe1, 0x92, 0xdf, 0xed}}, + { // 26 + []byte{ + 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0x1a, 0xfa, 0x9a, 0x66, 0xa6, 0xdf, 0x92, 0xae}}, + { // 27 + []byte{ + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01}, + nil, + []byte{0xb3, 0xc1, 0xcc, 0x71, 0x5c, 0xb8, 0x79, 0xd8}}, + { // 28 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01}, + nil, + []byte{0x19, 0xd0, 0x32, 0xe6, 0x4a, 0xb0, 0xbd, 0x8b}}, + { // 29 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01}, + nil, + []byte{0x3c, 0xfa, 0xa7, 0xa7, 0xdc, 0x87, 0x20, 0xdc}}, + { // 30 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01}, + nil, + []byte{0xb7, 0x26, 0x5f, 0x7f, 0x44, 0x7a, 0xc6, 0xf3}}, + { // 31 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01}, + nil, + []byte{0x9d, 0xb7, 0x3b, 0x3c, 0x0d, 0x16, 0x3f, 0x54}}, + { // 32 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01}, + nil, + []byte{0x81, 0x81, 0xb6, 0x5b, 0xab, 0xf4, 0xa9, 0x75}}, + { // 33 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01}, + nil, + []byte{0x93, 0xc9, 0xb6, 0x40, 0x42, 0xea, 0xa2, 0x40}}, + { // 34 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01}, + nil, + []byte{0x55, 0x70, 0x53, 0x08, 0x29, 0x70, 0x55, 0x92}}, + { // 35 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01}, + nil, + []byte{0x86, 0x38, 0x80, 0x9e, 0x87, 0x87, 0x87, 0xa0}}, + { // 36 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01}, + nil, + []byte{0x41, 0xb9, 0xa7, 0x9a, 0xf7, 0x9a, 0xc2, 0x08}}, + { // 37 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01}, + nil, + []byte{0x7a, 0x9b, 0xe4, 0x2f, 0x20, 0x09, 0xa8, 0x92}}, + { // 38 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01}, + nil, + []byte{0x29, 0x03, 0x8d, 0x56, 0xba, 0x6d, 0x27, 0x45}}, + { // 39 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01}, + nil, + []byte{0x54, 0x95, 0xc6, 0xab, 0xf1, 0xe5, 0xdf, 0x51}}, + { // 40 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01}, + nil, + []byte{0xae, 0x13, 0xdb, 0xd5, 0x61, 0x48, 0x89, 0x33}}, + { // 41 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01}, + nil, + []byte{0x02, 0x4d, 0x1f, 0xfa, 0x89, 0x04, 0xe3, 0x89}}, + { // 42 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01}, + nil, + []byte{0xd1, 0x39, 0x97, 0x12, 0xf9, 0x9b, 0xf0, 0x2e}}, + { // 43 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01}, + nil, + []byte{0x14, 0xc1, 0xd7, 0xc1, 0xcf, 0xfe, 0xc7, 0x9e}}, + { // 44 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01}, + nil, + []byte{0x1d, 0xe5, 0x27, 0x9d, 0xae, 0x3b, 0xed, 0x6f}}, + { // 45 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01}, + nil, + []byte{0xe9, 0x41, 0xa3, 0x3f, 0x85, 0x50, 0x13, 0x03}}, + { // 46 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01}, + nil, + []byte{0xda, 0x99, 0xdb, 0xbc, 0x9a, 0x03, 0xf3, 0x79}}, + { // 47 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01}, + nil, + []byte{0xb7, 0xfc, 0x92, 0xf9, 0x1d, 0x8e, 0x92, 0xe9}}, + { // 48 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01}, + nil, + []byte{0xae, 0x8e, 0x5c, 0xaa, 0x3c, 0xa0, 0x4e, 0x85}}, + { // 49 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80}, + nil, + []byte{0x9c, 0xc6, 0x2d, 0xf4, 0x3b, 0x6e, 0xed, 0x74}}, + { // 50 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40}, + nil, + []byte{0xd8, 0x63, 0xdb, 0xb5, 0xc5, 0x9a, 0x91, 0xa0}}, + { // 50 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20}, + nil, + []byte{0xa1, 0xab, 0x21, 0x90, 0x54, 0x5b, 0x91, 0xd7}}, + { // 52 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10}, + nil, + []byte{0x08, 0x75, 0x04, 0x1e, 0x64, 0xc5, 0x70, 0xf7}}, + { // 53 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08}, + nil, + []byte{0x5a, 0x59, 0x45, 0x28, 0xbe, 0xbe, 0xf1, 0xcc}}, + { // 54 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04}, + nil, + []byte{0xfc, 0xdb, 0x32, 0x91, 0xde, 0x21, 0xf0, 0xc0}}, + { // 55 + []byte{ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02}, + nil, + []byte{0x86, 0x9e, 0xfd, 0x7f, 0x9f, 0x26, 0x5a, 0x09}}, +} + +// Plaintext for use with Table A.3 tests +var tableA3Plaintext = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + +// Table A.3 Values To Be Used for the Permutation Operation Known Answer Test +var tableA3Tests = []CryptTest{ + { // 0 + []byte{ + 0x10, 0x46, 0x91, 0x34, 0x89, 0x98, 0x01, 0x31, + 0x10, 0x46, 0x91, 0x34, 0x89, 0x98, 0x01, 0x31, + 0x10, 0x46, 0x91, 0x34, 0x89, 0x98, 0x01, 0x31, + }, + nil, + []byte{0x88, 0xd5, 0x5e, 0x54, 0xf5, 0x4c, 0x97, 0xb4}}, + { // 1 + []byte{ + 0x10, 0x07, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20, + 0x10, 0x07, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20, + 0x10, 0x07, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20, + }, + nil, + []byte{0x0c, 0x0c, 0xc0, 0x0c, 0x83, 0xea, 0x48, 0xfd}}, + { // 2 + []byte{ + 0x10, 0x07, 0x10, 0x34, 0xc8, 0x98, 0x01, 0x20, + 0x10, 0x07, 0x10, 0x34, 0xc8, 0x98, 0x01, 0x20, + 0x10, 0x07, 0x10, 0x34, 0xc8, 0x98, 0x01, 0x20, + }, + nil, + []byte{0x83, 0xbc, 0x8e, 0xf3, 0xa6, 0x57, 0x01, 0x83}}, + { // 3 + []byte{ + 0x10, 0x46, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20, + 0x10, 0x46, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20, + 0x10, 0x46, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20, + }, + nil, + []byte{0xdf, 0x72, 0x5d, 0xca, 0xd9, 0x4e, 0xa2, 0xe9}}, + { // 4 + []byte{ + 0x10, 0x86, 0x91, 0x15, 0x19, 0x19, 0x01, 0x01, + 0x10, 0x86, 0x91, 0x15, 0x19, 0x19, 0x01, 0x01, + 0x10, 0x86, 0x91, 0x15, 0x19, 0x19, 0x01, 0x01, + }, + nil, + []byte{0xe6, 0x52, 0xb5, 0x3b, 0x55, 0x0b, 0xe8, 0xb0}}, + { // 5 + []byte{ + 0x10, 0x86, 0x91, 0x15, 0x19, 0x58, 0x01, 0x01, + 0x10, 0x86, 0x91, 0x15, 0x19, 0x58, 0x01, 0x01, + 0x10, 0x86, 0x91, 0x15, 0x19, 0x58, 0x01, 0x01, + }, + nil, + []byte{0xaf, 0x52, 0x71, 0x20, 0xc4, 0x85, 0xcb, 0xb0}}, + { // 6 + []byte{ + 0x51, 0x07, 0xb0, 0x15, 0x19, 0x58, 0x01, 0x01, + 0x51, 0x07, 0xb0, 0x15, 0x19, 0x58, 0x01, 0x01, + 0x51, 0x07, 0xb0, 0x15, 0x19, 0x58, 0x01, 0x01, + }, + nil, + []byte{0x0f, 0x04, 0xce, 0x39, 0x3d, 0xb9, 0x26, 0xd5}}, + { // 7 + []byte{ + 0x10, 0x07, 0xb0, 0x15, 0x19, 0x19, 0x01, 0x01, + 0x10, 0x07, 0xb0, 0x15, 0x19, 0x19, 0x01, 0x01, + 0x10, 0x07, 0xb0, 0x15, 0x19, 0x19, 0x01, 0x01, + }, + nil, + []byte{0xc9, 0xf0, 0x0f, 0xfc, 0x74, 0x07, 0x90, 0x67}}, + { // 8 + []byte{ + 0x31, 0x07, 0x91, 0x54, 0x98, 0x08, 0x01, 0x01, + 0x31, 0x07, 0x91, 0x54, 0x98, 0x08, 0x01, 0x01, + 0x31, 0x07, 0x91, 0x54, 0x98, 0x08, 0x01, 0x01, + }, + nil, + []byte{0x7c, 0xfd, 0x82, 0xa5, 0x93, 0x25, 0x2b, 0x4e}}, + { // 9 + []byte{ + 0x31, 0x07, 0x91, 0x94, 0x98, 0x08, 0x01, 0x01, + 0x31, 0x07, 0x91, 0x94, 0x98, 0x08, 0x01, 0x01, + 0x31, 0x07, 0x91, 0x94, 0x98, 0x08, 0x01, 0x01, + }, + nil, + []byte{0xcb, 0x49, 0xa2, 0xf9, 0xe9, 0x13, 0x63, 0xe3}}, + { // 10 + []byte{ + 0x10, 0x07, 0x91, 0x15, 0xb9, 0x08, 0x01, 0x40, + 0x10, 0x07, 0x91, 0x15, 0xb9, 0x08, 0x01, 0x40, + 0x10, 0x07, 0x91, 0x15, 0xb9, 0x08, 0x01, 0x40, + }, + nil, + []byte{0x00, 0xb5, 0x88, 0xbe, 0x70, 0xd2, 0x3f, 0x56}}, + { // 11 + []byte{ + 0x31, 0x07, 0x91, 0x15, 0x98, 0x08, 0x01, 0x40, + 0x31, 0x07, 0x91, 0x15, 0x98, 0x08, 0x01, 0x40, + 0x31, 0x07, 0x91, 0x15, 0x98, 0x08, 0x01, 0x40, + }, + nil, + []byte{0x40, 0x6a, 0x9a, 0x6a, 0xb4, 0x33, 0x99, 0xae}}, + { // 12 + []byte{ + 0x10, 0x07, 0xd0, 0x15, 0x89, 0x98, 0x01, 0x01, + 0x10, 0x07, 0xd0, 0x15, 0x89, 0x98, 0x01, 0x01, + 0x10, 0x07, 0xd0, 0x15, 0x89, 0x98, 0x01, 0x01, + }, + nil, + []byte{0x6c, 0xb7, 0x73, 0x61, 0x1d, 0xca, 0x9a, 0xda}}, + { // 13 + []byte{ + 0x91, 0x07, 0x91, 0x15, 0x89, 0x98, 0x01, 0x01, + 0x91, 0x07, 0x91, 0x15, 0x89, 0x98, 0x01, 0x01, + 0x91, 0x07, 0x91, 0x15, 0x89, 0x98, 0x01, 0x01, + }, + nil, + []byte{0x67, 0xfd, 0x21, 0xc1, 0x7d, 0xbb, 0x5d, 0x70}}, + { // 14 + []byte{ + 0x91, 0x07, 0xd0, 0x15, 0x89, 0x19, 0x01, 0x01, + 0x91, 0x07, 0xd0, 0x15, 0x89, 0x19, 0x01, 0x01, + 0x91, 0x07, 0xd0, 0x15, 0x89, 0x19, 0x01, 0x01, + }, + nil, + []byte{0x95, 0x92, 0xcb, 0x41, 0x10, 0x43, 0x07, 0x87}}, + { // 15 + []byte{ + 0x10, 0x07, 0xd0, 0x15, 0x98, 0x98, 0x01, 0x20, + 0x10, 0x07, 0xd0, 0x15, 0x98, 0x98, 0x01, 0x20, + 0x10, 0x07, 0xd0, 0x15, 0x98, 0x98, 0x01, 0x20, + }, + nil, + []byte{0xa6, 0xb7, 0xff, 0x68, 0xa3, 0x18, 0xdd, 0xd3}}, + { // 16 + []byte{ + 0x10, 0x07, 0x94, 0x04, 0x98, 0x19, 0x01, 0x01, + 0x10, 0x07, 0x94, 0x04, 0x98, 0x19, 0x01, 0x01, + 0x10, 0x07, 0x94, 0x04, 0x98, 0x19, 0x01, 0x01, + }, + nil, + []byte{0x4d, 0x10, 0x21, 0x96, 0xc9, 0x14, 0xca, 0x16}}, + { // 17 + []byte{ + 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x04, 0x01, + 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x04, 0x01, + 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x04, 0x01, + }, + nil, + []byte{0x2d, 0xfa, 0x9f, 0x45, 0x73, 0x59, 0x49, 0x65}}, + { // 18 + []byte{ + 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x01, 0x01, + 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x01, 0x01, + 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x01, 0x01, + }, + nil, + []byte{0xb4, 0x66, 0x04, 0x81, 0x6c, 0x0e, 0x07, 0x74}}, + { // 19 + []byte{ + 0x01, 0x07, 0x94, 0x04, 0x91, 0x19, 0x04, 0x01, + 0x01, 0x07, 0x94, 0x04, 0x91, 0x19, 0x04, 0x01, + 0x01, 0x07, 0x94, 0x04, 0x91, 0x19, 0x04, 0x01, + }, + nil, + []byte{0x6e, 0x7e, 0x62, 0x21, 0xa4, 0xf3, 0x4e, 0x87}}, + { // 20 + []byte{ + 0x19, 0x07, 0x92, 0x10, 0x98, 0x1a, 0x01, 0x01, + 0x19, 0x07, 0x92, 0x10, 0x98, 0x1a, 0x01, 0x01, + 0x19, 0x07, 0x92, 0x10, 0x98, 0x1a, 0x01, 0x01, + }, + nil, + []byte{0xaa, 0x85, 0xe7, 0x46, 0x43, 0x23, 0x31, 0x99}}, + { // 21 + []byte{ + 0x10, 0x07, 0x91, 0x19, 0x98, 0x19, 0x08, 0x01, + 0x10, 0x07, 0x91, 0x19, 0x98, 0x19, 0x08, 0x01, + 0x10, 0x07, 0x91, 0x19, 0x98, 0x19, 0x08, 0x01, + }, + nil, + []byte{0x2e, 0x5a, 0x19, 0xdb, 0x4d, 0x19, 0x62, 0xd6}}, + { // 22 + []byte{ + 0x10, 0x07, 0x91, 0x19, 0x98, 0x1a, 0x08, 0x01, + 0x10, 0x07, 0x91, 0x19, 0x98, 0x1a, 0x08, 0x01, + 0x10, 0x07, 0x91, 0x19, 0x98, 0x1a, 0x08, 0x01, + }, + nil, + []byte{0x23, 0xa8, 0x66, 0xa8, 0x09, 0xd3, 0x08, 0x94}}, + { // 23 + []byte{ + 0x10, 0x07, 0x92, 0x10, 0x98, 0x19, 0x01, 0x01, + 0x10, 0x07, 0x92, 0x10, 0x98, 0x19, 0x01, 0x01, + 0x10, 0x07, 0x92, 0x10, 0x98, 0x19, 0x01, 0x01, + }, + nil, + []byte{0xd8, 0x12, 0xd9, 0x61, 0xf0, 0x17, 0xd3, 0x20}}, + { // 24 + []byte{ + 0x10, 0x07, 0x91, 0x15, 0x98, 0x19, 0x01, 0x0b, + 0x10, 0x07, 0x91, 0x15, 0x98, 0x19, 0x01, 0x0b, + 0x10, 0x07, 0x91, 0x15, 0x98, 0x19, 0x01, 0x0b, + }, + nil, + []byte{0x05, 0x56, 0x05, 0x81, 0x6e, 0x58, 0x60, 0x8f}}, + { // 25 + []byte{ + 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x01, + 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x01, + 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x01, + }, + nil, + []byte{0xab, 0xd8, 0x8e, 0x8b, 0x1b, 0x77, 0x16, 0xf1}}, + { // 26 + []byte{ + 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x02, + 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x02, + 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x02, + }, + nil, + []byte{0x53, 0x7a, 0xc9, 0x5b, 0xe6, 0x9d, 0xa1, 0xe1}}, + { // 27 + []byte{ + 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x08, + 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x08, + 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x08, + }, + nil, + []byte{0xae, 0xd0, 0xf6, 0xae, 0x3c, 0x25, 0xcd, 0xd8}}, + { // 28 + []byte{ + 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x01, 0x04, + 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x01, 0x04, + 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x01, 0x04, + }, + nil, + []byte{0xb3, 0xe3, 0x5a, 0x5e, 0xe5, 0x3e, 0x7b, 0x8d}}, + { // 29 + []byte{ + 0x10, 0x02, 0x91, 0x15, 0x98, 0x19, 0x01, 0x04, + 0x10, 0x02, 0x91, 0x15, 0x98, 0x19, 0x01, 0x04, + 0x10, 0x02, 0x91, 0x15, 0x98, 0x19, 0x01, 0x04, + }, + nil, + []byte{0x61, 0xc7, 0x9c, 0x71, 0x92, 0x1a, 0x2e, 0xf8}}, + { // 30 + []byte{ + 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x02, 0x01, + 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x02, 0x01, + 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x02, 0x01, + }, + nil, + []byte{0xe2, 0xf5, 0x72, 0x8f, 0x09, 0x95, 0x01, 0x3c}}, + { // 31 + []byte{ + 0x10, 0x02, 0x91, 0x16, 0x98, 0x10, 0x01, 0x01, + 0x10, 0x02, 0x91, 0x16, 0x98, 0x10, 0x01, 0x01, + 0x10, 0x02, 0x91, 0x16, 0x98, 0x10, 0x01, 0x01, + }, + nil, + []byte{0x1a, 0xea, 0xc3, 0x9a, 0x61, 0xf0, 0xa4, 0x64}}, +} + +// Table A.4 Values To Be Used for the Substitution Table Known Answer Test +var tableA4Tests = []CryptTest{ + { // 0 + []byte{ + 0x7c, 0xa1, 0x10, 0x45, 0x4a, 0x1a, 0x6e, 0x57, + 0x7c, 0xa1, 0x10, 0x45, 0x4a, 0x1a, 0x6e, 0x57, + 0x7c, 0xa1, 0x10, 0x45, 0x4a, 0x1a, 0x6e, 0x57}, + []byte{0x01, 0xa1, 0xd6, 0xd0, 0x39, 0x77, 0x67, 0x42}, + []byte{0x69, 0x0f, 0x5b, 0x0d, 0x9a, 0x26, 0x93, 0x9b}}, + { // 1 + []byte{ + 0x01, 0x31, 0xd9, 0x61, 0x9d, 0xc1, 0x37, 0x6e, + 0x01, 0x31, 0xd9, 0x61, 0x9d, 0xc1, 0x37, 0x6e, + 0x01, 0x31, 0xd9, 0x61, 0x9d, 0xc1, 0x37, 0x6e}, + []byte{0x5c, 0xd5, 0x4c, 0xa8, 0x3d, 0xef, 0x57, 0xda}, + []byte{0x7a, 0x38, 0x9d, 0x10, 0x35, 0x4b, 0xd2, 0x71}}, + { // 2 + []byte{ + 0x07, 0xa1, 0x13, 0x3e, 0x4a, 0x0b, 0x26, 0x86, + 0x07, 0xa1, 0x13, 0x3e, 0x4a, 0x0b, 0x26, 0x86, + 0x07, 0xa1, 0x13, 0x3e, 0x4a, 0x0b, 0x26, 0x86}, + []byte{0x02, 0x48, 0xd4, 0x38, 0x06, 0xf6, 0x71, 0x72}, + []byte{0x86, 0x8e, 0xbb, 0x51, 0xca, 0xb4, 0x59, 0x9a}}, + { // 3 + []byte{ + 0x38, 0x49, 0x67, 0x4c, 0x26, 0x02, 0x31, 0x9e, + 0x38, 0x49, 0x67, 0x4c, 0x26, 0x02, 0x31, 0x9e, + 0x38, 0x49, 0x67, 0x4c, 0x26, 0x02, 0x31, 0x9e}, + []byte{0x51, 0x45, 0x4b, 0x58, 0x2d, 0xdf, 0x44, 0x0a}, + []byte{0x71, 0x78, 0x87, 0x6e, 0x01, 0xf1, 0x9b, 0x2a}}, + { // 4 + []byte{ + 0x04, 0xb9, 0x15, 0xba, 0x43, 0xfe, 0xb5, 0xb6, + 0x04, 0xb9, 0x15, 0xba, 0x43, 0xfe, 0xb5, 0xb6, + 0x04, 0xb9, 0x15, 0xba, 0x43, 0xfe, 0xb5, 0xb6}, + []byte{0x42, 0xfd, 0x44, 0x30, 0x59, 0x57, 0x7f, 0xa2}, + []byte{0xaf, 0x37, 0xfb, 0x42, 0x1f, 0x8c, 0x40, 0x95}}, + { // 5 + []byte{ + 0x01, 0x13, 0xb9, 0x70, 0xfd, 0x34, 0xf2, 0xce, + 0x01, 0x13, 0xb9, 0x70, 0xfd, 0x34, 0xf2, 0xce, + 0x01, 0x13, 0xb9, 0x70, 0xfd, 0x34, 0xf2, 0xce}, + []byte{0x05, 0x9b, 0x5e, 0x08, 0x51, 0xcf, 0x14, 0x3a}, + []byte{0x86, 0xa5, 0x60, 0xf1, 0x0e, 0xc6, 0xd8, 0x5b}}, + { // 6 + []byte{ + 0x01, 0x70, 0xf1, 0x75, 0x46, 0x8f, 0xb5, 0xe6, + 0x01, 0x70, 0xf1, 0x75, 0x46, 0x8f, 0xb5, 0xe6, + 0x01, 0x70, 0xf1, 0x75, 0x46, 0x8f, 0xb5, 0xe6}, + []byte{0x07, 0x56, 0xd8, 0xe0, 0x77, 0x47, 0x61, 0xd2}, + []byte{0x0c, 0xd3, 0xda, 0x02, 0x00, 0x21, 0xdc, 0x09}}, + { // 7 + []byte{ + 0x43, 0x29, 0x7f, 0xad, 0x38, 0xe3, 0x73, 0xfe, + 0x43, 0x29, 0x7f, 0xad, 0x38, 0xe3, 0x73, 0xfe, + 0x43, 0x29, 0x7f, 0xad, 0x38, 0xe3, 0x73, 0xfe}, + []byte{0x76, 0x25, 0x14, 0xb8, 0x29, 0xbf, 0x48, 0x6a}, + []byte{0xea, 0x67, 0x6b, 0x2c, 0xb7, 0xdb, 0x2b, 0x7a}}, + { // 8 + []byte{ + 0x07, 0xa7, 0x13, 0x70, 0x45, 0xda, 0x2a, 0x16, + 0x07, 0xa7, 0x13, 0x70, 0x45, 0xda, 0x2a, 0x16, + 0x07, 0xa7, 0x13, 0x70, 0x45, 0xda, 0x2a, 0x16}, + []byte{0x3b, 0xdd, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02}, + []byte{0xdf, 0xd6, 0x4a, 0x81, 0x5c, 0xaf, 0x1a, 0x0f}}, + { // 9 + []byte{ + 0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, 0x3b, 0x2f, + 0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, 0x3b, 0x2f, + 0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, 0x3b, 0x2f}, + []byte{0x26, 0x95, 0x5f, 0x68, 0x35, 0xaf, 0x60, 0x9a}, + []byte{0x5c, 0x51, 0x3c, 0x9c, 0x48, 0x86, 0xc0, 0x88}}, + { // 10 + []byte{ + 0x37, 0xd0, 0x6b, 0xb5, 0x16, 0xcb, 0x75, 0x46, + 0x37, 0xd0, 0x6b, 0xb5, 0x16, 0xcb, 0x75, 0x46, + 0x37, 0xd0, 0x6b, 0xb5, 0x16, 0xcb, 0x75, 0x46}, + []byte{0x16, 0x4d, 0x5e, 0x40, 0x4f, 0x27, 0x52, 0x32}, + []byte{0x0a, 0x2a, 0xee, 0xae, 0x3f, 0xf4, 0xab, 0x77}}, + { // 11 + []byte{ + 0x1f, 0x08, 0x26, 0x0d, 0x1a, 0xc2, 0x46, 0x5e, + 0x1f, 0x08, 0x26, 0x0d, 0x1a, 0xc2, 0x46, 0x5e, + 0x1f, 0x08, 0x26, 0x0d, 0x1a, 0xc2, 0x46, 0x5e}, + []byte{0x6b, 0x05, 0x6e, 0x18, 0x75, 0x9f, 0x5c, 0xca}, + []byte{0xef, 0x1b, 0xf0, 0x3e, 0x5d, 0xfa, 0x57, 0x5a}}, + { // 12 + []byte{ + 0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76, + 0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76, + 0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76}, + []byte{0x00, 0x4b, 0xd6, 0xef, 0x09, 0x17, 0x60, 0x62}, + []byte{0x88, 0xbf, 0x0d, 0xb6, 0xd7, 0x0d, 0xee, 0x56}}, + { // 13 + []byte{ + 0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xb0, 0x07, + 0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xb0, 0x07, + 0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xb0, 0x07}, + []byte{0x48, 0x0d, 0x39, 0x00, 0x6e, 0xe7, 0x62, 0xf2}, + []byte{0xa1, 0xf9, 0x91, 0x55, 0x41, 0x02, 0x0b, 0x56}}, + { // 14 + []byte{ + 0x49, 0x79, 0x3e, 0xbc, 0x79, 0xb3, 0x25, 0x8f, + 0x49, 0x79, 0x3e, 0xbc, 0x79, 0xb3, 0x25, 0x8f, + 0x49, 0x79, 0x3e, 0xbc, 0x79, 0xb3, 0x25, 0x8f}, + []byte{0x43, 0x75, 0x40, 0xc8, 0x69, 0x8f, 0x3c, 0xfa}, + []byte{0x6f, 0xbf, 0x1c, 0xaf, 0xcf, 0xfd, 0x05, 0x56}}, + { // 15 + []byte{ + 0x4f, 0xb0, 0x5e, 0x15, 0x15, 0xab, 0x73, 0xa7, + 0x4f, 0xb0, 0x5e, 0x15, 0x15, 0xab, 0x73, 0xa7, + 0x4f, 0xb0, 0x5e, 0x15, 0x15, 0xab, 0x73, 0xa7}, + []byte{0x07, 0x2d, 0x43, 0xa0, 0x77, 0x07, 0x52, 0x92}, + []byte{0x2f, 0x22, 0xe4, 0x9b, 0xab, 0x7c, 0xa1, 0xac}}, + { // 16 + []byte{ + 0x49, 0xe9, 0x5d, 0x6d, 0x4c, 0xa2, 0x29, 0xbf, + 0x49, 0xe9, 0x5d, 0x6d, 0x4c, 0xa2, 0x29, 0xbf, + 0x49, 0xe9, 0x5d, 0x6d, 0x4c, 0xa2, 0x29, 0xbf}, + []byte{0x02, 0xfe, 0x55, 0x77, 0x81, 0x17, 0xf1, 0x2a}, + []byte{0x5a, 0x6b, 0x61, 0x2c, 0xc2, 0x6c, 0xce, 0x4a}}, + { // 17 + []byte{ + 0x01, 0x83, 0x10, 0xdc, 0x40, 0x9b, 0x26, 0xd6, + 0x01, 0x83, 0x10, 0xdc, 0x40, 0x9b, 0x26, 0xd6, + 0x01, 0x83, 0x10, 0xdc, 0x40, 0x9b, 0x26, 0xd6}, + []byte{0x1d, 0x9d, 0x5c, 0x50, 0x18, 0xf7, 0x28, 0xc2}, + []byte{0x5f, 0x4c, 0x03, 0x8e, 0xd1, 0x2b, 0x2e, 0x41}}, + { // 18 + []byte{ + 0x1c, 0x58, 0x7f, 0x1c, 0x13, 0x92, 0x4f, 0xef, + 0x1c, 0x58, 0x7f, 0x1c, 0x13, 0x92, 0x4f, 0xef, + 0x1c, 0x58, 0x7f, 0x1c, 0x13, 0x92, 0x4f, 0xef}, + []byte{0x30, 0x55, 0x32, 0x28, 0x6d, 0x6f, 0x29, 0x5a}, + []byte{0x63, 0xfa, 0xc0, 0xd0, 0x34, 0xd9, 0xf7, 0x93}}, +} + +func newDESCipher(key []byte) cipher.Block { + c, err := openssl.NewDESCipher(key) + if err != nil { + panic("NewDESCipher failed: " + err.Error()) + } + return c +} + +// Use the known weak keys to test DES implementation +func TestDESWeakKeys(t *testing.T) { + if !openssl.SupportsDESCipher() { + t.Skip("DES is not supported") + } + for i, tt := range weakKeyTests { + var encrypt = func(in []byte) (out []byte) { + c := newDESCipher(tt.key) + out = make([]byte, len(in)) + c.Encrypt(out, in) + return + } + + // Encrypting twice with a DES weak + // key should reproduce the original input + result := encrypt(tt.in) + result = encrypt(result) + + if !bytes.Equal(result, tt.in) { + t.Errorf("#%d: result: %x want: %x", i, result, tt.in) + } + } +} + +// Use the known semi-weak key pairs to test DES implementation +func TestDESSemiWeakKeyPairs(t *testing.T) { + if !openssl.SupportsDESCipher() { + t.Skip("DES is not supported") + } + for i, tt := range semiWeakKeyTests { + var encrypt = func(key, in []byte) (out []byte) { + c := newDESCipher(key) + out = make([]byte, len(in)) + c.Encrypt(out, in) + return + } + + // Encrypting with one member of the semi-weak pair + // and then encrypting the result with the other member + // should reproduce the original input. + result := encrypt(tt.key, tt.in) + result = encrypt(tt.out, result) + + if !bytes.Equal(result, tt.in) { + t.Errorf("#%d: result: %x want: %x", i, result, tt.in) + } + } +} + +func TestDESEncryptBlock(t *testing.T) { + if !openssl.SupportsDESCipher() { + t.Skip("DES is not supported") + } + for i, tt := range encryptDESTests { + c := newDESCipher(tt.key) + out := make([]byte, len(tt.in)) + c.Encrypt(out, tt.in) + + if !bytes.Equal(out, tt.out) { + t.Errorf("#%d: result: %x want: %x", i, out, tt.out) + } + } +} + +func TestDESDecryptBlock(t *testing.T) { + if !openssl.SupportsDESCipher() { + t.Skip("DES is not supported") + } + for i, tt := range encryptDESTests { + c := newDESCipher(tt.key) + plain := make([]byte, len(tt.in)) + c.Decrypt(plain, tt.out) + + if !bytes.Equal(plain, tt.in) { + t.Errorf("#%d: result: %x want: %x", i, plain, tt.in) + } + } +} + +func TestDESEncryptTripleDES(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range encryptTripleDESTests { + c, _ := openssl.NewTripleDESCipher(tt.key) + out := make([]byte, len(tt.in)) + c.Encrypt(out, tt.in) + + if !bytes.Equal(out, tt.out) { + t.Errorf("#%d: result: %x want: %x", i, out, tt.out) + } + } +} + +func TestDESDecryptTripleDES(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range encryptTripleDESTests { + c, _ := openssl.NewTripleDESCipher(tt.key) + + plain := make([]byte, len(tt.in)) + c.Decrypt(plain, tt.out) + + if !bytes.Equal(plain, tt.in) { + t.Errorf("#%d: result: %x want: %x", i, plain, tt.in) + } + } +} + +// Defined in Pub 800-20 +func TestDESVariablePlaintextKnownAnswer(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA1Tests { + c, _ := openssl.NewTripleDESCipher(tableA1Key) + + out := make([]byte, len(tt.in)) + c.Encrypt(out, tt.in) + + if !bytes.Equal(out, tt.out) { + t.Errorf("#%d: result: %x want: %x", i, out, tt.out) + } + } +} + +// Defined in Pub 800-20 +func TestDESVariableCiphertextKnownAnswer(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA1Tests { + c, _ := openssl.NewTripleDESCipher(tableA1Key) + + plain := make([]byte, len(tt.out)) + c.Decrypt(plain, tt.out) + + if !bytes.Equal(plain, tt.in) { + t.Errorf("#%d: result: %x want: %x", i, plain, tt.in) + } + } +} + +// Defined in Pub 800-20 +// Encrypting the Table A.1 ciphertext with the +// 0x01... key produces the original plaintext +func TestDESInversePermutationKnownAnswer(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA1Tests { + c, _ := openssl.NewTripleDESCipher(tableA1Key) + + plain := make([]byte, len(tt.in)) + c.Encrypt(plain, tt.out) + + if !bytes.Equal(plain, tt.in) { + t.Errorf("#%d: result: %x want: %x", i, plain, tt.in) + } + } +} + +// Defined in Pub 800-20 +// Decrypting the Table A.1 plaintext with the +// 0x01... key produces the corresponding ciphertext +func TestDESInitialPermutationKnownAnswer(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA1Tests { + c, _ := openssl.NewTripleDESCipher(tableA1Key) + + out := make([]byte, len(tt.in)) + c.Decrypt(out, tt.in) + + if !bytes.Equal(out, tt.out) { + t.Errorf("#%d: result: %x want: %x", i, out, tt.out) + } + } +} + +// Defined in Pub 800-20 +func TestDESVariableKeyKnownAnswerEncrypt(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA2Tests { + c, _ := openssl.NewTripleDESCipher(tt.key) + + out := make([]byte, len(tableA2Plaintext)) + c.Encrypt(out, tableA2Plaintext) + + if !bytes.Equal(out, tt.out) { + t.Errorf("#%d: result: %x want: %x", i, out, tt.out) + } + } +} + +// Defined in Pub 800-20 +func TestDESVariableKeyKnownAnswerDecrypt(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA2Tests { + c, _ := openssl.NewTripleDESCipher(tt.key) + + out := make([]byte, len(tt.out)) + c.Decrypt(out, tt.out) + + if !bytes.Equal(out, tableA2Plaintext) { + t.Errorf("#%d: result: %x want: %x", i, out, tableA2Plaintext) + } + } +} + +// Defined in Pub 800-20 +func TestDESPermutationOperationKnownAnswerEncrypt(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA3Tests { + c, _ := openssl.NewTripleDESCipher(tt.key) + + out := make([]byte, len(tableA3Plaintext)) + c.Encrypt(out, tableA3Plaintext) + + if !bytes.Equal(out, tt.out) { + t.Errorf("#%d: result: %x want: %x", i, out, tt.out) + } + } +} + +// Defined in Pub 800-20 +func TestDESPermutationOperationKnownAnswerDecrypt(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA3Tests { + c, _ := openssl.NewTripleDESCipher(tt.key) + + out := make([]byte, len(tt.out)) + c.Decrypt(out, tt.out) + + if !bytes.Equal(out, tableA3Plaintext) { + t.Errorf("#%d: result: %x want: %x", i, out, tableA3Plaintext) + } + } +} + +// Defined in Pub 800-20 +func TestDESSubstitutionTableKnownAnswerEncrypt(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA4Tests { + c, _ := openssl.NewTripleDESCipher(tt.key) + + out := make([]byte, len(tt.in)) + c.Encrypt(out, tt.in) + + if !bytes.Equal(out, tt.out) { + t.Errorf("#%d: result: %x want: %x", i, out, tt.out) + } + } +} + +// Defined in Pub 800-20 +func TestDESSubstitutionTableKnownAnswerDecrypt(t *testing.T) { + if !openssl.SupportsTripleDESCipher() { + t.Skip("3DES is not supported") + } + for i, tt := range tableA4Tests { + c, _ := openssl.NewTripleDESCipher(tt.key) + + out := make([]byte, len(tt.out)) + c.Decrypt(out, tt.out) + + if !bytes.Equal(out, tt.in) { + t.Errorf("#%d: result: %x want: %x", i, out, tt.in) + } + } +} + +func TestDESSharedBufferWithoutLengthAdjustment(t *testing.T) { + if !openssl.SupportsDESCipher() { + t.Skip("DES is not supported") + } + c := newDESCipher(encryptDESTests[0].key) + srcPlusDst := make([]byte, c.BlockSize()*2) + src := srcPlusDst + dst := srcPlusDst[c.BlockSize():] + c.Encrypt(dst, src) +} + +func TestDESCBCBlobEncryptBasicBlockEncryption(t *testing.T) { + if !openssl.SupportsDESCipher() { + t.Skip("DES is not supported") + } + key := []byte{0x24, 0xcd, 0x8b, 0x13, 0x37, 0xc5, 0xc1, 0xb1} + iv := []byte{0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb} + + block, err := openssl.NewDESCipher(key) + if err != nil { + t.Fatalf("expected no error for aes.NewCipher, got: %s", err) + } + + blockSize := block.BlockSize() + if blockSize != 8 { + t.Fatalf("unexpected block size, expected 8 got: %d", blockSize) + } + encryptor := cipher.NewCBCEncrypter(block, iv) + encrypted := make([]byte, 16) + + // First block. 8 bytes. + srcBlock1 := bytes.Repeat([]byte{0x01}, 8) + encryptor.CryptBlocks(encrypted, srcBlock1) + if !bytes.Equal([]byte{ + 0x7a, 0xe7, 0x20, 0x13, 0xc7, 0x7a, 0x5b, 0xb3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, encrypted) { + t.Error("unexpected CryptBlocks result for first block") + } + + // Second block. 8 bytes. + srcBlock2 := bytes.Repeat([]byte{0x02}, 8) + encryptor.CryptBlocks(encrypted[8:], srcBlock2) + if !bytes.Equal([]byte{ + 0x7a, 0xe7, 0x20, 0x13, 0xc7, 0x7a, 0x5b, 0xb3, + 0xd9, 0x22, 0xa2, 0x22, 0x3f, 0x22, 0x7b, 0x42, + }, encrypted) { + t.Error("unexpected CryptBlocks result for second block") + } + + decrypter := cipher.NewCBCDecrypter(block, iv) + plainText := append(srcBlock1, srcBlock2...) + decrypted := make([]byte, len(plainText)) + decrypter.CryptBlocks(decrypted, encrypted[:8]) + decrypter.CryptBlocks(decrypted[8:], encrypted[8:]) + if !bytes.Equal(decrypted, plainText) { + t.Errorf("unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, plainText) + } +} + +func TestDESCBCDecryptSimple(t *testing.T) { + if !openssl.SupportsDESCipher() { + t.Skip("DES is not supported") + } + key := []byte{0x24, 0xcd, 0x8b, 0x13, 0x37, 0xc5, 0xc1, 0xb1} + + block, err := openssl.NewDESCipher(key) + if err != nil { + t.Fatal(err) + } + + iv := []byte{0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb} + + encrypter := cipher.NewCBCEncrypter(block, iv) + decrypter := cipher.NewCBCDecrypter(block, iv) + + plainText := []byte{ + 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, + 0x65, 0x20, 0x4c, 0x6f, 0x72, 0x64, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x69, + 0x6e, 0x67, 0x2c, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + } + cipherText := make([]byte, len(plainText)) + + encrypter.CryptBlocks(cipherText, plainText[:40]) + encrypter.CryptBlocks(cipherText[40:], plainText[40:]) + + expectedCipherText := []byte{ + 0xbf, 0x7b, 0x02, 0x01, 0xa3, 0xde, 0xd0, 0xcd, + 0x00, 0xa4, 0x03, 0xdc, 0x05, 0x6f, 0xc4, 0xb4, + 0xe0, 0x35, 0x30, 0x03, 0x12, 0xef, 0x51, 0xd7, + 0x38, 0x98, 0x91, 0xba, 0x80, 0xd6, 0x08, 0xbd, + 0x1c, 0xdc, 0x1e, 0xcd, 0x6f, 0xd7, 0xcb, 0x81, + } + + if !bytes.Equal(expectedCipherText, cipherText) { + t.Fail() + } + + decrypted := make([]byte, len(plainText)) + + decrypter.CryptBlocks(decrypted, cipherText[:40]) + decrypter.CryptBlocks(decrypted[40:], cipherText[40:]) + + if len(decrypted) != len(plainText) { + t.Fail() + } + + if !bytes.Equal(plainText, decrypted) { + t.Errorf("decryption incorrect\nexp %v, got %v\n", plainText, decrypted) + } +} + +func BenchmarkEncrypt(b *testing.B) { + if !openssl.SupportsDESCipher() { + b.Skip("DES is not supported") + } + tt := encryptDESTests[0] + c, err := openssl.NewDESCipher(tt.key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.in)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Encrypt(out, tt.in) + } +} + +func BenchmarkDecrypt(b *testing.B) { + if !openssl.SupportsDESCipher() { + b.Skip("DES is not supported") + } + tt := encryptDESTests[0] + c, err := openssl.NewDESCipher(tt.key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.out)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Decrypt(out, tt.out) + } +} + +func BenchmarkTDESEncrypt(b *testing.B) { + if !openssl.SupportsTripleDESCipher() { + b.Skip("3DES is not supported") + } + tt := encryptTripleDESTests[0] + c, err := openssl.NewTripleDESCipher(tt.key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.in)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Encrypt(out, tt.in) + } +} + +func BenchmarkTDESDecrypt(b *testing.B) { + if !openssl.SupportsTripleDESCipher() { + b.Skip("3DES is not supported") + } + tt := encryptTripleDESTests[0] + c, err := openssl.NewTripleDESCipher(tt.key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.out)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Decrypt(out, tt.out) + } +} diff --git a/shims.h b/shims.h index b93529eb..f48cbb38 100644 --- a/shims.h +++ b/shims.h @@ -251,6 +251,10 @@ DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_cbc, (void), ()) \ DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_ctr, (void), ()) \ DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_ecb, (void), ()) \ DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_gcm, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_des_ecb, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_des_cbc, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_des_ede3_ecb, (void), ()) \ +DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_des_ede3_cbc, (void), ()) \ DEFINEFUNC(void, EVP_CIPHER_CTX_free, (GO_EVP_CIPHER_CTX_PTR arg0), (arg0)) \ DEFINEFUNC(int, EVP_CIPHER_CTX_ctrl, (GO_EVP_CIPHER_CTX_PTR ctx, int type, int arg, void *ptr), (ctx, type, arg, ptr)) \ DEFINEFUNC(GO_EVP_PKEY_PTR, EVP_PKEY_new, (void), ()) \ From a2980ab4978a3e741e478e920ffdefa18aae3de2 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 23 Aug 2023 15:40:20 +0200 Subject: [PATCH 5/8] get cipher block size from openssl --- aes.go | 26 +++++++++---------- cipher.go | 75 ++++++++++++++++++++++++++++++++++--------------------- des.go | 27 ++++++++------------ shims.h | 1 + 4 files changed, 70 insertions(+), 59 deletions(-) diff --git a/aes.go b/aes.go index a8759480..f54dbdb2 100644 --- a/aes.go +++ b/aes.go @@ -7,11 +7,8 @@ import "C" import ( "crypto/cipher" "errors" - "runtime" ) -const aesBlockSize = 16 - type extraModes interface { // Copied out of crypto/aes/modes.go. NewCBCEncrypter(iv []byte) cipher.BlockMode @@ -26,22 +23,21 @@ type extraModes interface { var _ extraModes = (*aesCipher)(nil) func NewAESCipher(key []byte) (cipher.Block, error) { - c := &evpCipher{key: make([]byte, len(key))} - copy(c.key, key) - - switch len(c.key) * 8 { + var kind cipherKind + switch len(key) * 8 { case 128: - c.kind = cipherAES128 + kind = cipherAES128 case 192: - c.kind = cipherAES192 + kind = cipherAES192 case 256: - c.kind = cipherAES256 + kind = cipherAES256 default: return nil, errors.New("crypto/aes: invalid key size") } - - runtime.SetFinalizer(c, (*evpCipher).finalize) - + c, err := newEVPCipher(key, kind) + if err != nil { + return nil, err + } return &aesCipher{c}, nil } @@ -55,7 +51,9 @@ type aesCipher struct { *evpCipher } -func (c *aesCipher) BlockSize() int { return aesBlockSize } +func (c *aesCipher) BlockSize() int { + return c.blockSize +} func (c *aesCipher) Encrypt(dst, src []byte) { c.encrypt(dst, src) diff --git a/cipher.go b/cipher.go index a1edcb9e..dcf73077 100644 --- a/cipher.go +++ b/cipher.go @@ -24,6 +24,23 @@ const ( cipherDES3 ) +func (c cipherKind) String() string { + switch c { + case cipherAES128: + return "AES-128" + case cipherAES192: + return "AES-192" + case cipherAES256: + return "AES-256" + case cipherDES: + return "DES" + case cipherDES3: + return "DES3" + default: + panic("unknown cipher kind: " + strconv.Itoa(int(c))) + } +} + type cipherMode int8 const ( @@ -109,10 +126,23 @@ func loadCipher(k cipherKind, mode cipherMode) (cipher C.GO_EVP_CIPHER_PTR) { } type evpCipher struct { - key []byte - enc_ctx C.GO_EVP_CIPHER_CTX_PTR - dec_ctx C.GO_EVP_CIPHER_CTX_PTR - kind cipherKind + key []byte + enc_ctx C.GO_EVP_CIPHER_CTX_PTR + dec_ctx C.GO_EVP_CIPHER_CTX_PTR + kind cipherKind + blockSize int +} + +func newEVPCipher(key []byte, kind cipherKind) (*evpCipher, error) { + cipher := loadCipher(kind, cipherModeECB) + if cipher == nil { + return nil, errors.New("crypto/cipher: unsupported cipher: " + kind.String()) + } + c := &evpCipher{key: make([]byte, len(key)), kind: kind} + copy(c.key, key) + c.blockSize = int(C.go_openssl_EVP_CIPHER_get_block_size(cipher)) + runtime.SetFinalizer(c, (*evpCipher).finalize) + return c, nil } func (c *evpCipher) finalize() { @@ -124,28 +154,16 @@ func (c *evpCipher) finalize() { } } -func (c *evpCipher) blockSize() int { - switch c.kind { - case cipherAES128, cipherAES192, cipherAES256: - return aesBlockSize - case cipherDES, cipherDES3: - return desBlockSize - default: - panic("openssl: unsupported cipher: " + strconv.Itoa(int(c.kind))) - } -} - func (c *evpCipher) encrypt(dst, src []byte) { - blockSize := c.blockSize() - if len(src) < blockSize { + if len(src) < c.blockSize { panic("crypto/cipher: input not full block") } - if len(dst) < blockSize { + if len(dst) < c.blockSize { panic("crypto/cipher: output not full block") } // Only check for overlap between the parts of src and dst that will actually be used. // This matches Go standard library behavior. - if inexactOverlap(dst[:blockSize], src[:blockSize]) { + if inexactOverlap(dst[:c.blockSize], src[:c.blockSize]) { panic("crypto/cipher: invalid buffer overlap") } if c.enc_ctx == nil { @@ -156,23 +174,22 @@ func (c *evpCipher) encrypt(dst, src []byte) { } } - if C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), C.int(blockSize)) != 1 { + if C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), C.int(c.blockSize)) != 1 { panic("crypto/cipher: EncryptUpdate failed") } runtime.KeepAlive(c) } func (c *evpCipher) decrypt(dst, src []byte) { - blockSize := c.blockSize() - if len(src) < blockSize { + if len(src) < c.blockSize { panic("crypto/cipher: input not full block") } - if len(dst) < blockSize { + if len(dst) < c.blockSize { panic("crypto/cipher: output not full block") } // Only check for overlap between the parts of src and dst that will actually be used. // This matches Go standard library behavior. - if inexactOverlap(dst[:blockSize], src[:blockSize]) { + if inexactOverlap(dst[:c.blockSize], src[:c.blockSize]) { panic("crypto/cipher: invalid buffer overlap") } if c.dec_ctx == nil { @@ -186,7 +203,7 @@ func (c *evpCipher) decrypt(dst, src []byte) { } } - C.go_openssl_EVP_DecryptUpdate_wrapper(c.dec_ctx, base(dst), base(src), C.int(blockSize)) + C.go_openssl_EVP_DecryptUpdate_wrapper(c.dec_ctx, base(dst), base(src), C.int(c.blockSize)) runtime.KeepAlive(c) } @@ -237,7 +254,7 @@ func (c *evpCipher) newCBC(iv []byte, encrypt bool) cipher.BlockMode { if err != nil { panic(err) } - x := &cipherCBC{ctx: ctx, blockSize: c.blockSize()} + x := &cipherCBC{ctx: ctx, blockSize: c.blockSize} runtime.SetFinalizer(x, (*cipherCBC).finalize) if C.go_openssl_EVP_CIPHER_CTX_set_padding(x.ctx, 0) != 1 { panic("cipher: unable to set padding") @@ -298,7 +315,7 @@ type noGCM struct { } func (g *noGCM) BlockSize() int { - return g.blockSize() + return g.blockSize } func (g *noGCM) Encrypt(dst, src []byte) { @@ -328,7 +345,7 @@ func (c *evpCipher) newGCM(tls bool) (cipher.AEAD, error) { if err != nil { return nil, err } - g := &cipherGCM{ctx: ctx, tls: tls, blockSize: c.blockSize()} + g := &cipherGCM{ctx: ctx, tls: tls, blockSize: c.blockSize} runtime.SetFinalizer(g, (*cipherGCM).finalize) return g, nil } @@ -455,7 +472,7 @@ func sliceForAppend(in []byte, n int) (head, tail []byte) { func newCipherCtx(kind cipherKind, mode cipherMode, encrypt int, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) { cipher := loadCipher(kind, mode) if cipher == nil { - panic("openssl: unsupported cipher: " + strconv.Itoa(int(kind))) + panic("crypto/cipher: unsupported cipher: " + kind.String()) } ctx := C.go_openssl_EVP_CIPHER_CTX_new() if ctx == nil { diff --git a/des.go b/des.go index f2c69607..8fb10e20 100644 --- a/des.go +++ b/des.go @@ -7,11 +7,8 @@ import "C" import ( "crypto/cipher" "errors" - "runtime" ) -const desBlockSize = 8 - // SupportsDESCipher returns true if NewDESCipher is supported. func SupportsDESCipher() bool { // True for stock OpenSSL 1. @@ -27,28 +24,24 @@ func SupportsTripleDESCipher() bool { } func NewDESCipher(key []byte) (cipher.Block, error) { - if !SupportsDESCipher() { - return nil, errors.New("crypto/des: not supported") - } if len(key) != 8 { return nil, errors.New("crypto/des: invalid key size") } - c := &evpCipher{key: make([]byte, len(key)), kind: cipherDES} - copy(c.key, key) - runtime.SetFinalizer(c, (*evpCipher).finalize) + c, err := newEVPCipher(key, cipherDES) + if err != nil { + return nil, err + } return &desCipher{c}, nil } func NewTripleDESCipher(key []byte) (cipher.Block, error) { - if !SupportsTripleDESCipher() { - return nil, errors.New("crypto/des: not supported") - } if len(key) != 24 { return nil, errors.New("crypto/des: invalid key size") } - c := &evpCipher{key: make([]byte, len(key)), kind: cipherDES3} - copy(c.key, key) - runtime.SetFinalizer(c, (*evpCipher).finalize) + c, err := newEVPCipher(key, cipherDES3) + if err != nil { + return nil, err + } return &desCipher{c}, nil } @@ -63,7 +56,9 @@ type desCipher struct { *evpCipher } -func (c *desCipher) BlockSize() int { return desBlockSize } +func (c *desCipher) BlockSize() int { + return c.blockSize +} func (c *desCipher) Encrypt(dst, src []byte) { c.encrypt(dst, src) diff --git a/shims.h b/shims.h index f48cbb38..0a34f00e 100644 --- a/shims.h +++ b/shims.h @@ -255,6 +255,7 @@ DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_des_ecb, (void), ()) \ DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_des_cbc, (void), ()) \ DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_des_ede3_ecb, (void), ()) \ DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_des_ede3_cbc, (void), ()) \ +DEFINEFUNC_RENAMED_3_0(int, EVP_CIPHER_get_block_size, EVP_CIPHER_block_size, (const GO_EVP_CIPHER_PTR cipher), (cipher)) \ DEFINEFUNC(void, EVP_CIPHER_CTX_free, (GO_EVP_CIPHER_CTX_PTR arg0), (arg0)) \ DEFINEFUNC(int, EVP_CIPHER_CTX_ctrl, (GO_EVP_CIPHER_CTX_PTR ctx, int type, int arg, void *ptr), (ctx, type, arg, ptr)) \ DEFINEFUNC(GO_EVP_PKEY_PTR, EVP_PKEY_new, (void), ()) \ From 13f6d9d253467e820a58a1b2dc6b5787262f4c23 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 25 Aug 2023 10:00:32 +0200 Subject: [PATCH 6/8] add type for allowed cipher operations --- aes.go | 4 ++-- cipher.go | 32 ++++++++++++++++++++------------ des.go | 4 ++-- goopenssl.h | 4 ++-- shims.h | 1 + 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/aes.go b/aes.go index f54dbdb2..ecda35a9 100644 --- a/aes.go +++ b/aes.go @@ -64,11 +64,11 @@ func (c *aesCipher) Decrypt(dst, src []byte) { } func (c *aesCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { - return c.newCBC(iv, true) + return c.newCBC(iv, cipherOpEncrypt) } func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { - return c.newCBC(iv, false) + return c.newCBC(iv, cipherOpDecrypt) } func (c *aesCipher) NewCTR(iv []byte) cipher.Stream { diff --git a/cipher.go b/cipher.go index dcf73077..94edb7b8 100644 --- a/cipher.go +++ b/cipher.go @@ -50,6 +50,18 @@ const ( cipherModeGCM ) +// cipherOp is the allowed operations for a cipher, +// as documented in [EVP_CipherInit_ex]. +// +// [EVP_CipherInit_ex]: https://www.openssl.org/docs/man3.0/man3/EVP_CipherInit_ex.html +type cipherOp int8 + +const ( + cipherOpNone cipherOp = -1 // leaves the value of the previous call, if any. + cipherOpDecrypt cipherOp = 0 + cipherOpEncrypt cipherOp = 1 +) + // cacheCipher is a cache of cipherKind to GO_EVP_CIPHER_PTR. var cacheCipher sync.Map @@ -168,7 +180,7 @@ func (c *evpCipher) encrypt(dst, src []byte) { } if c.enc_ctx == nil { var err error - c.enc_ctx, err = newCipherCtx(c.kind, cipherModeECB, 1, c.key, nil) + c.enc_ctx, err = newCipherCtx(c.kind, cipherModeECB, cipherOpEncrypt, c.key, nil) if err != nil { panic(err) } @@ -194,7 +206,7 @@ func (c *evpCipher) decrypt(dst, src []byte) { } if c.dec_ctx == nil { var err error - c.dec_ctx, err = newCipherCtx(c.kind, cipherModeECB, 0, c.key, nil) + c.dec_ctx, err = newCipherCtx(c.kind, cipherModeECB, cipherOpDecrypt, c.key, nil) if err != nil { panic(err) } @@ -240,17 +252,13 @@ func (x *cipherCBC) SetIV(iv []byte) { if len(iv) != x.blockSize { panic("cipher: incorrect length IV") } - if C.go_openssl_EVP_CipherInit_ex(x.ctx, nil, nil, nil, base(iv), -1) != 1 { + if C.go_openssl_EVP_CipherInit_ex(x.ctx, nil, nil, nil, base(iv), C.int(cipherOpNone)) != 1 { panic("cipher: unable to initialize EVP cipher ctx") } } -func (c *evpCipher) newCBC(iv []byte, encrypt bool) cipher.BlockMode { - enc := 1 - if !encrypt { - enc = 0 - } - ctx, err := newCipherCtx(c.kind, cipherModeCBC, enc, c.key, iv) +func (c *evpCipher) newCBC(iv []byte, op cipherOp) cipher.BlockMode { + ctx, err := newCipherCtx(c.kind, cipherModeCBC, op, c.key, iv) if err != nil { panic(err) } @@ -283,7 +291,7 @@ func (x *cipherCTR) XORKeyStream(dst, src []byte) { } func (c *evpCipher) newCTR(iv []byte) cipher.Stream { - ctx, err := newCipherCtx(c.kind, cipherModeCTR, 1, c.key, iv) + ctx, err := newCipherCtx(c.kind, cipherModeCTR, cipherOpEncrypt, c.key, iv) if err != nil { panic(err) } @@ -341,7 +349,7 @@ func (c *evpCipher) newGCMChecked(nonceSize, tagSize int) (cipher.AEAD, error) { } func (c *evpCipher) newGCM(tls bool) (cipher.AEAD, error) { - ctx, err := newCipherCtx(c.kind, cipherModeGCM, -1, c.key, nil) + ctx, err := newCipherCtx(c.kind, cipherModeGCM, cipherOpNone, c.key, nil) if err != nil { return nil, err } @@ -469,7 +477,7 @@ func sliceForAppend(in []byte, n int) (head, tail []byte) { return } -func newCipherCtx(kind cipherKind, mode cipherMode, encrypt int, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) { +func newCipherCtx(kind cipherKind, mode cipherMode, encrypt cipherOp, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) { cipher := loadCipher(kind, mode) if cipher == nil { panic("crypto/cipher: unsupported cipher: " + kind.String()) diff --git a/des.go b/des.go index 8fb10e20..4c711e41 100644 --- a/des.go +++ b/des.go @@ -69,9 +69,9 @@ func (c *desCipher) Decrypt(dst, src []byte) { } func (c *desCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { - return c.newCBC(iv, true) + return c.newCBC(iv, cipherOpEncrypt) } func (c *desCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { - return c.newCBC(iv, false) + return c.newCBC(iv, cipherOpDecrypt) } diff --git a/goopenssl.h b/goopenssl.h index a523ec61..8b74b071 100644 --- a/goopenssl.h +++ b/goopenssl.h @@ -119,7 +119,7 @@ go_openssl_EVP_CIPHER_CTX_seal_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx, if (in_len == 0) in = (const unsigned char *)""; if (aad_len == 0) aad = (const unsigned char *)""; - if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, 1) != 1) + if (go_openssl_EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce) != 1) return 0; int discard_len, out_len; @@ -147,7 +147,7 @@ go_openssl_EVP_CIPHER_CTX_open_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx, if (in_len == 0) in = (const unsigned char *)""; if (aad_len == 0) aad = (const unsigned char *)""; - if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, 0) != 1) + if (go_openssl_EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, nonce) != 1) return 0; int discard_len, out_len; diff --git a/shims.h b/shims.h index 0a34f00e..637f272c 100644 --- a/shims.h +++ b/shims.h @@ -235,6 +235,7 @@ DEFINEFUNC(int, EVP_CipherUpdate, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out DEFINEFUNC(int, EVP_EncryptInit_ex, (GO_EVP_CIPHER_CTX_PTR ctx, const GO_EVP_CIPHER_PTR type, GO_ENGINE_PTR impl, const unsigned char *key, const unsigned char *iv), (ctx, type, impl, key, iv)) \ DEFINEFUNC(int, EVP_EncryptUpdate, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), (ctx, out, outl, in, inl)) \ DEFINEFUNC(int, EVP_EncryptFinal_ex, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl), (ctx, out, outl)) \ +DEFINEFUNC(int, EVP_DecryptInit_ex, (GO_EVP_CIPHER_CTX_PTR ctx, const GO_EVP_CIPHER_PTR type, GO_ENGINE_PTR impl, const unsigned char *key, const unsigned char *iv), (ctx, type, impl, key, iv)) \ DEFINEFUNC(int, EVP_DecryptUpdate, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), (ctx, out, outl, in, inl)) \ DEFINEFUNC(int, EVP_DecryptFinal_ex, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *outm, int *outl), (ctx, outm, outl)) \ DEFINEFUNC_3_0(GO_EVP_CIPHER_PTR, EVP_CIPHER_fetch, (GO_OSSL_LIB_CTX_PTR ctx, const char *algorithm, const char *properties), (ctx, algorithm, properties)) \ From 05c4c969b1a48331be903c78bdd5bf5bf3cc7d29 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 25 Aug 2023 10:25:53 +0200 Subject: [PATCH 7/8] support OpenSSL providers without DES CBC support --- des.go | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/des.go b/des.go index 4c711e41..878d4660 100644 --- a/des.go +++ b/des.go @@ -9,14 +9,20 @@ import ( "errors" ) -// SupportsDESCipher returns true if NewDESCipher is supported. +// SupportsDESCipher returns true if NewDESCipher is supported, +// which uses ECB mode. +// If CBC is also supported, then the returned cipher.Block +// will also implement NewCBCEncrypter and NewCBCDecrypter. func SupportsDESCipher() bool { // True for stock OpenSSL 1. // False for stock OpenSSL 3 unless the legacy provider is available. return loadCipher(cipherDES, cipherModeECB) != nil } -// SupportsTripleDESCipher returns true if NewTripleDESCipher is supported. +// SupportsTripleDESCipher returns true if NewTripleDESCipher is supported, +// which uses ECB mode. +// If CBC is also supported, then the returned cipher.Block +// will also implement NewCBCEncrypter and NewCBCDecrypter. func SupportsTripleDESCipher() bool { // Should always be true for stock OpenSSL, // even when using the FIPS provider. @@ -31,6 +37,10 @@ func NewDESCipher(key []byte) (cipher.Block, error) { if err != nil { return nil, err } + // Should always be true for stock OpenSSL. + if loadCipher(cipherDES, cipherModeCBC) == nil { + return &desCipherWithoutCBC{c}, nil + } return &desCipher{c}, nil } @@ -42,6 +52,10 @@ func NewTripleDESCipher(key []byte) (cipher.Block, error) { if err != nil { return nil, err } + // Should always be true for stock OpenSSL. + if loadCipher(cipherDES, cipherModeCBC) != nil { + return &desCipherWithoutCBC{c}, nil + } return &desCipher{c}, nil } @@ -75,3 +89,19 @@ func (c *desCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { func (c *desCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { return c.newCBC(iv, cipherOpDecrypt) } + +type desCipherWithoutCBC struct { + *evpCipher +} + +func (c *desCipherWithoutCBC) BlockSize() int { + return c.blockSize +} + +func (c *desCipherWithoutCBC) Encrypt(dst, src []byte) { + c.encrypt(dst, src) +} + +func (c *desCipherWithoutCBC) Decrypt(dst, src []byte) { + c.decrypt(dst, src) +} From 3fefef47f410347d412398d7fb091394edd15c6a Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 25 Aug 2023 16:53:52 +0200 Subject: [PATCH 8/8] fix build constraints --- aes_test.go | 2 +- cipher.go | 2 +- des.go | 2 +- des_test.go | 2 -- export_test.go | 2 -- 5 files changed, 3 insertions(+), 7 deletions(-) diff --git a/aes_test.go b/aes_test.go index c00c1944..14c798cd 100644 --- a/aes_test.go +++ b/aes_test.go @@ -1,4 +1,4 @@ -package openssl +package openssl_test import ( "bytes" diff --git a/cipher.go b/cipher.go index 94edb7b8..df6c40f1 100644 --- a/cipher.go +++ b/cipher.go @@ -1,4 +1,4 @@ -//go:build linux && !cmd_go_bootstrap +//go:build !cmd_go_bootstrap package openssl diff --git a/des.go b/des.go index 878d4660..5f5e3748 100644 --- a/des.go +++ b/des.go @@ -1,4 +1,4 @@ -//go:build linux && !cmd_go_bootstrap +//go:build !cmd_go_bootstrap package openssl diff --git a/des_test.go b/des_test.go index 32eaee41..8d222b97 100644 --- a/des_test.go +++ b/des_test.go @@ -1,5 +1,3 @@ -//go:build linux - package openssl_test import ( diff --git a/export_test.go b/export_test.go index 00dd9374..7cdae356 100644 --- a/export_test.go +++ b/export_test.go @@ -1,5 +1,3 @@ -//go:build linux - package openssl var (