aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRené Korthaus <[email protected]>2016-03-06 22:13:45 +0100
committerRené Korthaus <[email protected]>2016-03-25 20:37:28 +0100
commit4835852dec10d3882fd193ee686993571b92210b (patch)
tree15aee6e274689dfb53204127608bab5f7f6f749d /src
parent858cf5c82260e45e5bf51ff17b63f493d8295356 (diff)
Add more tests for ffi
Diffstat (limited to 'src')
-rw-r--r--src/tests/test_ffi.cpp290
-rw-r--r--src/tests/tests.cpp14
-rw-r--r--src/tests/tests.h1
3 files changed, 300 insertions, 5 deletions
diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp
index 77beaf855..d055d093d 100644
--- a/src/tests/test_ffi.cpp
+++ b/src/tests/test_ffi.cpp
@@ -1,5 +1,6 @@
/*
* (C) 2015 Jack Lloyd
+* (C) 2016 René Korthaus
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -20,6 +21,7 @@ namespace {
#define TEST_FFI_OK(func, args) result.test_rc_ok(#func, func args)
#define TEST_FFI_FAIL(msg, func, args) result.test_rc_fail(#func, msg, func args)
+#define TEST_FFI_RC(rc, func, args) result.test_rc(#func, rc, func args)
#define REQUIRE_FFI_OK(func, args) \
if(!TEST_FFI_OK(func, args)) { \
@@ -38,6 +40,15 @@ class FFI_Unit_Tests : public Test
result.test_is_eq("Major version", botan_version_major(), Botan::version_major());
result.test_is_eq("Minor version", botan_version_minor(), Botan::version_minor());
result.test_is_eq("Patch version", botan_version_patch(), Botan::version_patch());
+ result.test_is_eq("Botan version", botan_version_string(), Botan::version_cstr());
+ result.test_is_eq("Botan version datestamp", botan_version_datestamp(), Botan::version_datestamp());
+
+ const std::vector<uint8_t> mem1 = { 0xFF, 0xAA, 0xFF };
+ const std::vector<uint8_t> mem2 = mem1;
+ const std::vector<uint8_t> mem3 = { 0xFF, 0xA9, 0xFF };
+
+ TEST_FFI_RC(0, botan_same_mem, (mem1.data(), mem2.data(), mem1.size()));
+ TEST_FFI_RC(-1, botan_same_mem, (mem1.data(), mem3.data(), mem1.size()));
const std::vector<uint8_t> bin = { 0xAA, 0xDE, 0x01 };
const char* input_str = "ABC";
@@ -134,20 +145,27 @@ class FFI_Unit_Tests : public Test
result.test_eq("MAC output length", output_len, 32);
const byte mac_key[] = { 0xAA, 0xBB, 0xCC, 0xDD };
- TEST_FFI_OK(botan_mac_set_key, (mac, mac_key, sizeof(mac_key)));
-
outbuf.resize(output_len);
- TEST_FFI_OK(botan_mac_update, (mac, reinterpret_cast<const uint8_t*>(input_str), std::strlen(input_str)));
+ // Test that after clear or final the object can be reused
+ for(size_t r = 0; r != 2; ++r)
+ {
+ TEST_FFI_OK(botan_mac_set_key, (mac, mac_key, sizeof(mac_key)));
+ TEST_FFI_OK(botan_mac_update, (mac, reinterpret_cast<const uint8_t*>(input_str), std::strlen(input_str)));
+ TEST_FFI_OK(botan_mac_clear, (mac));
- TEST_FFI_OK(botan_mac_final, (mac, outbuf.data()));
+ TEST_FFI_OK(botan_mac_set_key, (mac, mac_key, sizeof(mac_key)));
+ TEST_FFI_OK(botan_mac_update, (mac, reinterpret_cast<const uint8_t*>(input_str), std::strlen(input_str)));
+ TEST_FFI_OK(botan_mac_final, (mac, outbuf.data()));
- result.test_eq("HMAC output", outbuf, "1A82EEA984BC4A7285617CC0D05F1FE1D6C96675924A81BC965EE8FF7B0697A7");
+ result.test_eq("HMAC output", outbuf, "1A82EEA984BC4A7285617CC0D05F1FE1D6C96675924A81BC965EE8FF7B0697A7");
+ }
}
TEST_FFI_OK(botan_mac_destroy, (mac));
}
+ // KDF test
const std::vector<uint8_t> pbkdf_salt = Botan::hex_decode("ED1F39A0A7F3889AAF7E60743B3BC1CC2C738E60");
const std::string passphrase = "ltexmfeyylmlbrsyikaw";
const size_t pbkdf_out_len = 10;
@@ -206,10 +224,162 @@ class FFI_Unit_Tests : public Test
TEST_FFI_FAIL("bad password", botan_bcrypt_is_valid, ("nope", outstr.data()));
}
+ // TODO: Cipher test
+ /*
+ botan_cipher_t cipher_encrypt, cipher_decrypt;
+
+ if(TEST_FFI_OK(botan_cipher_init, (&cipher_encrypt, "AES-128/CBC", BOTAN_CIPHER_INIT_FLAG_ENCRYPT)))
+ {
+ size_t min_keylen = 0;
+ size_t max_keylen = 0;
+ TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen));
+ result.test_int_eq(min_keylen, 16, "Min key length");
+ result.test_int_eq(max_keylen, 16, "Max key length");
+
+ std::vector<uint8_t> plaintext(256);
+ std::vector<uint8_t> nonce(16);
+ TEST_FFI_OK(botan_rng_get, (rng, plaintext.data(), plaintext.size()));
+ TEST_FFI_OK(botan_rng_get, (rng, nonce.data(), nonce.size()));
+
+ std::vector<uint8_t> ciphertext(plaintext.size()); // TODO: no way to know this size from API
+
+ const std::vector<uint8_t> symkey = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
+
+ size_t output_written = 0;
+ size_t input_consumed = 0;
+
+ // Test that after clear or final the object can be reused
+ for(size_t r = 0; r != 2; ++r)
+ {
+ TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+ TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+ TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, 0, ciphertext.data(), ciphertext.size(), &output_written,
+ plaintext.data(), plaintext.size(), &input_consumed));
+ TEST_FFI_OK(botan_cipher_clear, (cipher_encrypt));
+
+ TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+ TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+ TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL, ciphertext.data(), ciphertext.size(), &output_written,
+ plaintext.data(), plaintext.size(), &input_consumed));
+
+ if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/CBC", BOTAN_CIPHER_INIT_FLAG_DECRYPT)))
+ {
+ std::vector<uint8_t> decrypted(plaintext.size());
+
+ TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size()));
+ TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size()));
+ TEST_FFI_OK(botan_cipher_update, (cipher_decrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL, decrypted.data(), decrypted.size(), &output_written,
+ ciphertext.data(), ciphertext.size(), &input_consumed));
+
+ result.test_eq("AES plaintext", decrypted, plaintext);
+
+ TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt));
+ }
+ }
+
+ TEST_FFI_OK(botan_cipher_destroy, (cipher_encrypt));
+ }
+ */
+
+ // TODO: AEAD test
+ // TODO ONCE MORE WITH AES-GCM
+ // botan_cipher_set_associated_data
+ // botan_cipher_valid_nonce_length
+ // botan_cipher_get_default_nonce_length
+ // botan_cipher_get_tag_length
+
+ // x509 cert test
+ botan_x509_cert_t cert;
+ if(TEST_FFI_OK(botan_x509_cert_load_file, (&cert, "src/tests/data/ecc/CSCA.CSCA.csca-germany.1.crt")))
+ {
+ size_t date_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_time_starts, (cert, nullptr, &date_len));
+
+ std::string date(date_len-1, '0');
+ TEST_FFI_OK(botan_x509_cert_get_time_starts, (cert, &date[0], &date_len));
+ result.test_eq("cert valid from", date, "070719152718Z");
+
+ date_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_time_expires, (cert, nullptr, &date_len));
+
+ date.resize(date_len-1);
+ TEST_FFI_OK(botan_x509_cert_get_time_expires, (cert, &date[0], &date_len));
+ result.test_eq("cert valid until", date, "280119151800Z");
+
+ size_t serial_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_serial_number, (cert, nullptr, &serial_len));
+
+ std::vector<uint8_t> serial(serial_len);
+ TEST_FFI_OK(botan_x509_cert_get_serial_number, (cert, serial.data(), &serial_len));
+ result.test_eq("cert serial length", serial.size(), 1);
+ result.test_int_eq(serial[0], 1, "cert serial");
+
+ size_t fingerprint_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_fingerprint, (cert, "SHA-256", nullptr, &fingerprint_len));
+
+ std::vector<uint8_t> fingerprint(fingerprint_len);
+ TEST_FFI_OK(botan_x509_cert_get_fingerprint, (cert, "SHA-256", fingerprint.data(), &fingerprint_len));
+ result.test_eq("cert fingerprint", reinterpret_cast<const char*>(fingerprint.data()), "3B:6C:99:1C:D6:5A:51:FC:EB:17:E3:AA:F6:3C:1A:DA:14:1F:82:41:30:6F:64:EE:FF:63:F3:1F:D6:07:14:9F");
+
+ size_t key_id_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_authority_key_id, (cert, nullptr, &key_id_len));
+
+ std::vector<uint8_t> key_id(key_id_len);
+ TEST_FFI_OK(botan_x509_cert_get_authority_key_id, (cert, key_id.data(), &key_id_len));
+ result.test_eq("cert authority key id", Botan::hex_encode(key_id.data(), key_id.size(), true), "0096452DE588F966C4CCDF161DD1F3F5341B71E7");
+
+ key_id_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_subject_key_id, (cert, nullptr, &key_id_len));
+
+ key_id.resize(key_id_len);
+ TEST_FFI_OK(botan_x509_cert_get_subject_key_id, (cert, key_id.data(), &key_id_len));
+ result.test_eq("cert subject key id", Botan::hex_encode(key_id.data(), key_id.size(), true), "0096452DE588F966C4CCDF161DD1F3F5341B71E7");
+
+ size_t pubkey_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_public_key_bits, (cert, nullptr, &pubkey_len));
+
+ std::vector<uint8_t> pubkey(pubkey_len);
+ TEST_FFI_OK(botan_x509_cert_get_public_key_bits, (cert, pubkey.data(), &pubkey_len));
+
+ botan_pubkey_t pub;
+ if(TEST_FFI_OK(botan_x509_cert_get_public_key, (cert, &pub)))
+ {
+ TEST_FFI_OK(botan_pubkey_destroy, (pub));
+ }
+
+ size_t dn_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_issuer_dn, (cert, "Name", 0, nullptr, &dn_len));
+
+ std::vector<uint8_t> dn(dn_len);
+ TEST_FFI_OK(botan_x509_cert_get_issuer_dn, (cert, "Name", 0, dn.data(), &dn_len));
+ result.test_eq("issuer dn", reinterpret_cast<const char*>(dn.data()), "csca-germany");
+
+ dn_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_subject_dn, (cert, "Name", 0, nullptr, &dn_len));
+
+ dn.resize(dn_len);
+ TEST_FFI_OK(botan_x509_cert_get_subject_dn, (cert, "Name", 0, dn.data(), &dn_len));
+ result.test_eq("subject dn", reinterpret_cast<const char*>(dn.data()), "csca-germany");
+
+ size_t printable_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_to_string, (cert, nullptr, &printable_len));
+
+ std::string printable(printable_len-1, '0');
+ TEST_FFI_OK(botan_x509_cert_to_string, (cert, &printable[0], &printable_len));
+
+ TEST_FFI_RC(0, botan_x509_cert_allowed_usage, (cert, KEY_CERT_SIGN));
+ TEST_FFI_RC(0, botan_x509_cert_allowed_usage, (cert, CRL_SIGN));
+ TEST_FFI_RC(1, botan_x509_cert_allowed_usage, (cert, DIGITAL_SIGNATURE));
+
+ TEST_FFI_OK(botan_x509_cert_destroy, (cert));
+ }
+
std::vector<Test::Result> results;
results.push_back(ffi_test_rsa(rng));
results.push_back(ffi_test_ecdsa(rng));
results.push_back(ffi_test_ecdh(rng));
+ results.push_back(ffi_test_mceliece(rng));
TEST_FFI_OK(botan_rng_destroy, (rng));
@@ -218,6 +388,59 @@ class FFI_Unit_Tests : public Test
}
private:
+ void ffi_test_pubkey_export(Test::Result& result, botan_pubkey_t pub, botan_privkey_t priv, botan_rng_t rng)
+ {
+ // export public key
+ size_t pubkey_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pubkey_export, (pub, nullptr, &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+ std::vector<uint8_t> pubkey(pubkey_len);
+ TEST_FFI_OK(botan_pubkey_export, (pub, pubkey.data(), &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+ pubkey_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pubkey_export, (pub, nullptr, &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+ pubkey.resize(pubkey_len);
+ TEST_FFI_OK(botan_pubkey_export, (pub, pubkey.data(), &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+ // export private key
+ size_t privkey_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export, (priv, nullptr, &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+ std::vector<uint8_t> privkey(privkey_len);
+ TEST_FFI_OK(botan_privkey_export, (priv, privkey.data(), &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+ privkey_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export, (priv, nullptr, &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+ privkey.resize(privkey_len);
+ TEST_FFI_OK(botan_privkey_export, (priv, privkey.data(), &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+ // export private key encrypted
+ privkey_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export_encrypted, (priv, nullptr, &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+ privkey.resize(privkey_len);
+ TEST_FFI_OK(botan_privkey_export_encrypted, (priv, privkey.data(), &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+ privkey_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export_encrypted, (priv, nullptr, &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+ privkey.resize(privkey_len);
+ TEST_FFI_OK(botan_privkey_export_encrypted, (priv, privkey.data(), &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+ // calculate fingerprint
+ size_t strength = 0;
+ TEST_FFI_OK(botan_pubkey_estimated_strength, (pub, &strength));
+ result.test_gte("estimated strength", strength, 1);
+
+ size_t fingerprint_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pubkey_fingerprint, (pub, "SHA-512", nullptr, &fingerprint_len));
+
+ std::vector<uint8_t> fingerprint(fingerprint_len);
+ TEST_FFI_OK(botan_pubkey_fingerprint, (pub, "SHA-512", fingerprint.data(), &fingerprint_len));
+ }
+
Test::Result ffi_test_rsa(botan_rng_t rng)
{
Test::Result result("FFI");
@@ -228,6 +451,8 @@ class FFI_Unit_Tests : public Test
botan_pubkey_t pub;
TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+ ffi_test_pubkey_export(result, pub, priv, rng);
+
char namebuf[32] = { 0 };
size_t name_len = sizeof(namebuf);
if(TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf, &name_len)))
@@ -287,6 +512,8 @@ class FFI_Unit_Tests : public Test
botan_pubkey_t pub;
TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+ ffi_test_pubkey_export(result, pub, priv, rng);
+
char namebuf[32] = { 0 };
size_t name_len = sizeof(namebuf);
TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len));
@@ -361,6 +588,9 @@ class FFI_Unit_Tests : public Test
botan_pubkey_t pub2;
REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&pub2, priv2));
+ ffi_test_pubkey_export(result, pub1, priv1, rng);
+ ffi_test_pubkey_export(result, pub2, priv2, rng);
+
botan_pk_op_ka_t ka1;
REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, priv1, "KDF2(SHA-256)", 0));
botan_pk_op_ka_t ka2;
@@ -404,6 +634,56 @@ class FFI_Unit_Tests : public Test
return result;
}
+
+ Test::Result ffi_test_mceliece(botan_rng_t rng)
+ {
+ Test::Result result("FFI");
+
+ botan_privkey_t priv;
+ if (TEST_FFI_OK(botan_privkey_create_mceliece, (&priv, rng, 2048, 50)))
+ {
+ botan_pubkey_t pub;
+ TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+
+ ffi_test_pubkey_export(result, pub, priv, rng);
+
+ char namebuf[32] = { 0 };
+ size_t name_len = sizeof(namebuf);
+ if (TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf, &name_len)))
+ {
+ result.test_eq("algo name", std::string(namebuf), "McEliece");
+ }
+
+ size_t i = 0;
+ uint8_t ad[8];
+ Botan::store_be(static_cast<Botan::u64bit>(i), ad);
+ const size_t ad_len = sizeof(ad);
+
+ const Botan::secure_vector<byte> plaintext = Test::rng().random_vec(Test::rng().next_byte());
+ size_t plaintext_len = plaintext.size();
+ size_t ciphertext_len = 0;
+
+ // first calculate ciphertext length
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_mceies_encrypt, (pub, rng, "AES-256/OCB", plaintext.data(), plaintext.size(), ad, ad_len, nullptr, &ciphertext_len));
+ std::vector<uint8_t> ciphertext(ciphertext_len);
+
+ // now encrypt
+ if (TEST_FFI_OK(botan_mceies_encrypt, (pub, rng, "AES-256/OCB", plaintext.data(), plaintext.size(), ad, ad_len, ciphertext.data(), &ciphertext_len)))
+ {
+ std::vector<uint8_t> decrypted(plaintext.size());
+ size_t decrypted_len = plaintext_len;
+
+ TEST_FFI_OK(botan_mceies_decrypt, (priv, "AES-256/OCB", ciphertext.data(), ciphertext.size(), ad, ad_len, decrypted.data(), &decrypted_len));
+
+ result.test_eq("MCIES plaintext", decrypted, plaintext);
+ }
+
+ TEST_FFI_OK(botan_pubkey_destroy, (pub));
+ TEST_FFI_OK(botan_privkey_destroy, (priv));
+ }
+
+ return result;
+ }
};
BOTAN_REGISTER_TEST("ffi", FFI_Unit_Tests);
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
index 6d419310b..a79e05aba 100644
--- a/src/tests/tests.cpp
+++ b/src/tests/tests.cpp
@@ -282,6 +282,20 @@ bool Test::Result::test_rc_fail(const std::string& func, const std::string& why,
return test_success();
}
+bool Test::Result::test_rc(const std::string& func, int expected, int rc)
+ {
+ if(expected != rc)
+ {
+ std::ostringstream err;
+ err << m_who;
+ err << " call to " << func << " unexpectedly returned " << rc;
+ err << " but expecting " << expected;
+ return test_failure(err.str());
+ }
+
+ return test_success();
+ }
+
namespace {
std::string format_time(uint64_t ns)
diff --git a/src/tests/tests.h b/src/tests/tests.h
index 0bb65778a..c7bed64d9 100644
--- a/src/tests/tests.h
+++ b/src/tests/tests.h
@@ -186,6 +186,7 @@ class Test
bool test_rc_ok(const std::string& func, int rc);
bool test_rc_fail(const std::string& func, const std::string& why, int rc);
+ bool test_rc(const std::string& func, int expected, int rc);
#if defined(BOTAN_HAS_BIGINT)
bool test_eq(const std::string& what, const BigInt& produced, const BigInt& expected);