diff --git a/aes.go b/aes.go index a7c3aea6..1fc11f00 100644 --- a/aes.go +++ b/aes.go @@ -6,93 +6,9 @@ package openssl import "C" import ( "crypto/cipher" - "encoding/binary" "errors" - "runtime" - "strconv" - "sync" - "unsafe" ) -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 -) - -// 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 { - 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(k, 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() - } - 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 { - 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 @@ -107,446 +23,68 @@ type extraModes interface { var _ extraModes = (*aesCipher)(nil) func NewAESCipher(key []byte) (cipher.Block, error) { - c := &aesCipher{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_ECB + kind = cipherAES128 case 192: - c.kind = cipherAES192_ECB + kind = cipherAES192 case 256: - c.kind = cipherAES256_ECB + kind = cipherAES256 default: - return nil, errors.New("crypto/cipher: Invalid key size") - } - - runtime.SetFinalizer(c, (*aesCipher).finalize) - - return c, nil -} - -func (c *aesCipher) finalize() { - if c.enc_ctx != nil { - C.go_openssl_EVP_CIPHER_CTX_free(c.enc_ctx) + return nil, errors.New("crypto/aes: invalid key size") } - if c.dec_ctx != nil { - C.go_openssl_EVP_CIPHER_CTX_free(c.dec_ctx) + c, err := newEVPCipher(key, kind) + if err != nil { + return nil, err } + return &aesCipher{c}, nil } -func (c *aesCipher) BlockSize() int { return aesBlockSize } - -func (c *aesCipher) Encrypt(dst, src []byte) { - if len(src) < aesBlockSize { - panic("crypto/aes: input not full block") - } - if len(dst) < aesBlockSize { - panic("crypto/aes: 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]) { - 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) - if err != nil { - panic(err) - } - } - - if C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), aesBlockSize) != 1 { - panic("crypto/cipher: EncryptUpdate failed") - } - runtime.KeepAlive(c) +// 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 *aesCipher) Decrypt(dst, src []byte) { - if len(src) < aesBlockSize { - panic("crypto/aes: input not full block") - } - if len(dst) < aesBlockSize { - panic("crypto/aes: 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]) { - 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) - 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), aesBlockSize) - runtime.KeepAlive(c) +type aesCipher struct { + *evpCipher } -type aesCBC struct { - ctx C.GO_EVP_CIPHER_CTX_PTR +func (c *aesCipher) BlockSize() int { + return c.blockSize } -func (x *aesCBC) BlockSize() int { return aesBlockSize } - -func (x *aesCBC) CryptBlocks(dst, src []byte) { - if inexactOverlap(dst, src) { - panic("crypto/cipher: invalid buffer overlap") - } - if len(src)%aesBlockSize != 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 (c *aesCipher) Encrypt(dst, src []byte) { + if err := c.encrypt(dst, src); err != nil { + // crypto/aes expects that the panic message starts with "crypto/aes: ". + panic("crypto/aes: " + err.Error()) } } -func (x *aesCBC) SetIV(iv []byte) { - if len(iv) != aesBlockSize { - 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 *aesCipher) Decrypt(dst, src []byte) { + if err := c.decrypt(dst, src); err != nil { + // crypto/aes expects that the panic message starts with "crypto/aes: ". + panic("crypto/aes: " + err.Error()) } } 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) + return c.newCBC(iv, cipherOpEncrypt) } 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") - } - - var err error - x.ctx, err = newCipherCtx(cipher, C.GO_AES_DECRYPT, 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 -} - -type aesCTR struct { - ctx C.GO_EVP_CIPHER_CTX_PTR -} - -func (x *aesCTR) 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) + return c.newCBC(iv, cipherOpDecrypt) } 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) - if err != nil { - panic(err) - } - - runtime.SetFinalizer(x, (*aesCTR).finalize) - - return x -} - -func (c *aesCTR) finalize() { - C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) -} - -type aesGCM struct { - ctx C.GO_EVP_CIPHER_CTX_PTR - tls bool - minNextNonce uint64 -} - -const ( - gcmTagSize = 16 - gcmStandardNonceSize = 12 - gcmTlsAddSize = 13 - gcmTlsFixedNonceSize = 4 -) - -type aesNonceSizeError int - -func (n aesNonceSizeError) Error() string { - return "crypto/aes: invalid GCM nonce size " + strconv.Itoa(int(n)) -} - -type noGCM struct { - cipher.Block + return c.newCTR(iv) } func (c *aesCipher) NewGCM(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") - } - // 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() + return c.newGCMChecked(nonceSize, tagSize) } 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) - if err != nil { - return nil, err - } - g := &aesGCM{ctx: ctx, tls: tls} - runtime.SetFinalizer(g, (*aesGCM).finalize) - return g, nil -} - -func (g *aesGCM) finalize() { - C.go_openssl_EVP_CIPHER_CTX_free(g.ctx) -} - -func (g *aesGCM) NonceSize() int { - return gcmStandardNonceSize -} - -func (g *aesGCM) Overhead() int { - return gcmTagSize -} - -func (g *aesGCM) 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) { - 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 *aesGCM) 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 { - 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 C.int, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) { - cipher := newCipher(kind) - 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), mode) != 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/aes_test.go b/aes_test.go index a5add071..39cc1600 100644 --- a/aes_test.go +++ b/aes_test.go @@ -1,25 +1,58 @@ -package openssl +package openssl_test import ( "bytes" "crypto/cipher" "math" "testing" + + "github.com/golang-fips/openssl/v2" ) +func TestAESShortBlocks(t *testing.T) { + bytes := func(n int) []byte { return make([]byte, n) } + + c, _ := openssl.NewAESCipher(bytes(16)) + + mustPanic(t, "crypto/aes: input not full block", func() { c.Encrypt(bytes(1), bytes(1)) }) + mustPanic(t, "crypto/aes: input not full block", func() { c.Decrypt(bytes(1), bytes(1)) }) + mustPanic(t, "crypto/aes: input not full block", func() { c.Encrypt(bytes(100), bytes(1)) }) + mustPanic(t, "crypto/aes: input not full block", func() { c.Decrypt(bytes(100), bytes(1)) }) + mustPanic(t, "crypto/aes: output not full block", func() { c.Encrypt(bytes(1), bytes(100)) }) + mustPanic(t, "crypto/aes: output not full block", func() { c.Decrypt(bytes(1), bytes(100)) }) +} + +func mustPanic(t *testing.T, msg string, f func()) { + defer func() { + err := recover() + if err == nil { + t.Errorf("function did not panic, wanted %q", msg) + } else if err != msg { + t.Errorf("got panic %v, wanted %q", err, msg) + } + }() + f() +} + 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 +82,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 +105,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 +126,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 +176,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 +189,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 +205,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 +225,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 +234,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 +257,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 +275,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 +284,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 +362,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 +404,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 +422,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 +440,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 +456,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 +480,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 +503,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/cipher.go b/cipher.go new file mode 100644 index 00000000..aadba3e7 --- /dev/null +++ b/cipher.go @@ -0,0 +1,534 @@ +//go:build !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 + cipherDES + cipherDES3 + cipherRC4 +) + +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" + case cipherRC4: + return "RC4" + default: + panic("unknown cipher kind: " + strconv.Itoa(int(c))) + } +} + +type cipherMode int8 + +const ( + cipherModeNone cipherMode = -1 + cipherModeECB cipherMode = iota + cipherModeCBC + cipherModeCTR + 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 + +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() + } + 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() + } + case cipherRC4: + cipher = C.go_openssl_EVP_rc4() + } + 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 + 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() { + 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) encrypt(dst, src []byte) error { + if len(src) < c.blockSize { + return errors.New("input not full block") + } + if len(dst) < c.blockSize { + return errors.New("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[:c.blockSize], src[:c.blockSize]) { + return errors.New("invalid buffer overlap") + } + if c.enc_ctx == nil { + var err error + c.enc_ctx, err = newCipherCtx(c.kind, cipherModeECB, cipherOpEncrypt, c.key, nil) + if err != nil { + return err + } + } + + if C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), C.int(c.blockSize)) != 1 { + return errors.New("EncryptUpdate failed") + } + runtime.KeepAlive(c) + return nil +} + +func (c *evpCipher) decrypt(dst, src []byte) error { + if len(src) < c.blockSize { + return errors.New("input not full block") + } + if len(dst) < c.blockSize { + return errors.New("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[:c.blockSize], src[:c.blockSize]) { + return errors.New("invalid buffer overlap") + } + if c.dec_ctx == nil { + var err error + c.dec_ctx, err = newCipherCtx(c.kind, cipherModeECB, cipherOpDecrypt, c.key, nil) + if err != nil { + return err + } + if C.go_openssl_EVP_CIPHER_CTX_set_padding(c.dec_ctx, 0) != 1 { + return errors.New("could not disable cipher padding") + } + } + + C.go_openssl_EVP_DecryptUpdate_wrapper(c.dec_ctx, base(dst), base(src), C.int(c.blockSize)) + runtime.KeepAlive(c) + return nil +} + +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), C.int(cipherOpNone)) != 1 { + panic("cipher: unable to initialize EVP cipher ctx") + } +} + +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) + } + 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, cipherOpEncrypt, 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, cipherOpNone, 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 cipherOp, key, iv []byte) (ctx C.GO_EVP_CIPHER_CTX_PTR, err error) { + cipher := loadCipher(kind, mode) + if cipher == nil { + panic("crypto/cipher: unsupported cipher: " + kind.String()) + } + ctx = C.go_openssl_EVP_CIPHER_CTX_new() + if ctx == nil { + return nil, fail("unable to create EVP cipher ctx") + } + defer func() { + if err != nil { + C.go_openssl_EVP_CIPHER_CTX_free(ctx) + } + }() + if kind == cipherRC4 { + // RC4 cipher supports a variable key length. + // We need to set the key length before setting the key, + // and to do so we need to have an initialized cipher ctx. + if C.go_openssl_EVP_CipherInit_ex(ctx, cipher, nil, nil, nil, C.int(encrypt)) != 1 { + return nil, newOpenSSLError("EVP_CipherInit_ex") + } + if C.go_openssl_EVP_CIPHER_CTX_set_key_length(ctx, C.int(len(key))) != 1 { + return nil, newOpenSSLError("EVP_CIPHER_CTX_set_key_length") + } + // Pass nil to the next call to EVP_CipherInit_ex to avoid resetting ctx's cipher. + cipher = nil + } + if C.go_openssl_EVP_CipherInit_ex(ctx, cipher, nil, base(key), base(iv), C.int(encrypt)) != 1 { + 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/des.go b/des.go new file mode 100644 index 00000000..98b15d2d --- /dev/null +++ b/des.go @@ -0,0 +1,113 @@ +//go:build !cmd_go_bootstrap + +package openssl + +// #include "goopenssl.h" +import "C" +import ( + "crypto/cipher" + "errors" +) + +// 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, +// 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. + return loadCipher(cipherDES3, cipherModeECB) != nil +} + +func NewDESCipher(key []byte) (cipher.Block, error) { + if len(key) != 8 { + return nil, errors.New("crypto/des: invalid key size") + } + c, err := newEVPCipher(key, cipherDES) + 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 +} + +func NewTripleDESCipher(key []byte) (cipher.Block, error) { + if len(key) != 24 { + return nil, errors.New("crypto/des: invalid key size") + } + c, err := newEVPCipher(key, cipherDES3) + 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 +} + +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 c.blockSize +} + +func (c *desCipher) Encrypt(dst, src []byte) { + if err := c.encrypt(dst, src); err != nil { + // crypto/des expects that the panic message starts with "crypto/des: ". + panic("crypto/des: " + err.Error()) + } +} + +func (c *desCipher) Decrypt(dst, src []byte) { + if err := c.decrypt(dst, src); err != nil { + // crypto/des expects that the panic message starts with "crypto/des: ". + panic("crypto/des: " + err.Error()) + } +} + +func (c *desCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { + return c.newCBC(iv, cipherOpEncrypt) +} + +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) +} diff --git a/des_test.go b/des_test.go new file mode 100644 index 00000000..8d222b97 --- /dev/null +++ b/des_test.go @@ -0,0 +1,1734 @@ +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/evp.go b/evp.go index 0c307c52..3775fa42 100644 --- a/evp.go +++ b/evp.go @@ -210,15 +210,27 @@ func setupEVP(withKey withKeyFunc, padding C.int, clabel = (*C.uchar)(cryptoMalloc(len(label))) copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label) } - var ret C.int + var err error if vMajor == 3 { - ret = C.go_openssl_EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, unsafe.Pointer(clabel), C.int(len(label))) + // Docs say EVP_PKEY_CTX_set0_rsa_oaep_label accepts a null label, + // but it does not: https://github.com/openssl/openssl/issues/21288 + if len(label) == 0 { + // cryptoMalloc can't create a zero-length array: use size 1. + clabel = (*C.uchar)(cryptoMalloc(1)) + } + ret := C.go_openssl_EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, unsafe.Pointer(clabel), C.int(len(label))) + if ret != 1 { + err = newOpenSSLError("EVP_PKEY_CTX_set0_rsa_oaep_label failed") + } } else { - ret = C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_RSA, -1, C.GO_EVP_PKEY_CTRL_RSA_OAEP_LABEL, C.int(len(label)), unsafe.Pointer(clabel)) + ret := C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_RSA, -1, C.GO_EVP_PKEY_CTRL_RSA_OAEP_LABEL, C.int(len(label)), unsafe.Pointer(clabel)) + if ret != 1 { + err = newOpenSSLError("EVP_PKEY_CTX_ctrl failed") + } } - if ret != 1 { + if err != nil { cryptoFree(unsafe.Pointer(clabel)) - return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed") + return nil, err } case C.GO_RSA_PKCS1_PSS_PADDING: md := cryptoHashToMD(ch) diff --git a/export_test.go b/export_test.go new file mode 100644 index 00000000..7cdae356 --- /dev/null +++ b/export_test.go @@ -0,0 +1,6 @@ +package openssl + +var ( + ErrOpen = errOpen + EVPCipherFinalize = new(evpCipher).finalize +) diff --git a/goopenssl.h b/goopenssl.h index d9efb1ba..d0ffe6cb 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_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, GO_AES_DECRYPT) != 1) + if (go_openssl_EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, nonce) != 1) return 0; int discard_len, out_len; diff --git a/rc4.go b/rc4.go new file mode 100644 index 00000000..db28b61f --- /dev/null +++ b/rc4.go @@ -0,0 +1,63 @@ +//go:build !cmd_go_bootstrap + +package openssl + +// #include "goopenssl.h" +import "C" +import "runtime" + +// SupportsRC4 returns true if NewRC4Cipher is supported. +func SupportsRC4() bool { + // True for stock OpenSSL 1. + // False for stock OpenSSL 3 unless the legacy provider is available. + return loadCipher(cipherRC4, cipherModeNone) != nil +} + +// A RC4Cipher is an instance of RC4 using a particular key. +type RC4Cipher struct { + ctx C.GO_EVP_CIPHER_CTX_PTR +} + +// NewRC4Cipher creates and returns a new Cipher. +func NewRC4Cipher(key []byte) (*RC4Cipher, error) { + ctx, err := newCipherCtx(cipherRC4, cipherModeNone, cipherOpEncrypt, key, nil) + if err != nil { + return nil, err + } + c := &RC4Cipher{ctx} + runtime.SetFinalizer(c, (*RC4Cipher).finalize) + return c, nil +} + +func (c *RC4Cipher) finalize() { + if c.ctx != nil { + C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) + } +} + +// Reset zeros the key data and makes the Cipher unusable. +func (c *RC4Cipher) Reset() { + if c.ctx != nil { + C.go_openssl_EVP_CIPHER_CTX_free(c.ctx) + c.ctx = nil + } +} + +// XORKeyStream sets dst to the result of XORing src with the key stream. +// Dst and src must overlap entirely or not at all. +func (c *RC4Cipher) XORKeyStream(dst, src []byte) { + if c.ctx == nil || len(src) == 0 { + return + } + if inexactOverlap(dst[:len(src)], src) { + panic("crypto/rc4: invalid buffer overlap") + } + var outLen C.int + if C.go_openssl_EVP_EncryptUpdate(c.ctx, base(dst), &outLen, base(src), C.int(len(src))) != 1 { + panic("crypto/cipher: EncryptUpdate failed") + } + if int(outLen) != len(src) { + panic("crypto/rc4: src not fully XORed") + } + runtime.KeepAlive(c) +} diff --git a/rc4_test.go b/rc4_test.go new file mode 100644 index 00000000..e9f8dca9 --- /dev/null +++ b/rc4_test.go @@ -0,0 +1,167 @@ +package openssl_test + +import ( + "bytes" + "fmt" + "testing" + + "github.com/golang-fips/openssl/v2" +) + +type rc4Test struct { + key, keystream []byte +} + +var golden = []rc4Test{ + // Test vectors from the original cypherpunk posting of ARC4: + // https://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0?pli=1 + { + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + []byte{0x74, 0x94, 0xc2, 0xe7, 0x10, 0x4b, 0x08, 0x79}, + }, + { + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a}, + }, + { + []byte{0xef, 0x01, 0x23, 0x45}, + []byte{0xd6, 0xa1, 0x41, 0xa7, 0xec, 0x3c, 0x38, 0xdf, 0xbd, 0x61}, + }, + + // Test vectors from the Wikipedia page: https://en.wikipedia.org/wiki/RC4 + { + []byte{0x4b, 0x65, 0x79}, + []byte{0xeb, 0x9f, 0x77, 0x81, 0xb7, 0x34, 0xca, 0x72, 0xa7, 0x19}, + }, + { + []byte{0x57, 0x69, 0x6b, 0x69}, + []byte{0x60, 0x44, 0xdb, 0x6d, 0x41, 0xb7}, + }, + { + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{ + 0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a, + 0x8a, 0x06, 0x1e, 0x67, 0x57, 0x6e, 0x92, 0x6d, + 0xc7, 0x1a, 0x7f, 0xa3, 0xf0, 0xcc, 0xeb, 0x97, + 0x45, 0x2b, 0x4d, 0x32, 0x27, 0x96, 0x5f, 0x9e, + 0xa8, 0xcc, 0x75, 0x07, 0x6d, 0x9f, 0xb9, 0xc5, + 0x41, 0x7a, 0xa5, 0xcb, 0x30, 0xfc, 0x22, 0x19, + 0x8b, 0x34, 0x98, 0x2d, 0xbb, 0x62, 0x9e, 0xc0, + 0x4b, 0x4f, 0x8b, 0x05, 0xa0, 0x71, 0x08, 0x50, + 0x92, 0xa0, 0xc3, 0x58, 0x4a, 0x48, 0xe4, 0xa3, + 0x0a, 0x39, 0x7b, 0x8a, 0xcd, 0x1d, 0x00, 0x9e, + 0xc8, 0x7d, 0x68, 0x11, 0xf2, 0x2c, 0xf4, 0x9c, + 0xa3, 0xe5, 0x93, 0x54, 0xb9, 0x45, 0x15, 0x35, + 0xa2, 0x18, 0x7a, 0x86, 0x42, 0x6c, 0xca, 0x7d, + 0x5e, 0x82, 0x3e, 0xba, 0x00, 0x44, 0x12, 0x67, + 0x12, 0x57, 0xb8, 0xd8, 0x60, 0xae, 0x4c, 0xbd, + 0x4c, 0x49, 0x06, 0xbb, 0xc5, 0x35, 0xef, 0xe1, + 0x58, 0x7f, 0x08, 0xdb, 0x33, 0x95, 0x5c, 0xdb, + 0xcb, 0xad, 0x9b, 0x10, 0xf5, 0x3f, 0xc4, 0xe5, + 0x2c, 0x59, 0x15, 0x65, 0x51, 0x84, 0x87, 0xfe, + 0x08, 0x4d, 0x0e, 0x3f, 0x03, 0xde, 0xbc, 0xc9, + 0xda, 0x1c, 0xe9, 0x0d, 0x08, 0x5c, 0x2d, 0x8a, + 0x19, 0xd8, 0x37, 0x30, 0x86, 0x16, 0x36, 0x92, + 0x14, 0x2b, 0xd8, 0xfc, 0x5d, 0x7a, 0x73, 0x49, + 0x6a, 0x8e, 0x59, 0xee, 0x7e, 0xcf, 0x6b, 0x94, + 0x06, 0x63, 0xf4, 0xa6, 0xbe, 0xe6, 0x5b, 0xd2, + 0xc8, 0x5c, 0x46, 0x98, 0x6c, 0x1b, 0xef, 0x34, + 0x90, 0xd3, 0x7b, 0x38, 0xda, 0x85, 0xd3, 0x2e, + 0x97, 0x39, 0xcb, 0x23, 0x4a, 0x2b, 0xe7, 0x40, + }, + }, +} + +func TestRC4Golden(t *testing.T) { + if !openssl.SupportsRC4() { + t.Skip("RC4 is not supported") + } + for gi, g := range golden { + data := make([]byte, len(g.keystream)) + for i := range data { + data[i] = byte(i) + } + + expect := make([]byte, len(g.keystream)) + for i := range expect { + expect[i] = byte(i) ^ g.keystream[i] + } + + for size := 1; size <= len(g.keystream); size++ { + c, err := openssl.NewRC4Cipher(g.key) + if err != nil { + t.Fatalf("#%d: NewCipher: %v", gi, err) + } + + off := 0 + for off < len(g.keystream) { + n := len(g.keystream) - off + if n > size { + n = size + } + desc := fmt.Sprintf("#%d@[%d:%d]", gi, off, off+n) + src := data[off : off+n] + expect := expect[off : off+n] + dst := make([]byte, len(src)) + c.XORKeyStream(dst, src) + for i, v := range dst { + if v != expect[i] { + t.Fatalf("%s: mismatch at byte %d:\nhave %x\nwant %x", desc, i, dst, expect) + } + } + off += n + } + } + } +} + +func TestRC4Block(t *testing.T) { + if !openssl.SupportsRC4() { + t.Skip("RC4 is not supported") + } + c1a, _ := openssl.NewRC4Cipher(golden[0].key) + c1b, _ := openssl.NewRC4Cipher(golden[1].key) + data1 := make([]byte, 1<<20) + for i := range data1 { + c1a.XORKeyStream(data1[i:i+1], data1[i:i+1]) + c1b.XORKeyStream(data1[i:i+1], data1[i:i+1]) + } + + c2a, _ := openssl.NewRC4Cipher(golden[0].key) + c2b, _ := openssl.NewRC4Cipher(golden[1].key) + data2 := make([]byte, 1<<20) + c2a.XORKeyStream(data2, data2) + c2b.XORKeyStream(data2, data2) + + if !bytes.Equal(data1, data2) { + t.Fatalf("bad block") + } +} + +func benchmarkRC4(b *testing.B, size int64) { + if !openssl.SupportsRC4() { + b.Skip("RC4 is not supported") + } + buf := make([]byte, size) + c, err := openssl.NewRC4Cipher(golden[0].key) + if err != nil { + panic(err) + } + b.SetBytes(size) + + for i := 0; i < b.N; i++ { + c.XORKeyStream(buf, buf) + } +} + +func BenchmarkRC4_128(b *testing.B) { + benchmarkRC4(b, 128) +} + +func BenchmarkRC4_1K(b *testing.B) { + benchmarkRC4(b, 1024) +} + +func BenchmarkRC4_8K(b *testing.B) { + benchmarkRC4(b, 8096) +} diff --git a/rsa_test.go b/rsa_test.go index 4313c06f..c926e9cf 100644 --- a/rsa_test.go +++ b/rsa_test.go @@ -57,6 +57,29 @@ func TestEncryptDecryptOAEP(t *testing.T) { } } +func TestEncryptDecryptOAEP_EmptyLabel(t *testing.T) { + sha256 := openssl.NewSHA256() + msg := []byte("hi!") + label := []byte("") + priv, pub := newRSAKey(t, 2048) + enc, err := openssl.EncryptRSAOAEP(sha256, nil, pub, msg, label) + if err != nil { + t.Fatal(err) + } + dec, err := openssl.DecryptRSAOAEP(sha256, nil, priv, enc, label) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(dec, msg) { + t.Errorf("got:%x want:%x", dec, msg) + } + sha1 := openssl.NewSHA1() + _, err = openssl.DecryptRSAOAEP(sha1, nil, priv, enc, label) + if err == nil { + t.Error("decrypt failure expected due to hash mismatch") + } +} + func TestEncryptDecryptOAEP_WithMGF1Hash(t *testing.T) { sha1 := openssl.NewSHA1() sha256 := openssl.NewSHA256() diff --git a/shims.h b/shims.h index 0560ed77..4457a3e4 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, @@ -246,6 +240,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)) \ @@ -262,6 +257,13 @@ 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(const GO_EVP_CIPHER_PTR, EVP_rc4, (void), ()) \ +DEFINEFUNC_RENAMED_3_0(int, EVP_CIPHER_get_block_size, EVP_CIPHER_block_size, (const GO_EVP_CIPHER_PTR cipher), (cipher)) \ +DEFINEFUNC(int, EVP_CIPHER_CTX_set_key_length, (GO_EVP_CIPHER_CTX_PTR x, int keylen), (x, keylen)) \ 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), ()) \ diff --git a/tls1prf.go b/tls1prf.go index 7043b3bc..3153fc81 100644 --- a/tls1prf.go +++ b/tls1prf.go @@ -16,7 +16,10 @@ func SupportsTLS1PRF() bool { (vMajor >= 1 && vMinor >= 1) } -func TLS1PRF(secret, label, seed []byte, keyLen int, h func() hash.Hash) ([]byte, error) { +// TLS1PRF implements the TLS 1.0/1.1 pseudo-random function if h is nil, +// else it implements the TLS 1.2 pseudo-random function. +// The pseudo-random number will be written to result and will be of length len(result). +func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { var md C.GO_EVP_MD_PTR if h == nil { // TLS 1.0/1.1 PRF doesn't allow to specify the hash function, @@ -29,70 +32,73 @@ func TLS1PRF(secret, label, seed []byte, keyLen int, h func() hash.Hash) ([]byte md = hashToMD(h()) } if md == nil { - return nil, errors.New("unsupported hash function") + return errors.New("unsupported hash function") } ctx := C.go_openssl_EVP_PKEY_CTX_new_id(C.GO_EVP_PKEY_TLS1_PRF, nil) if ctx == nil { - return nil, newOpenSSLError("EVP_PKEY_CTX_new_id") + return newOpenSSLError("EVP_PKEY_CTX_new_id") } defer func() { C.go_openssl_EVP_PKEY_CTX_free(ctx) }() if C.go_openssl_EVP_PKEY_derive_init(ctx) != 1 { - return nil, newOpenSSLError("EVP_PKEY_derive_init") + return newOpenSSLError("EVP_PKEY_derive_init") } switch vMajor { case 3: if C.go_openssl_EVP_PKEY_CTX_set_tls1_prf_md(ctx, md) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_set_tls1_prf_md") + return newOpenSSLError("EVP_PKEY_CTX_set_tls1_prf_md") } if C.go_openssl_EVP_PKEY_CTX_set1_tls1_prf_secret(ctx, base(secret), C.int(len(secret))) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_set1_tls1_prf_secret") + return newOpenSSLError("EVP_PKEY_CTX_set1_tls1_prf_secret") } if C.go_openssl_EVP_PKEY_CTX_add1_tls1_prf_seed(ctx, base(label), C.int(len(label))) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_add1_tls1_prf_seed") + return newOpenSSLError("EVP_PKEY_CTX_add1_tls1_prf_seed") } if C.go_openssl_EVP_PKEY_CTX_add1_tls1_prf_seed(ctx, base(seed), C.int(len(seed))) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_add1_tls1_prf_seed") + return newOpenSSLError("EVP_PKEY_CTX_add1_tls1_prf_seed") } case 1: if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, -1, C.GO1_EVP_PKEY_OP_DERIVE, C.GO_EVP_PKEY_CTRL_TLS_MD, 0, unsafe.Pointer(md)) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_set_tls1_prf_md") + return newOpenSSLError("EVP_PKEY_CTX_set_tls1_prf_md") } if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, -1, C.GO1_EVP_PKEY_OP_DERIVE, C.GO_EVP_PKEY_CTRL_TLS_SECRET, C.int(len(secret)), unsafe.Pointer(base(secret))) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_set1_tls1_prf_secret") + return newOpenSSLError("EVP_PKEY_CTX_set1_tls1_prf_secret") } if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, -1, C.GO1_EVP_PKEY_OP_DERIVE, C.GO_EVP_PKEY_CTRL_TLS_SEED, C.int(len(label)), unsafe.Pointer(base(label))) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_add1_tls1_prf_seed") + return newOpenSSLError("EVP_PKEY_CTX_add1_tls1_prf_seed") } if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, -1, C.GO1_EVP_PKEY_OP_DERIVE, C.GO_EVP_PKEY_CTRL_TLS_SEED, C.int(len(seed)), unsafe.Pointer(base(seed))) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_add1_tls1_prf_seed") + return newOpenSSLError("EVP_PKEY_CTX_add1_tls1_prf_seed") } } - outLen := C.size_t(keyLen) - out := make([]byte, outLen) - if C.go_openssl_EVP_PKEY_derive(ctx, base(out), &outLen) != 1 { - return nil, newOpenSSLError("EVP_PKEY_derive") + outLen := C.size_t(len(result)) + if C.go_openssl_EVP_PKEY_derive(ctx, base(result), &outLen) != 1 { + return newOpenSSLError("EVP_PKEY_derive") } - if outLen != C.size_t(keyLen) { - return nil, errors.New("tls1-prf: entropy limit reached") + // The Go standard library expects TLS1PRF to return the requested number of bytes, + // fail if it doesn't. While there is no known situation where this will happen, + // EVP_PKEY_derive handles multiple algorithms and there could be a subtle mismatch + // after more code changes in the future. + if outLen != C.size_t(len(result)) { + return errors.New("tls1-prf: derived less bytes than requested") } - return out[:outLen], nil + return nil } diff --git a/tls1prf_test.go b/tls1prf_test.go index bc81d984..80324603 100644 --- a/tls1prf_test.go +++ b/tls1prf_test.go @@ -155,12 +155,13 @@ func TestTLS1PRF(t *testing.T) { if !openssl.SupportsHash(tt.hash) { t.Skip("skipping: hash not supported") } - out, err := openssl.TLS1PRF(tt.secret, tt.label, tt.seed, len(tt.out), cryptoToHash(tt.hash)) + result := make([]byte, len(tt.out)) + err := openssl.TLS1PRF(result, tt.secret, tt.label, tt.seed, cryptoToHash(tt.hash)) if err != nil { t.Fatalf("error deriving TLS 1.2 PRF: %v.", err) } - if !bytes.Equal(out, tt.out) { - t.Errorf("incorrect key output: have %v, need %v.", out, tt.out) + if !bytes.Equal(result, tt.out) { + t.Errorf("incorrect key output: have %v, need %v.", result, tt.out) } }) }