YACA: Low-level RSA API tests 80/85080/11
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 23 Aug 2016 12:52:29 +0000 (14:52 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 13 Sep 2016 08:27:00 +0000 (10:27 +0200)
Change-Id: If83467a2cbcf409667336e7c5ec726b1dd11ae0f

src/yaca/yaca-test-rsa.cpp [new file with mode: 0644]

diff --git a/src/yaca/yaca-test-rsa.cpp b/src/yaca/yaca-test-rsa.cpp
new file mode 100644 (file)
index 0000000..89634ac
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ *  Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk (d.michaluk@samsung.com)
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+
+/**
+ * @file
+ * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
+ */
+
+#include <vector>
+
+#include "dpl/test/test_runner.h"
+
+#include <yaca_rsa.h>
+#include <yaca_types.h>
+#include <yaca_key.h>
+
+#include "yaca-test-common.h"
+#include "yaca-test-vector.h"
+#include "lorem.h"
+
+
+namespace {
+
+const size_t SHORT_ENOUGH = 16;
+
+typedef int (*CryptoOp)(yaca_padding_e, const yaca_key_h, const char *, size_t, char **, size_t *);
+
+void rsa_invalid_param(CryptoOp op,
+                       yaca_padding_e padding,
+                       const yaca_key_h key,
+                       const char *input,
+                       size_t input_len,
+                       char **output,
+                       size_t *output_len)
+{
+    if (output != nullptr)
+        *output = nullptr;
+    if (output_len != nullptr)
+        *output_len = 0;
+
+    YACA_INVALID_PARAM(op(padding, key, input, input_len, output, output_len));
+}
+
+void test_rsa_common_invalid_param(CryptoOp op, const KeyPtr& valid_key,
+                                   const char *input, size_t input_len)
+{
+    char *output = nullptr;
+    size_t output_len;
+
+    auto sym_key = generate_key(YACA_KEY_TYPE_SYMMETRIC, YACA_KEY_LENGTH_192BIT);
+    auto iv = generate_key(YACA_KEY_TYPE_IV, YACA_KEY_LENGTH_IV_128BIT);
+    auto dsa_param = generate_key(YACA_KEY_TYPE_DSA_PARAMS, YACA_KEY_LENGTH_1024BIT);
+    auto dsa_priv = generate_key_from_parameters(dsa_param);
+    auto dsa_pub = extract_public_key(dsa_priv);
+
+    YACA_INVALID_PARAM(op(static_cast<yaca_padding_e>(-1), valid_key.get(),
+                          input, input_len,
+                          &output, &output_len));
+
+    YACA_INVALID_PARAM(op(YACA_PADDING_PKCS1, YACA_KEY_NULL,
+                          input, input_len,
+                          &output, &output_len));
+    YACA_INVALID_PARAM(op(YACA_PADDING_PKCS1, dsa_pub.get(),
+                          input, input_len,
+                          &output, &output_len));
+    YACA_INVALID_PARAM(op(YACA_PADDING_PKCS1, sym_key.get(),
+                          input, input_len,
+                          &output, &output_len));
+    YACA_INVALID_PARAM(op(YACA_PADDING_PKCS1, iv.get(),
+                          input, input_len,
+                          &output, &output_len));
+    YACA_INVALID_PARAM(op(YACA_PADDING_PKCS1, dsa_param.get(),
+                          input, input_len,
+                          &output, &output_len));
+
+    YACA_INVALID_PARAM(op(YACA_PADDING_PKCS1, valid_key.get(),
+                          nullptr, SHORT_ENOUGH,
+                          &output, &output_len));
+    YACA_INVALID_PARAM(op(YACA_PADDING_PKCS1, valid_key.get(),
+                          input, 0,
+                          &output, &output_len));
+
+    YACA_INVALID_PARAM(op(YACA_PADDING_PKCS1, valid_key.get(),
+                          input, input_len,
+                          nullptr, &output_len));
+    YACA_INVALID_PARAM(op(YACA_PADDING_PKCS1, valid_key.get(),
+                          input, input_len,
+                          &output, nullptr));
+}
+
+enum EncryptionType
+{
+    ET_NONE = 0,
+    ET_PUB = 1 << 0,
+    ET_PRV = 1 << 1
+};
+
+struct PaddingInfo {
+    yaca_padding_e padding;
+    int supported_encryption;
+    size_t padding_size;
+    bool exact;
+};
+
+const std::vector<PaddingInfo> PADDINGS = {
+    { YACA_PADDING_NONE,         ET_PUB | ET_PRV, 0,  true  },
+    { YACA_PADDING_X931,         ET_NONE,         0,  false },
+    { YACA_PADDING_PKCS1,        ET_PUB | ET_PRV, 11, false },
+    { YACA_PADDING_PKCS1_PSS,    ET_NONE,         0,  false },
+    { YACA_PADDING_PKCS1_OAEP,   ET_PUB,          42, false },
+    { YACA_PADDING_PKCS1_SSLV23, ET_PUB,          11, false },
+    { YACA_PADDING_PKCS7,        ET_NONE,         0,  false }
+};
+
+void test_rsa_padding(const KeyPair& kp, const PaddingInfo& pi, EncryptionType et)
+{
+    char *tmp;
+    size_t ciphertext_len;
+    size_t plaintext_len;
+    int expected;
+
+    yaca_padding_e padding = pi.padding;
+    size_t max_len = kp.bit_len / 8 - pi.padding_size;
+    const KeyPtr& enc_key = (et == ET_PUB) ? kp.pub : kp.prv;
+    const KeyPtr& dec_key = (et == ET_PUB) ? kp.prv : kp.pub;
+    CryptoOp encrypt = (et == ET_PUB) ? yaca_rsa_public_encrypt : yaca_rsa_private_encrypt;
+    CryptoOp decrypt = (et == ET_PUB) ? yaca_rsa_private_decrypt : yaca_rsa_public_decrypt;
+
+    /* padding not suitable for this operation */
+    if ((pi.supported_encryption & et) == 0) {
+        YACA_INVALID_PARAM(encrypt(pi.padding, enc_key.get(),
+                                   lorem1024, SHORT_ENOUGH,
+                                   &tmp, &ciphertext_len));
+        return;
+    }
+
+    /* input too long */
+    YACA_INVALID_PARAM(encrypt(padding, enc_key.get(),
+                               lorem1024, max_len + 1,
+                               &tmp, &ciphertext_len));
+
+    /* input shorter than max len */
+    expected = pi.exact ? YACA_ERROR_INVALID_PARAMETER : YACA_ERROR_NONE;
+    tmp = NULL;
+    YACA_RESULT(expected, encrypt(padding, enc_key.get(),
+                                  lorem1024, max_len - 1,
+                                  &tmp, &ciphertext_len));
+    yaca_free(tmp);
+    tmp = NULL;
+    YACA_RESULT(expected, encrypt(padding, enc_key.get(),
+                                  nullptr, 0,
+                                  &tmp, &ciphertext_len));
+    yaca_free(tmp);
+
+    /* valid length */
+    tmp = NULL;
+    YACA_SUCCESS(encrypt(padding, enc_key.get(),
+                         lorem1024, max_len,
+                         &tmp, &ciphertext_len));
+
+    auto ciphertext = wrap_ptr(tmp);
+
+    YACA_ASSERT_MSG(ciphertext != nullptr, "Empty ciphertext");
+    YACA_ASSERT_MSG(ciphertext_len == kp.bit_len / 8,
+                    "Expected ciphertext length: " << kp.bit_len / 8 <<
+                    " got: " << ciphertext_len);
+
+    /* decrypt with incorrect paddings */
+    for (auto& p : PADDINGS) {
+        /* don't decrypt with the same padding except for SSLV23 */
+        if (p.padding == padding && p.padding != YACA_PADDING_PKCS1_SSLV23)
+            continue;
+
+        /*
+         * - YACA_PADDING_PKCS1 & YACA_PADDING_PKCS1_SSLV23 are compatible in case of
+         *   public_encrypt/private_decrypt
+         * - YACA_PADDING_NONE checks only the input length
+         */
+        expected = YACA_ERROR_INVALID_PARAMETER;
+        if (p.padding == YACA_PADDING_NONE ||
+            (et == ET_PUB && ((p.padding == YACA_PADDING_PKCS1 && padding == YACA_PADDING_PKCS1_SSLV23) ||
+                              (p.padding == YACA_PADDING_PKCS1_SSLV23 && padding == YACA_PADDING_PKCS1))))
+            expected = YACA_ERROR_NONE;
+
+        YACA_RESULT(expected, decrypt(p.padding, dec_key.get(),
+                                      ciphertext.get(), ciphertext_len,
+                                      &tmp, &plaintext_len));
+    }
+
+    /* decryption with SSLV23 will fail if it was used during encryption */
+    if (padding == YACA_PADDING_PKCS1_SSLV23)
+        padding = YACA_PADDING_PKCS1;
+
+    /*
+     * Shortened ciphertext. During encryption without padding OpenSSL allows
+     * input of length equal to the key length but during decryption it allows
+     * also shorter input. Yaca API does the same.
+     */
+    if (padding != YACA_PADDING_NONE)
+        YACA_INVALID_PARAM(decrypt(padding, dec_key.get(),
+                                   ciphertext.get(), ciphertext_len - 1,
+                                   &tmp, &plaintext_len));
+
+    /* extended ciphertext */
+    std::vector<char> extended(ciphertext.get(), ciphertext.get() + ciphertext_len);
+    extended.push_back(' ');
+    YACA_INVALID_PARAM(decrypt(padding, dec_key.get(),
+                               extended.data(), extended.size(),
+                               &tmp, &plaintext_len));
+
+    /* valid ciphertext */
+    YACA_SUCCESS(decrypt(padding, dec_key.get(),
+                         ciphertext.get(), ciphertext_len,
+                         &tmp, &plaintext_len));
+    ChrPtr plaintext = wrap_ptr(tmp);
+
+    RUNNER_ASSERT_MSG(plaintext_len == max_len,
+                      "Decrypted message has different length (" << plaintext_len <<
+                      "B) than the original (" << max_len << "B)");
+
+    YACA_SUCCESS(yaca_memcmp(plaintext.get(), lorem1024, plaintext_len));
+}
+
+} // anonymous namespace
+
+RUNNER_TEST_GROUP_INIT(T9000_YACA_RSA);
+
+RUNNER_TEST(T9010_yaca_rsa_public_encrypt_invalid_param, YacaTest)
+{
+    char *ciphertext = nullptr;
+    size_t ciphertext_len;
+
+    KeyPair rsa(YACA_KEY_TYPE_RSA_PRIV, YACA_KEY_LENGTH_1024BIT);
+
+    test_rsa_common_invalid_param(yaca_rsa_public_encrypt, rsa.pub, lorem1024, SHORT_ENOUGH);
+
+    rsa_invalid_param(yaca_rsa_public_encrypt, YACA_PADDING_PKCS1, rsa.prv.get(),
+                      lorem1024, SHORT_ENOUGH,
+                      &ciphertext, &ciphertext_len);
+}
+
+RUNNER_TEST(T9020_yaca_rsa_private_decrypt_invalid_param, YacaTest)
+{
+    char *tmp;
+    size_t ciphertext_len;
+    char *plaintext = nullptr;
+    size_t plaintext_len;
+
+    KeyPair rsa(YACA_KEY_TYPE_RSA_PRIV, YACA_KEY_LENGTH_1024BIT);
+
+    YACA_SUCCESS(yaca_rsa_public_encrypt(YACA_PADDING_PKCS1, rsa.pub.get(),
+                                         lorem1024, SHORT_ENOUGH,
+                                         &tmp, &ciphertext_len));
+
+    ChrPtr ciphertext = wrap_ptr(tmp);
+
+    test_rsa_common_invalid_param(yaca_rsa_private_decrypt, rsa.prv,
+                                  ciphertext.get(), ciphertext_len);
+
+    rsa_invalid_param(yaca_rsa_private_decrypt, YACA_PADDING_PKCS1, rsa.pub.get(),
+                      ciphertext.get(), ciphertext_len,
+                      &plaintext, &plaintext_len);
+}
+
+RUNNER_TEST(T9030_yaca_rsa_private_encrypt_invalid_param, YacaTest)
+{
+    char *ciphertext = nullptr;
+    size_t ciphertext_len;
+
+    KeyPair rsa(YACA_KEY_TYPE_RSA_PRIV, YACA_KEY_LENGTH_1024BIT);
+
+    test_rsa_common_invalid_param(yaca_rsa_private_encrypt, rsa.prv, lorem1024, SHORT_ENOUGH);
+
+    rsa_invalid_param(yaca_rsa_private_encrypt, YACA_PADDING_PKCS1, rsa.pub.get(),
+                      lorem1024, SHORT_ENOUGH,
+                      &ciphertext, &ciphertext_len);
+}
+
+RUNNER_TEST(T9040_yaca_rsa_public_decrypt_invalid_param, YacaTest)
+{
+    char *tmp;
+    size_t ciphertext_len;
+    char *plaintext = nullptr;
+    size_t plaintext_len;
+
+    KeyPair rsa(YACA_KEY_TYPE_RSA_PRIV, YACA_KEY_LENGTH_1024BIT);
+
+    YACA_SUCCESS(yaca_rsa_private_encrypt(YACA_PADDING_PKCS1, rsa.prv.get(),
+                                          lorem1024, SHORT_ENOUGH,
+                                          &tmp, &ciphertext_len));
+
+    ChrPtr ciphertext = wrap_ptr(tmp);
+
+    test_rsa_common_invalid_param(yaca_rsa_public_decrypt, rsa.pub,
+                                  ciphertext.get(), ciphertext_len);
+
+    rsa_invalid_param(yaca_rsa_public_decrypt, YACA_PADDING_PKCS1, rsa.prv.get(),
+                      ciphertext.get(), ciphertext_len,
+                      &plaintext, &plaintext_len);
+}
+
+RUNNER_TEST(T9050_yaca_rsa_encryption_paddings, YacaTest)
+{
+    std::vector<KeyPair> key_pairs;
+    key_pairs.emplace_back(YACA_KEY_TYPE_RSA_PRIV, YACA_KEY_LENGTH_512BIT);
+    key_pairs.emplace_back(YACA_KEY_TYPE_RSA_PRIV, YACA_KEY_LENGTH_1024BIT);
+    key_pairs.emplace_back(YACA_KEY_TYPE_RSA_PRIV, YACA_KEY_LENGTH_2048BIT);
+    key_pairs.emplace_back(YACA_KEY_TYPE_RSA_PRIV, YACA_KEY_LENGTH_3072BIT);
+    key_pairs.emplace_back(YACA_KEY_TYPE_RSA_PRIV, YACA_KEY_LENGTH_4096BIT);
+
+    for (auto& kp : key_pairs) {
+        for (auto& pi : PADDINGS) {
+            test_rsa_padding(kp, pi, ET_PUB);
+            test_rsa_padding(kp, pi, ET_PRV);
+        }
+    }
+}