/* Copyright (c) (2014-2016,2018-2020) Apple Inc. All rights reserved.
 *
 * corecrypto is licensed under Apple Inc.’s Internal Use License Agreement (which
 * is contained in the License.txt file distributed with corecrypto) and only to
 * people who accept that license. IMPORTANT:  Any license rights granted to you by
 * Apple Inc. (if any) are limited to internal use within your organization only on
 * devices and computers you own or control, for the sole purpose of verifying the
 * security characteristics and correct functioning of the Apple Software.  You may
 * not, directly or indirectly, redistribute the Apple Software or any portions thereof.
 */

#include "testmore.h"
#include "testbyteBuffer.h"
#include "cc_debug.h"
#include <corecrypto/ccaes.h>
#include <corecrypto/ccdes.h>
#include <corecrypto/ccmode.h>
#include <corecrypto/ccpad.h>
#include <corecrypto/cc_runtime_config.h>
#include <corecrypto/ccn.h>
#include <stdbool.h>
#include "ccconstanttime.h"

#if (CCPAD == 0)
entryPoint(ccpad_tests,"ccpad")
#else
#include "crypto_test_modes.h"
#include "ccsymmetric_pad.h"

static const int kTestTestCount = 1205;

static const int verbose=0;

#include <corecrypto/ccrng_test.h>
#include "cccycles.h"
#include "ccstats.h"

//======================================================================
// Constant time verification parameters
//======================================================================

// Number of iteration of test where timings are not taken into account.
// Made to reach a stable performance state
#define CCPAD_WARMUP        0

// Each sample is the average time for many iteration with identical inputs
#define CCPAD_TIMING_REPEAT  150

// Number of sample for the statistical analysis
// typically 100~1000 is a good range
#define CCPAD_TIMING_SAMPLES 200

// In case of failure, try many times
// This is to reduce false positives due to noise/timing accuracy.
// If implementation is not constant time, the behavior will be consistent
// So that this does not reduce the detection power.
#define CCPAD_TIMING_RETRIES 10

// Two statitical tools are available: T-test and Wilcoxon.
// T-test assumes that the distribution to be compared are normal
// Wilcoxon measure offset between distribution.
// Due to potential switches between performance state or occasional
// latencies, Wilcoxon is recommended.
// > Set to 1 to use T-test instead of Wilcoxon
#define T_TEST  1

// Number of iteration of the full test (to play with to evaluate chances of false positives)
#define CMP_TIMING_TEST_ITERATION 1

// Quantile for the repeated timing. Empirical value.
#define CCPAD_PERCENTILE 9

//======================================================================

// Local types
typedef struct {
    ccpad_select padding_mode;
    char *keyStr;
    char *init_ivStr;
    char *ptStr;
    char *outputStr;
} ccpad_test_vector_t;

static int
ccsymmetric_pad_ciphertext_tests(duplex_cryptor cryptor, const ccpad_test_vector_t *test);

static int
ccsymmetric_pad_plaintext_tests(duplex_cryptor cryptor, ccpad_select padding_mode, const ccpad_test_vector_t *test);

static int
ccsymmetric_pad_roundtrip_tests(duplex_cryptor cryptor, ccpad_select padding_mode, size_t message_size);

// Test vectors

// These are stock keys/IVs/blocks to encode - don't change them - add if you

#define keystr64     "0001020304050607"
#define keystr128    "000102030405060708090a0b0c0d0e0f"
#define ivstr64      "0f0e0d0c0b0a0908"
#define ivstr128     "0f0e0d0c0b0a09080706050403020100"

#define MSG_01       "00"
#define MSG_02       "0000"
#define MSG_03       "000000"
#define MSG_04       "00000000"
#define MSG_05       "0000000000"
#define MSG_06       "000000000000"
#define MSG_07       "00000000000000"
#define MSG_08       "0000000000000000"
#define MSG_09       "000000000000000000"
#define MSG_10       "00000000000000000000"
#define MSG_11       "0000000000000000000000"
#define MSG_12       "000000000000000000000000"
#define MSG_13       "00000000000000000000000000"
#define MSG_14       "0000000000000000000000000000"
#define MSG_15       "000000000000000000000000000000"
#define MSG_16       "00000000000000000000000000000000"
#define MSG_17       "0000000000000000000000000000000000"
#define MSG_18       "000000000000000000000000000000000000"
#define MSG_19       "00000000000000000000000000000000000000"
#define MSG_20       "0000000000000000000000000000000000000000"
#define MSG_21       "000000000000000000000000000000000000000000"
#define MSG_22       "00000000000000000000000000000000000000000000"
#define MSG_23       "0000000000000000000000000000000000000000000000"
#define MSG_24       "000000000000000000000000000000000000000000000000"
#define MSG_25       "00000000000000000000000000000000000000000000000000"
#define MSG_26       "0000000000000000000000000000000000000000000000000000"
#define MSG_27       "000000000000000000000000000000000000000000000000000000"
#define MSG_28       "00000000000000000000000000000000000000000000000000000000"
#define MSG_29       "0000000000000000000000000000000000000000000000000000000000"
#define MSG_30       "000000000000000000000000000000000000000000000000000000000000"
#define MSG_31       "00000000000000000000000000000000000000000000000000000000000000"
#define MSG_32       "0000000000000000000000000000000000000000000000000000000000000000"


#define PAD_16       "10101010101010101010101010101010"
#define PAD_15       "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"
#define PAD_14       "0e0e0e0e0e0e0e0e0e0e0e0e0e0e"
#define PAD_13       "0d0d0d0d0d0d0d0d0d0d0d0d0d"
#define PAD_12       "0c0c0c0c0c0c0c0c0c0c0c0c"
#define PAD_11       "0b0b0b0b0b0b0b0b0b0b0b"
#define PAD_10       "0a0a0a0a0a0a0a0a0a0a"
#define PAD_09       "090909090909090909"
#define PAD_08       "0808080808080808"
#define PAD_07       "07070707070707"
#define PAD_06       "060606060606"
#define PAD_05       "0505050505"
#define PAD_04       "04040404"
#define PAD_03       "030303"
#define PAD_02       "0202"
#define PAD_01       "01"

#define END_VECTOR   {0,  NULL, NULL, NULL, NULL}

static const ccpad_test_vector_t aes_plaintext_vectors[] = {
    {ccpad_pkcs7,keystr128,ivstr128, MSG_01,  PAD_15},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_02,  PAD_14},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_03,  PAD_13},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_04,  PAD_12},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_05,  PAD_11},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_06,  PAD_10},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_07,  PAD_09},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_08,  PAD_08},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_09,  PAD_07},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_10,  PAD_06},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_11,  PAD_05},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_12,  PAD_04},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_13,  PAD_03},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_14,  PAD_02},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_15,  PAD_01},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_16,  PAD_16},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_17,  PAD_15},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_18,  PAD_14},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_19,  PAD_13},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_20,  PAD_12},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_21,  PAD_11},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_22,  PAD_10},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_23,  PAD_09},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_24,  PAD_08},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_25,  PAD_07},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_26,  PAD_06},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_27,  PAD_05},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_28,  PAD_04},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_29,  PAD_03},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_30,  PAD_02},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_31,  PAD_01},
    {ccpad_pkcs7,keystr128,ivstr128, MSG_32,  PAD_16},
    END_VECTOR
};

static const ccpad_test_vector_t des_plaintext_vectors[] = {
    {ccpad_pkcs7,keystr64, ivstr64, MSG_01,  PAD_07},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_02,  PAD_06},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_03,  PAD_05},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_04,  PAD_04},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_05,  PAD_03},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_06,  PAD_02},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_07,  PAD_01},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_08,  PAD_08},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_09,  PAD_07},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_10,  PAD_06},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_11,  PAD_05},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_12,  PAD_04},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_13,  PAD_03},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_14,  PAD_02},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_15,  PAD_01},
    {ccpad_pkcs7,keystr64, ivstr64, MSG_16,  PAD_08},
    END_VECTOR
};

#define keystr128_cts    "636869636b656e207465726979616b69"
#define ivstr128_cts     "0f0e0d0c0b0a09080706050403020100"
#define zeroX16          "00000000000000000000000000000000"

static const ccpad_test_vector_t aes_cbc_ciphertext_vectors[] = {
    {ccpad_pkcs7,  "badfd2102e1e180a634204249c5a6933", "84c06c16c151007ca9ed9bb926e66eec",
        "fb58510beb65062c525a3de42d934d4b4ec433d600a1467142751886a10e7bf96f236c196d12dcf0698e09efc79a4bea072bc0830da8886674cf6174206cca2d4e9e543f0016ec4dcc602ffd0a417c722879e259497f89aee5ad99a4f65887058242250fbe44f61eab5e668adbb780a4cba97393f6ff152c13c39b57ed727bb94cf19d1b4a55f45cceb22b6c4f26f736d20a48cb6230578591c8d33d72b778d30b304818b20d918ef654cabeae1038f2a0db5170d2b4df38c6efc887bc1f837fba34e97daf8920414b748a909ad5ef56fb47fa53c680aae808f3e6065689339728251e18cd264f5385c969f87104099563a411cfe681d19134e9479e059d09b69f5010912291d0232f733a2688b3042ec4e82ce5163c384ee54a9f10e48a8ab46fd7147351dd8514bda5d8c4ce8babcc3ef82dbf44799fc59e37d8f3c99506d2168c84d8381f4f9a84cbce7bd0bb4bbbcdef0c626356d3ca126c8776e3a291881af518e23dbd067016c5898bed5f64d6e8f8acefba83f92b0c318ec7b905165fb6b81bc60528c0a0e3db38ab1ee6f37e56dbf270c0751674e0ddb1a6076d8f78084ce31f0d3673e638e0110575b16d9d9f151c1b9aca8d15d7a8111c0de5acf5ae3b307e8064c90329e421e3434a1ecd253b153447c21c79c9946666dae444c49a31b1f94da603a8377168dc4f874e98fff5ae89dd35d44e89df5748223b7a24",
        "4b52b5e85aecaaaf886bd9e8805390c62e12e13357e4beb3b713e37d217c6f7a9e432a04f87bd8a4dd0ef79eb7bf41b5a2a27e63361d7cb7af7b3c9a8f0b56ae27dc9cfd6c10eb1a79c7be35d31c3965b8e7099775f7644029bd79321f5dd12c55280a30fabd1b95e27c2d4dec6ca4d8716f36e7abe3408f5120560b573e5495ae7aad668fa84d6a8a1156c231a5b6d983ece3e27d199a806dc629c1a60c08ccb0e4807d9fed88f28ce0f59583708f540f97110b2620b1679220abe13e3c4b727186b289794583b20154ce9a07a284df3e63572f462142cae8949d7dd6f2b26fb90d556ec75e93dd33b59d697883312af89e52945b9baedfebe28759cdba4dfbf6e6f201b087478642cf0b34f983593c68947e4ee05bd17716e6cfb7c74c876c0ba650f3979f5eceb72a71d0d46aac4474ae2048d2a9884aa12e292950c77b17de11e8d3e895e60b1c584b1c8d9edd40ba7917e396d1d3bfd1941923aa40213195e8b8f7f4d5ae1057cbecdf89c8959745d1fcece59115819dc661e7b097c132e8f98720a57a83469cb82c374fdebc97badd7cef8d160a7f27d50f35b7e4af6f1b78361828e32a55b25fd56efbc12f8fcb7e2e4f882afa0c7747a455a1fae00a561cbb878e01b32fafb23f397371a8b3441c8da654b902d8489383542188821859a44f0fb2b63a49835f8ba5f0231ff0f8f5fc3d5c812331b11e39bc03394e2875313166ac5f77e4900146c6faafba81"},
    {ccpad_cts1, keystr128_cts, zeroX16, "4920776f756c64206c696b652074686520", "97c6353568f2bf8cb4d8a580362da7ff7f"},
    {ccpad_cts1, keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c20476175277320", "97687268d6ecccc0c07b25e25ecfe5fc00783e0efdb2c1d445d4c8eff7ed22"},
    {ccpad_cts1, keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c2047617527732043", "97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a8"},
    {ccpad_cts1, keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c", "97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5b3fffd940c16a18c1b5549d2f838029e"},
    {ccpad_cts1, keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20", "97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a89dad8bbb96c4cdc03bc103e1a194bbd8"},
    {ccpad_cts1, keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e", "97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a89dad8bbb96c4cdc03bc103e1a194bbd84807efe836ee89a526730dbc2f7bc840"},
    {ccpad_cts1, keystr128_cts, ivstr128_cts, "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", "e22abba9d2a201b18dc2f57e04aba21a16e0ed6358164c59ca64d204f33247ee2dc88b70f6ae0243d2dbcd6822a1058604b1c432a7a71395b36d820e2c3de4ee"},
    {ccpad_cts1, keystr128_cts, ivstr128_cts, "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a",
    "e22abba9d2a201b18dc2f57e04aba21a16e0ed6358164c59ca64d204f33247ee2dc88b70f6ae0243d2dbcd6822a105950b6576660739916d058623d688e27e"},
    {ccpad_cts1, keystr128_cts, ivstr128_cts, "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a",
        "e22abba9d2a201b18dc2f57e04aba21a16e0ed6358164c59ca64d204f33247ee2dc88b70f6ae0243d2db751002ef7a0f9d915d15346571eee7aa"},
     // Test vectors from CommonCrypto
     {ccpad_cts2, keystr128_cts, ivstr128_cts, "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", "e22abba9d2a201b18dc2f57e04aba21a16e0ed6358164c59ca64d204f33247ee2dc88b70f6ae0243d2dbcd6822a1058604b1c432a7a71395b36d820e2c3de4ee"},
     {ccpad_cts2, keystr128_cts, ivstr128_cts, "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a",
     "e22abba9d2a201b18dc2f57e04aba21a16e0ed6358164c59ca64d204f33247ee950b6576660739916d058623d688e27e2dc88b70f6ae0243d2dbcd6822a105"},
     {ccpad_cts2, keystr128_cts, ivstr128_cts, "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a",
     "e22abba9d2a201b18dc2f57e04aba21a16e0ed6358164c59ca64d204f33247ee751002ef7a0f9d915d15346571eee7aa2dc88b70f6ae0243d2db"},
     // Test vectors from RFC3962
     {ccpad_cts3, keystr128_cts, zeroX16, "4920776f756c64206c696b652074686520", "c6353568f2bf8cb4d8a580362da7ff7f97"},
     {ccpad_cts3,  keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c20476175277320", "fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5"},
     {ccpad_cts3,  keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c2047617527732043", "39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584"},
     {ccpad_cts3,  keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c", "97687268d6ecccc0c07b25e25ecfe584b3fffd940c16a18c1b5549d2f838029e39312523a78662d5be7fcbcc98ebf5"},
     {ccpad_cts3,  keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20", "97687268d6ecccc0c07b25e25ecfe5849dad8bbb96c4cdc03bc103e1a194bbd839312523a78662d5be7fcbcc98ebf5a8"},
     {ccpad_cts3,  keystr128_cts, zeroX16, "4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e", "97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a84807efe836ee89a526730dbc2f7bc8409dad8bbb96c4cdc03bc103e1a194bbd8"},
      // Test vectors from CommonCrypto
     {ccpad_cts3,  keystr128_cts, ivstr128_cts, "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", "e22abba9d2a201b18dc2f57e04aba21a16e0ed6358164c59ca64d204f33247ee04b1c432a7a71395b36d820e2c3de4ee2dc88b70f6ae0243d2dbcd6822a10586"},
     {ccpad_cts3,  keystr128_cts, ivstr128_cts, "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a",
     "e22abba9d2a201b18dc2f57e04aba21a16e0ed6358164c59ca64d204f33247ee950b6576660739916d058623d688e27e2dc88b70f6ae0243d2dbcd6822a105"},
     {ccpad_cts3,  keystr128_cts, ivstr128_cts, "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a",
     "e22abba9d2a201b18dc2f57e04aba21a16e0ed6358164c59ca64d204f33247ee751002ef7a0f9d915d15346571eee7aa2dc88b70f6ae0243d2db"},
     END_VECTOR
 };

// Compare the padded plaintext with what is expected
static int
ccsymmetric_pad_plaintext_tests(duplex_cryptor cryptor, ccpad_select padding_mode, const ccpad_test_vector_t *test) {
    // Retrieve test case info
    byteBuffer key =        hexStringToBytes(test->keyStr);
    byteBuffer init_iv =    hexStringToBytes(test->init_ivStr);
    byteBuffer message =    hexStringToBytes(test->ptStr);
    byteBuffer expected_padding =    hexStringToBytes(test->outputStr);
    size_t len = message->len;
    size_t result_len;
    size_t block_size=0;
    size_t padded_len=0;

    byteBuffer ciphertext,padded_message;

    // Set cipher
    cc_ciphermode_descriptor_s encrypt_desc;
    cc_ciphermode_descriptor_s decrypt_desc;

    encrypt_desc.cipher = cryptor->cipher;
    encrypt_desc.mode = cryptor->mode;
    encrypt_desc.direction = cc_Encrypt;
    encrypt_desc.ciphermode = cryptor->encrypt_ciphermode;

    decrypt_desc.cipher = cryptor->cipher;
    decrypt_desc.mode = cryptor->mode;
    decrypt_desc.direction = cc_Decrypt;
    decrypt_desc.ciphermode = cryptor->decrypt_ciphermode;

    block_size=cc_symmetric_bloc_size(&encrypt_desc);
    padded_len=(len+block_size) & (~(block_size-1)); // Assumes block size is a power of 2.

    MAKE_GENERIC_MODE_CONTEXT(encrypt_ctx, &encrypt_desc);
    MAKE_GENERIC_MODE_CONTEXT(decrypt_ctx, &decrypt_desc);
    ok_or_fail((cc_symmetric_setup(&encrypt_desc, key->bytes, key->len, init_iv->bytes, encrypt_ctx) == 0), "Encrypted cipher-mode is initted");
    ok_or_fail((cc_symmetric_setup(&decrypt_desc, key->bytes, key->len, init_iv->bytes, decrypt_ctx) == 0), "Decrypted cipher-mode is initted");

    // Temporary buffers
    ciphertext  = mallocByteBuffer(padded_len);
    padded_message = mallocByteBuffer(padded_len);

    //----------------------------------
    // 1. Test padding encryption
    //----------------------------------

    // a. Encrypt using padding function
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) encrypt_ctx,padding_mode, init_iv->bytes, message->bytes, ciphertext->bytes, len);
    ok_or_fail(result_len >= len, "Encryption with padding failed");

    // b. Decrypt raw data
    cc_symmetric_crypt((cc_symmetric_context_p) decrypt_ctx, init_iv->bytes, ciphertext->bytes, padded_message->bytes, result_len);

    // c. Compare last block with expected value
    ok_memcmp_or_fail(&padded_message->bytes[result_len-expected_padding->len], expected_padding->bytes, expected_padding->len, "Padding as expected");

    //----------------------------------
    // 2. Test padding decryption
    //----------------------------------

    // a. Construct the good padded zero message
    memcpy(padded_message->bytes,message->bytes, len);
    memcpy(&padded_message->bytes[len],expected_padding->bytes,expected_padding->len);

    // b. Encrypt with proper padding
    cc_symmetric_crypt((cc_symmetric_context_p) encrypt_ctx, init_iv->bytes, padded_message->bytes, ciphertext->bytes, len+expected_padding->len);
    memset(padded_message->bytes,0xff,padded_len); // Clean buffer to catch mismatches below

    // b. Decrypt with padding decrypt function
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) decrypt_ctx,padding_mode, init_iv->bytes, ciphertext->bytes, padded_message->bytes,len+expected_padding->len);
    ok_or_fail(result_len <= len+expected_padding->len, "Decryption with padding failed");

    // c. Compare that the message is all there.
    ok_memcmp_or_fail(padded_message->bytes,
                      message->bytes, len, "Decrypted handcrafted padding");

    ok_or_fail(len == result_len, "Decrypted length from handcrafted padding");

    free(ciphertext);
    free(padded_message);
    free(key);
    free(init_iv);
    free(message);
    free(expected_padding);
    return 1;
}

// Compare the ciphertext with what is expected
static int
ccsymmetric_pad_ciphertext_tests(duplex_cryptor cryptor, const ccpad_test_vector_t *test) {
    // Retrieve test case info
    byteBuffer key =                    hexStringToBytes(test->keyStr);
    byteBuffer init_iv =                hexStringToBytes(test->init_ivStr);
    byteBuffer plaintext =              hexStringToBytes(test->ptStr);
    byteBuffer expected_ciphertext =    hexStringToBytes(test->outputStr);
    ccpad_select padding_mode=test->padding_mode;
    size_t len = plaintext->len;
    size_t result_len;

    byteBuffer computed_ciphertext,recomputed_plaintext;

    // Set cipher
    cc_ciphermode_descriptor_s encrypt_desc;
    cc_ciphermode_descriptor_s decrypt_desc;

    encrypt_desc.cipher = cryptor->cipher;
    encrypt_desc.mode = cryptor->mode;
    encrypt_desc.direction = cc_Encrypt;
    encrypt_desc.ciphermode = cryptor->encrypt_ciphermode;

    decrypt_desc.cipher = cryptor->cipher;
    decrypt_desc.mode = cryptor->mode;
    decrypt_desc.direction = cc_Decrypt;
    decrypt_desc.ciphermode = cryptor->decrypt_ciphermode;

    MAKE_GENERIC_MODE_CONTEXT(encrypt_ctx, &encrypt_desc);
    MAKE_GENERIC_MODE_CONTEXT(decrypt_ctx, &decrypt_desc);
    ok_or_fail((cc_symmetric_setup(&encrypt_desc, key->bytes, key->len, init_iv->bytes, encrypt_ctx) == 0), "Encrypted cipher-mode is initted");
    ok_or_fail((cc_symmetric_setup(&decrypt_desc, key->bytes, key->len, init_iv->bytes, decrypt_ctx) == 0), "Decrypted cipher-mode is initted");

    // Temporary buffers
    computed_ciphertext  = mallocByteBuffer(expected_ciphertext->len);
    recomputed_plaintext = mallocByteBuffer(expected_ciphertext->len );

    //----------------------------------
    // Round Trip, out of place
    //----------------------------------

    // a. Encrypt with padding
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) encrypt_ctx,padding_mode, init_iv->bytes, plaintext->bytes, computed_ciphertext->bytes, len);
    ok_or_fail(result_len == expected_ciphertext->len, "Ciphertext length mismatch");

    ok_memcmp_or_fail(computed_ciphertext->bytes,
                      expected_ciphertext->bytes, expected_ciphertext->len, "Wrong ciphertext");

    // b. Decrypt with padding
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) decrypt_ctx,padding_mode, init_iv->bytes, expected_ciphertext->bytes, recomputed_plaintext->bytes, expected_ciphertext->len);
    ok_or_fail(result_len == plaintext->len, "Recovered plaintext length mismatch");

    ok_memcmp_or_fail(recomputed_plaintext->bytes,
                      plaintext->bytes, plaintext->len, "Wrong decrypted message");

    //----------------------------------
    // Round Trip, in place
    //----------------------------------
    memcpy(computed_ciphertext->bytes,plaintext->bytes,len);

    // a. Encrypt with padding
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) encrypt_ctx,padding_mode, init_iv->bytes, computed_ciphertext->bytes, computed_ciphertext->bytes, len);
    ok_or_fail(result_len == expected_ciphertext->len, "Ciphertext computed in place length mismatch");

    ok_memcmp_or_fail(computed_ciphertext->bytes,
                      expected_ciphertext->bytes, expected_ciphertext->len, "Wrong ciphertext in place");

    // b. Decrypt with padding
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) decrypt_ctx,padding_mode, init_iv->bytes, computed_ciphertext->bytes, computed_ciphertext->bytes, result_len);
    ok_or_fail(result_len == plaintext->len, "Recovered plaintext length mismatch");

    // c. Compare with original messsage
    ok_memcmp_or_fail(computed_ciphertext->bytes,
                      plaintext->bytes, len, "Recovered in place plaintext length mismatch");

    //----------------------------------
    free(recomputed_plaintext);
    free(plaintext);
    free(computed_ciphertext);
    free(expected_ciphertext);
    free(key);
    free(init_iv);

    return 1;
}

// Roundtrip: does the encrypt/decrypt recover the plaintext.
static int
ccsymmetric_pad_roundtrip_tests(duplex_cryptor cryptor, ccpad_select padding_mode, size_t message_size) {
    // Retrieve test case info
    byteBuffer key;
    byteBuffer init_iv;
    byteBuffer message;
    size_t len = message_size;
    size_t result_len;
    size_t block_size=0;
    size_t padded_len=0;

    int status;

    byteBuffer ciphertext,padded_message;

    // Set cipher
    cc_ciphermode_descriptor_s encrypt_desc;
    cc_ciphermode_descriptor_s decrypt_desc;

    encrypt_desc.cipher = cryptor->cipher;
    encrypt_desc.mode = cryptor->mode;
    encrypt_desc.direction = cc_Encrypt;
    encrypt_desc.ciphermode = cryptor->encrypt_ciphermode;

    decrypt_desc.cipher = cryptor->cipher;
    decrypt_desc.mode = cryptor->mode;
    decrypt_desc.direction = cc_Decrypt;
    decrypt_desc.ciphermode = cryptor->decrypt_ciphermode;

    block_size=cc_symmetric_bloc_size(&encrypt_desc);
    padded_len=(len+block_size) & (~(block_size-1)); // Assumes block size is a power of 2.

    key = mallocByteBuffer(block_size);
    status = ccrng_generate(global_test_rng, key->len, key->bytes);
    cc_assert(status == 0);
    (void)status;

    init_iv = mallocByteBuffer(block_size);
    status = ccrng_generate(global_test_rng, init_iv->len, init_iv->bytes);
    cc_assert(status == 0);
    (void)status;
    
    message = mallocByteBuffer(len);
    status = ccrng_generate(global_test_rng, message->len, message->bytes);
    cc_assert(status == 0);
    (void)status;
    
    MAKE_GENERIC_MODE_CONTEXT(encrypt_ctx, &encrypt_desc);
    MAKE_GENERIC_MODE_CONTEXT(decrypt_ctx, &decrypt_desc);
    ok_or_fail((cc_symmetric_setup(&encrypt_desc, key->bytes, key->len, init_iv->bytes, encrypt_ctx) == 0), "Encrypted cipher-mode is initted");
    ok_or_fail((cc_symmetric_setup(&decrypt_desc, key->bytes, key->len, init_iv->bytes, decrypt_ctx) == 0), "Decrypted cipher-mode is initted");

    // Temporary buffers
    ciphertext  = mallocByteBuffer(padded_len);
    padded_message = mallocByteBuffer(padded_len);

    //----------------------------------
    // Round Trip, out of place
    //----------------------------------

    // a. Encrypt with padding
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) encrypt_ctx,padding_mode, init_iv->bytes, message->bytes, ciphertext->bytes, len);
    ok_or_fail(result_len >= len, "Encryption with padding failed");

    // b. Decrypt with padding
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) decrypt_ctx,padding_mode, init_iv->bytes, ciphertext->bytes, padded_message->bytes, result_len);
    ok_or_fail(result_len <= len, "Decryption with padding failed");

    // c. Compare with original messsage
    ok_memcmp_or_fail(padded_message->bytes,
                      message->bytes, len, "Wrap/Unwrapped decrypted message");

    ok_or_fail(message->len == result_len, "Wrap/Unwrapped decrypted length");

    //----------------------------------
    // Round Trip, in place
    //----------------------------------

    // a. Encrypt with padding
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) encrypt_ctx,padding_mode, init_iv->bytes, padded_message->bytes, padded_message->bytes, len);
    ok_or_fail(result_len >= len, "Encryption in place failed");

    // Makes sure the plaintext has been encrypted
    ok_or_fail(memcmp(padded_message->bytes,message->bytes,len)!=0, "Encryption in place did nothing?");

    // b. Decrypt with padding
    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) decrypt_ctx,padding_mode, init_iv->bytes, padded_message->bytes, padded_message->bytes, result_len);
    ok_or_fail(result_len <= len, "Decryption in place failed");

    // c. Compare with original messsage
    ok_memcmp_or_fail(padded_message->bytes,
                      message->bytes, len, "Wrap/Unwrapped in place decrypted message");

    ok_or_fail(message->len == result_len, "Wrap/Unwrapped in place decrypted length");

    //----------------------------------
    free(ciphertext);
    free(padded_message);
    free(key);
    free(init_iv);
    free(message);
    return 1;
}



CC_UNUSED static int
ccsymmetric_crypt_pad_timing_tests(duplex_cryptor cryptor, ccpad_select padding_mode, const ccpad_test_vector_t *test) {
    // Retrieve test case info
    // Message is ignore, random messages are generated
    byteBuffer key =        hexStringToBytes(test->keyStr);
    byteBuffer init_iv =    hexStringToBytes(test->init_ivStr);
    byteBuffer plaintext,output,ciphertext_valid,ciphertext_random;
    size_t len = 0;
    size_t max_len = 0;
    size_t result_len = 0;
    size_t block_size=0;
    size_t padded_len=0;
    int failure_cnt=0;
    int early_abort=1;
    uint32_t j,sample_counter;
    bool retry=true;

    // Random for messages
    struct ccrng_state *rng = global_test_rng;

    // Set cipher
    cc_ciphermode_descriptor_s encrypt_desc;
    cc_ciphermode_descriptor_s decrypt_desc;

    encrypt_desc.cipher = cryptor->cipher;
    encrypt_desc.mode = cryptor->mode;
    encrypt_desc.direction = cc_Encrypt;
    encrypt_desc.ciphermode = cryptor->encrypt_ciphermode;

    decrypt_desc.cipher = cryptor->cipher;
    decrypt_desc.mode = cryptor->mode;
    decrypt_desc.direction = cc_Decrypt;
    decrypt_desc.ciphermode = cryptor->decrypt_ciphermode;

    MAKE_GENERIC_MODE_CONTEXT(encrypt_ctx, &encrypt_desc);
    MAKE_GENERIC_MODE_CONTEXT(decrypt_ctx, &decrypt_desc);
    ok_or_fail((cc_symmetric_setup(&encrypt_desc, key->bytes, key->len, init_iv->bytes, encrypt_ctx) == 0), "Encrypted cipher-mode is initted");
    ok_or_fail((cc_symmetric_setup(&decrypt_desc, key->bytes, key->len, init_iv->bytes, decrypt_ctx) == 0), "Decrypted cipher-mode is initted");

    // Work on messages of size 0 < len <= blocksize
    block_size=cc_symmetric_bloc_size(&encrypt_desc);
    len=block_size;
    max_len=(len+block_size) & (~(block_size-1)); // Assumes block size is a power of 2.

    // Temporary buffers
    plaintext  = mallocByteBuffer(max_len);
    ciphertext_valid  = mallocByteBuffer(max_len);
    ciphertext_random  = mallocByteBuffer(max_len);
    output  = mallocByteBuffer(max_len);

    for (len=0; len<=block_size;len++)
    {
        j=0;
        while(retry)
        {
            sample_counter=0; // Index of current sample
            measurement_t timing_sample[2*CCPAD_TIMING_SAMPLES];

            for (size_t i=0;i<2*CCPAD_TIMING_SAMPLES+(CCPAD_WARMUP/CCPAD_TIMING_REPEAT);i++)
            {
                padded_len=(len+block_size) & (~(block_size-1)); // Assumes block size is a power of 2.
                volatile size_t decode_result;
                if ((len==0) || ((i&1) == 0))
                {
                    // -------------------------
                    //      Random
                    // -------------------------
                    ccrng_generate(rng,padded_len,plaintext->bytes); // Full length
                    cc_symmetric_crypt((cc_symmetric_context_p) encrypt_ctx, init_iv->bytes, plaintext->bytes, ciphertext_valid->bytes, padded_len);
                    result_len=padded_len;
                }
                else
                {
                    // -------------------------
                    //      Correct padding
                    // -------------------------
                    // Create message with good padding
                    ccrng_generate(rng,len,plaintext->bytes);         // Actual length
                    result_len=cc_symmetric_crypt_pad((cc_symmetric_context_p) encrypt_ctx,padding_mode, init_iv->bytes, plaintext->bytes, ciphertext_valid->bytes, len);
                    cc_assert(result_len == padded_len);
                }

                // Decrypt with padding decrypt function
                cc_symmetric_crypt((cc_symmetric_context_p) decrypt_ctx,init_iv->bytes, ciphertext_valid->bytes, output->bytes,result_len);

                TIMING_WITH_QUANTILE(timing_sample[sample_counter].timing,
                                     CCPAD_TIMING_REPEAT,
                                     CCPAD_PERCENTILE,
                                     decode_result=ccpad_pkcs7_decode(block_size,output->bytes+result_len-block_size),errOut);

                timing_sample[sample_counter].group=sample_counter&1;
#if CCPAD_WARMUP
                if (i>=(CCPAD_WARMUP/CCPAD_TIMING_REPEAT))
#endif
                {
                    sample_counter++;
                }
            }
#if CCN_OSX
            if (verbose>1) {
                char file_name[64];
                snprintf(file_name,sizeof(file_name),"corecrypto_test_timings_%.2zu.csv",len);
                export_measurement_to_file(file_name,timing_sample,sample_counter);
            }
#endif
            // Process results
#if T_TEST
            // T test
            int status=T_test_isRejected(timing_sample,sample_counter);
#else
            // Wilcoxon Rank-Sum Test
            int status=WilcoxonRankSumTest(timing_sample,sample_counter);
#endif
            if (status!=0)
            {
                j++; // retry counter
                if (j>=CCPAD_TIMING_RETRIES)
                {
                    // If it fails for len==0 it's a test issue since it is all random
                    if (len==0) {
                        diag("Constant timing FAILED for all random, this is a test issue",len,j);
                    }
                    else
                    {
                        diag("Constant timing FAILED for len %d after %d attempts",len,j);
                        //ok_or_fail((status==0),"Decrypt+padding constant timing");
                        failure_cnt++;
                    }
                    break;
                }
            }
            else
            {
                if ((verbose>1) && (j>0)) diag("Constant timing ok for len %d after %d attempts (of %d)",len,j+1,CCPAD_TIMING_RETRIES);
                break;
            }
        } // retry
    }
    early_abort=0;
errOut:
    free(plaintext);
    free(ciphertext_valid);
    free(ciphertext_random);
    free(output);
    free(key);
    free(init_iv);
    if (failure_cnt || early_abort)
    {
        return 0;
    }
    return 1;
}

// For PKCS7 since it is special with regards to modes
static int test_pad_plaintext_pkcs7(ciphermode_t encrypt_ciphermode, ciphermode_t decrypt_ciphermode,cc_cipher_select cipher,cc_mode_select mode) {
    duplex_cryptor_s cryptor;
    cryptor.cipher = cipher;
    cryptor.mode = mode;
    cryptor.encrypt_ciphermode = encrypt_ciphermode;
    cryptor.decrypt_ciphermode = decrypt_ciphermode;
    ccpad_select padding_mode=ccpad_pkcs7;

    const ccpad_test_vector_t *run_vector=NULL;

    if (cc_cipherAES==cryptor.cipher)
    {
        run_vector=aes_plaintext_vectors;
    }
    else if (cc_cipherDES==cryptor.cipher)
    {
        run_vector=des_plaintext_vectors;
    } else {
        fail("Test not implemented");
        return 0;
    }

    // Detect timing attacks on PKCS7 padding
    if (cc_ModeECB!=cryptor.mode && padding_mode==ccpad_pkcs7)
    {   // Timing attack not relevant on ECB
        for (int i=0;i<CMP_TIMING_TEST_ITERATION;i++)
            ok_or_warning(ccsymmetric_crypt_pad_timing_tests(&cryptor,padding_mode,&run_vector[0]), "Constant Time test");
    }

    // Plaintext tests
    for(int i=0; run_vector[i].keyStr != NULL; i++) {
        const ccpad_test_vector_t *test = &run_vector[i];
        ok_or_fail(ccsymmetric_pad_plaintext_tests(&cryptor,padding_mode,test), "Test Vector Passed");
    }
    return 1;
}

// Generic test framework that checks input/output
static int test_pad_roundtrip(ciphermode_t encrypt_ciphermode, ciphermode_t decrypt_ciphermode,cc_cipher_select cipher,cc_mode_select mode) {
    duplex_cryptor_s cryptor;
    cryptor.cipher = cipher;
    cryptor.mode = mode;
    cryptor.encrypt_ciphermode = encrypt_ciphermode;
    cryptor.decrypt_ciphermode = decrypt_ciphermode;

    ok_or_fail(cc_cipherAES==cryptor.cipher, "Test not implemented");

    for(ccpad_select padding_mode=0; padding_mode<ccpad_cnt; padding_mode++) {
        if  ((cc_ModeCBC==cryptor.mode && cbc_pad_crypt_funcs[padding_mode][cc_Encrypt]!=NULL)
        ||   (cc_ModeECB==cryptor.mode && ecb_pad_crypt_funcs[padding_mode][cc_Encrypt]!=NULL))
        {
            // AES CBC
            ok_or_fail(ccsymmetric_pad_roundtrip_tests(&cryptor,padding_mode,47), "Random roundtrip 47 bytes Passed");
            ok_or_fail(ccsymmetric_pad_roundtrip_tests(&cryptor,padding_mode,48), "Random roundtrip 48 bytes Passed");
            ok_or_fail(ccsymmetric_pad_roundtrip_tests(&cryptor,padding_mode,40), "Random roundtrip 40 bytes Passed");
        }
    }
    return 1;
}

// Generic test framework that checks input/output
static int test_pad_ciphertext(ciphermode_t encrypt_ciphermode, ciphermode_t decrypt_ciphermode,cc_cipher_select cipher,cc_mode_select mode) {
    duplex_cryptor_s cryptor;
    cryptor.cipher = cipher;
    cryptor.mode = mode;
    cryptor.encrypt_ciphermode = encrypt_ciphermode;
    cryptor.decrypt_ciphermode = decrypt_ciphermode;

    const ccpad_test_vector_t *run_vector=NULL;

    if (cc_cipherAES==cryptor.cipher && cc_ModeCBC==cryptor.mode)
    {
        run_vector=aes_cbc_ciphertext_vectors;
    }
    else
    {
        fail("Test not implemented");
        return 0;
    }

    // Plaintext tests
    for(int i=0; run_vector[i].keyStr != NULL; i++) {
        ok_or_fail(ccsymmetric_pad_ciphertext_tests(&cryptor,&run_vector[i]), "Encryption/Decryption KAT passed");
    }
    return 1;
}

// Main of the test
int ccpad_tests(TM_UNUSED int argc, TM_UNUSED char *const *argv)
{
	plan_tests(kTestTestCount);

    if (verbose) diag("Generic AES KAT padding tests");
    ok(test_pad_ciphertext((ciphermode_t) ccaes_cbc_encrypt_mode(), (ciphermode_t) ccaes_cbc_decrypt_mode(),cc_cipherAES,cc_ModeCBC) == 1, "Generic KAT padding tests");

    if (verbose) diag("Generic AES roundtrip padding tests");
    ok(test_pad_roundtrip((ciphermode_t) ccaes_ecb_encrypt_mode(), (ciphermode_t) ccaes_ecb_decrypt_mode(),cc_cipherAES,cc_ModeECB) == 1, "Generic AES ECB roundtrip padding tests");
    ok(test_pad_roundtrip((ciphermode_t) ccaes_cbc_encrypt_mode(), (ciphermode_t) ccaes_cbc_decrypt_mode(),cc_cipherAES,cc_ModeCBC) == 1, "Generic AES CBC roundtrip padding tests");

    if (verbose) diag("PKCS7 Padding - Default AES-ECB");
    ok(test_pad_plaintext_pkcs7((ciphermode_t) ccaes_ecb_encrypt_mode(), (ciphermode_t) ccaes_ecb_decrypt_mode(),cc_cipherAES,cc_ModeECB) == 1, "PKCS7 Padding - Default AES-ECB");
    if (verbose) diag("PKCS7 Padding - Default AES-CBC");
    ok(test_pad_plaintext_pkcs7((ciphermode_t) ccaes_cbc_encrypt_mode(), (ciphermode_t) ccaes_cbc_decrypt_mode(),cc_cipherAES,cc_ModeCBC) == 1, "PKCS7 Padding - Default AES-CBC");
    if (verbose) diag("PKCS7 Padding - Default DES-ECB");
    ok(test_pad_plaintext_pkcs7((ciphermode_t) ccdes_ecb_encrypt_mode(), (ciphermode_t) ccdes_ecb_decrypt_mode(),cc_cipherDES,cc_ModeECB) == 1, "PKCS7 Padding - Default DES-ECB");
    if (verbose) diag("PKCS7 Padding - Default DES-CBC");
    ok(test_pad_plaintext_pkcs7((ciphermode_t) ccdes_cbc_encrypt_mode(), (ciphermode_t) ccdes_cbc_decrypt_mode(),cc_cipherDES,cc_ModeCBC) == 1, "PKCS7 Padding - Default DES-CBC");

    return 0;
}
#endif

