summaryrefslogtreecommitdiffstats
path: root/src/tests/cipher_cxx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/cipher_cxx.cpp')
-rw-r--r--src/tests/cipher_cxx.cpp381
1 files changed, 381 insertions, 0 deletions
diff --git a/src/tests/cipher_cxx.cpp b/src/tests/cipher_cxx.cpp
new file mode 100644
index 0000000..b5f7f83
--- /dev/null
+++ b/src/tests/cipher_cxx.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2017-2021 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <fstream>
+#include <vector>
+#include <string>
+
+#include <cstring>
+#include <rnp/rnp.h>
+#include "rnp_tests.h"
+#include "support.h"
+#include "utils.h"
+#include <vector>
+#include <string>
+#include <crypto/cipher.hpp>
+#include <crypto/mem.h>
+
+static std::vector<uint8_t>
+decode_hex(const char *hex)
+{
+ if (!hex) {
+ return {};
+ }
+ std::vector<uint8_t> data(strlen(hex) / 2);
+ if (!rnp::hex_decode(hex, data.data(), data.size())) {
+ throw std::invalid_argument("hex");
+ }
+ return data;
+}
+
+static void
+test_cipher(pgp_symm_alg_t alg,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding,
+ const char * key_hex,
+ const char * iv_hex,
+ const char * ad_hex,
+ const char * pt_hex,
+ const char * expected_ct_hex)
+{
+ const std::vector<uint8_t> key(decode_hex(key_hex));
+ const std::vector<uint8_t> iv(decode_hex(iv_hex));
+ const std::vector<uint8_t> ad(decode_hex(ad_hex));
+ const std::vector<uint8_t> pt(decode_hex(pt_hex));
+ const std::vector<uint8_t> expected_ct(decode_hex(expected_ct_hex));
+
+ auto enc = Cipher::encryption(alg, mode, tag_size, disable_padding);
+ assert_non_null(enc);
+ const size_t block_size = enc->block_size();
+ const size_t ud = enc->update_granularity();
+ std::vector<uint8_t> ct;
+ // make room for padding
+ ct.resize(((pt.size() + tag_size) / block_size + 1) * block_size);
+ // set key & iv
+ assert_true(enc->set_key(key.data(), key.size()));
+ assert_true(enc->set_iv(iv.data(), iv.size()));
+ if (!ad.empty()) {
+ assert_true(enc->set_ad(ad.data(), ad.size()));
+ }
+
+ // encrypt all in one go
+ size_t output_written, input_consumed;
+ assert_true(enc->finish(
+ ct.data(), ct.size(), &output_written, pt.data(), pt.size(), &input_consumed));
+ ct.resize(output_written);
+ assert_memory_equal(ct.data(), expected_ct.data(), expected_ct.size());
+
+ // start over
+ enc.reset(Cipher::encryption(alg, mode, tag_size, disable_padding).release());
+ assert_true(enc->set_key(key.data(), key.size()));
+ assert_true(enc->set_iv(iv.data(), iv.size()));
+ if (!ad.empty()) {
+ assert_true(enc->set_ad(ad.data(), ad.size()));
+ }
+ ct.clear();
+ ct.resize(((pt.size() + tag_size) / block_size + 1) * block_size);
+ // encrypt in pieces
+ assert_memory_not_equal(ct.data(), expected_ct.data(), expected_ct.size());
+ // all except the last block
+ size_t nonfinal_bytes = rnp_round_up(pt.size(), ud) - ud;
+ output_written = 0;
+ input_consumed = 0;
+ size_t written, consumed;
+ while (input_consumed != nonfinal_bytes) {
+ assert_true(enc->update(ct.data() + output_written,
+ ct.size() - output_written,
+ &written,
+ pt.data() + input_consumed,
+ ud,
+ &consumed));
+ output_written += written;
+ input_consumed += consumed;
+ }
+ assert_true(enc->finish(ct.data() + output_written,
+ ct.size() - output_written,
+ &written,
+ pt.data() + input_consumed,
+ pt.size() - input_consumed,
+ &consumed));
+ output_written += written;
+ ct.resize(output_written);
+ assert_int_equal(ct.size(), expected_ct.size());
+ assert_memory_equal(ct.data(), expected_ct.data(), expected_ct.size());
+ enc.reset();
+
+ // decrypt
+ auto dec = Cipher::decryption(alg, mode, tag_size, disable_padding);
+ assert_true(dec->set_key(key.data(), key.size()));
+ assert_true(dec->set_iv(iv.data(), iv.size()));
+ if (!ad.empty()) {
+ assert_true(dec->set_ad(ad.data(), ad.size()));
+ }
+ // decrypt in pieces
+ std::vector<uint8_t> decrypted(ct.size());
+ // all except the last block but see below for openssl
+ nonfinal_bytes = rnp_round_up(ct.size(), ud) - ud;
+#ifdef CRYPTO_BACKEND_OPENSSL
+ /* Since ossl backend sets tag explicitly tag bytes cannot be
+ split between two blocks.
+ The issue may easily occur is (for example)
+ us = 16
+ ct.size() = 24
+ tag_size=16
+ */
+ if (ct.size() - nonfinal_bytes < tag_size) {
+ nonfinal_bytes = ct.size() - tag_size;
+ }
+#endif // CRYPTO_BACKEND_OPENSSL
+ output_written = 0;
+ input_consumed = 0;
+ while (input_consumed != nonfinal_bytes) {
+ assert_true(dec->update(decrypted.data() + output_written,
+ decrypted.size() - output_written,
+ &written,
+ (const uint8_t *) ct.data() + input_consumed,
+ // ++++ ud,
+ std::min(ud, nonfinal_bytes - input_consumed),
+ &consumed));
+ output_written += written;
+ input_consumed += consumed;
+ }
+ assert_true(dec->finish(decrypted.data() + output_written,
+ decrypted.size() - output_written,
+ &written,
+ (const uint8_t *) ct.data() + input_consumed,
+ ct.size() - input_consumed,
+ &consumed));
+ output_written += written;
+ decrypted.resize(output_written);
+ assert_int_equal(decrypted.size(), pt.size());
+ assert_memory_equal(decrypted.data(), pt.data(), pt.size());
+
+ // decrypt with a bad tag
+ if (tag_size != 0) {
+ dec.reset(Cipher::decryption(alg, mode, tag_size, disable_padding).release());
+ assert_true(dec->set_key(key.data(), key.size()));
+ assert_true(dec->set_iv(iv.data(), iv.size()));
+ if (!ad.empty()) {
+ assert_true(dec->set_ad(ad.data(), ad.size()));
+ }
+ // decrypt in pieces
+ std::vector<uint8_t> decrypted(ct.size());
+ // all except the last block but see above for openssl
+ nonfinal_bytes = rnp_round_up(ct.size(), ud) - ud;
+#ifdef CRYPTO_BACKEND_OPENSSL
+ if (ct.size() - nonfinal_bytes < tag_size) {
+ nonfinal_bytes = ct.size() - tag_size;
+ }
+#endif // CRYPTO_BACKEND_OPENSSL
+
+ output_written = 0;
+ input_consumed = 0;
+ while (input_consumed != nonfinal_bytes) {
+ assert_true(dec->update(decrypted.data() + output_written,
+ decrypted.size() - output_written,
+ &written,
+ (const uint8_t *) ct.data() + input_consumed,
+ // ++++ ud,
+ std::min(ud, nonfinal_bytes - input_consumed),
+ &consumed));
+ output_written += written;
+ input_consumed += consumed;
+ }
+ // tamper with the tag
+ ct.back() ^= 0xff;
+ assert_false(dec->finish(decrypted.data() + output_written,
+ decrypted.size() - output_written,
+ &written,
+ (const uint8_t *) ct.data() + input_consumed,
+ ct.size() - input_consumed,
+ &consumed));
+ }
+}
+
+TEST_F(rnp_tests, test_cipher_idea)
+{
+#if defined(ENABLE_IDEA)
+ assert_true(idea_enabled());
+ // OpenSSL do_crypt man page example
+ test_cipher(PGP_SA_IDEA,
+ PGP_CIPHER_MODE_CBC,
+ 0,
+ false,
+ "000102030405060708090a0b0c0d0e0f",
+ "0102030405060708",
+ NULL,
+ "536f6d652043727970746f2054657874",
+ "8974b718d0cb68b44e27c480546dfcc7a33895f461733219");
+#else
+ assert_false(idea_enabled());
+ assert_null(Cipher::encryption(PGP_SA_IDEA, PGP_CIPHER_MODE_CBC, 0, false));
+#endif
+}
+
+TEST_F(rnp_tests, test_cipher_aes_128_ocb)
+{
+ // RFC 7253 -- Appendix A -- Sample Results
+ // ( The first ten test sets )
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F", // key
+ "BBAA99887766554433221100", // nounce
+ nullptr, // ad
+ nullptr, // data
+ "785407BFFFC8AD9EDCC5520AC9111EE6");
+
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F",
+ "BBAA99887766554433221101",
+ "0001020304050607",
+ "0001020304050607",
+ "6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009");
+
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F",
+ "BBAA99887766554433221102",
+ "0001020304050607",
+ nullptr,
+ "81017F8203F081277152FADE694A0A00");
+
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F",
+ "BBAA99887766554433221103",
+ nullptr,
+ "0001020304050607",
+ "45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9");
+
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F",
+ "BBAA99887766554433221104",
+ "000102030405060708090A0B0C0D0E0F",
+ "000102030405060708090A0B0C0D0E0F",
+ "571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5701C1CCEC8FC3358");
+
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F",
+ "BBAA99887766554433221105",
+ "000102030405060708090A0B0C0D0E0F",
+ nullptr,
+ "8CF761B6902EF764462AD86498CA6B97");
+
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F",
+ "BBAA99887766554433221106",
+ nullptr,
+ "000102030405060708090A0B0C0D0E0F",
+ "5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436BDF06D8FA1ECA343D");
+
+ test_cipher(
+ PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F",
+ "BBAA99887766554433221107",
+ "000102030405060708090A0B0C0D0E0F1011121314151617",
+ "000102030405060708090A0B0C0D0E0F1011121314151617",
+ "1CA2207308C87C010756104D8840CE1952F09673A448A122C92C62241051F57356D7F3C90BB0E07F");
+
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F",
+ "BBAA99887766554433221108",
+ "000102030405060708090A0B0C0D0E0F1011121314151617",
+ nullptr,
+ "6DC225A071FC1B9F7C69F93B0F1E10DE");
+
+ test_cipher(
+ PGP_SA_AES_128,
+ PGP_CIPHER_MODE_OCB,
+ 16,
+ false,
+ "000102030405060708090A0B0C0D0E0F",
+ "BBAA99887766554433221109",
+ nullptr,
+ "000102030405060708090A0B0C0D0E0F1011121314151617",
+ "221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3CE725F32494B9F914D85C0B1EB38357FF");
+}
+
+TEST_F(rnp_tests, test_cipher_aes_128_cbc)
+{
+ // botan test vectors
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_CBC,
+ 0,
+ false,
+ "10d6f8e78c0ccf8736e4307aaf5b07ef",
+ "3eb182d95bbd5a609aecfb59a0ca898b",
+ NULL,
+ "3a",
+ "a7d290687ae325054a8691014d6821d7");
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_CBC,
+ 0,
+ false,
+ "10d6f8e78c0ccf8736e4307aaf5b07ef",
+ "3eb182d95bbd5a609aecfb59a0ca898b",
+ NULL,
+ "3a513eb569a503b4413b31fa883ddc88",
+ "0cbaf4fa94df265fe264633a994bc25fc7654f19c282a3e2db81499c941ca2b3");
+}
+
+TEST_F(rnp_tests, test_cipher_aes_128_cbc_nopadding)
+{
+ // botan test vectors
+ test_cipher(PGP_SA_AES_128,
+ PGP_CIPHER_MODE_CBC,
+ 0,
+ true,
+ "1f8e4973953f3fb0bd6b16662e9a3c17",
+ "2fe2b333ceda8f98f4a99b40d2cd34a8",
+ NULL,
+ "45cf12964fc824ab76616ae2f4bf0822",
+ "0f61c4d44c5147c03c195ad7e2cc12b2");
+}