summaryrefslogtreecommitdiffstats
path: root/src/lib/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/crypto')
-rw-r--r--src/lib/crypto/backend_version.cpp184
-rw-r--r--src/lib/crypto/backend_version.h44
-rw-r--r--src/lib/crypto/bn.cpp107
-rw-r--r--src/lib/crypto/bn.h64
-rw-r--r--src/lib/crypto/bn_ossl.cpp84
-rw-r--r--src/lib/crypto/cipher.cpp78
-rw-r--r--src/lib/crypto/cipher.hpp77
-rw-r--r--src/lib/crypto/cipher_botan.cpp245
-rw-r--r--src/lib/crypto/cipher_botan.hpp73
-rw-r--r--src/lib/crypto/cipher_ossl.cpp284
-rw-r--r--src/lib/crypto/cipher_ossl.hpp80
-rw-r--r--src/lib/crypto/common.h51
-rw-r--r--src/lib/crypto/dl_ossl.cpp200
-rw-r--r--src/lib/crypto/dl_ossl.h44
-rw-r--r--src/lib/crypto/dsa.cpp382
-rw-r--r--src/lib/crypto/dsa.h141
-rw-r--r--src/lib/crypto/dsa_ossl.cpp355
-rw-r--r--src/lib/crypto/ec.cpp187
-rw-r--r--src/lib/crypto/ec.h173
-rw-r--r--src/lib/crypto/ec_curves.cpp323
-rw-r--r--src/lib/crypto/ec_ossl.cpp349
-rw-r--r--src/lib/crypto/ec_ossl.h42
-rw-r--r--src/lib/crypto/ecdh.cpp377
-rw-r--r--src/lib/crypto/ecdh.h117
-rw-r--r--src/lib/crypto/ecdh_ossl.cpp389
-rw-r--r--src/lib/crypto/ecdh_utils.cpp166
-rw-r--r--src/lib/crypto/ecdh_utils.h47
-rw-r--r--src/lib/crypto/ecdsa.cpp267
-rw-r--r--src/lib/crypto/ecdsa.h57
-rw-r--r--src/lib/crypto/ecdsa_ossl.cpp190
-rw-r--r--src/lib/crypto/eddsa.cpp212
-rw-r--r--src/lib/crypto/eddsa.h54
-rw-r--r--src/lib/crypto/eddsa_ossl.cpp156
-rw-r--r--src/lib/crypto/elgamal.cpp302
-rw-r--r--src/lib/crypto/elgamal.h116
-rw-r--r--src/lib/crypto/elgamal_ossl.cpp418
-rw-r--r--src/lib/crypto/hash.cpp160
-rw-r--r--src/lib/crypto/hash.hpp98
-rw-r--r--src/lib/crypto/hash_botan.hpp68
-rw-r--r--src/lib/crypto/hash_common.cpp194
-rw-r--r--src/lib/crypto/hash_crc24.cpp288
-rw-r--r--src/lib/crypto/hash_crc24.hpp47
-rw-r--r--src/lib/crypto/hash_ossl.cpp152
-rw-r--r--src/lib/crypto/hash_ossl.hpp55
-rw-r--r--src/lib/crypto/hash_sha1cd.cpp94
-rw-r--r--src/lib/crypto/hash_sha1cd.hpp52
-rw-r--r--src/lib/crypto/mem.cpp68
-rw-r--r--src/lib/crypto/mem.h158
-rw-r--r--src/lib/crypto/mem_ossl.cpp113
-rw-r--r--src/lib/crypto/mpi.cpp119
-rw-r--r--src/lib/crypto/mpi.h58
-rw-r--r--src/lib/crypto/ossl_common.h40
-rw-r--r--src/lib/crypto/rng.cpp59
-rw-r--r--src/lib/crypto/rng.h78
-rw-r--r--src/lib/crypto/rng_ossl.cpp48
-rw-r--r--src/lib/crypto/rsa.cpp419
-rw-r--r--src/lib/crypto/rsa.h90
-rw-r--r--src/lib/crypto/rsa_ossl.cpp629
-rw-r--r--src/lib/crypto/s2k.cpp203
-rw-r--r--src/lib/crypto/s2k.h65
-rw-r--r--src/lib/crypto/s2k_ossl.cpp97
-rw-r--r--src/lib/crypto/sha1cd/sha1.c2162
-rw-r--r--src/lib/crypto/sha1cd/sha1.h122
-rw-r--r--src/lib/crypto/sha1cd/ubc_check.c908
-rw-r--r--src/lib/crypto/sha1cd/ubc_check.h62
-rw-r--r--src/lib/crypto/signatures.cpp281
-rw-r--r--src/lib/crypto/signatures.h69
-rw-r--r--src/lib/crypto/sm2.cpp383
-rw-r--r--src/lib/crypto/sm2.h79
-rw-r--r--src/lib/crypto/sm2_ossl.cpp76
-rw-r--r--src/lib/crypto/symmetric.cpp648
-rw-r--r--src/lib/crypto/symmetric.h252
-rw-r--r--src/lib/crypto/symmetric_ossl.cpp644
73 files changed, 15573 insertions, 0 deletions
diff --git a/src/lib/crypto/backend_version.cpp b/src/lib/crypto/backend_version.cpp
new file mode 100644
index 0000000..859b048
--- /dev/null
+++ b/src/lib/crypto/backend_version.cpp
@@ -0,0 +1,184 @@
+/*-
+ * Copyright (c) 2021 Ribose Inc.
+ * 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 HOLDERS 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 "backend_version.h"
+#include "logging.h"
+#if defined(CRYPTO_BACKEND_BOTAN)
+#include <botan/version.h>
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+#include <openssl/opensslv.h>
+#include <openssl/crypto.h>
+#include "ossl_common.h"
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+#include <openssl/provider.h>
+#endif
+#include <string.h>
+#include "config.h"
+#ifndef RNP_USE_STD_REGEX
+#include <regex.h>
+#else
+#include <regex>
+#endif
+#endif
+#include <cassert>
+
+namespace rnp {
+
+const char *
+backend_string()
+{
+#if defined(CRYPTO_BACKEND_BOTAN)
+ return "Botan";
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ return "OpenSSL";
+#else
+#error "Unknown backend"
+#endif
+}
+
+const char *
+backend_version()
+{
+#if defined(CRYPTO_BACKEND_BOTAN)
+ return Botan::short_version_cstr();
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ /* Use regexp to retrieve version (second word) from version string
+ * like "OpenSSL 1.1.1l 24 Aug 2021"
+ * */
+ static char version[32] = {};
+ if (version[0]) {
+ return version;
+ }
+ const char *reg = "OpenSSL (([0-9]\\.[0-9]\\.[0-9])[a-z]*(-beta[0-9])*(-dev)*) ";
+#ifndef RNP_USE_STD_REGEX
+ static regex_t r;
+ regmatch_t matches[5];
+ const char * ver = OpenSSL_version(OPENSSL_VERSION);
+
+ if (!strlen(version)) {
+ if (regcomp(&r, reg, REG_EXTENDED) != 0) {
+ RNP_LOG("failed to compile regexp");
+ return "unknown";
+ }
+ }
+ if (regexec(&r, ver, 5, matches, 0) != 0) {
+ return "unknown";
+ }
+ assert(sizeof(version) > matches[1].rm_eo - matches[1].rm_so);
+ memcpy(version, ver + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
+ version[matches[1].rm_eo - matches[1].rm_so] = '\0';
+#else
+ static std::regex re(reg, std::regex_constants::extended);
+ std::smatch result;
+ std::string ver = OpenSSL_version(OPENSSL_VERSION);
+ if (!std::regex_search(ver, result, re)) {
+ return "unknown";
+ }
+ assert(sizeof(version) > result[1].str().size());
+ strncpy(version, result[1].str().c_str(), sizeof(version) - 1);
+#endif
+ return version;
+#else
+#error "Unknown backend"
+#endif
+}
+
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+
+#if defined(ENABLE_IDEA) || defined(ENABLE_CAST5) || defined(ENABLE_BLOWFISH) || \
+ defined(ENABLE_RIPEMD160)
+#define OPENSSL_LOAD_LEGACY
+#endif
+
+typedef struct openssl3_state {
+#if defined(OPENSSL_LOAD_LEGACY)
+ OSSL_PROVIDER *legacy;
+#endif
+ OSSL_PROVIDER *def;
+} openssl3_state;
+
+bool
+backend_init(void **param)
+{
+ if (!param) {
+ return false;
+ }
+
+ *param = NULL;
+ openssl3_state *state = (openssl3_state *) calloc(1, sizeof(openssl3_state));
+ if (!state) {
+ RNP_LOG("Allocation failure.");
+ return false;
+ }
+ /* Load default crypto provider */
+ state->def = OSSL_PROVIDER_load(NULL, "default");
+ if (!state->def) {
+ RNP_LOG("Failed to load default crypto provider: %s", ossl_latest_err());
+ free(state);
+ return false;
+ }
+ /* Load legacy crypto provider if needed */
+#if defined(OPENSSL_LOAD_LEGACY)
+ state->legacy = OSSL_PROVIDER_load(NULL, "legacy");
+ if (!state->legacy) {
+ RNP_LOG("Failed to load legacy crypto provider: %s", ossl_latest_err());
+ OSSL_PROVIDER_unload(state->def);
+ free(state);
+ return false;
+ }
+#endif
+ *param = state;
+ return true;
+}
+
+void
+backend_finish(void *param)
+{
+ if (!param) {
+ return;
+ }
+ openssl3_state *state = (openssl3_state *) param;
+ OSSL_PROVIDER_unload(state->def);
+#if defined(OPENSSL_LOAD_LEGACY)
+ OSSL_PROVIDER_unload(state->legacy);
+#endif
+ free(state);
+}
+#else
+bool
+backend_init(void **param)
+{
+ return true;
+}
+
+void
+backend_finish(void *param)
+{
+ // Do nothing
+}
+#endif
+
+} // namespace rnp
diff --git a/src/lib/crypto/backend_version.h b/src/lib/crypto/backend_version.h
new file mode 100644
index 0000000..13c5269
--- /dev/null
+++ b/src/lib/crypto/backend_version.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2021 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef CRYPTO_BACKEND_VERSION_H_
+#define CRYPTO_BACKEND_VERSION_H_
+
+#include "config.h"
+
+namespace rnp {
+
+const char *backend_string();
+
+const char *backend_version();
+
+bool backend_init(void **param);
+
+void backend_finish(void *param);
+
+} // namespace rnp
+
+#endif // CRYPTO_BACKEND_VERSION_H_
diff --git a/src/lib/crypto/bn.cpp b/src/lib/crypto/bn.cpp
new file mode 100644
index 0000000..d5ae6b4
--- /dev/null
+++ b/src/lib/crypto/bn.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017-2021 Ribose Inc.
+ * Copyright (c) 2012 Alistair Crooks <agc@NetBSD.org>
+ * 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 AUTHOR ``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 AUTHOR 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 "bn.h"
+#include <botan/ffi.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "utils.h"
+
+/* essentiually, these are just wrappers around the botan functions */
+/* usually the order of args changes */
+/* the bignum_t API tends to have more const poisoning */
+/* these wrappers also check the arguments passed for sanity */
+
+/* store in unsigned [big endian] format */
+int
+bn_bn2bin(const bignum_t *a, unsigned char *b)
+{
+ if (!a || !b) {
+ return -1;
+ }
+ return botan_mp_to_bin(a->mp, b);
+}
+
+bignum_t *
+mpi2bn(const pgp_mpi_t *val)
+{
+ assert(val);
+ if (!val) {
+ RNP_LOG("NULL val.");
+ return NULL;
+ }
+ bignum_t *res = bn_new();
+ if (!res) {
+ return NULL;
+ }
+ if (botan_mp_from_bin(res->mp, val->mpi, val->len)) {
+ bn_free(res);
+ res = NULL;
+ }
+ return res;
+}
+
+bool
+bn2mpi(const bignum_t *bn, pgp_mpi_t *val)
+{
+ val->len = bn_num_bytes(*bn);
+ if (val->len > PGP_MPINT_SIZE) {
+ RNP_LOG("Too large MPI.");
+ val->len = 0;
+ return false;
+ }
+ return bn_bn2bin(bn, val->mpi) == 0;
+}
+
+bignum_t *
+bn_new(void)
+{
+ bignum_t *a = (bignum_t *) calloc(1, sizeof(*a));
+ if (!a) {
+ return NULL;
+ }
+ botan_mp_init(&a->mp);
+ return a;
+}
+
+void
+bn_free(bignum_t *a)
+{
+ if (a) {
+ botan_mp_destroy(a->mp);
+ free(a);
+ }
+}
+
+size_t
+bn_num_bytes(const bignum_t &a)
+{
+ size_t bytes = 0;
+ if (botan_mp_num_bits(a.mp, &bytes)) {
+ RNP_LOG("botan_mp_num_bits failed.");
+ }
+ return BITS_TO_BYTES(bytes);
+}
diff --git a/src/lib/crypto/bn.h b/src/lib/crypto/bn.h
new file mode 100644
index 0000000..26cc547
--- /dev/null
+++ b/src/lib/crypto/bn.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2017-2021 Ribose Inc.
+ * Copyright (c) 2012 Alistair Crooks <agc@NetBSD.org>
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef RNP_BN_H_
+#define RNP_BN_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include "config.h"
+#include "mpi.h"
+
+#if defined(CRYPTO_BACKEND_OPENSSL)
+#include <openssl/bn.h>
+
+#define bignum_t BIGNUM
+#elif defined(CRYPTO_BACKEND_BOTAN)
+typedef struct botan_mp_struct *botan_mp_t;
+typedef struct bignum_t_st {
+ botan_mp_t mp;
+} bignum_t;
+
+#define BN_HANDLE(x) ((x).mp)
+#define BN_HANDLE_PTR(x) ((x)->mp)
+#else
+#error "Unknown crypto backend."
+#endif
+
+/*********************************/
+
+bignum_t *bn_new(void);
+void bn_free(bignum_t * /*a*/);
+
+int bn_bn2bin(const bignum_t * /*a*/, unsigned char * /*b*/);
+
+bignum_t *mpi2bn(const pgp_mpi_t *val);
+
+bool bn2mpi(const bignum_t *bn, pgp_mpi_t *val);
+
+size_t bn_num_bytes(const bignum_t &a);
+
+#endif
diff --git a/src/lib/crypto/bn_ossl.cpp b/src/lib/crypto/bn_ossl.cpp
new file mode 100644
index 0000000..34e1a3e
--- /dev/null
+++ b/src/lib/crypto/bn_ossl.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 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 <stdlib.h>
+#include <assert.h>
+#include "bn.h"
+#include "logging.h"
+
+/* store in unsigned [big endian] format */
+int
+bn_bn2bin(const bignum_t *a, unsigned char *b)
+{
+ if (!a || !b) {
+ return -1;
+ }
+ return BN_bn2bin(a, b) >= 0 ? 0 : -1;
+}
+
+bignum_t *
+mpi2bn(const pgp_mpi_t *val)
+{
+ assert(val);
+ if (!val) {
+ RNP_LOG("NULL val.");
+ return NULL;
+ }
+ bignum_t *res = bn_new();
+ if (!res) {
+ return NULL;
+ }
+ if (!BN_bin2bn(val->mpi, val->len, res)) {
+ bn_free(res);
+ res = NULL;
+ }
+ return res;
+}
+
+bool
+bn2mpi(const bignum_t *bn, pgp_mpi_t *val)
+{
+ val->len = bn_num_bytes(*bn);
+ return bn_bn2bin(bn, val->mpi) == 0;
+}
+
+bignum_t *
+bn_new(void)
+{
+ return BN_new();
+}
+
+void
+bn_free(bignum_t *a)
+{
+ BN_clear_free(a);
+}
+
+size_t
+bn_num_bytes(const bignum_t &a)
+{
+ return (BN_num_bits(&a) + 7) / 8;
+}
diff --git a/src/lib/crypto/cipher.cpp b/src/lib/crypto/cipher.cpp
new file mode 100644
index 0000000..a6c6fce
--- /dev/null
+++ b/src/lib/crypto/cipher.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 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 "config.h"
+#include "symmetric.h"
+#include "cipher.hpp"
+
+#if defined(CRYPTO_BACKEND_OPENSSL)
+#include "cipher_ossl.hpp"
+#elif defined(CRYPTO_BACKEND_BOTAN)
+#include "cipher_botan.hpp"
+#endif
+
+std::unique_ptr<Cipher>
+Cipher::encryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding)
+{
+#if defined(CRYPTO_BACKEND_OPENSSL)
+ return Cipher_OpenSSL::encryption(cipher, mode, tag_size, disable_padding);
+#elif defined(CRYPTO_BACKEND_BOTAN)
+ return Cipher_Botan::encryption(cipher, mode, tag_size, disable_padding);
+#else
+#error "Crypto backend not specified"
+#endif
+}
+
+std::unique_ptr<Cipher>
+Cipher::decryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding)
+{
+#if defined(CRYPTO_BACKEND_OPENSSL)
+ return Cipher_OpenSSL::decryption(cipher, mode, tag_size, disable_padding);
+#elif defined(CRYPTO_BACKEND_BOTAN)
+ return Cipher_Botan::decryption(cipher, mode, tag_size, disable_padding);
+#else
+#error "Crypto backend not specified"
+#endif
+}
+
+Cipher::Cipher(pgp_symm_alg_t alg) : m_alg(alg)
+{
+}
+
+Cipher::~Cipher()
+{
+}
+
+size_t
+Cipher::block_size() const
+{
+ return pgp_block_size(m_alg);
+}
diff --git a/src/lib/crypto/cipher.hpp b/src/lib/crypto/cipher.hpp
new file mode 100644
index 0000000..c9edf15
--- /dev/null
+++ b/src/lib/crypto/cipher.hpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 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.
+ */
+#ifndef RNP_CIPHER_HPP
+#define RNP_CIPHER_HPP
+
+#include <memory>
+#include <string>
+#include <repgp/repgp_def.h>
+
+// Note: for AEAD modes we append the authentication tag to the ciphertext as in RFC 5116
+class Cipher {
+ public:
+ // the tag size should be 0 for non-AEAD and must be non-zero for AEAD modes (no default)
+ static std::unique_ptr<Cipher> encryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size = 0,
+ bool disable_padding = false);
+ static std::unique_ptr<Cipher> decryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size = 0,
+ bool disable_padding = false);
+
+ virtual bool set_key(const uint8_t *key, size_t key_length) = 0;
+ virtual bool set_iv(const uint8_t *iv, size_t iv_length) = 0;
+ // only valid for AEAD modes
+ virtual bool set_ad(const uint8_t *ad, size_t ad_length) = 0;
+
+ virtual size_t block_size() const;
+ virtual size_t update_granularity() const = 0;
+
+ // input_length must be a multiple of update_granularity
+ virtual bool update(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed) = 0;
+ // process final block and perform any padding
+ virtual bool finish(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed) = 0;
+
+ virtual ~Cipher();
+
+ protected:
+ Cipher(pgp_symm_alg_t alg);
+
+ pgp_symm_alg_t m_alg;
+};
+
+#endif
diff --git a/src/lib/crypto/cipher_botan.cpp b/src/lib/crypto/cipher_botan.cpp
new file mode 100644
index 0000000..c2c4ab3
--- /dev/null
+++ b/src/lib/crypto/cipher_botan.cpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 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 <sstream>
+#include <cassert>
+#include <botan/aead.h>
+#include "cipher_botan.hpp"
+#include "utils.h"
+#include "types.h"
+
+static const id_str_pair cipher_mode_map[] = {
+ {PGP_CIPHER_MODE_CBC, "CBC"},
+ {PGP_CIPHER_MODE_OCB, "OCB"},
+ {0, NULL},
+};
+
+static const id_str_pair cipher_map[] = {
+ {PGP_SA_AES_128, "AES-128"},
+ {PGP_SA_AES_256, "AES-256"},
+ {PGP_SA_IDEA, "IDEA"},
+ {0, NULL},
+};
+
+Cipher_Botan *
+Cipher_Botan::create(pgp_symm_alg_t alg, const std::string &name, bool encrypt)
+{
+#if !defined(ENABLE_IDEA)
+ if (alg == PGP_SA_IDEA) {
+ RNP_LOG("IDEA support has been disabled");
+ return nullptr;
+ }
+#endif
+#if !defined(ENABLE_BLOWFISH)
+ if (alg == PGP_SA_BLOWFISH) {
+ RNP_LOG("Blowfish support has been disabled");
+ return nullptr;
+ }
+#endif
+#if !defined(ENABLE_CAST5)
+ if (alg == PGP_SA_CAST5) {
+ RNP_LOG("CAST5 support has been disabled");
+ return nullptr;
+ }
+#endif
+ auto cipher = Botan::Cipher_Mode::create(
+ name, encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION);
+ if (!cipher) {
+ RNP_LOG("Failed to create cipher '%s'", name.c_str());
+ return nullptr;
+ }
+ return new (std::nothrow) Cipher_Botan(alg, std::move(cipher));
+}
+
+static std::string
+make_name(pgp_symm_alg_t cipher, pgp_cipher_mode_t mode, size_t tag_size, bool disable_padding)
+{
+ const char *cipher_string = id_str_pair::lookup(cipher_map, cipher, NULL);
+ const char *mode_string = id_str_pair::lookup(cipher_mode_map, mode, NULL);
+ if (!cipher_string || !mode_string) {
+ return "";
+ }
+ try {
+ std::stringstream ss;
+ ss << cipher_string << "/" << mode_string;
+ if (tag_size) {
+ ss << "(" << tag_size << ")";
+ }
+ if (mode == PGP_CIPHER_MODE_CBC && disable_padding) {
+ ss << "/NoPadding";
+ }
+ return ss.str();
+ } catch (const std::exception &e) {
+ return "";
+ }
+}
+
+std::unique_ptr<Cipher_Botan>
+Cipher_Botan::encryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding)
+{
+ return std::unique_ptr<Cipher_Botan>(
+ create(cipher, make_name(cipher, mode, tag_size, disable_padding), true));
+}
+
+std::unique_ptr<Cipher_Botan>
+Cipher_Botan::decryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding)
+{
+ return std::unique_ptr<Cipher_Botan>(
+ create(cipher, make_name(cipher, mode, tag_size, disable_padding), false));
+}
+
+size_t
+Cipher_Botan::update_granularity() const
+{
+ return m_cipher->update_granularity();
+}
+
+bool
+Cipher_Botan::set_key(const uint8_t *key, size_t key_length)
+{
+ try {
+ m_cipher->set_key(key, key_length);
+ } catch (const std::exception &e) {
+ RNP_LOG("Failed to set key: %s", e.what());
+ return false;
+ }
+ return true;
+}
+
+bool
+Cipher_Botan::set_iv(const uint8_t *iv, size_t iv_length)
+{
+ try {
+ m_cipher->start(iv, iv_length);
+ m_buf.reserve(this->update_granularity());
+ } catch (const std::exception &e) {
+ RNP_LOG("Failed to set IV: %s", e.what());
+ return false;
+ }
+ return true;
+}
+
+bool
+Cipher_Botan::set_ad(const uint8_t *ad, size_t ad_length)
+{
+ assert(m_cipher->authenticated());
+ try {
+ dynamic_cast<Botan::AEAD_Mode &>(*m_cipher).set_associated_data(ad, ad_length);
+ } catch (const std::exception &e) {
+ RNP_LOG("Failed to set AAD: %s", e.what());
+ return false;
+ }
+ return true;
+}
+
+bool
+Cipher_Botan::update(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed)
+{
+ try {
+ size_t ud = this->update_granularity();
+ m_buf.resize(ud);
+
+ *input_consumed = 0;
+ *output_written = 0;
+ while (input_length >= ud && output_length >= ud) {
+ m_buf.assign(input, input + ud);
+ size_t written = m_cipher->process(m_buf.data(), ud);
+ std::copy(m_buf.data(), m_buf.data() + written, output);
+ input += ud;
+ output += written;
+ input_length -= ud;
+ output_length -= written;
+
+ *output_written += written;
+ *input_consumed += ud;
+ }
+ } catch (const std::exception &e) {
+ RNP_LOG("%s", e.what());
+ return false;
+ }
+ return true;
+}
+
+bool
+Cipher_Botan::finish(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed)
+{
+ try {
+ *input_consumed = 0;
+ *output_written = 0;
+ size_t ud = this->update_granularity();
+ if (input_length > ud) {
+ if (!update(output,
+ output_length,
+ output_written,
+ input,
+ input_length - ud,
+ input_consumed)) {
+ return false;
+ }
+ input += *input_consumed;
+ input_length = input_length - *input_consumed;
+ output += *output_written;
+ output_length -= *output_written;
+ }
+ Botan::secure_vector<uint8_t> final_block(input, input + input_length);
+ m_cipher->finish(final_block);
+ if (final_block.size() > output_length) {
+ RNP_LOG("Insufficient buffer");
+ return false;
+ }
+ std::copy(final_block.begin(), final_block.end(), output);
+ *output_written += final_block.size();
+ *input_consumed += input_length;
+ } catch (const std::exception &e) {
+ RNP_LOG("%s", e.what());
+ return false;
+ }
+ return true;
+}
+
+Cipher_Botan::Cipher_Botan(pgp_symm_alg_t alg, std::unique_ptr<Botan::Cipher_Mode> cipher)
+ : Cipher(alg), m_cipher(std::move(cipher))
+{
+}
+
+Cipher_Botan::~Cipher_Botan()
+{
+}
diff --git a/src/lib/crypto/cipher_botan.hpp b/src/lib/crypto/cipher_botan.hpp
new file mode 100644
index 0000000..517d38b
--- /dev/null
+++ b/src/lib/crypto/cipher_botan.hpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 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.
+ */
+#ifndef RNP_CIPHER_BOTAN_HPP
+#define RNP_CIPHER_BOTAN_HPP
+
+#include "cipher.hpp"
+#include <botan/cipher_mode.h>
+#include <repgp/repgp_def.h>
+
+class Cipher_Botan : public Cipher {
+ public:
+ static std::unique_ptr<Cipher_Botan> encryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding);
+ static std::unique_ptr<Cipher_Botan> decryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding);
+
+ bool set_key(const uint8_t *key, size_t key_length) override;
+ bool set_iv(const uint8_t *iv, size_t iv_length) override;
+ bool set_ad(const uint8_t *ad, size_t ad_length) override;
+
+ size_t update_granularity() const override;
+
+ bool update(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed) override;
+ bool finish(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed) override;
+ virtual ~Cipher_Botan();
+
+ private:
+ Cipher_Botan(pgp_symm_alg_t alg, std::unique_ptr<Botan::Cipher_Mode> cipher);
+
+ std::unique_ptr<Botan::Cipher_Mode> m_cipher;
+ std::vector<uint8_t> m_buf;
+
+ static Cipher_Botan *create(pgp_symm_alg_t alg, const std::string &name, bool encrypt);
+};
+
+#endif
diff --git a/src/lib/crypto/cipher_ossl.cpp b/src/lib/crypto/cipher_ossl.cpp
new file mode 100644
index 0000000..bb81d48
--- /dev/null
+++ b/src/lib/crypto/cipher_ossl.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 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 <cassert>
+#include <algorithm>
+
+#include "cipher_ossl.hpp"
+#include "utils.h"
+#include "types.h"
+#include <openssl/err.h>
+
+static const id_str_pair cipher_mode_map[] = {
+ {PGP_CIPHER_MODE_CBC, "CBC"},
+ {PGP_CIPHER_MODE_OCB, "OCB"},
+ {0, NULL},
+};
+
+static const id_str_pair cipher_map[] = {
+ {PGP_SA_AES_128, "AES-128"},
+ {PGP_SA_AES_256, "AES-256"},
+ {PGP_SA_IDEA, "IDEA"},
+ {0, NULL},
+};
+
+EVP_CIPHER_CTX *
+Cipher_OpenSSL::create(pgp_symm_alg_t alg,
+ const std::string &name,
+ bool encrypt,
+ size_t tag_size,
+ bool disable_padding)
+{
+#if !defined(ENABLE_IDEA)
+ if (alg == PGP_SA_IDEA) {
+ RNP_LOG("IDEA support has been disabled");
+ return nullptr;
+ }
+#endif
+#if !defined(ENABLE_BLOWFISH)
+ if (alg == PGP_SA_BLOWFISH) {
+ RNP_LOG("Blowfish support has been disabled");
+ return nullptr;
+ }
+#endif
+#if !defined(ENABLE_CAST5)
+ if (alg == PGP_SA_CAST5) {
+ RNP_LOG("CAST5 support has been disabled");
+ return nullptr;
+ }
+#endif
+ const EVP_CIPHER *cipher = EVP_get_cipherbyname(name.c_str());
+ if (!cipher) {
+ RNP_LOG("Unsupported cipher: %s", name.c_str());
+ return nullptr;
+ }
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ if (!ctx) {
+ RNP_LOG("Failed to create cipher context: %lu", ERR_peek_last_error());
+ return nullptr;
+ }
+ if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt ? 1 : 0) != 1) {
+ RNP_LOG("Failed to initialize cipher: %lu", ERR_peek_last_error());
+ EVP_CIPHER_CTX_free(ctx);
+ return nullptr;
+ }
+ // set tag size
+ if (encrypt && tag_size) {
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_size, NULL) != 1) {
+ RNP_LOG("Failed to set AEAD tag length: %lu", ERR_peek_last_error());
+ EVP_CIPHER_CTX_free(ctx);
+ return nullptr;
+ }
+ }
+ if (disable_padding) {
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ }
+ return ctx;
+}
+
+static std::string
+make_name(pgp_symm_alg_t cipher, pgp_cipher_mode_t mode)
+{
+ const char *cipher_string = id_str_pair::lookup(cipher_map, cipher, NULL);
+ const char *mode_string = id_str_pair::lookup(cipher_mode_map, mode, NULL);
+ if (!cipher_string || !mode_string) {
+ return "";
+ }
+ return std::string(cipher_string) + "-" + mode_string;
+}
+
+std::unique_ptr<Cipher_OpenSSL>
+Cipher_OpenSSL::encryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding)
+{
+ EVP_CIPHER_CTX *ossl_ctx =
+ create(cipher, make_name(cipher, mode), true, tag_size, disable_padding);
+ if (!ossl_ctx) {
+ return NULL;
+ }
+ return std::unique_ptr<Cipher_OpenSSL>(new (std::nothrow)
+ Cipher_OpenSSL(cipher, ossl_ctx, tag_size, true));
+}
+
+std::unique_ptr<Cipher_OpenSSL>
+Cipher_OpenSSL::decryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding)
+{
+ EVP_CIPHER_CTX *ossl_ctx =
+ create(cipher, make_name(cipher, mode), false, tag_size, disable_padding);
+ if (!ossl_ctx) {
+ return NULL;
+ }
+ return std::unique_ptr<Cipher_OpenSSL>(
+ new (std::nothrow) Cipher_OpenSSL(cipher, ossl_ctx, tag_size, false));
+}
+
+bool
+Cipher_OpenSSL::set_key(const uint8_t *key, size_t key_length)
+{
+ assert(key_length <= INT_MAX);
+ return EVP_CIPHER_CTX_set_key_length(m_ctx, (int) key_length) == 1 &&
+ EVP_CipherInit_ex(m_ctx, NULL, NULL, key, NULL, -1) == 1;
+}
+
+bool
+Cipher_OpenSSL::set_iv(const uint8_t *iv, size_t iv_length)
+{
+ assert(iv_length <= INT_MAX);
+ // set IV len for AEAD modes
+ if (m_tag_size &&
+ EVP_CIPHER_CTX_ctrl(m_ctx, EVP_CTRL_AEAD_SET_IVLEN, (int) iv_length, NULL) != 1) {
+ RNP_LOG("Failed to set AEAD IV length: %lu", ERR_peek_last_error());
+ return false;
+ }
+ if (EVP_CIPHER_CTX_iv_length(m_ctx) != (int) iv_length) {
+ RNP_LOG("IV length mismatch");
+ return false;
+ }
+ if (EVP_CipherInit_ex(m_ctx, NULL, NULL, NULL, iv, -1) != 1) {
+ RNP_LOG("Failed to set IV: %lu", ERR_peek_last_error());
+ }
+ return true;
+}
+
+bool
+Cipher_OpenSSL::set_ad(const uint8_t *ad, size_t ad_length)
+{
+ assert(m_tag_size);
+ int outlen = 0;
+ if (EVP_CipherUpdate(m_ctx, NULL, &outlen, ad, ad_length) != 1) {
+ RNP_LOG("Failed to set AD: %lu", ERR_peek_last_error());
+ return false;
+ }
+ return true;
+}
+
+size_t
+Cipher_OpenSSL::update_granularity() const
+{
+ return (size_t) EVP_CIPHER_CTX_block_size(m_ctx);
+}
+
+bool
+Cipher_OpenSSL::update(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed)
+{
+ if (input_length > INT_MAX) {
+ return false;
+ }
+ *input_consumed = 0;
+ *output_written = 0;
+ if (input_length == 0) {
+ return true;
+ }
+ int outl = 0;
+ if (EVP_CipherUpdate(m_ctx, output, &outl, input, (int) input_length) != 1) {
+ RNP_LOG("EVP_CipherUpdate failed: %lu", ERR_peek_last_error());
+ return false;
+ }
+ assert((size_t) outl < output_length);
+ *input_consumed = input_length;
+ *output_written = (size_t) outl;
+ return true;
+}
+
+bool
+Cipher_OpenSSL::finish(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed)
+{
+ if (input_length > INT_MAX) {
+ return false;
+ }
+ if (!m_encrypt && input_length < m_tag_size) {
+ RNP_LOG("Insufficient input for final block (missing tag)");
+ return false;
+ }
+ *input_consumed = 0;
+ *output_written = 0;
+ if (!m_encrypt && m_tag_size) {
+ // set the tag from the end of the ciphertext
+ if (EVP_CIPHER_CTX_ctrl(m_ctx,
+ EVP_CTRL_AEAD_SET_TAG,
+ m_tag_size,
+ const_cast<uint8_t *>(input) + input_length - m_tag_size) !=
+ 1) {
+ RNP_LOG("Failed to set expected AEAD tag: %lu", ERR_peek_last_error());
+ return false;
+ }
+ size_t ats = std::min(m_tag_size, input_length);
+ input_length -= ats; // m_tag_size;
+ *input_consumed += ats; // m_tag_size;
+ }
+ int outl = 0;
+ if (EVP_CipherUpdate(m_ctx, output, &outl, input, (int) input_length) != 1) {
+ RNP_LOG("EVP_CipherUpdate failed: %lu", ERR_peek_last_error());
+ return false;
+ }
+ input += input_length;
+ *input_consumed += input_length;
+ output += outl;
+ *output_written += (size_t) outl;
+ if (EVP_CipherFinal_ex(m_ctx, output, &outl) != 1) {
+ RNP_LOG("EVP_CipherFinal_ex failed: %lu", ERR_peek_last_error());
+ return false;
+ }
+ *output_written += (size_t) outl;
+ output += (size_t) outl;
+ if (m_encrypt && m_tag_size) {
+ // append the tag
+ if (EVP_CIPHER_CTX_ctrl(m_ctx, EVP_CTRL_AEAD_GET_TAG, m_tag_size, output) != 1) {
+ RNP_LOG("Failed to append AEAD tag: %lu", ERR_peek_last_error());
+ return false;
+ }
+ *output_written += m_tag_size;
+ }
+ return true;
+}
+
+Cipher_OpenSSL::Cipher_OpenSSL(pgp_symm_alg_t alg,
+ EVP_CIPHER_CTX *ctx,
+ size_t tag_size,
+ bool encrypt)
+ : Cipher(alg), m_ctx(ctx), m_tag_size(tag_size), m_encrypt(encrypt)
+{
+ m_block_size = EVP_CIPHER_CTX_block_size(m_ctx);
+}
+
+Cipher_OpenSSL::~Cipher_OpenSSL()
+{
+ EVP_CIPHER_CTX_free(m_ctx);
+}
diff --git a/src/lib/crypto/cipher_ossl.hpp b/src/lib/crypto/cipher_ossl.hpp
new file mode 100644
index 0000000..da2bb4e
--- /dev/null
+++ b/src/lib/crypto/cipher_ossl.hpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 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.
+ */
+#ifndef RNP_CIPHER_OSSL_HPP
+#define RNP_CIPHER_OSSL_HPP
+
+#include "cipher.hpp"
+#include <openssl/evp.h>
+#include <repgp/repgp_def.h>
+
+class Cipher_OpenSSL : public Cipher {
+ public:
+ static std::unique_ptr<Cipher_OpenSSL> encryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding);
+ static std::unique_ptr<Cipher_OpenSSL> decryption(pgp_symm_alg_t cipher,
+ pgp_cipher_mode_t mode,
+ size_t tag_size,
+ bool disable_padding);
+
+ bool set_key(const uint8_t *key, size_t key_length) override;
+ bool set_iv(const uint8_t *iv, size_t iv_length) override;
+ bool set_ad(const uint8_t *ad, size_t ad_length) override;
+
+ size_t update_granularity() const override;
+
+ // input_length should not exceed INT_MAX
+ bool update(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed) override;
+ bool finish(uint8_t * output,
+ size_t output_length,
+ size_t * output_written,
+ const uint8_t *input,
+ size_t input_length,
+ size_t * input_consumed) override;
+ virtual ~Cipher_OpenSSL();
+
+ private:
+ Cipher_OpenSSL(pgp_symm_alg_t alg, EVP_CIPHER_CTX *ctx, size_t tag_size, bool encrypt);
+
+ EVP_CIPHER_CTX *m_ctx;
+ size_t m_block_size;
+ size_t m_tag_size;
+ bool m_encrypt;
+
+ static EVP_CIPHER_CTX *create(pgp_symm_alg_t alg,
+ const std::string &name,
+ bool encrypt,
+ size_t tag_size,
+ bool disable_padding);
+};
+
+#endif
diff --git a/src/lib/crypto/common.h b/src/lib/crypto/common.h
new file mode 100644
index 0000000..3d9b837
--- /dev/null
+++ b/src/lib/crypto/common.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2018 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef RNP_CRYPTO_COMMON_H_
+#define RNP_CRYPTO_COMMON_H_
+
+/* base */
+#include "mpi.h"
+#include "rng.h"
+/* asymmetric crypto */
+#include "rsa.h"
+#include "dsa.h"
+#include "elgamal.h"
+#include "ec.h"
+#include "ecdh.h"
+#include "ecdsa.h"
+#include "sm2.h"
+#include "eddsa.h"
+/* symmetric crypto */
+#include "symmetric.h"
+/* hash */
+#include "hash.hpp"
+/* s2k */
+#include "s2k.h"
+/* backend name and version */
+#include "backend_version.h"
+
+#endif // RNP_CRYPTO_COMMON_H_
diff --git a/src/lib/crypto/dl_ossl.cpp b/src/lib/crypto/dl_ossl.cpp
new file mode 100644
index 0000000..1e96218
--- /dev/null
+++ b/src/lib/crypto/dl_ossl.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 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 <cstdlib>
+#include <string>
+#include <cassert>
+#include "bn.h"
+#include "dl_ossl.h"
+#include "utils.h"
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+EVP_PKEY *
+dl_load_key(const pgp_mpi_t &mp,
+ const pgp_mpi_t *mq,
+ const pgp_mpi_t &mg,
+ const pgp_mpi_t &my,
+ const pgp_mpi_t *mx)
+{
+ DH * dh = NULL;
+ EVP_PKEY *evpkey = NULL;
+ bignum_t *p = mpi2bn(&mp);
+ bignum_t *q = mq ? mpi2bn(mq) : NULL;
+ bignum_t *g = mpi2bn(&mg);
+ bignum_t *y = mpi2bn(&my);
+ bignum_t *x = mx ? mpi2bn(mx) : NULL;
+
+ if (!p || (mq && !q) || !g || !y || (mx && !x)) {
+ RNP_LOG("out of memory");
+ goto done;
+ }
+
+ dh = DH_new();
+ if (!dh) {
+ RNP_LOG("out of memory");
+ goto done;
+ }
+ int res;
+ /* line below must not fail */
+ res = DH_set0_pqg(dh, p, q, g);
+ assert(res == 1);
+ if (res < 1) {
+ goto done;
+ }
+ p = NULL;
+ q = NULL;
+ g = NULL;
+ /* line below must not fail */
+ res = DH_set0_key(dh, y, x);
+ assert(res == 1);
+ if (res < 1) {
+ goto done;
+ }
+ y = NULL;
+ x = NULL;
+
+ evpkey = EVP_PKEY_new();
+ if (!evpkey) {
+ RNP_LOG("allocation failed");
+ goto done;
+ }
+ if (EVP_PKEY_set1_DH(evpkey, dh) <= 0) {
+ RNP_LOG("Failed to set key: %lu", ERR_peek_last_error());
+ EVP_PKEY_free(evpkey);
+ evpkey = NULL;
+ }
+done:
+ DH_free(dh);
+ bn_free(p);
+ bn_free(q);
+ bn_free(g);
+ bn_free(y);
+ bn_free(x);
+ return evpkey;
+}
+
+static rnp_result_t
+dl_validate_secret_key(EVP_PKEY *dlkey, const pgp_mpi_t &mx)
+{
+ const DH *dh = EVP_PKEY_get0_DH(dlkey);
+ assert(dh);
+ const bignum_t *p = DH_get0_p(dh);
+ const bignum_t *q = DH_get0_q(dh);
+ const bignum_t *g = DH_get0_g(dh);
+ const bignum_t *y = DH_get0_pub_key(dh);
+ assert(p && g && y);
+ bignum_t *p1 = NULL;
+
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+
+ BN_CTX * ctx = BN_CTX_new();
+ bignum_t *x = mpi2bn(&mx);
+ bignum_t *cy = bn_new();
+
+ if (!x || !cy || !ctx) {
+ RNP_LOG("Allocation failed");
+ goto done;
+ }
+ if (!q) {
+ /* if q is NULL then group order is (p - 1) / 2 */
+ p1 = BN_dup(p);
+ if (!p1) {
+ RNP_LOG("Allocation failed");
+ goto done;
+ }
+ int res;
+ res = BN_rshift(p1, p1, 1);
+ assert(res == 1);
+ if (res < 1) {
+ RNP_LOG("BN_rshift failed.");
+ goto done;
+ }
+ q = p1;
+ }
+ if (BN_cmp(x, q) != -1) {
+ RNP_LOG("x is too large.");
+ goto done;
+ }
+ if (BN_mod_exp_mont_consttime(cy, g, x, p, ctx, NULL) < 1) {
+ RNP_LOG("Exponentiation failed");
+ goto done;
+ }
+ if (BN_cmp(cy, y) == 0) {
+ ret = RNP_SUCCESS;
+ }
+done:
+ BN_CTX_free(ctx);
+ bn_free(x);
+ bn_free(cy);
+ bn_free(p1);
+ return ret;
+}
+
+rnp_result_t
+dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *x)
+{
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ int res;
+ res = EVP_PKEY_param_check(ctx);
+ if (res < 0) {
+ RNP_LOG("Param validation error: %lu (%s)",
+ ERR_peek_last_error(),
+ ERR_reason_error_string(ERR_peek_last_error()));
+ }
+ if (res < 1) {
+ /* ElGamal specification doesn't seem to restrict P to the safe prime */
+ auto err = ERR_peek_last_error();
+ DHerr(DH_F_DH_CHECK_EX, DH_R_CHECK_P_NOT_SAFE_PRIME);
+ if ((ERR_GET_REASON(err) == DH_R_CHECK_P_NOT_SAFE_PRIME)) {
+ RNP_LOG("Warning! P is not a safe prime.");
+ } else {
+ goto done;
+ }
+ }
+ res = EVP_PKEY_public_check(ctx);
+ if (res < 0) {
+ RNP_LOG("Key validation error: %lu", ERR_peek_last_error());
+ }
+ if (res < 1) {
+ goto done;
+ }
+ /* There is no private key check in OpenSSL yet, so need to check x vs y manually */
+ if (!x) {
+ ret = RNP_SUCCESS;
+ goto done;
+ }
+ ret = dl_validate_secret_key(pkey, *x);
+done:
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
diff --git a/src/lib/crypto/dl_ossl.h b/src/lib/crypto/dl_ossl.h
new file mode 100644
index 0000000..fcafc0a
--- /dev/null
+++ b/src/lib/crypto/dl_ossl.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef DL_OSSL_H_
+#define DL_OSSL_H_
+
+#include "types.h"
+#include "config.h"
+#include <rnp/rnp_def.h>
+#include "mpi.h"
+#include <openssl/evp.h>
+
+EVP_PKEY *dl_load_key(const pgp_mpi_t &mp,
+ const pgp_mpi_t *mq,
+ const pgp_mpi_t &mg,
+ const pgp_mpi_t &my,
+ const pgp_mpi_t *mx);
+
+rnp_result_t dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *mx);
+
+#endif
diff --git a/src/lib/crypto/dsa.cpp b/src/lib/crypto/dsa.cpp
new file mode 100644
index 0000000..8763f00
--- /dev/null
+++ b/src/lib/crypto/dsa.cpp
@@ -0,0 +1,382 @@
+/*-
+ * Copyright (c) 2017-2018 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * 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 HOLDERS 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.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <botan/ffi.h>
+#include <rnp/rnp_def.h>
+#include "dsa.h"
+#include "bn.h"
+#include "utils.h"
+
+#define DSA_MAX_Q_BITLEN 256
+
+rnp_result_t
+dsa_validate_key(rnp::RNG *rng, const pgp_dsa_key_t *key, bool secret)
+{
+ bignum_t * p = NULL;
+ bignum_t * q = NULL;
+ bignum_t * g = NULL;
+ bignum_t * y = NULL;
+ bignum_t * x = NULL;
+ botan_pubkey_t bpkey = NULL;
+ botan_privkey_t bskey = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+
+ /* load and check public key part */
+ p = mpi2bn(&key->p);
+ q = mpi2bn(&key->q);
+ g = mpi2bn(&key->g);
+ y = mpi2bn(&key->y);
+
+ if (!p || !q || !g || !y) {
+ RNP_LOG("out of memory");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ if (botan_pubkey_load_dsa(
+ &bpkey, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q), BN_HANDLE_PTR(g), BN_HANDLE_PTR(y))) {
+ goto done;
+ }
+
+ if (botan_pubkey_check_key(bpkey, rng->handle(), 0)) {
+ goto done;
+ }
+
+ if (!secret) {
+ ret = RNP_SUCCESS;
+ goto done;
+ }
+
+ /* load and check secret key part */
+ if (!(x = mpi2bn(&key->x))) {
+ RNP_LOG("out of memory");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ if (botan_privkey_load_dsa(
+ &bskey, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q), BN_HANDLE_PTR(g), BN_HANDLE_PTR(x))) {
+ goto done;
+ }
+
+ if (botan_privkey_check_key(bskey, rng->handle(), 0)) {
+ goto done;
+ }
+
+ ret = RNP_SUCCESS;
+done:
+ bn_free(p);
+ bn_free(q);
+ bn_free(g);
+ bn_free(y);
+ bn_free(x);
+ botan_privkey_destroy(bskey);
+ botan_pubkey_destroy(bpkey);
+ return ret;
+}
+
+rnp_result_t
+dsa_sign(rnp::RNG * rng,
+ pgp_dsa_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_dsa_key_t *key)
+{
+ botan_privkey_t dsa_key = NULL;
+ botan_pk_op_sign_t sign_op = NULL;
+ size_t q_order = 0;
+ uint8_t sign_buf[2 * BITS_TO_BYTES(DSA_MAX_Q_BITLEN)] = {0};
+ bignum_t * p = NULL, *q = NULL, *g = NULL, *x = NULL;
+ rnp_result_t ret = RNP_ERROR_SIGNING_FAILED;
+ size_t sigbuf_size = sizeof(sign_buf);
+
+ size_t z_len = 0;
+
+ memset(sig, 0, sizeof(*sig));
+ q_order = mpi_bytes(&key->q);
+ if ((2 * q_order) > sizeof(sign_buf)) {
+ RNP_LOG("wrong q order");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ // As 'Raw' is used we need to reduce hash size (as per FIPS-186-4, 4.6)
+ z_len = hash_len < q_order ? hash_len : q_order;
+
+ p = mpi2bn(&key->p);
+ q = mpi2bn(&key->q);
+ g = mpi2bn(&key->g);
+ x = mpi2bn(&key->x);
+
+ if (!p || !q || !g || !x) {
+ RNP_LOG("out of memory");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ if (botan_privkey_load_dsa(
+ &dsa_key, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q), BN_HANDLE_PTR(g), BN_HANDLE_PTR(x))) {
+ RNP_LOG("Can't load key");
+ goto end;
+ }
+
+ if (botan_pk_op_sign_create(&sign_op, dsa_key, "Raw", 0)) {
+ goto end;
+ }
+
+ if (botan_pk_op_sign_update(sign_op, hash, z_len)) {
+ goto end;
+ }
+
+ if (botan_pk_op_sign_finish(sign_op, rng->handle(), sign_buf, &sigbuf_size)) {
+ RNP_LOG("Signing has failed");
+ goto end;
+ }
+
+ // Now load the DSA (r,s) values from the signature.
+ if (!mem2mpi(&sig->r, sign_buf, q_order) ||
+ !mem2mpi(&sig->s, sign_buf + q_order, q_order)) {
+ goto end;
+ }
+ ret = RNP_SUCCESS;
+
+end:
+ bn_free(p);
+ bn_free(q);
+ bn_free(g);
+ bn_free(x);
+ botan_pk_op_sign_destroy(sign_op);
+ botan_privkey_destroy(dsa_key);
+ return ret;
+}
+
+rnp_result_t
+dsa_verify(const pgp_dsa_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_dsa_key_t * key)
+{
+ botan_pubkey_t dsa_key = NULL;
+ botan_pk_op_verify_t verify_op = NULL;
+ uint8_t sign_buf[2 * BITS_TO_BYTES(DSA_MAX_Q_BITLEN)] = {0};
+ size_t q_order = 0;
+ size_t r_blen, s_blen;
+ bignum_t * p = NULL, *q = NULL, *g = NULL, *y = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ size_t z_len = 0;
+
+ q_order = mpi_bytes(&key->q);
+ if ((2 * q_order) > sizeof(sign_buf)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ z_len = hash_len < q_order ? hash_len : q_order;
+
+ r_blen = mpi_bytes(&sig->r);
+ s_blen = mpi_bytes(&sig->s);
+ if ((r_blen > q_order) || (s_blen > q_order)) {
+ RNP_LOG("Wrong signature");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ p = mpi2bn(&key->p);
+ q = mpi2bn(&key->q);
+ g = mpi2bn(&key->g);
+ y = mpi2bn(&key->y);
+
+ if (!p || !q || !g || !y) {
+ RNP_LOG("out of memory");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ if (botan_pubkey_load_dsa(
+ &dsa_key, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q), BN_HANDLE_PTR(g), BN_HANDLE_PTR(y))) {
+ RNP_LOG("Wrong key");
+ goto end;
+ }
+
+ mpi2mem(&sig->r, sign_buf + q_order - r_blen);
+ mpi2mem(&sig->s, sign_buf + 2 * q_order - s_blen);
+
+ if (botan_pk_op_verify_create(&verify_op, dsa_key, "Raw", 0)) {
+ RNP_LOG("Can't create verifier");
+ goto end;
+ }
+
+ if (botan_pk_op_verify_update(verify_op, hash, z_len)) {
+ goto end;
+ }
+
+ ret = (botan_pk_op_verify_finish(verify_op, sign_buf, 2 * q_order) == BOTAN_FFI_SUCCESS) ?
+ RNP_SUCCESS :
+ RNP_ERROR_SIGNATURE_INVALID;
+
+end:
+ bn_free(p);
+ bn_free(q);
+ bn_free(g);
+ bn_free(y);
+ botan_pk_op_verify_destroy(verify_op);
+ botan_pubkey_destroy(dsa_key);
+ return ret;
+}
+
+rnp_result_t
+dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
+{
+ if ((keylen < 1024) || (keylen > 3072) || (qbits < 160) || (qbits > 256)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ botan_privkey_t key_priv = NULL;
+ botan_pubkey_t key_pub = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ bignum_t * p = bn_new();
+ bignum_t * q = bn_new();
+ bignum_t * g = bn_new();
+ bignum_t * y = bn_new();
+ bignum_t * x = bn_new();
+
+ if (!p || !q || !g || !y || !x) {
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ if (botan_privkey_create_dsa(&key_priv, rng->handle(), keylen, qbits) ||
+ botan_privkey_check_key(key_priv, rng->handle(), 1) ||
+ botan_privkey_export_pubkey(&key_pub, key_priv)) {
+ RNP_LOG("Wrong parameters");
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ goto end;
+ }
+
+ if (botan_pubkey_get_field(BN_HANDLE_PTR(p), key_pub, "p") ||
+ botan_pubkey_get_field(BN_HANDLE_PTR(q), key_pub, "q") ||
+ botan_pubkey_get_field(BN_HANDLE_PTR(g), key_pub, "g") ||
+ botan_pubkey_get_field(BN_HANDLE_PTR(y), key_pub, "y") ||
+ botan_privkey_get_field(BN_HANDLE_PTR(x), key_priv, "x")) {
+ RNP_LOG("Botan FFI call failed");
+ ret = RNP_ERROR_GENERIC;
+ goto end;
+ }
+
+ if (!bn2mpi(p, &key->p) || !bn2mpi(q, &key->q) || !bn2mpi(g, &key->g) ||
+ !bn2mpi(y, &key->y) || !bn2mpi(x, &key->x)) {
+ RNP_LOG("failed to copy mpi");
+ goto end;
+ }
+ ret = RNP_SUCCESS;
+end:
+ bn_free(p);
+ bn_free(q);
+ bn_free(g);
+ bn_free(y);
+ bn_free(x);
+ botan_privkey_destroy(key_priv);
+ botan_pubkey_destroy(key_pub);
+ return ret;
+}
+
+pgp_hash_alg_t
+dsa_get_min_hash(size_t qsize)
+{
+ /*
+ * I'm using _broken_ SHA1 here only because
+ * some old implementations may not understand keys created
+ * with other hashes. If you're sure we don't have to support
+ * such implementations, please be my guest and remove it.
+ */
+ return (qsize < 160) ? PGP_HASH_UNKNOWN :
+ (qsize == 160) ? PGP_HASH_SHA1 :
+ (qsize <= 224) ? PGP_HASH_SHA224 :
+ (qsize <= 256) ? PGP_HASH_SHA256 :
+ (qsize <= 384) ? PGP_HASH_SHA384 :
+ (qsize <= 512) ? PGP_HASH_SHA512
+ /*(qsize>512)*/ :
+ PGP_HASH_UNKNOWN;
+}
+
+size_t
+dsa_choose_qsize_by_psize(size_t psize)
+{
+ return (psize == 1024) ? 160 :
+ (psize <= 2047) ? 224 :
+ (psize <= 3072) ? DSA_MAX_Q_BITLEN :
+ 0;
+}
diff --git a/src/lib/crypto/dsa.h b/src/lib/crypto/dsa.h
new file mode 100644
index 0000000..52a186a
--- /dev/null
+++ b/src/lib/crypto/dsa.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2017-2018, [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * This code is originally derived from software contributed to
+ * The NetBSD Foundation by Alistair Crooks (agc@netbsd.org), and
+ * carried further by Ribose Inc (https://www.ribose.com).
+ *
+ * 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 HOLDERS 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.
+ */
+
+#ifndef RNP_DSA_H_
+#define RNP_DSA_H_
+
+#include <rnp/rnp_def.h>
+#include <repgp/repgp_def.h>
+#include "crypto/rng.h"
+#include "crypto/mpi.h"
+
+typedef struct pgp_dsa_key_t {
+ pgp_mpi_t p;
+ pgp_mpi_t q;
+ pgp_mpi_t g;
+ pgp_mpi_t y;
+ /* secret mpi */
+ pgp_mpi_t x;
+} pgp_dsa_key_t;
+
+typedef struct pgp_dsa_signature_t {
+ pgp_mpi_t r;
+ pgp_mpi_t s;
+} pgp_dsa_signature_t;
+
+/**
+ * @brief Checks DSA key fields for validity
+ *
+ * @param rng initialized PRNG
+ * @param key initialized DSA key structure
+ * @param secret flag which tells whether key has populated secret fields
+ *
+ * @return RNP_SUCCESS if key is valid or error code otherwise
+ */
+rnp_result_t dsa_validate_key(rnp::RNG *rng, const pgp_dsa_key_t *key, bool secret);
+
+/*
+ * @brief Performs DSA signing
+ *
+ * @param rng initialized PRNG
+ * @param sig[out] created signature
+ * @param hash hash to sign
+ * @param hash_len length of `hash`
+ * @param key DSA key (must include secret mpi)
+ *
+ * @returns RNP_SUCCESS
+ * RNP_ERROR_BAD_PARAMETERS wrong input provided
+ * RNP_ERROR_SIGNING_FAILED internal error
+ */
+rnp_result_t dsa_sign(rnp::RNG * rng,
+ pgp_dsa_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_dsa_key_t *key);
+
+/*
+ * @brief Performs DSA verification
+ *
+ * @param hash hash to verify
+ * @param hash_len length of `hash`
+ * @param sig signature to be verified
+ * @param key DSA key (secret mpi is not needed)
+ *
+ * @returns RNP_SUCCESS
+ * RNP_ERROR_BAD_PARAMETERS wrong input provided
+ * RNP_ERROR_GENERIC internal error
+ * RNP_ERROR_SIGNATURE_INVALID signature is invalid
+ */
+rnp_result_t dsa_verify(const pgp_dsa_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_dsa_key_t * key);
+
+/*
+ * @brief Performs DSA key generation
+ *
+ * @param rng initialized PRNG
+ * @param key[out] generated key data will be stored here
+ * @param keylen length of the key, in bits
+ * @param qbits subgroup size in bits
+ *
+ * @returns RNP_SUCCESS
+ * RNP_ERROR_BAD_PARAMETERS wrong input provided
+ * RNP_ERROR_OUT_OF_MEMORY memory allocation failed
+ * RNP_ERROR_GENERIC internal error
+ * RNP_ERROR_SIGNATURE_INVALID signature is invalid
+ */
+rnp_result_t dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits);
+
+/*
+ * @brief Returns minimally sized hash which will work
+ * with the DSA subgroup.
+ *
+ * @param qsize subgroup order
+ *
+ * @returns Either ID of the hash algorithm, or PGP_HASH_UNKNOWN
+ * if not found
+ */
+pgp_hash_alg_t dsa_get_min_hash(size_t qsize);
+
+/*
+ * @brief Helps to determine subgroup size by size of p
+ * In order not to confuse users, we use less complicated
+ * approach than suggested by FIPS-186, which is:
+ * p=1024 => q=160
+ * p<2048 => q=224
+ * p<=3072 => q=256
+ * So we don't generate (2048, 224) pair
+ *
+ * @return Size of `q' or 0 in case `psize' is not in <1024,3072> range
+ */
+size_t dsa_choose_qsize_by_psize(size_t psize);
+
+#endif
diff --git a/src/lib/crypto/dsa_ossl.cpp b/src/lib/crypto/dsa_ossl.cpp
new file mode 100644
index 0000000..1fb75b5
--- /dev/null
+++ b/src/lib/crypto/dsa_ossl.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 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 <stdlib.h>
+#include <string.h>
+#include <rnp/rnp_def.h>
+#include "bn.h"
+#include "dsa.h"
+#include "dl_ossl.h"
+#include "utils.h"
+#include <openssl/dsa.h>
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#define DSA_MAX_Q_BITLEN 256
+
+static bool
+dsa_decode_sig(const uint8_t *data, size_t len, pgp_dsa_signature_t &sig)
+{
+ DSA_SIG *dsig = d2i_DSA_SIG(NULL, &data, len);
+ if (!dsig) {
+ RNP_LOG("Failed to parse DSA sig: %lu", ERR_peek_last_error());
+ return false;
+ }
+ const BIGNUM *r, *s;
+ DSA_SIG_get0(dsig, &r, &s);
+ bn2mpi(r, &sig.r);
+ bn2mpi(s, &sig.s);
+ DSA_SIG_free(dsig);
+ return true;
+}
+
+static bool
+dsa_encode_sig(uint8_t *data, size_t *len, const pgp_dsa_signature_t &sig)
+{
+ bool res = false;
+ DSA_SIG *dsig = DSA_SIG_new();
+ BIGNUM * r = mpi2bn(&sig.r);
+ BIGNUM * s = mpi2bn(&sig.s);
+ if (!dsig || !r || !s) {
+ RNP_LOG("Allocation failed.");
+ goto done;
+ }
+ DSA_SIG_set0(dsig, r, s);
+ r = NULL;
+ s = NULL;
+ int outlen;
+ outlen = i2d_DSA_SIG(dsig, &data);
+ if (outlen < 0) {
+ RNP_LOG("Failed to encode signature.");
+ goto done;
+ }
+ *len = outlen;
+ res = true;
+done:
+ DSA_SIG_free(dsig);
+ BN_free(r);
+ BN_free(s);
+ return res;
+}
+
+static EVP_PKEY *
+dsa_load_key(const pgp_dsa_key_t *key, bool secret = false)
+{
+ DSA * dsa = NULL;
+ EVP_PKEY *evpkey = NULL;
+ bignum_t *p = mpi2bn(&key->p);
+ bignum_t *q = mpi2bn(&key->q);
+ bignum_t *g = mpi2bn(&key->g);
+ bignum_t *y = mpi2bn(&key->y);
+ bignum_t *x = secret ? mpi2bn(&key->x) : NULL;
+
+ if (!p || !q || !g || !y || (secret && !x)) {
+ RNP_LOG("out of memory");
+ goto done;
+ }
+
+ dsa = DSA_new();
+ if (!dsa) {
+ RNP_LOG("Out of memory");
+ goto done;
+ }
+ if (DSA_set0_pqg(dsa, p, q, g) != 1) {
+ RNP_LOG("Failed to set pqg. Error: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ p = NULL;
+ q = NULL;
+ g = NULL;
+ if (DSA_set0_key(dsa, y, x) != 1) {
+ RNP_LOG("Secret key load error: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ y = NULL;
+ x = NULL;
+
+ evpkey = EVP_PKEY_new();
+ if (!evpkey) {
+ RNP_LOG("allocation failed");
+ goto done;
+ }
+ if (EVP_PKEY_set1_DSA(evpkey, dsa) <= 0) {
+ RNP_LOG("Failed to set key: %lu", ERR_peek_last_error());
+ EVP_PKEY_free(evpkey);
+ evpkey = NULL;
+ }
+done:
+ DSA_free(dsa);
+ bn_free(p);
+ bn_free(q);
+ bn_free(g);
+ bn_free(y);
+ bn_free(x);
+ return evpkey;
+}
+
+rnp_result_t
+dsa_validate_key(rnp::RNG *rng, const pgp_dsa_key_t *key, bool secret)
+{
+ /* OpenSSL doesn't implement key checks for the DSA, however we may use DL via DH */
+ EVP_PKEY *pkey = dl_load_key(key->p, &key->q, key->g, key->y, NULL);
+ if (!pkey) {
+ RNP_LOG("Failed to load key");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ rnp_result_t ret = dl_validate_key(pkey, secret ? &key->x : NULL);
+ EVP_PKEY_free(pkey);
+ return ret;
+}
+
+rnp_result_t
+dsa_sign(rnp::RNG * rng,
+ pgp_dsa_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_dsa_key_t *key)
+{
+ if (mpi_bytes(&key->x) == 0) {
+ RNP_LOG("private key not set");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ /* Load secret key to DSA structure*/
+ EVP_PKEY *evpkey = dsa_load_key(key, true);
+ if (!evpkey) {
+ RNP_LOG("Failed to load key");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ /* init context and sign */
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_sign_init(ctx) <= 0) {
+ RNP_LOG("Failed to initialize signing: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ sig->s.len = PGP_MPINT_SIZE;
+ if (EVP_PKEY_sign(ctx, sig->s.mpi, &sig->s.len, hash, hash_len) <= 0) {
+ RNP_LOG("Signing failed: %lu", ERR_peek_last_error());
+ sig->s.len = 0;
+ goto done;
+ }
+ if (!dsa_decode_sig(&sig->s.mpi[0], sig->s.len, *sig)) {
+ RNP_LOG("Failed to parse DSA sig: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(evpkey);
+ return ret;
+}
+
+rnp_result_t
+dsa_verify(const pgp_dsa_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_dsa_key_t * key)
+{
+ /* Load secret key to DSA structure*/
+ EVP_PKEY *evpkey = dsa_load_key(key, false);
+ if (!evpkey) {
+ RNP_LOG("Failed to load key");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ /* init context and sign */
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_verify_init(ctx) <= 0) {
+ RNP_LOG("Failed to initialize verify: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ pgp_mpi_t sigbuf;
+ if (!dsa_encode_sig(sigbuf.mpi, &sigbuf.len, *sig)) {
+ goto done;
+ }
+ if (EVP_PKEY_verify(ctx, sigbuf.mpi, sigbuf.len, hash, hash_len) <= 0) {
+ ret = RNP_ERROR_SIGNATURE_INVALID;
+ } else {
+ ret = RNP_SUCCESS;
+ }
+done:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(evpkey);
+ return ret;
+}
+
+rnp_result_t
+dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
+{
+ if ((keylen < 1024) || (keylen > 3072) || (qbits < 160) || (qbits > 256)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ const DSA * dsa = NULL;
+ EVP_PKEY * pkey = NULL;
+ EVP_PKEY * parmkey = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+
+ /* Generate DSA params */
+ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
+ if (!ctx) {
+ RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
+ return ret;
+ }
+ if (EVP_PKEY_paramgen_init(ctx) <= 0) {
+ RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx, keylen) <= 0) {
+ RNP_LOG("Failed to set key bits: %lu", ERR_peek_last_error());
+ goto done;
+ }
+#if OPENSSL_VERSION_NUMBER < 0x1010105fL
+ EVP_PKEY_CTX_ctrl(
+ ctx, EVP_PKEY_DSA, EVP_PKEY_OP_PARAMGEN, EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, qbits, NULL);
+#else
+ if (EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, qbits) <= 0) {
+ RNP_LOG("Failed to set key qbits: %lu", ERR_peek_last_error());
+ goto done;
+ }
+#endif
+ if (EVP_PKEY_paramgen(ctx, &parmkey) <= 0) {
+ RNP_LOG("Failed to generate parameters: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ EVP_PKEY_CTX_free(ctx);
+ /* Generate DSA key */
+ ctx = EVP_PKEY_CTX_new(parmkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_keygen_init(ctx) <= 0) {
+ RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
+ RNP_LOG("DSA keygen failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ dsa = EVP_PKEY_get0_DSA(pkey);
+ if (!dsa) {
+ RNP_LOG("Failed to retrieve DSA key: %lu", ERR_peek_last_error());
+ goto done;
+ }
+
+ const bignum_t *p;
+ const bignum_t *q;
+ const bignum_t *g;
+ const bignum_t *y;
+ const bignum_t *x;
+ p = DSA_get0_p(dsa);
+ q = DSA_get0_q(dsa);
+ g = DSA_get0_g(dsa);
+ y = DSA_get0_pub_key(dsa);
+ x = DSA_get0_priv_key(dsa);
+ if (!p || !q || !g || !y || !x) {
+ ret = RNP_ERROR_BAD_STATE;
+ goto done;
+ }
+ bn2mpi(p, &key->p);
+ bn2mpi(q, &key->q);
+ bn2mpi(g, &key->g);
+ bn2mpi(y, &key->y);
+ bn2mpi(x, &key->x);
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(parmkey);
+ EVP_PKEY_free(pkey);
+ return ret;
+}
+
+pgp_hash_alg_t
+dsa_get_min_hash(size_t qsize)
+{
+ /*
+ * I'm using _broken_ SHA1 here only because
+ * some old implementations may not understand keys created
+ * with other hashes. If you're sure we don't have to support
+ * such implementations, please be my guest and remove it.
+ */
+ return (qsize < 160) ? PGP_HASH_UNKNOWN :
+ (qsize == 160) ? PGP_HASH_SHA1 :
+ (qsize <= 224) ? PGP_HASH_SHA224 :
+ (qsize <= 256) ? PGP_HASH_SHA256 :
+ (qsize <= 384) ? PGP_HASH_SHA384 :
+ (qsize <= 512) ? PGP_HASH_SHA512
+ /*(qsize>512)*/ :
+ PGP_HASH_UNKNOWN;
+}
+
+size_t
+dsa_choose_qsize_by_psize(size_t psize)
+{
+ return (psize == 1024) ? 160 :
+ (psize <= 2047) ? 224 :
+ (psize <= 3072) ? DSA_MAX_Q_BITLEN :
+ 0;
+}
diff --git a/src/lib/crypto/ec.cpp b/src/lib/crypto/ec.cpp
new file mode 100644
index 0000000..144c362
--- /dev/null
+++ b/src/lib/crypto/ec.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2017, [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 HOLDERS 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 <botan/ffi.h>
+#include <string.h>
+#include <cassert>
+#include "ec.h"
+#include "types.h"
+#include "utils.h"
+#include "mem.h"
+#include "bn.h"
+
+static id_str_pair ec_algo_to_botan[] = {
+ {PGP_PKA_ECDH, "ECDH"},
+ {PGP_PKA_ECDSA, "ECDSA"},
+ {PGP_PKA_SM2, "SM2_Sig"},
+ {0, NULL},
+};
+
+rnp_result_t
+x25519_generate(rnp::RNG *rng, pgp_ec_key_t *key)
+{
+ botan_privkey_t pr_key = NULL;
+ botan_pubkey_t pu_key = NULL;
+ rnp_result_t ret = RNP_ERROR_KEY_GENERATION;
+
+ rnp::secure_array<uint8_t, 32> keyle;
+
+ if (botan_privkey_create(&pr_key, "Curve25519", "", rng->handle())) {
+ goto end;
+ }
+
+ if (botan_privkey_export_pubkey(&pu_key, pr_key)) {
+ goto end;
+ }
+
+ /* botan returns key in little-endian, while mpi is big-endian */
+ if (botan_privkey_x25519_get_privkey(pr_key, keyle.data())) {
+ goto end;
+ }
+ for (int i = 0; i < 32; i++) {
+ key->x.mpi[31 - i] = keyle[i];
+ }
+ key->x.len = 32;
+ /* botan doesn't tweak secret key bits, so we should do that here */
+ if (!x25519_tweak_bits(*key)) {
+ goto end;
+ }
+
+ if (botan_pubkey_x25519_get_pubkey(pu_key, &key->p.mpi[1])) {
+ goto end;
+ }
+ key->p.len = 33;
+ key->p.mpi[0] = 0x40;
+
+ ret = RNP_SUCCESS;
+end:
+ botan_privkey_destroy(pr_key);
+ botan_pubkey_destroy(pu_key);
+ return ret;
+}
+
+rnp_result_t
+ec_generate(rnp::RNG * rng,
+ pgp_ec_key_t * key,
+ const pgp_pubkey_alg_t alg_id,
+ const pgp_curve_t curve)
+{
+ /**
+ * Keeps "0x04 || x || y"
+ * \see 13.2. ECDSA, ECDH, SM2 Conversion Primitives
+ *
+ * P-521 is biggest supported curve
+ */
+ botan_privkey_t pr_key = NULL;
+ botan_pubkey_t pu_key = NULL;
+ bignum_t * px = NULL;
+ bignum_t * py = NULL;
+ bignum_t * x = NULL;
+ rnp_result_t ret = RNP_ERROR_KEY_GENERATION;
+ size_t filed_byte_size = 0;
+
+ if (!alg_allows_curve(alg_id, curve)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ const char *ec_algo = id_str_pair::lookup(ec_algo_to_botan, alg_id, NULL);
+ assert(ec_algo);
+ const ec_curve_desc_t *ec_desc = get_curve_desc(curve);
+ if (!ec_desc) {
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ goto end;
+ }
+ filed_byte_size = BITS_TO_BYTES(ec_desc->bitlen);
+
+ // at this point it must succeed
+ if (botan_privkey_create(&pr_key, ec_algo, ec_desc->botan_name, rng->handle())) {
+ goto end;
+ }
+
+ if (botan_privkey_export_pubkey(&pu_key, pr_key)) {
+ goto end;
+ }
+
+ // Crash if seckey is null. It's clean and easy to debug design
+ px = bn_new();
+ py = bn_new();
+ x = bn_new();
+
+ if (!px || !py || !x) {
+ RNP_LOG("Allocation failed");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ if (botan_pubkey_get_field(BN_HANDLE_PTR(px), pu_key, "public_x")) {
+ goto end;
+ }
+
+ if (botan_pubkey_get_field(BN_HANDLE_PTR(py), pu_key, "public_y")) {
+ goto end;
+ }
+
+ if (botan_privkey_get_field(BN_HANDLE_PTR(x), pr_key, "x")) {
+ goto end;
+ }
+
+ size_t x_bytes;
+ size_t y_bytes;
+ x_bytes = bn_num_bytes(*px);
+ y_bytes = bn_num_bytes(*py);
+
+ // Safety check
+ if ((x_bytes > filed_byte_size) || (y_bytes > filed_byte_size)) {
+ RNP_LOG("Key generation failed");
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ goto end;
+ }
+
+ /*
+ * Convert coordinates to MPI stored as
+ * "0x04 || x || y"
+ *
+ * \see 13.2. ECDSA and ECDH Conversion Primitives
+ *
+ * Note: Generated pk/sk may not always have exact number of bytes
+ * which is important when converting to octet-string
+ */
+ memset(key->p.mpi, 0, sizeof(key->p.mpi));
+ key->p.mpi[0] = 0x04;
+ bn_bn2bin(px, &key->p.mpi[1 + filed_byte_size - x_bytes]);
+ bn_bn2bin(py, &key->p.mpi[1 + filed_byte_size + (filed_byte_size - y_bytes)]);
+ key->p.len = 2 * filed_byte_size + 1;
+ /* secret key value */
+ bn2mpi(x, &key->x);
+ ret = RNP_SUCCESS;
+end:
+ botan_privkey_destroy(pr_key);
+ botan_pubkey_destroy(pu_key);
+ bn_free(px);
+ bn_free(py);
+ bn_free(x);
+ return ret;
+}
diff --git a/src/lib/crypto/ec.h b/src/lib/crypto/ec.h
new file mode 100644
index 0000000..07cb8e8
--- /dev/null
+++ b/src/lib/crypto/ec.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2017-2020 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * This code is originally derived from software contributed to
+ * The NetBSD Foundation by Alistair Crooks (agc@netbsd.org), and
+ * carried further by Ribose Inc (https://www.ribose.com).
+ *
+ * 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 HOLDERS 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.
+ */
+#ifndef EC_H_
+#define EC_H_
+
+#include "config.h"
+#include <rnp/rnp_def.h>
+#include <repgp/repgp_def.h>
+#include "crypto/rng.h"
+#include "crypto/mpi.h"
+
+#define MAX_CURVE_BIT_SIZE 521 // secp521r1
+/* Maximal byte size of elliptic curve order (NIST P-521) */
+#define MAX_CURVE_BYTELEN ((MAX_CURVE_BIT_SIZE + 7) / 8)
+
+/**
+ * Maximal length of the OID in hex representation.
+ *
+ * \see RFC4880 bis01 - 9.2 ECC Curve OID
+ */
+#define MAX_CURVE_OID_HEX_LEN 10U
+
+/**
+ * Structure holds description of elliptic curve
+ */
+typedef struct ec_curve_desc_t {
+ const pgp_curve_t rnp_curve_id;
+ const size_t bitlen;
+ const uint8_t OIDhex[MAX_CURVE_OID_HEX_LEN];
+ const size_t OIDhex_len;
+#if defined(CRYPTO_BACKEND_BOTAN)
+ const char *botan_name;
+#endif
+#if defined(CRYPTO_BACKEND_OPENSSL)
+ const char *openssl_name;
+#endif
+ const char *pgp_name;
+ /* Curve is supported for keygen/sign/encrypt operations */
+ bool supported;
+ /* Curve parameters below. Needed for grip calculation */
+ const char *p;
+ const char *a;
+ const char *b;
+ const char *n;
+ const char *gx;
+ const char *gy;
+ const char *h;
+} ec_curve_desc_t;
+
+typedef struct pgp_ec_key_t {
+ pgp_curve_t curve;
+ pgp_mpi_t p;
+ /* secret mpi */
+ pgp_mpi_t x;
+ /* ecdh params */
+ pgp_hash_alg_t kdf_hash_alg; /* Hash used by kdf */
+ pgp_symm_alg_t key_wrap_alg; /* Symmetric algorithm used to wrap KEK*/
+} pgp_ec_key_t;
+
+typedef struct pgp_ec_signature_t {
+ pgp_mpi_t r;
+ pgp_mpi_t s;
+} pgp_ec_signature_t;
+
+/*
+ * @brief Finds curve ID by hex representation of OID
+ *
+ * @param oid buffer with OID in hex
+ * @param oid_len length of oid buffer
+ *
+ * @returns success curve ID
+ * failure PGP_CURVE_MAX is returned
+ *
+ * @remarks see RFC 4880 bis 01 - 9.2 ECC Curve OID
+ */
+pgp_curve_t find_curve_by_OID(const uint8_t *oid, size_t oid_len);
+
+pgp_curve_t find_curve_by_name(const char *name);
+
+/*
+ * @brief Returns pointer to the curve descriptor
+ *
+ * @param Valid curve ID
+ *
+ * @returns NULL if wrong ID provided, otherwise descriptor
+ *
+ */
+const ec_curve_desc_t *get_curve_desc(const pgp_curve_t curve_id);
+
+bool alg_allows_curve(pgp_pubkey_alg_t alg, pgp_curve_t curve);
+
+/**
+ * @brief Check whether curve is supported for operations.
+ * All available curves are supported for reading/parsing key data, however some of them
+ * may be disabled for use, i.e. for key generation/signing/encryption.
+ */
+bool curve_supported(pgp_curve_t curve);
+
+/*
+ * @brief Generates EC key in uncompressed format
+ *
+ * @param rng initialized rnp::RNG context*
+ * @param key key data to be generated
+ * @param alg_id ID of EC algorithm
+ * @param curve underlying ECC curve ID
+ *
+ * @pre alg_id MUST be supported algorithm
+ *
+ * @returns RNP_ERROR_BAD_PARAMETERS unknown curve_id
+ * @returns RNP_ERROR_OUT_OF_MEMORY memory allocation failed
+ * @returns RNP_ERROR_KEY_GENERATION implementation error
+ */
+rnp_result_t ec_generate(rnp::RNG * rng,
+ pgp_ec_key_t * key,
+ const pgp_pubkey_alg_t alg_id,
+ const pgp_curve_t curve);
+
+/*
+ * @brief Generates x25519 ECDH key in x25519-specific format
+ *
+ * @param rng initialized rnp::RNG context*
+ * @param key key data to be generated
+ *
+ * @returns RNP_ERROR_KEY_GENERATION implementation error
+ */
+rnp_result_t x25519_generate(rnp::RNG *rng, pgp_ec_key_t *key);
+
+/**
+ * @brief Set least significant/most significant bits of the 25519 secret key as per
+ * specification.
+ *
+ * @param key secret key.
+ * @return true on success or false otherwise.
+ */
+bool x25519_tweak_bits(pgp_ec_key_t &key);
+
+/**
+ * @brief Check whether least significant/most significant bits of 25519 secret key are
+ * correctly tweaked.
+ *
+ * @param key secret key.
+ * @return true if bits are set correctly, and false otherwise.
+ */
+bool x25519_bits_tweaked(const pgp_ec_key_t &key);
+
+#endif
diff --git a/src/lib/crypto/ec_curves.cpp b/src/lib/crypto/ec_curves.cpp
new file mode 100644
index 0000000..db5cf09
--- /dev/null
+++ b/src/lib/crypto/ec_curves.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 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 HOLDERS 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 <string.h>
+#include "ec.h"
+#include "types.h"
+#include "utils.h"
+#include "str-utils.h"
+
+/**
+ * EC Curves definition used by implementation
+ *
+ * \see RFC4880 bis01 - 9.2. ECC Curve OID
+ *
+ * Order of the elements in this array corresponds to
+ * values in pgp_curve_t enum.
+ */
+static const ec_curve_desc_t ec_curves[] = {
+ {PGP_CURVE_UNKNOWN, 0, {0}, 0, NULL, NULL},
+
+ {PGP_CURVE_NIST_P_256,
+ 256,
+ {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07},
+ 8,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "secp256r1",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "prime256v1",
+#endif
+ "NIST P-256",
+ true,
+ "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
+ "0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
+ "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
+ "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
+ "0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
+ "0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
+ "0x01"},
+ {PGP_CURVE_NIST_P_384,
+ 384,
+ {0x2B, 0x81, 0x04, 0x00, 0x22},
+ 5,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "secp384r1",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "secp384r1",
+#endif
+ "NIST P-384",
+ true,
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000"
+ "ffffffff",
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000"
+ "fffffffc",
+ "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8ed"
+ "d3ec2aef",
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196a"
+ "ccc52973",
+ "0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e38"
+ "72760ab7",
+ "0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c"
+ "90ea0e5f",
+ "0x01"},
+ {PGP_CURVE_NIST_P_521,
+ 521,
+ {0x2B, 0x81, 0x04, 0x00, 0x23},
+ 5,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "secp521r1",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "secp521r1",
+#endif
+ "NIST P-521",
+ true,
+ "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffff",
+ "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffc",
+ "0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652"
+ "c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
+ "0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc"
+ "0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
+ "0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1d"
+ "c127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
+ "0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550"
+ "b9013fad0761353c7086a272c24088be94769fd16650",
+ "0x01"},
+ {PGP_CURVE_ED25519,
+ 255,
+ {0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01},
+ 9,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "Ed25519",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "ED25519",
+#endif
+ "Ed25519",
+ true,
+ "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
+ /* two below are actually negative */
+ "0x01",
+ "0x2dfc9311d490018c7338bf8688861767ff8ff5b2bebe27548a14b235eca6874a",
+ "0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed",
+ "0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a",
+ "0x6666666666666666666666666666666666666666666666666666666666666658",
+ "0x08"},
+ {PGP_CURVE_25519,
+ 255,
+ {0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01},
+ 10,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "curve25519",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "X25519",
+#endif
+ "Curve25519",
+ true,
+ "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
+ "0x01db41",
+ "0x01",
+ "0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed",
+ "0x0000000000000000000000000000000000000000000000000000000000000009",
+ "0x20ae19a1b8a086b4e01edd2c7748d14c923d4d7e6d7c61b229e9c5a27eced3d9",
+ "0x08"},
+ {PGP_CURVE_BP256,
+ 256,
+ {0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07},
+ 9,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "brainpool256r1",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "brainpoolP256r1",
+#endif
+ "brainpoolP256r1",
+#if defined(ENABLE_BRAINPOOL)
+ true,
+#else
+ false,
+#endif
+ "0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
+ "0x7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9",
+ "0x26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6",
+ "0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
+ "0x8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262",
+ "0x547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997",
+ "0x01"},
+ {PGP_CURVE_BP384,
+ 384,
+ {0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B},
+ 9,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "brainpool384r1",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "brainpoolP384r1",
+#endif
+ "brainpoolP384r1",
+#if defined(ENABLE_BRAINPOOL)
+ true,
+#else
+ false,
+#endif
+ "0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a7187470013"
+ "3107ec53",
+ "0x7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd"
+ "22ce2826",
+ "0x04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696"
+ "fa504c11",
+ "0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202"
+ "e9046565",
+ "0x1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e2"
+ "47d4af1e",
+ "0x8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341"
+ "263c5315",
+ "0x01"},
+ {PGP_CURVE_BP512,
+ 512,
+ {0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D},
+ 9,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "brainpool512r1",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "brainpoolP512r1",
+#endif
+ "brainpoolP512r1",
+#if defined(ENABLE_BRAINPOOL)
+ true,
+#else
+ false,
+#endif
+ "0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12a"
+ "e6a380e62881ff2f2d82c68528aa6056583a48f3",
+ "0x7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c9"
+ "8b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca",
+ "0x3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca"
+ "dc083e67984050b75ebae5dd2809bd638016f723",
+ "0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca9261941866119"
+ "7fac10471db1d381085ddaddb58796829ca90069",
+ "0x81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b"
+ "93b97d5f7c6d5047406a5e688b352209bcb9f822",
+ "0x7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd8"
+ "8a2763aed1ca2b2fa8f0540678cd1e0f3ad80892",
+ "0x01"},
+ {PGP_CURVE_P256K1,
+ 256,
+ {0x2B, 0x81, 0x04, 0x00, 0x0A},
+ 5,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "secp256k1",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "secp256k1",
+#endif
+ "secp256k1",
+ true,
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000007",
+ "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
+ "0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+ "0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
+ "0x01"},
+ {
+ PGP_CURVE_SM2_P_256,
+ 256,
+ {0x2A, 0x81, 0x1C, 0xCF, 0x55, 0x01, 0x82, 0x2D},
+ 8,
+#if defined(CRYPTO_BACKEND_BOTAN)
+ "sm2p256v1",
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ "sm2",
+#endif
+ "SM2 P-256",
+#if defined(ENABLE_SM2)
+ true,
+#else
+ false,
+#endif
+ "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
+ "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
+ "0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
+ "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
+ "0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
+ "0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0",
+ },
+};
+
+pgp_curve_t
+find_curve_by_OID(const uint8_t *oid, size_t oid_len)
+{
+ for (size_t i = 0; i < PGP_CURVE_MAX; i++) {
+ if ((oid_len == ec_curves[i].OIDhex_len) &&
+ (!memcmp(oid, ec_curves[i].OIDhex, oid_len))) {
+ return static_cast<pgp_curve_t>(i);
+ }
+ }
+
+ return PGP_CURVE_MAX;
+}
+
+pgp_curve_t
+find_curve_by_name(const char *name)
+{
+ for (size_t i = 1; i < PGP_CURVE_MAX; i++) {
+ if (rnp::str_case_eq(ec_curves[i].pgp_name, name)) {
+ return ec_curves[i].rnp_curve_id;
+ }
+ }
+
+ return PGP_CURVE_MAX;
+}
+
+const ec_curve_desc_t *
+get_curve_desc(const pgp_curve_t curve_id)
+{
+ return (curve_id < PGP_CURVE_MAX && curve_id > 0) ? &ec_curves[curve_id] : NULL;
+}
+
+bool
+alg_allows_curve(pgp_pubkey_alg_t alg, pgp_curve_t curve)
+{
+ /* SM2 curve is only for SM2 algo */
+ if ((alg == PGP_PKA_SM2) || (curve == PGP_CURVE_SM2_P_256)) {
+ return (alg == PGP_PKA_SM2) && (curve == PGP_CURVE_SM2_P_256);
+ }
+ /* EDDSA and PGP_CURVE_ED25519 */
+ if ((alg == PGP_PKA_EDDSA) || (curve == PGP_CURVE_ED25519)) {
+ return (alg == PGP_PKA_EDDSA) && (curve == PGP_CURVE_ED25519);
+ }
+ /* Curve x25519 is only for ECDH */
+ if (curve == PGP_CURVE_25519) {
+ return alg == PGP_PKA_ECDH;
+ }
+ /* Other curves are good for both ECDH and ECDSA */
+ return true;
+}
+
+bool
+curve_supported(pgp_curve_t curve)
+{
+ const ec_curve_desc_t *info = get_curve_desc(curve);
+ return info && info->supported;
+}
diff --git a/src/lib/crypto/ec_ossl.cpp b/src/lib/crypto/ec_ossl.cpp
new file mode 100644
index 0000000..6974b4c
--- /dev/null
+++ b/src/lib/crypto/ec_ossl.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 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 <string>
+#include <cassert>
+#include "ec.h"
+#include "ec_ossl.h"
+#include "bn.h"
+#include "types.h"
+#include "mem.h"
+#include "utils.h"
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/err.h>
+#include <openssl/ec.h>
+
+static bool
+ec_is_raw_key(const pgp_curve_t curve)
+{
+ return (curve == PGP_CURVE_ED25519) || (curve == PGP_CURVE_25519);
+}
+
+rnp_result_t
+x25519_generate(rnp::RNG *rng, pgp_ec_key_t *key)
+{
+ return ec_generate(rng, key, PGP_PKA_ECDH, PGP_CURVE_25519);
+}
+
+EVP_PKEY *
+ec_generate_pkey(const pgp_pubkey_alg_t alg_id, const pgp_curve_t curve)
+{
+ if (!alg_allows_curve(alg_id, curve)) {
+ return NULL;
+ }
+ const ec_curve_desc_t *ec_desc = get_curve_desc(curve);
+ if (!ec_desc) {
+ return NULL;
+ }
+ int nid = OBJ_sn2nid(ec_desc->openssl_name);
+ if (nid == NID_undef) {
+ RNP_LOG("Unknown SN: %s", ec_desc->openssl_name);
+ return NULL;
+ }
+ bool raw = ec_is_raw_key(curve);
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(raw ? nid : EVP_PKEY_EC, NULL);
+ if (!ctx) {
+ RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
+ return NULL;
+ }
+ EVP_PKEY *pkey = NULL;
+ if (EVP_PKEY_keygen_init(ctx) <= 0) {
+ RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (!raw && (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) <= 0)) {
+ RNP_LOG("Failed to set curve nid: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
+ RNP_LOG("EC keygen failed: %lu", ERR_peek_last_error());
+ }
+done:
+ EVP_PKEY_CTX_free(ctx);
+ return pkey;
+}
+
+static bool
+ec_write_raw_seckey(EVP_PKEY *pkey, pgp_ec_key_t *key)
+{
+ /* EdDSA and X25519 keys are saved in a different way */
+ static_assert(sizeof(key->x.mpi) > 32, "mpi is too small.");
+ key->x.len = sizeof(key->x.mpi);
+ if (EVP_PKEY_get_raw_private_key(pkey, key->x.mpi, &key->x.len) <= 0) {
+ RNP_LOG("Failed get raw private key: %lu", ERR_peek_last_error());
+ return false;
+ }
+ assert(key->x.len == 32);
+ if (EVP_PKEY_id(pkey) == EVP_PKEY_X25519) {
+ /* in OpenSSL private key is exported as little-endian, while MPI is big-endian */
+ for (size_t i = 0; i < 16; i++) {
+ std::swap(key->x.mpi[i], key->x.mpi[31 - i]);
+ }
+ }
+ return true;
+}
+
+rnp_result_t
+ec_generate(rnp::RNG * rng,
+ pgp_ec_key_t * key,
+ const pgp_pubkey_alg_t alg_id,
+ const pgp_curve_t curve)
+{
+ EVP_PKEY *pkey = ec_generate_pkey(alg_id, curve);
+ if (!pkey) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ if (ec_is_raw_key(curve)) {
+ if (ec_write_pubkey(pkey, key->p, curve) && ec_write_raw_seckey(pkey, key)) {
+ ret = RNP_SUCCESS;
+ }
+ EVP_PKEY_free(pkey);
+ return ret;
+ }
+ const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+ if (!ec) {
+ RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (!ec_write_pubkey(pkey, key->p, curve)) {
+ RNP_LOG("Failed to write pubkey.");
+ goto done;
+ }
+ const bignum_t *x;
+ x = EC_KEY_get0_private_key(ec);
+ if (!x) {
+ ret = RNP_ERROR_BAD_STATE;
+ goto done;
+ }
+ if (bn2mpi(x, &key->x)) {
+ ret = RNP_SUCCESS;
+ }
+done:
+ EVP_PKEY_free(pkey);
+ return ret;
+}
+
+static EVP_PKEY *
+ec_load_raw_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, int nid)
+{
+ if (!keyx) {
+ /* as per RFC, EdDSA & 25519 keys must use 0x40 byte for encoding */
+ if ((mpi_bytes(&keyp) != 33) || (keyp.mpi[0] != 0x40)) {
+ RNP_LOG("Invalid 25519 public key.");
+ return NULL;
+ }
+
+ EVP_PKEY *evpkey =
+ EVP_PKEY_new_raw_public_key(nid, NULL, &keyp.mpi[1], mpi_bytes(&keyp) - 1);
+ if (!evpkey) {
+ RNP_LOG("Failed to load public key: %lu", ERR_peek_last_error());
+ }
+ return evpkey;
+ }
+
+ EVP_PKEY *evpkey = NULL;
+ if (nid == EVP_PKEY_X25519) {
+ if (keyx->len != 32) {
+ RNP_LOG("Invalid 25519 secret key");
+ return NULL;
+ }
+ /* need to reverse byte order since in mpi we have big-endian */
+ rnp::secure_array<uint8_t, 32> prkey;
+ for (int i = 0; i < 32; i++) {
+ prkey[i] = keyx->mpi[31 - i];
+ }
+ evpkey = EVP_PKEY_new_raw_private_key(nid, NULL, prkey.data(), keyx->len);
+ } else {
+ if (keyx->len > 32) {
+ RNP_LOG("Invalid Ed25519 secret key");
+ return NULL;
+ }
+ /* keyx->len may be smaller then 32 as high byte is random and could become 0 */
+ rnp::secure_array<uint8_t, 32> prkey{};
+ memcpy(prkey.data() + 32 - keyx->len, keyx->mpi, keyx->len);
+ evpkey = EVP_PKEY_new_raw_private_key(nid, NULL, prkey.data(), 32);
+ }
+ if (!evpkey) {
+ RNP_LOG("Failed to load private key: %lu", ERR_peek_last_error());
+ }
+ return evpkey;
+}
+
+EVP_PKEY *
+ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve)
+{
+ const ec_curve_desc_t *curv_desc = get_curve_desc(curve);
+ if (!curv_desc) {
+ RNP_LOG("unknown curve");
+ return NULL;
+ }
+ if (!curve_supported(curve)) {
+ RNP_LOG("Curve %s is not supported.", curv_desc->pgp_name);
+ return NULL;
+ }
+ int nid = OBJ_sn2nid(curv_desc->openssl_name);
+ if (nid == NID_undef) {
+ RNP_LOG("Unknown SN: %s", curv_desc->openssl_name);
+ return NULL;
+ }
+ /* EdDSA and X25519 keys are loaded in a different way */
+ if (ec_is_raw_key(curve)) {
+ return ec_load_raw_key(keyp, keyx, nid);
+ }
+ EC_KEY *ec = EC_KEY_new_by_curve_name(nid);
+ if (!ec) {
+ RNP_LOG("Failed to create EC key with group %d (%s): %s",
+ nid,
+ curv_desc->openssl_name,
+ ERR_reason_error_string(ERR_peek_last_error()));
+ return NULL;
+ }
+
+ bool res = false;
+ bignum_t *x = NULL;
+ EVP_PKEY *pkey = NULL;
+ EC_POINT *p = EC_POINT_new(EC_KEY_get0_group(ec));
+ if (!p) {
+ RNP_LOG("Failed to allocate point: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EC_POINT_oct2point(EC_KEY_get0_group(ec), p, keyp.mpi, keyp.len, NULL) <= 0) {
+ RNP_LOG("Failed to decode point: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EC_KEY_set_public_key(ec, p) <= 0) {
+ RNP_LOG("Failed to set public key: %lu", ERR_peek_last_error());
+ goto done;
+ }
+
+ pkey = EVP_PKEY_new();
+ if (!pkey) {
+ RNP_LOG("EVP_PKEY allocation failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (!keyx) {
+ res = true;
+ goto done;
+ }
+
+ x = mpi2bn(keyx);
+ if (!x) {
+ RNP_LOG("allocation failed");
+ goto done;
+ }
+ if (EC_KEY_set_private_key(ec, x) <= 0) {
+ RNP_LOG("Failed to set secret key: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ res = true;
+done:
+ if (res) {
+ res = EVP_PKEY_set1_EC_KEY(pkey, ec) > 0;
+ }
+ EC_POINT_free(p);
+ BN_free(x);
+ EC_KEY_free(ec);
+ if (!res) {
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+ return pkey;
+}
+
+rnp_result_t
+ec_validate_key(const pgp_ec_key_t &key, bool secret)
+{
+ if (key.curve == PGP_CURVE_25519) {
+ /* No key check implementation for x25519 in the OpenSSL yet, so just basic size checks
+ */
+ if ((mpi_bytes(&key.p) != 33) || (key.p.mpi[0] != 0x40)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ if (secret && mpi_bytes(&key.x) != 32) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ return RNP_SUCCESS;
+ }
+ EVP_PKEY *evpkey = ec_load_key(key.p, secret ? &key.x : NULL, key.curve);
+ if (!evpkey) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ int res;
+ res = secret ? EVP_PKEY_check(ctx) : EVP_PKEY_public_check(ctx);
+ if (res < 0) {
+ auto err = ERR_peek_last_error();
+ RNP_LOG("EC key check failed: %lu (%s)", err, ERR_reason_error_string(err));
+ }
+ if (res > 0) {
+ ret = RNP_SUCCESS;
+ }
+done:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(evpkey);
+ return ret;
+}
+
+bool
+ec_write_pubkey(EVP_PKEY *pkey, pgp_mpi_t &mpi, pgp_curve_t curve)
+{
+ if (ec_is_raw_key(curve)) {
+ /* EdDSA and X25519 keys are saved in a different way */
+ mpi.len = sizeof(mpi.mpi) - 1;
+ if (EVP_PKEY_get_raw_public_key(pkey, &mpi.mpi[1], &mpi.len) <= 0) {
+ RNP_LOG("Failed get raw public key: %lu", ERR_peek_last_error());
+ return false;
+ }
+ assert(mpi.len == 32);
+ mpi.mpi[0] = 0x40;
+ mpi.len++;
+ return true;
+ }
+ const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+ if (!ec) {
+ RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error());
+ return false;
+ }
+ const EC_POINT *p = EC_KEY_get0_public_key(ec);
+ if (!p) {
+ RNP_LOG("Null point: %lu", ERR_peek_last_error());
+ return false;
+ }
+ /* call below adds leading zeroes if needed */
+ mpi.len = EC_POINT_point2oct(
+ EC_KEY_get0_group(ec), p, POINT_CONVERSION_UNCOMPRESSED, mpi.mpi, sizeof(mpi.mpi), NULL);
+ if (!mpi.len) {
+ RNP_LOG("Failed to encode public key: %lu", ERR_peek_last_error());
+ }
+ return mpi.len;
+}
diff --git a/src/lib/crypto/ec_ossl.h b/src/lib/crypto/ec_ossl.h
new file mode 100644
index 0000000..f16afe8
--- /dev/null
+++ b/src/lib/crypto/ec_ossl.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef EC_OSSL_H_
+#define EC_OSSL_H_
+
+#include "types.h"
+#include "ec.h"
+#include <openssl/evp.h>
+
+EVP_PKEY *ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve);
+
+rnp_result_t ec_validate_key(const pgp_ec_key_t &key, bool secret);
+
+EVP_PKEY *ec_generate_pkey(const pgp_pubkey_alg_t alg_id, const pgp_curve_t curve);
+
+bool ec_write_pubkey(EVP_PKEY *key, pgp_mpi_t &mpi, pgp_curve_t curve);
+
+#endif
diff --git a/src/lib/crypto/ecdh.cpp b/src/lib/crypto/ecdh.cpp
new file mode 100644
index 0000000..d4411c3
--- /dev/null
+++ b/src/lib/crypto/ecdh.cpp
@@ -0,0 +1,377 @@
+/*-
+ * Copyright (c) 2017-2022 Ribose Inc.
+ * 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 HOLDERS 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 <string.h>
+#include <botan/ffi.h>
+#include "hash_botan.hpp"
+#include "ecdh.h"
+#include "ecdh_utils.h"
+#include "symmetric.h"
+#include "types.h"
+#include "utils.h"
+#include "mem.h"
+#include "bn.h"
+
+// Produces kek of size kek_len which corresponds to length of wrapping key
+static bool
+compute_kek(uint8_t * kek,
+ size_t kek_len,
+ const uint8_t * other_info,
+ size_t other_info_size,
+ const ec_curve_desc_t *curve_desc,
+ const pgp_mpi_t * ec_pubkey,
+ const botan_privkey_t ec_prvkey,
+ const pgp_hash_alg_t hash_alg)
+{
+ const uint8_t *p = ec_pubkey->mpi;
+ uint8_t p_len = ec_pubkey->len;
+
+ if (curve_desc->rnp_curve_id == PGP_CURVE_25519) {
+ if ((p_len != 33) || (p[0] != 0x40)) {
+ return false;
+ }
+ p++;
+ p_len--;
+ }
+
+ rnp::secure_array<uint8_t, MAX_CURVE_BYTELEN * 2 + 1> s;
+
+ botan_pk_op_ka_t op_key_agreement = NULL;
+ bool ret = false;
+ char kdf_name[32] = {0};
+ size_t s_len = s.size();
+
+ if (botan_pk_op_key_agreement_create(&op_key_agreement, ec_prvkey, "Raw", 0) ||
+ botan_pk_op_key_agreement(op_key_agreement, s.data(), &s_len, p, p_len, NULL, 0)) {
+ goto end;
+ }
+
+ snprintf(
+ kdf_name, sizeof(kdf_name), "SP800-56A(%s)", rnp::Hash_Botan::name_backend(hash_alg));
+ ret = !botan_kdf(
+ kdf_name, kek, kek_len, s.data(), s_len, NULL, 0, other_info, other_info_size);
+end:
+ return ret && !botan_pk_op_key_agreement_destroy(op_key_agreement);
+}
+
+static bool
+ecdh_load_public_key(botan_pubkey_t *pubkey, const pgp_ec_key_t *key)
+{
+ bool res = false;
+
+ const ec_curve_desc_t *curve = get_curve_desc(key->curve);
+ if (!curve) {
+ RNP_LOG("unknown curve");
+ return false;
+ }
+
+ if (curve->rnp_curve_id == PGP_CURVE_25519) {
+ if ((key->p.len != 33) || (key->p.mpi[0] != 0x40)) {
+ return false;
+ }
+ rnp::secure_array<uint8_t, 32> pkey;
+ memcpy(pkey.data(), key->p.mpi + 1, 32);
+ return !botan_pubkey_load_x25519(pubkey, pkey.data());
+ }
+
+ if (!mpi_bytes(&key->p) || (key->p.mpi[0] != 0x04)) {
+ RNP_LOG("Failed to load public key");
+ return false;
+ }
+
+ botan_mp_t px = NULL;
+ botan_mp_t py = NULL;
+ const size_t curve_order = BITS_TO_BYTES(curve->bitlen);
+
+ if (botan_mp_init(&px) || botan_mp_init(&py) ||
+ botan_mp_from_bin(px, &key->p.mpi[1], curve_order) ||
+ botan_mp_from_bin(py, &key->p.mpi[1 + curve_order], curve_order)) {
+ goto end;
+ }
+
+ if (!(res = !botan_pubkey_load_ecdh(pubkey, px, py, curve->botan_name))) {
+ RNP_LOG("failed to load ecdh public key");
+ }
+end:
+ botan_mp_destroy(px);
+ botan_mp_destroy(py);
+ return res;
+}
+
+static bool
+ecdh_load_secret_key(botan_privkey_t *seckey, const pgp_ec_key_t *key)
+{
+ const ec_curve_desc_t *curve = get_curve_desc(key->curve);
+
+ if (!curve) {
+ return false;
+ }
+
+ if (curve->rnp_curve_id == PGP_CURVE_25519) {
+ if (key->x.len != 32) {
+ RNP_LOG("wrong x25519 key");
+ return false;
+ }
+ /* need to reverse byte order since in mpi we have big-endian */
+ rnp::secure_array<uint8_t, 32> prkey;
+ for (int i = 0; i < 32; i++) {
+ prkey[i] = key->x.mpi[31 - i];
+ }
+ return !botan_privkey_load_x25519(seckey, prkey.data());
+ }
+
+ bignum_t *x = NULL;
+ if (!(x = mpi2bn(&key->x))) {
+ return false;
+ }
+ bool res = !botan_privkey_load_ecdh(seckey, BN_HANDLE_PTR(x), curve->botan_name);
+ bn_free(x);
+ return res;
+}
+
+rnp_result_t
+ecdh_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret)
+{
+ botan_pubkey_t bpkey = NULL;
+ botan_privkey_t bskey = NULL;
+ rnp_result_t ret = RNP_ERROR_BAD_PARAMETERS;
+
+ const ec_curve_desc_t *curve_desc = get_curve_desc(key->curve);
+ if (!curve_desc) {
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+
+ if (!ecdh_load_public_key(&bpkey, key) ||
+ botan_pubkey_check_key(bpkey, rng->handle(), 0)) {
+ goto done;
+ }
+ if (!secret) {
+ ret = RNP_SUCCESS;
+ goto done;
+ }
+
+ if (!ecdh_load_secret_key(&bskey, key) ||
+ botan_privkey_check_key(bskey, rng->handle(), 0)) {
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ botan_privkey_destroy(bskey);
+ botan_pubkey_destroy(bpkey);
+ return ret;
+}
+
+rnp_result_t
+ecdh_encrypt_pkcs5(rnp::RNG * rng,
+ pgp_ecdh_encrypted_t * out,
+ const uint8_t *const in,
+ size_t in_len,
+ const pgp_ec_key_t * key,
+ const pgp_fingerprint_t &fingerprint)
+{
+ botan_privkey_t eph_prv_key = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ uint8_t other_info[MAX_SP800_56A_OTHER_INFO];
+ uint8_t kek[32] = {0}; // Size of SHA-256 or smaller
+ // 'm' is padded to the 8-byte granularity
+ uint8_t m[MAX_SESSION_KEY_SIZE];
+ const size_t m_padded_len = ((in_len / 8) + 1) * 8;
+
+ if (!key || !out || !in || (in_len > sizeof(m))) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+#if !defined(ENABLE_SM2)
+ if (key->curve == PGP_CURVE_SM2_P_256) {
+ RNP_LOG("SM2 curve support is disabled.");
+ return RNP_ERROR_NOT_IMPLEMENTED;
+ }
+#endif
+ const ec_curve_desc_t *curve_desc = get_curve_desc(key->curve);
+ if (!curve_desc) {
+ RNP_LOG("unsupported curve");
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+
+ // +8 because of AES-wrap adds 8 bytes
+ if (ECDH_WRAPPED_KEY_SIZE < (m_padded_len + 8)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ // See 13.5 of RFC 4880 for definition of other_info_size
+ const size_t other_info_size = curve_desc->OIDhex_len + 46;
+ const size_t kek_len = pgp_key_size(key->key_wrap_alg);
+ size_t tmp_len = kdf_other_info_serialize(
+ other_info, curve_desc, fingerprint, key->kdf_hash_alg, key->key_wrap_alg);
+
+ if (tmp_len != other_info_size) {
+ RNP_LOG("Serialization of other info failed");
+ return RNP_ERROR_GENERIC;
+ }
+
+ if (!strcmp(curve_desc->botan_name, "curve25519")) {
+ if (botan_privkey_create(&eph_prv_key, "Curve25519", "", rng->handle())) {
+ goto end;
+ }
+ } else {
+ if (botan_privkey_create(
+ &eph_prv_key, "ECDH", curve_desc->botan_name, rng->handle())) {
+ goto end;
+ }
+ }
+
+ if (!compute_kek(kek,
+ kek_len,
+ other_info,
+ other_info_size,
+ curve_desc,
+ &key->p,
+ eph_prv_key,
+ key->kdf_hash_alg)) {
+ RNP_LOG("KEK computation failed");
+ goto end;
+ }
+
+ memcpy(m, in, in_len);
+ if (!pad_pkcs7(m, m_padded_len, in_len)) {
+ // Should never happen
+ goto end;
+ }
+
+ out->mlen = sizeof(out->m);
+ if (botan_key_wrap3394(m, m_padded_len, kek, kek_len, out->m, &out->mlen)) {
+ goto end;
+ }
+
+ /* we need to prepend 0x40 for the x25519 */
+ if (key->curve == PGP_CURVE_25519) {
+ out->p.len = sizeof(out->p.mpi) - 1;
+ if (botan_pk_op_key_agreement_export_public(
+ eph_prv_key, out->p.mpi + 1, &out->p.len)) {
+ goto end;
+ }
+ out->p.mpi[0] = 0x40;
+ out->p.len++;
+ } else {
+ out->p.len = sizeof(out->p.mpi);
+ if (botan_pk_op_key_agreement_export_public(eph_prv_key, out->p.mpi, &out->p.len)) {
+ goto end;
+ }
+ }
+
+ // All OK
+ ret = RNP_SUCCESS;
+end:
+ botan_privkey_destroy(eph_prv_key);
+ return ret;
+}
+
+rnp_result_t
+ecdh_decrypt_pkcs5(uint8_t * out,
+ size_t * out_len,
+ const pgp_ecdh_encrypted_t *in,
+ const pgp_ec_key_t * key,
+ const pgp_fingerprint_t & fingerprint)
+{
+ if (!out_len || !in || !key || !mpi_bytes(&key->x)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ const ec_curve_desc_t *curve_desc = get_curve_desc(key->curve);
+ if (!curve_desc) {
+ RNP_LOG("unknown curve");
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+
+ const pgp_symm_alg_t wrap_alg = key->key_wrap_alg;
+ const pgp_hash_alg_t kdf_hash = key->kdf_hash_alg;
+ /* Ensure that AES is used for wrapping */
+ if ((wrap_alg != PGP_SA_AES_128) && (wrap_alg != PGP_SA_AES_192) &&
+ (wrap_alg != PGP_SA_AES_256)) {
+ RNP_LOG("non-aes wrap algorithm");
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+
+ // See 13.5 of RFC 4880 for definition of other_info_size
+ uint8_t other_info[MAX_SP800_56A_OTHER_INFO];
+ const size_t other_info_size = curve_desc->OIDhex_len + 46;
+ const size_t tmp_len =
+ kdf_other_info_serialize(other_info, curve_desc, fingerprint, kdf_hash, wrap_alg);
+
+ if (other_info_size != tmp_len) {
+ RNP_LOG("Serialization of other info failed");
+ return RNP_ERROR_GENERIC;
+ }
+
+ botan_privkey_t prv_key = NULL;
+ if (!ecdh_load_secret_key(&prv_key, key)) {
+ RNP_LOG("failed to load ecdh secret key");
+ return RNP_ERROR_GENERIC;
+ }
+
+ // Size of SHA-256 or smaller
+ rnp::secure_array<uint8_t, MAX_SYMM_KEY_SIZE> kek;
+ rnp::secure_array<uint8_t, MAX_SESSION_KEY_SIZE> deckey;
+
+ size_t deckey_len = deckey.size();
+ size_t offset = 0;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+
+ /* Security: Always return same error code in case compute_kek,
+ * botan_key_unwrap3394 or unpad_pkcs7 fails
+ */
+ size_t kek_len = pgp_key_size(wrap_alg);
+ if (!compute_kek(kek.data(),
+ kek_len,
+ other_info,
+ other_info_size,
+ curve_desc,
+ &in->p,
+ prv_key,
+ kdf_hash)) {
+ goto end;
+ }
+
+ if (botan_key_unwrap3394(
+ in->m, in->mlen, kek.data(), kek_len, deckey.data(), &deckey_len)) {
+ goto end;
+ }
+
+ if (!unpad_pkcs7(deckey.data(), deckey_len, &offset)) {
+ goto end;
+ }
+
+ if (*out_len < offset) {
+ ret = RNP_ERROR_SHORT_BUFFER;
+ goto end;
+ }
+
+ *out_len = offset;
+ memcpy(out, deckey.data(), *out_len);
+ ret = RNP_SUCCESS;
+end:
+ botan_privkey_destroy(prv_key);
+ return ret;
+}
diff --git a/src/lib/crypto/ecdh.h b/src/lib/crypto/ecdh.h
new file mode 100644
index 0000000..017e1e6
--- /dev/null
+++ b/src/lib/crypto/ecdh.h
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 2017 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef ECDH_H_
+#define ECDH_H_
+
+#include "crypto/ec.h"
+
+/* Max size of wrapped and obfuscated key size
+ *
+ * RNP pads a key with PKCS-5 always to 8 byte granularity,
+ * then 8 bytes is added by AES-wrap (RFC3394).
+ */
+#define ECDH_WRAPPED_KEY_SIZE 48
+
+/* Forward declarations */
+typedef struct pgp_fingerprint_t pgp_fingerprint_t;
+
+typedef struct pgp_ecdh_encrypted_t {
+ pgp_mpi_t p;
+ uint8_t m[ECDH_WRAPPED_KEY_SIZE];
+ size_t mlen;
+} pgp_ecdh_encrypted_t;
+
+rnp_result_t ecdh_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret);
+
+/*
+ * @brief Sets hash algorithm and key wrapping algo
+ * based on curve_id
+ *
+ * @param key ec key to set parameters for
+ * @param curve underlying ECC curve ID
+ *
+ * @returns false if curve is not supported, otherwise true
+ */
+bool ecdh_set_params(pgp_ec_key_t *key, pgp_curve_t curve_id);
+
+/*
+ * Encrypts session key with a KEK agreed during ECDH as specified in
+ * RFC 4880 bis 01, 13.5
+ *
+ * @param rng initialized rnp::RNG object
+ * @param session_key key to be encrypted
+ * @param session_key_len length of the key buffer
+ * @param wrapped_key [out] resulting key wrapped in by some AES
+ * as specified in RFC 3394
+ * @param wrapped_key_len [out] length of the `wrapped_key' buffer
+ * Current implementation always produces 48 bytes as key
+ * is padded with PKCS-5/7
+ * @param ephemeral_key [out] public ephemeral ECDH key used for key
+ * agreement (private part). Must be initialized
+ * @param pubkey public key to be used for encryption
+ * @param fingerprint fingerprint of the pubkey
+ *
+ * @return RNP_SUCCESS on success and output parameters are populated
+ * @return RNP_ERROR_NOT_SUPPORTED unknown curve
+ * @return RNP_ERROR_BAD_PARAMETERS unexpected input provided
+ * @return RNP_ERROR_SHORT_BUFFER `wrapped_key_len' to small to store result
+ * @return RNP_ERROR_GENERIC implementation error
+ */
+rnp_result_t ecdh_encrypt_pkcs5(rnp::RNG * rng,
+ pgp_ecdh_encrypted_t * out,
+ const uint8_t *const in,
+ size_t in_len,
+ const pgp_ec_key_t * key,
+ const pgp_fingerprint_t &fingerprint);
+
+/*
+ * Decrypts session key with a KEK agreed during ECDH as specified in
+ * RFC 4880 bis 01, 13.5
+ *
+ * @param session_key [out] resulting session key
+ * @param session_key_len [out] length of the resulting session key
+ * @param wrapped_key session key wrapped with some AES as specified
+ * in RFC 3394
+ * @param wrapped_key_len length of the `wrapped_key' buffer
+ * @param ephemeral_key public ephemeral ECDH key coming from
+ * encrypted packet.
+ * @param seckey secret key to be used for decryption
+ * @param fingerprint fingerprint of the key
+ *
+ * @return RNP_SUCCESS on success and output parameters are populated
+ * @return RNP_ERROR_NOT_SUPPORTED unknown curve
+ * @return RNP_ERROR_BAD_PARAMETERS unexpected input provided
+ * @return RNP_ERROR_SHORT_BUFFER `session_key_len' to small to store result
+ * @return RNP_ERROR_GENERIC decryption failed or implementation error
+ */
+rnp_result_t ecdh_decrypt_pkcs5(uint8_t * out,
+ size_t * out_len,
+ const pgp_ecdh_encrypted_t *in,
+ const pgp_ec_key_t * key,
+ const pgp_fingerprint_t & fingerprint);
+
+#endif // ECDH_H_
diff --git a/src/lib/crypto/ecdh_ossl.cpp b/src/lib/crypto/ecdh_ossl.cpp
new file mode 100644
index 0000000..60b7260
--- /dev/null
+++ b/src/lib/crypto/ecdh_ossl.cpp
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2021-2022, [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 <string>
+#include <cassert>
+#include "ecdh.h"
+#include "ecdh_utils.h"
+#include "ec_ossl.h"
+#include "hash.hpp"
+#include "symmetric.h"
+#include "types.h"
+#include "utils.h"
+#include "logging.h"
+#include "mem.h"
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+static const struct ecdh_wrap_alg_map_t {
+ pgp_symm_alg_t alg;
+ const char * name;
+} ecdh_wrap_alg_map[] = {{PGP_SA_AES_128, "aes128-wrap"},
+ {PGP_SA_AES_192, "aes192-wrap"},
+ {PGP_SA_AES_256, "aes256-wrap"}};
+
+rnp_result_t
+ecdh_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret)
+{
+ return ec_validate_key(*key, secret);
+}
+
+static rnp_result_t
+ecdh_derive_kek(uint8_t * x,
+ size_t xlen,
+ const pgp_ec_key_t & key,
+ const pgp_fingerprint_t &fingerprint,
+ uint8_t * kek,
+ const size_t kek_len)
+{
+ const ec_curve_desc_t *curve_desc = get_curve_desc(key.curve);
+ if (!curve_desc) {
+ RNP_LOG("unsupported curve");
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+
+ // Serialize other info, see 13.5 of RFC 4880 bis
+ uint8_t other_info[MAX_SP800_56A_OTHER_INFO];
+ const size_t hash_len = rnp::Hash::size(key.kdf_hash_alg);
+ if (!hash_len) {
+ // must not assert here as kdf/hash algs are not checked during key parsing
+ RNP_LOG("Unsupported key wrap hash algorithm.");
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+ size_t other_len = kdf_other_info_serialize(
+ other_info, curve_desc, fingerprint, key.kdf_hash_alg, key.key_wrap_alg);
+ // Self-check
+ assert(other_len == curve_desc->OIDhex_len + 46);
+ // Derive KEK, using the KDF from SP800-56A
+ rnp::secure_array<uint8_t, PGP_MAX_HASH_SIZE> dgst;
+ assert(hash_len <= PGP_MAX_HASH_SIZE);
+ size_t reps = (kek_len + hash_len - 1) / hash_len;
+ // As we use AES & SHA2 we should not get more then 2 iterations
+ if (reps > 2) {
+ RNP_LOG("Invalid key wrap/hash alg combination.");
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+ size_t have = 0;
+ try {
+ for (size_t i = 1; i <= reps; i++) {
+ auto hash = rnp::Hash::create(key.kdf_hash_alg);
+ hash->add(i);
+ hash->add(x, xlen);
+ hash->add(other_info, other_len);
+ hash->finish(dgst.data());
+ size_t bytes = std::min(hash_len, kek_len - have);
+ memcpy(kek + have, dgst.data(), bytes);
+ have += bytes;
+ }
+ return RNP_SUCCESS;
+ } catch (const std::exception &e) {
+ RNP_LOG("Failed to derive kek: %s", e.what());
+ return RNP_ERROR_GENERIC;
+ }
+}
+
+static rnp_result_t
+ecdh_rfc3394_wrap_ctx(EVP_CIPHER_CTX **ctx,
+ pgp_symm_alg_t wrap_alg,
+ const uint8_t * key,
+ bool decrypt)
+{
+ /* get OpenSSL EVP cipher for key wrap */
+ const char *cipher_name = NULL;
+ ARRAY_LOOKUP_BY_ID(ecdh_wrap_alg_map, alg, name, wrap_alg, cipher_name);
+ if (!cipher_name) {
+ RNP_LOG("Unsupported key wrap algorithm: %d", (int) wrap_alg);
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+ const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_name);
+ if (!cipher) {
+ RNP_LOG("Cipher %s is not supported by OpenSSL.", cipher_name);
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+ *ctx = EVP_CIPHER_CTX_new();
+ if (!ctx) {
+ RNP_LOG("Context allocation failed : %lu", ERR_peek_last_error());
+ return RNP_ERROR_OUT_OF_MEMORY;
+ }
+ EVP_CIPHER_CTX_set_flags(*ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+ int res = decrypt ? EVP_DecryptInit_ex(*ctx, cipher, NULL, key, NULL) :
+ EVP_EncryptInit_ex(*ctx, cipher, NULL, key, NULL);
+ if (res <= 0) {
+ RNP_LOG("Failed to initialize cipher : %lu", ERR_peek_last_error());
+ EVP_CIPHER_CTX_free(*ctx);
+ *ctx = NULL;
+ return RNP_ERROR_GENERIC;
+ }
+ return RNP_SUCCESS;
+}
+
+static rnp_result_t
+ecdh_rfc3394_wrap(uint8_t * out,
+ size_t * out_len,
+ const uint8_t *const in,
+ size_t in_len,
+ const uint8_t * key,
+ pgp_symm_alg_t wrap_alg)
+{
+ EVP_CIPHER_CTX *ctx = NULL;
+ rnp_result_t ret = ecdh_rfc3394_wrap_ctx(&ctx, wrap_alg, key, false);
+ if (ret) {
+ RNP_LOG("Wrap context initialization failed.");
+ return ret;
+ }
+ int intlen = *out_len;
+ /* encrypts in one pass, no final is needed */
+ int res = EVP_EncryptUpdate(ctx, out, &intlen, in, in_len);
+ if (res <= 0) {
+ RNP_LOG("Failed to encrypt data : %lu", ERR_peek_last_error());
+ } else {
+ *out_len = intlen;
+ }
+ EVP_CIPHER_CTX_free(ctx);
+ return res > 0 ? RNP_SUCCESS : RNP_ERROR_GENERIC;
+}
+
+static rnp_result_t
+ecdh_rfc3394_unwrap(uint8_t * out,
+ size_t * out_len,
+ const uint8_t *const in,
+ size_t in_len,
+ const uint8_t * key,
+ pgp_symm_alg_t wrap_alg)
+{
+ if ((in_len < 16) || (in_len % 8)) {
+ RNP_LOG("Invalid wrapped key size.");
+ return RNP_ERROR_GENERIC;
+ }
+ EVP_CIPHER_CTX *ctx = NULL;
+ rnp_result_t ret = ecdh_rfc3394_wrap_ctx(&ctx, wrap_alg, key, true);
+ if (ret) {
+ RNP_LOG("Unwrap context initialization failed.");
+ return ret;
+ }
+ int intlen = *out_len;
+ /* decrypts in one pass, no final is needed */
+ int res = EVP_DecryptUpdate(ctx, out, &intlen, in, in_len);
+ if (res <= 0) {
+ RNP_LOG("Failed to decrypt data : %lu", ERR_peek_last_error());
+ } else {
+ *out_len = intlen;
+ }
+ EVP_CIPHER_CTX_free(ctx);
+ return res > 0 ? RNP_SUCCESS : RNP_ERROR_GENERIC;
+}
+
+static bool
+ecdh_derive_secret(EVP_PKEY *sec, EVP_PKEY *peer, uint8_t *x, size_t *xlen)
+{
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(sec, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ return false;
+ }
+ bool res = false;
+ if (EVP_PKEY_derive_init(ctx) <= 0) {
+ RNP_LOG("Key derivation init failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_derive_set_peer(ctx, peer) <= 0) {
+ RNP_LOG("Peer setting failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_derive(ctx, x, xlen) <= 0) {
+ RNP_LOG("Failed to obtain shared secret size: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ res = true;
+done:
+ EVP_PKEY_CTX_free(ctx);
+ return res;
+}
+
+static size_t
+ecdh_kek_len(pgp_symm_alg_t wrap_alg)
+{
+ switch (wrap_alg) {
+ case PGP_SA_AES_128:
+ case PGP_SA_AES_192:
+ case PGP_SA_AES_256:
+ return pgp_key_size(wrap_alg);
+ default:
+ return 0;
+ }
+}
+
+rnp_result_t
+ecdh_encrypt_pkcs5(rnp::RNG * rng,
+ pgp_ecdh_encrypted_t * out,
+ const uint8_t *const in,
+ size_t in_len,
+ const pgp_ec_key_t * key,
+ const pgp_fingerprint_t &fingerprint)
+{
+ if (!key || !out || !in || (in_len > MAX_SESSION_KEY_SIZE)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+#if !defined(ENABLE_SM2)
+ if (key->curve == PGP_CURVE_SM2_P_256) {
+ RNP_LOG("SM2 curve support is disabled.");
+ return RNP_ERROR_NOT_IMPLEMENTED;
+ }
+#endif
+ /* check whether we have valid wrap_alg before doing heavy operations */
+ size_t keklen = ecdh_kek_len(key->key_wrap_alg);
+ if (!keklen) {
+ RNP_LOG("Unsupported key wrap algorithm: %d", (int) key->key_wrap_alg);
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+ /* load our public key */
+ EVP_PKEY *pkey = ec_load_key(key->p, NULL, key->curve);
+ if (!pkey) {
+ RNP_LOG("Failed to load public key.");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ rnp::secure_array<uint8_t, MAX_CURVE_BYTELEN + 1> sec;
+ rnp::secure_array<uint8_t, MAX_AES_KEY_SIZE> kek;
+ rnp::secure_array<uint8_t, MAX_SESSION_KEY_SIZE> mpad;
+
+ size_t seclen = sec.size();
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ /* generate ephemeral key */
+ EVP_PKEY *ephkey = ec_generate_pkey(PGP_PKA_ECDH, key->curve);
+ if (!ephkey) {
+ RNP_LOG("Failed to generate ephemeral key.");
+ ret = RNP_ERROR_KEY_GENERATION;
+ goto done;
+ }
+ /* do ECDH derivation */
+ if (!ecdh_derive_secret(ephkey, pkey, sec.data(), &seclen)) {
+ RNP_LOG("ECDH derivation failed.");
+ goto done;
+ }
+ /* here we got x value in sec, deriving kek */
+ ret = ecdh_derive_kek(sec.data(), seclen, *key, fingerprint, kek.data(), keklen);
+ if (ret) {
+ RNP_LOG("Failed to derive KEK.");
+ goto done;
+ }
+ /* add PKCS#7 padding */
+ size_t m_padded_len;
+ m_padded_len = ((in_len / 8) + 1) * 8;
+ memcpy(mpad.data(), in, in_len);
+ if (!pad_pkcs7(mpad.data(), m_padded_len, in_len)) {
+ RNP_LOG("Failed to add PKCS #7 padding.");
+ goto done;
+ }
+ /* do RFC 3394 AES key wrap */
+ static_assert(sizeof(out->m) == ECDH_WRAPPED_KEY_SIZE, "Wrong ECDH wrapped key size.");
+ out->mlen = ECDH_WRAPPED_KEY_SIZE;
+ ret = ecdh_rfc3394_wrap(
+ out->m, &out->mlen, mpad.data(), m_padded_len, kek.data(), key->key_wrap_alg);
+ if (ret) {
+ RNP_LOG("Failed to wrap key.");
+ goto done;
+ }
+ /* write ephemeral public key */
+ if (!ec_write_pubkey(ephkey, out->p, key->curve)) {
+ RNP_LOG("Failed to write ec key.");
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_free(ephkey);
+ EVP_PKEY_free(pkey);
+ return ret;
+}
+
+rnp_result_t
+ecdh_decrypt_pkcs5(uint8_t * out,
+ size_t * out_len,
+ const pgp_ecdh_encrypted_t *in,
+ const pgp_ec_key_t * key,
+ const pgp_fingerprint_t & fingerprint)
+{
+ if (!out || !out_len || !in || !key || !mpi_bytes(&key->x)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ /* check whether we have valid wrap_alg before doing heavy operations */
+ size_t keklen = ecdh_kek_len(key->key_wrap_alg);
+ if (!keklen) {
+ RNP_LOG("Unsupported key wrap algorithm: %d", (int) key->key_wrap_alg);
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+ /* load ephemeral public key */
+ EVP_PKEY *ephkey = ec_load_key(in->p, NULL, key->curve);
+ if (!ephkey) {
+ RNP_LOG("Failed to load ephemeral public key.");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ /* load our secret key */
+ rnp::secure_array<uint8_t, MAX_CURVE_BYTELEN + 1> sec;
+ rnp::secure_array<uint8_t, MAX_AES_KEY_SIZE> kek;
+ rnp::secure_array<uint8_t, MAX_SESSION_KEY_SIZE> mpad;
+
+ size_t seclen = sec.size();
+ size_t mpadlen = mpad.size();
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ EVP_PKEY * pkey = ec_load_key(key->p, &key->x, key->curve);
+ if (!pkey) {
+ RNP_LOG("Failed to load secret key.");
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ goto done;
+ }
+ /* do ECDH derivation */
+ if (!ecdh_derive_secret(pkey, ephkey, sec.data(), &seclen)) {
+ RNP_LOG("ECDH derivation failed.");
+ goto done;
+ }
+ /* here we got x value in sec, deriving kek */
+ ret = ecdh_derive_kek(sec.data(), seclen, *key, fingerprint, kek.data(), keklen);
+ if (ret) {
+ RNP_LOG("Failed to derive KEK.");
+ goto done;
+ }
+ /* do RFC 3394 AES key unwrap */
+ ret = ecdh_rfc3394_unwrap(
+ mpad.data(), &mpadlen, in->m, in->mlen, kek.data(), key->key_wrap_alg);
+ if (ret) {
+ RNP_LOG("Failed to unwrap key.");
+ goto done;
+ }
+ /* remove PKCS#7 padding */
+ if (!unpad_pkcs7(mpad.data(), mpadlen, &mpadlen)) {
+ RNP_LOG("Failed to unpad key.");
+ goto done;
+ }
+ assert(mpadlen <= *out_len);
+ *out_len = mpadlen;
+ memcpy(out, mpad.data(), mpadlen);
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_free(ephkey);
+ EVP_PKEY_free(pkey);
+ return ret;
+}
diff --git a/src/lib/crypto/ecdh_utils.cpp b/src/lib/crypto/ecdh_utils.cpp
new file mode 100644
index 0000000..3ceb153
--- /dev/null
+++ b/src/lib/crypto/ecdh_utils.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 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 "ecdh_utils.h"
+#include "types.h"
+#include "utils.h"
+#include <cassert>
+
+/* Used by ECDH keys. Specifies which hash and wrapping algorithm
+ * to be used (see point 15. of RFC 4880).
+ *
+ * Note: sync with ec_curves.
+ */
+static const struct ecdh_params_t {
+ pgp_curve_t curve; /* Curve ID */
+ pgp_hash_alg_t hash; /* Hash used by kdf */
+ pgp_symm_alg_t wrap_alg; /* Symmetric algorithm used to wrap KEK*/
+} ecdh_params[] = {
+ {PGP_CURVE_NIST_P_256, PGP_HASH_SHA256, PGP_SA_AES_128},
+ {PGP_CURVE_NIST_P_384, PGP_HASH_SHA384, PGP_SA_AES_192},
+ {PGP_CURVE_NIST_P_521, PGP_HASH_SHA512, PGP_SA_AES_256},
+ {PGP_CURVE_BP256, PGP_HASH_SHA256, PGP_SA_AES_128},
+ {PGP_CURVE_BP384, PGP_HASH_SHA384, PGP_SA_AES_192},
+ {PGP_CURVE_BP512, PGP_HASH_SHA512, PGP_SA_AES_256},
+ {PGP_CURVE_25519, PGP_HASH_SHA256, PGP_SA_AES_128},
+ {PGP_CURVE_P256K1, PGP_HASH_SHA256, PGP_SA_AES_128},
+};
+
+// "Anonymous Sender " in hex
+static const unsigned char ANONYMOUS_SENDER[] = {0x41, 0x6E, 0x6F, 0x6E, 0x79, 0x6D, 0x6F,
+ 0x75, 0x73, 0x20, 0x53, 0x65, 0x6E, 0x64,
+ 0x65, 0x72, 0x20, 0x20, 0x20, 0x20};
+
+// returns size of data written to other_info
+size_t
+kdf_other_info_serialize(uint8_t other_info[MAX_SP800_56A_OTHER_INFO],
+ const ec_curve_desc_t * ec_curve,
+ const pgp_fingerprint_t &fingerprint,
+ const pgp_hash_alg_t kdf_hash,
+ const pgp_symm_alg_t wrap_alg)
+{
+ assert(fingerprint.length >= 20);
+ uint8_t *buf_ptr = &other_info[0];
+
+ /* KDF-OtherInfo: AlgorithmID
+ * Current implementation will always use SHA-512 and AES-256 for KEK wrapping
+ */
+ *(buf_ptr++) = ec_curve->OIDhex_len;
+ memcpy(buf_ptr, ec_curve->OIDhex, ec_curve->OIDhex_len);
+ buf_ptr += ec_curve->OIDhex_len;
+ *(buf_ptr++) = PGP_PKA_ECDH;
+ // size of following 3 params (each 1 byte)
+ *(buf_ptr++) = 0x03;
+ // Value reserved for future use
+ *(buf_ptr++) = 0x01;
+ // Hash used with KDF
+ *(buf_ptr++) = kdf_hash;
+ // Algorithm ID used for key wrapping
+ *(buf_ptr++) = wrap_alg;
+
+ /* KDF-OtherInfo: PartyUInfo
+ * 20 bytes representing "Anonymous Sender "
+ */
+ memcpy(buf_ptr, ANONYMOUS_SENDER, sizeof(ANONYMOUS_SENDER));
+ buf_ptr += sizeof(ANONYMOUS_SENDER);
+
+ // keep 20, as per spec
+ memcpy(buf_ptr, fingerprint.fingerprint, 20);
+ return (buf_ptr - other_info) + 20 /*anonymous_sender*/;
+}
+
+bool
+pad_pkcs7(uint8_t *buf, size_t buf_len, size_t offset)
+{
+ if (buf_len <= offset) {
+ // Must have at least 1 byte of padding
+ return false;
+ }
+
+ const uint8_t pad_byte = buf_len - offset;
+ memset(buf + offset, pad_byte, pad_byte);
+ return true;
+}
+
+bool
+unpad_pkcs7(uint8_t *buf, size_t buf_len, size_t *offset)
+{
+ if (!buf || !offset || !buf_len) {
+ return false;
+ }
+
+ uint8_t err = 0;
+ const uint8_t pad_byte = buf[buf_len - 1];
+ const uint32_t pad_begin = buf_len - pad_byte;
+
+ // TODO: Still >, <, and <=,== are not constant time (maybe?)
+ err |= (pad_byte > buf_len);
+ err |= (pad_byte == 0);
+
+ /* Check if padding is OK */
+ for (size_t c = 0; c < buf_len; c++) {
+ err |= (buf[c] ^ pad_byte) * (pad_begin <= c);
+ }
+
+ *offset = pad_begin;
+ return (err == 0);
+}
+
+bool
+ecdh_set_params(pgp_ec_key_t *key, pgp_curve_t curve_id)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(ecdh_params); i++) {
+ if (ecdh_params[i].curve == curve_id) {
+ key->kdf_hash_alg = ecdh_params[i].hash;
+ key->key_wrap_alg = ecdh_params[i].wrap_alg;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+x25519_tweak_bits(pgp_ec_key_t &key)
+{
+ if (key.x.len != 32) {
+ return false;
+ }
+ /* MPI is big-endian, while raw x25519 key is little-endian */
+ key.x.mpi[31] &= 248; // zero 3 low bits
+ key.x.mpi[0] &= 127; // zero high bit
+ key.x.mpi[0] |= 64; // set high - 1 bit
+ return true;
+}
+
+bool
+x25519_bits_tweaked(const pgp_ec_key_t &key)
+{
+ if (key.x.len != 32) {
+ return false;
+ }
+ return !(key.x.mpi[31] & 7) && (key.x.mpi[0] < 128) && (key.x.mpi[0] >= 64);
+}
diff --git a/src/lib/crypto/ecdh_utils.h b/src/lib/crypto/ecdh_utils.h
new file mode 100644
index 0000000..2d37a71
--- /dev/null
+++ b/src/lib/crypto/ecdh_utils.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef ECDH_UTILS_H_
+#define ECDH_UTILS_H_
+
+#include "ecdh.h"
+
+#define MAX_SP800_56A_OTHER_INFO 56
+// Keys up to 312 bits (+1 bytes of PKCS5 padding)
+#define MAX_SESSION_KEY_SIZE 40
+#define MAX_AES_KEY_SIZE 32
+
+size_t kdf_other_info_serialize(uint8_t other_info[MAX_SP800_56A_OTHER_INFO],
+ const ec_curve_desc_t * ec_curve,
+ const pgp_fingerprint_t &fingerprint,
+ const pgp_hash_alg_t kdf_hash,
+ const pgp_symm_alg_t wrap_alg);
+
+bool pad_pkcs7(uint8_t *buf, size_t buf_len, size_t offset);
+
+bool unpad_pkcs7(uint8_t *buf, size_t buf_len, size_t *offset);
+
+#endif // ECDH_UTILS_H_
diff --git a/src/lib/crypto/ecdsa.cpp b/src/lib/crypto/ecdsa.cpp
new file mode 100644
index 0000000..cffce12
--- /dev/null
+++ b/src/lib/crypto/ecdsa.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2017, [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 HOLDERS 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 "ecdsa.h"
+#include "utils.h"
+#include <botan/ffi.h>
+#include <string.h>
+#include "bn.h"
+
+static bool
+ecdsa_load_public_key(botan_pubkey_t *pubkey, const pgp_ec_key_t *keydata)
+{
+ botan_mp_t px = NULL;
+ botan_mp_t py = NULL;
+ bool res = false;
+
+ const ec_curve_desc_t *curve = get_curve_desc(keydata->curve);
+ if (!curve) {
+ RNP_LOG("unknown curve");
+ return false;
+ }
+ const size_t curve_order = BITS_TO_BYTES(curve->bitlen);
+
+ if (!mpi_bytes(&keydata->p) || (keydata->p.mpi[0] != 0x04)) {
+ RNP_LOG(
+ "Failed to load public key: %zu, %02x", mpi_bytes(&keydata->p), keydata->p.mpi[0]);
+ return false;
+ }
+
+ if (botan_mp_init(&px) || botan_mp_init(&py) ||
+ botan_mp_from_bin(px, &keydata->p.mpi[1], curve_order) ||
+ botan_mp_from_bin(py, &keydata->p.mpi[1 + curve_order], curve_order)) {
+ goto end;
+ }
+
+ if (!(res = !botan_pubkey_load_ecdsa(pubkey, px, py, curve->botan_name))) {
+ RNP_LOG("failed to load ecdsa public key");
+ }
+end:
+ botan_mp_destroy(px);
+ botan_mp_destroy(py);
+ return res;
+}
+
+static bool
+ecdsa_load_secret_key(botan_privkey_t *seckey, const pgp_ec_key_t *keydata)
+{
+ const ec_curve_desc_t *curve;
+ bignum_t * x = NULL;
+ bool res = false;
+
+ if (!(curve = get_curve_desc(keydata->curve))) {
+ return false;
+ }
+ if (!(x = mpi2bn(&keydata->x))) {
+ return false;
+ }
+ if (!(res = !botan_privkey_load_ecdsa(seckey, BN_HANDLE_PTR(x), curve->botan_name))) {
+ RNP_LOG("Can't load private key");
+ }
+ bn_free(x);
+ return res;
+}
+
+rnp_result_t
+ecdsa_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret)
+{
+ botan_pubkey_t bpkey = NULL;
+ botan_privkey_t bskey = NULL;
+ rnp_result_t ret = RNP_ERROR_BAD_PARAMETERS;
+
+ if (!ecdsa_load_public_key(&bpkey, key) ||
+ botan_pubkey_check_key(bpkey, rng->handle(), 0)) {
+ goto done;
+ }
+ if (!secret) {
+ ret = RNP_SUCCESS;
+ goto done;
+ }
+
+ if (!ecdsa_load_secret_key(&bskey, key) ||
+ botan_privkey_check_key(bskey, rng->handle(), 0)) {
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ botan_privkey_destroy(bskey);
+ botan_pubkey_destroy(bpkey);
+ return ret;
+}
+
+static const char *
+ecdsa_padding_str_for(pgp_hash_alg_t hash_alg)
+{
+ switch (hash_alg) {
+ case PGP_HASH_MD5:
+ return "Raw(MD5)";
+ case PGP_HASH_SHA1:
+ return "Raw(SHA-1)";
+ case PGP_HASH_RIPEMD:
+ return "Raw(RIPEMD-160)";
+
+ case PGP_HASH_SHA256:
+ return "Raw(SHA-256)";
+ case PGP_HASH_SHA384:
+ return "Raw(SHA-384)";
+ case PGP_HASH_SHA512:
+ return "Raw(SHA-512)";
+ case PGP_HASH_SHA224:
+ return "Raw(SHA-224)";
+ case PGP_HASH_SHA3_256:
+ return "Raw(SHA3(256))";
+ case PGP_HASH_SHA3_512:
+ return "Raw(SHA3(512))";
+
+ case PGP_HASH_SM3:
+ return "Raw(SM3)";
+ default:
+ return "Raw";
+ }
+}
+
+rnp_result_t
+ecdsa_sign(rnp::RNG * rng,
+ pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t *key)
+{
+ botan_pk_op_sign_t signer = NULL;
+ botan_privkey_t b_key = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ uint8_t out_buf[2 * MAX_CURVE_BYTELEN] = {0};
+ const ec_curve_desc_t *curve = get_curve_desc(key->curve);
+ const char * padding_str = ecdsa_padding_str_for(hash_alg);
+
+ if (!curve) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ const size_t curve_order = BITS_TO_BYTES(curve->bitlen);
+ size_t sig_len = 2 * curve_order;
+
+ if (!ecdsa_load_secret_key(&b_key, key)) {
+ RNP_LOG("Can't load private key");
+ goto end;
+ }
+
+ if (botan_pk_op_sign_create(&signer, b_key, padding_str, 0)) {
+ goto end;
+ }
+
+ if (botan_pk_op_sign_update(signer, hash, hash_len)) {
+ goto end;
+ }
+
+ if (botan_pk_op_sign_finish(signer, rng->handle(), out_buf, &sig_len)) {
+ RNP_LOG("Signing failed");
+ goto end;
+ }
+
+ // Allocate memory and copy results
+ if (mem2mpi(&sig->r, out_buf, curve_order) &&
+ mem2mpi(&sig->s, out_buf + curve_order, curve_order)) {
+ ret = RNP_SUCCESS;
+ }
+end:
+ botan_privkey_destroy(b_key);
+ botan_pk_op_sign_destroy(signer);
+ return ret;
+}
+
+rnp_result_t
+ecdsa_verify(const pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t * key)
+{
+ botan_pubkey_t pub = NULL;
+ botan_pk_op_verify_t verifier = NULL;
+ rnp_result_t ret = RNP_ERROR_SIGNATURE_INVALID;
+ uint8_t sign_buf[2 * MAX_CURVE_BYTELEN] = {0};
+ size_t r_blen, s_blen;
+ const char * padding_str = ecdsa_padding_str_for(hash_alg);
+
+ const ec_curve_desc_t *curve = get_curve_desc(key->curve);
+ if (!curve) {
+ RNP_LOG("unknown curve");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ const size_t curve_order = BITS_TO_BYTES(curve->bitlen);
+
+ if (!ecdsa_load_public_key(&pub, key)) {
+ goto end;
+ }
+
+ if (botan_pk_op_verify_create(&verifier, pub, padding_str, 0)) {
+ goto end;
+ }
+
+ if (botan_pk_op_verify_update(verifier, hash, hash_len)) {
+ goto end;
+ }
+
+ r_blen = mpi_bytes(&sig->r);
+ s_blen = mpi_bytes(&sig->s);
+ if ((r_blen > curve_order) || (s_blen > curve_order) ||
+ (curve_order > MAX_CURVE_BYTELEN)) {
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ goto end;
+ }
+
+ // Both can't fail
+ mpi2mem(&sig->r, &sign_buf[curve_order - r_blen]);
+ mpi2mem(&sig->s, &sign_buf[curve_order + curve_order - s_blen]);
+
+ if (!botan_pk_op_verify_finish(verifier, sign_buf, curve_order * 2)) {
+ ret = RNP_SUCCESS;
+ }
+end:
+ botan_pubkey_destroy(pub);
+ botan_pk_op_verify_destroy(verifier);
+ return ret;
+}
+
+pgp_hash_alg_t
+ecdsa_get_min_hash(pgp_curve_t curve)
+{
+ switch (curve) {
+ case PGP_CURVE_NIST_P_256:
+ case PGP_CURVE_BP256:
+ case PGP_CURVE_P256K1:
+ return PGP_HASH_SHA256;
+ case PGP_CURVE_NIST_P_384:
+ case PGP_CURVE_BP384:
+ return PGP_HASH_SHA384;
+ case PGP_CURVE_NIST_P_521:
+ case PGP_CURVE_BP512:
+ return PGP_HASH_SHA512;
+ default:
+ return PGP_HASH_UNKNOWN;
+ }
+}
diff --git a/src/lib/crypto/ecdsa.h b/src/lib/crypto/ecdsa.h
new file mode 100644
index 0000000..aebfe6a
--- /dev/null
+++ b/src/lib/crypto/ecdsa.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2017 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef ECDSA_H_
+#define ECDSA_H_
+
+#include "crypto/ec.h"
+
+rnp_result_t ecdsa_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret);
+
+rnp_result_t ecdsa_sign(rnp::RNG * rng,
+ pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t *key);
+
+rnp_result_t ecdsa_verify(const pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t * key);
+
+/*
+ * @brief Returns hash which should be used with the curve
+ *
+ * @param curve Curve ID
+ *
+ * @returns Either ID of the hash algorithm, or PGP_HASH_UNKNOWN
+ * if not found
+ */
+pgp_hash_alg_t ecdsa_get_min_hash(pgp_curve_t curve);
+
+#endif // ECDSA_H_
diff --git a/src/lib/crypto/ecdsa_ossl.cpp b/src/lib/crypto/ecdsa_ossl.cpp
new file mode 100644
index 0000000..534811a
--- /dev/null
+++ b/src/lib/crypto/ecdsa_ossl.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 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 "ecdsa.h"
+#include "utils.h"
+#include <string.h>
+#include "bn.h"
+#include "ec_ossl.h"
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/ec.h>
+
+static bool
+ecdsa_decode_sig(const uint8_t *data, size_t len, pgp_ec_signature_t &sig)
+{
+ ECDSA_SIG *esig = d2i_ECDSA_SIG(NULL, &data, len);
+ if (!esig) {
+ RNP_LOG("Failed to parse ECDSA sig: %lu", ERR_peek_last_error());
+ return false;
+ }
+ const BIGNUM *r, *s;
+ ECDSA_SIG_get0(esig, &r, &s);
+ bn2mpi(r, &sig.r);
+ bn2mpi(s, &sig.s);
+ ECDSA_SIG_free(esig);
+ return true;
+}
+
+static bool
+ecdsa_encode_sig(uint8_t *data, size_t *len, const pgp_ec_signature_t &sig)
+{
+ bool res = false;
+ ECDSA_SIG *dsig = ECDSA_SIG_new();
+ BIGNUM * r = mpi2bn(&sig.r);
+ BIGNUM * s = mpi2bn(&sig.s);
+ if (!dsig || !r || !s) {
+ RNP_LOG("Allocation failed.");
+ goto done;
+ }
+ ECDSA_SIG_set0(dsig, r, s);
+ r = NULL;
+ s = NULL;
+ int outlen;
+ outlen = i2d_ECDSA_SIG(dsig, &data);
+ if (outlen < 0) {
+ RNP_LOG("Failed to encode signature.");
+ goto done;
+ }
+ *len = outlen;
+ res = true;
+done:
+ ECDSA_SIG_free(dsig);
+ BN_free(r);
+ BN_free(s);
+ return res;
+}
+
+rnp_result_t
+ecdsa_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret)
+{
+ return ec_validate_key(*key, secret);
+}
+
+rnp_result_t
+ecdsa_sign(rnp::RNG * rng,
+ pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t *key)
+{
+ if (mpi_bytes(&key->x) == 0) {
+ RNP_LOG("private key not set");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ /* Load secret key to DSA structure*/
+ EVP_PKEY *evpkey = ec_load_key(key->p, &key->x, key->curve);
+ if (!evpkey) {
+ RNP_LOG("Failed to load key");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ /* init context and sign */
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_sign_init(ctx) <= 0) {
+ RNP_LOG("Failed to initialize signing: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ sig->s.len = PGP_MPINT_SIZE;
+ if (EVP_PKEY_sign(ctx, sig->s.mpi, &sig->s.len, hash, hash_len) <= 0) {
+ RNP_LOG("Signing failed: %lu", ERR_peek_last_error());
+ sig->s.len = 0;
+ goto done;
+ }
+ if (!ecdsa_decode_sig(&sig->s.mpi[0], sig->s.len, *sig)) {
+ RNP_LOG("Failed to parse ECDSA sig: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(evpkey);
+ return ret;
+}
+
+rnp_result_t
+ecdsa_verify(const pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t * key)
+{
+ /* Load secret key to DSA structure*/
+ EVP_PKEY *evpkey = ec_load_key(key->p, NULL, key->curve);
+ if (!evpkey) {
+ RNP_LOG("Failed to load key");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ rnp_result_t ret = RNP_ERROR_SIGNATURE_INVALID;
+ /* init context and sign */
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_verify_init(ctx) <= 0) {
+ RNP_LOG("Failed to initialize verify: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ pgp_mpi_t sigbuf;
+ if (!ecdsa_encode_sig(sigbuf.mpi, &sigbuf.len, *sig)) {
+ goto done;
+ }
+ if (EVP_PKEY_verify(ctx, sigbuf.mpi, sigbuf.len, hash, hash_len) > 0) {
+ ret = RNP_SUCCESS;
+ }
+done:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(evpkey);
+ return ret;
+}
+
+pgp_hash_alg_t
+ecdsa_get_min_hash(pgp_curve_t curve)
+{
+ switch (curve) {
+ case PGP_CURVE_NIST_P_256:
+ case PGP_CURVE_BP256:
+ case PGP_CURVE_P256K1:
+ return PGP_HASH_SHA256;
+ case PGP_CURVE_NIST_P_384:
+ case PGP_CURVE_BP384:
+ return PGP_HASH_SHA384;
+ case PGP_CURVE_NIST_P_521:
+ case PGP_CURVE_BP512:
+ return PGP_HASH_SHA512;
+ default:
+ return PGP_HASH_UNKNOWN;
+ }
+}
diff --git a/src/lib/crypto/eddsa.cpp b/src/lib/crypto/eddsa.cpp
new file mode 100644
index 0000000..8669180
--- /dev/null
+++ b/src/lib/crypto/eddsa.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2017, [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 HOLDERS 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 <string.h>
+#include <botan/ffi.h>
+#include "eddsa.h"
+#include "utils.h"
+
+static bool
+eddsa_load_public_key(botan_pubkey_t *pubkey, const pgp_ec_key_t *keydata)
+{
+ if (keydata->curve != PGP_CURVE_ED25519) {
+ return false;
+ }
+ /*
+ * See draft-ietf-openpgp-rfc4880bis-01 section 13.3
+ */
+ if ((mpi_bytes(&keydata->p) != 33) || (keydata->p.mpi[0] != 0x40)) {
+ return false;
+ }
+ if (botan_pubkey_load_ed25519(pubkey, keydata->p.mpi + 1)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+eddsa_load_secret_key(botan_privkey_t *seckey, const pgp_ec_key_t *keydata)
+{
+ uint8_t keybuf[32] = {0};
+ size_t sz;
+
+ if (keydata->curve != PGP_CURVE_ED25519) {
+ return false;
+ }
+ sz = mpi_bytes(&keydata->x);
+ if (!sz || (sz > 32)) {
+ return false;
+ }
+ mpi2mem(&keydata->x, keybuf + 32 - sz);
+ if (botan_privkey_load_ed25519(seckey, keybuf)) {
+ return false;
+ }
+
+ return true;
+}
+
+rnp_result_t
+eddsa_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret)
+{
+ botan_pubkey_t bpkey = NULL;
+ botan_privkey_t bskey = NULL;
+ rnp_result_t ret = RNP_ERROR_BAD_PARAMETERS;
+
+ if (!eddsa_load_public_key(&bpkey, key) ||
+ botan_pubkey_check_key(bpkey, rng->handle(), 0)) {
+ goto done;
+ }
+
+ if (!secret) {
+ ret = RNP_SUCCESS;
+ goto done;
+ }
+
+ if (!eddsa_load_secret_key(&bskey, key) ||
+ botan_privkey_check_key(bskey, rng->handle(), 0)) {
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ botan_privkey_destroy(bskey);
+ botan_pubkey_destroy(bpkey);
+ return ret;
+}
+
+rnp_result_t
+eddsa_generate(rnp::RNG *rng, pgp_ec_key_t *key)
+{
+ botan_privkey_t eddsa = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ uint8_t key_bits[64];
+
+ if (botan_privkey_create(&eddsa, "Ed25519", NULL, rng->handle()) != 0) {
+ goto end;
+ }
+
+ if (botan_privkey_ed25519_get_privkey(eddsa, key_bits)) {
+ goto end;
+ }
+
+ // First 32 bytes of key_bits are the EdDSA seed (private key)
+ // Second 32 bytes are the EdDSA public key
+
+ mem2mpi(&key->x, key_bits, 32);
+ // insert the required 0x40 prefix on the public key
+ key_bits[31] = 0x40;
+ mem2mpi(&key->p, key_bits + 31, 33);
+ key->curve = PGP_CURVE_ED25519;
+
+ ret = RNP_SUCCESS;
+end:
+ botan_privkey_destroy(eddsa);
+ return ret;
+}
+
+rnp_result_t
+eddsa_verify(const pgp_ec_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t * key)
+{
+ botan_pubkey_t eddsa = NULL;
+ botan_pk_op_verify_t verify_op = NULL;
+ rnp_result_t ret = RNP_ERROR_SIGNATURE_INVALID;
+ uint8_t bn_buf[64] = {0};
+
+ if (!eddsa_load_public_key(&eddsa, key)) {
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ goto done;
+ }
+
+ if (botan_pk_op_verify_create(&verify_op, eddsa, "Pure", 0) != 0) {
+ goto done;
+ }
+
+ if (botan_pk_op_verify_update(verify_op, hash, hash_len) != 0) {
+ goto done;
+ }
+
+ // Unexpected size for Ed25519 signature
+ if ((mpi_bytes(&sig->r) > 32) || (mpi_bytes(&sig->s) > 32)) {
+ goto done;
+ }
+ mpi2mem(&sig->r, &bn_buf[32 - mpi_bytes(&sig->r)]);
+ mpi2mem(&sig->s, &bn_buf[64 - mpi_bytes(&sig->s)]);
+
+ if (botan_pk_op_verify_finish(verify_op, bn_buf, 64) == 0) {
+ ret = RNP_SUCCESS;
+ }
+done:
+ botan_pk_op_verify_destroy(verify_op);
+ botan_pubkey_destroy(eddsa);
+ return ret;
+}
+
+rnp_result_t
+eddsa_sign(rnp::RNG * rng,
+ pgp_ec_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t *key)
+{
+ botan_privkey_t eddsa = NULL;
+ botan_pk_op_sign_t sign_op = NULL;
+ rnp_result_t ret = RNP_ERROR_SIGNING_FAILED;
+ uint8_t bn_buf[64] = {0};
+ size_t sig_size = sizeof(bn_buf);
+
+ if (!eddsa_load_secret_key(&eddsa, key)) {
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ goto done;
+ }
+
+ if (botan_pk_op_sign_create(&sign_op, eddsa, "Pure", 0) != 0) {
+ goto done;
+ }
+
+ if (botan_pk_op_sign_update(sign_op, hash, hash_len) != 0) {
+ goto done;
+ }
+
+ if (botan_pk_op_sign_finish(sign_op, rng->handle(), bn_buf, &sig_size) != 0) {
+ goto done;
+ }
+
+ // Unexpected size...
+ if (sig_size != 64) {
+ goto done;
+ }
+
+ mem2mpi(&sig->r, bn_buf, 32);
+ mem2mpi(&sig->s, bn_buf + 32, 32);
+ ret = RNP_SUCCESS;
+done:
+ botan_pk_op_sign_destroy(sign_op);
+ botan_privkey_destroy(eddsa);
+ return ret;
+}
diff --git a/src/lib/crypto/eddsa.h b/src/lib/crypto/eddsa.h
new file mode 100644
index 0000000..7410a28
--- /dev/null
+++ b/src/lib/crypto/eddsa.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * This code is originally derived from software contributed to
+ * The NetBSD Foundation by Alistair Crooks (agc@netbsd.org), and
+ * carried further by Ribose Inc (https://www.ribose.com).
+ *
+ * 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 HOLDERS 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.
+ */
+
+#ifndef RNP_ED25519_H_
+#define RNP_ED25519_H_
+
+#include "ec.h"
+
+rnp_result_t eddsa_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret);
+/*
+ * curve_len must be 255 currently (for Ed25519)
+ * If Ed448 was supported in the future curve_len=448 would also be allowed.
+ */
+rnp_result_t eddsa_generate(rnp::RNG *rng, pgp_ec_key_t *key);
+
+rnp_result_t eddsa_verify(const pgp_ec_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t * key);
+
+rnp_result_t eddsa_sign(rnp::RNG * rng,
+ pgp_ec_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t *key);
+
+#endif
diff --git a/src/lib/crypto/eddsa_ossl.cpp b/src/lib/crypto/eddsa_ossl.cpp
new file mode 100644
index 0000000..16d8fad
--- /dev/null
+++ b/src/lib/crypto/eddsa_ossl.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 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 <string>
+#include <cassert>
+#include "eddsa.h"
+#include "ec.h"
+#include "ec_ossl.h"
+#include "utils.h"
+#include "bn.h"
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/err.h>
+#include <openssl/ec.h>
+
+rnp_result_t
+eddsa_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret)
+{
+ /* Not implemented in the OpenSSL, so just do basic size checks. */
+ if ((mpi_bytes(&key->p) != 33) || (key->p.mpi[0] != 0x40)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ if (secret && mpi_bytes(&key->x) > 32) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ return RNP_SUCCESS;
+}
+
+rnp_result_t
+eddsa_generate(rnp::RNG *rng, pgp_ec_key_t *key)
+{
+ rnp_result_t ret = ec_generate(rng, key, PGP_PKA_EDDSA, PGP_CURVE_ED25519);
+ if (!ret) {
+ key->curve = PGP_CURVE_ED25519;
+ }
+ return ret;
+}
+
+rnp_result_t
+eddsa_verify(const pgp_ec_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t * key)
+{
+ if ((mpi_bytes(&sig->r) > 32) || (mpi_bytes(&sig->s) > 32)) {
+ RNP_LOG("Invalid EdDSA signature.");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ if ((mpi_bytes(&key->p) != 33) || (key->p.mpi[0] != 0x40)) {
+ RNP_LOG("Invalid EdDSA public key.");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ EVP_PKEY *evpkey = ec_load_key(key->p, NULL, PGP_CURVE_ED25519);
+ if (!evpkey) {
+ RNP_LOG("Failed to load key");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ rnp_result_t ret = RNP_ERROR_SIGNATURE_INVALID;
+ uint8_t sigbuf[64] = {0};
+ /* init context and sign */
+ EVP_PKEY_CTX *ctx = NULL;
+ EVP_MD_CTX * md = EVP_MD_CTX_new();
+ if (!md) {
+ RNP_LOG("Failed to allocate MD ctx: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_DigestVerifyInit(md, &ctx, NULL, NULL, evpkey) <= 0) {
+ RNP_LOG("Failed to initialize signing: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ mpi2mem(&sig->r, &sigbuf[32 - mpi_bytes(&sig->r)]);
+ mpi2mem(&sig->s, &sigbuf[64 - mpi_bytes(&sig->s)]);
+
+ if (EVP_DigestVerify(md, sigbuf, 64, hash, hash_len) > 0) {
+ ret = RNP_SUCCESS;
+ }
+done:
+ /* line below will also free ctx */
+ EVP_MD_CTX_free(md);
+ EVP_PKEY_free(evpkey);
+ return ret;
+}
+
+rnp_result_t
+eddsa_sign(rnp::RNG * rng,
+ pgp_ec_signature_t *sig,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t *key)
+{
+ if (!mpi_bytes(&key->x)) {
+ RNP_LOG("private key not set");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ EVP_PKEY *evpkey = ec_load_key(key->p, &key->x, PGP_CURVE_ED25519);
+ if (!evpkey) {
+ RNP_LOG("Failed to load private key: %lu", ERR_peek_last_error());
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ /* init context and sign */
+ EVP_PKEY_CTX *ctx = NULL;
+ EVP_MD_CTX * md = EVP_MD_CTX_new();
+ if (!md) {
+ RNP_LOG("Failed to allocate MD ctx: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_DigestSignInit(md, &ctx, NULL, NULL, evpkey) <= 0) {
+ RNP_LOG("Failed to initialize signing: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ static_assert((sizeof(sig->r.mpi) == PGP_MPINT_SIZE) && (PGP_MPINT_SIZE >= 64),
+ "invalid mpi type/size");
+ sig->r.len = PGP_MPINT_SIZE;
+ if (EVP_DigestSign(md, sig->r.mpi, &sig->r.len, hash, hash_len) <= 0) {
+ RNP_LOG("Signing failed: %lu", ERR_peek_last_error());
+ sig->r.len = 0;
+ goto done;
+ }
+ assert(sig->r.len == 64);
+ sig->r.len = 32;
+ sig->s.len = 32;
+ memcpy(sig->s.mpi, &sig->r.mpi[32], 32);
+ ret = RNP_SUCCESS;
+done:
+ /* line below will also free ctx */
+ EVP_MD_CTX_free(md);
+ EVP_PKEY_free(evpkey);
+ return ret;
+}
diff --git a/src/lib/crypto/elgamal.cpp b/src/lib/crypto/elgamal.cpp
new file mode 100644
index 0000000..acebf4d
--- /dev/null
+++ b/src/lib/crypto/elgamal.cpp
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (c) 2017-2022 Ribose Inc.
+ * 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 HOLDERS 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 <stdlib.h>
+#include <string.h>
+#include <botan/ffi.h>
+#include <botan/bigint.h>
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <rnp/rnp_def.h>
+#include "elgamal.h"
+#include "utils.h"
+#include "bn.h"
+
+// Max supported key byte size
+#define ELGAMAL_MAX_P_BYTELEN BITS_TO_BYTES(PGP_MPINT_BITS)
+
+static bool
+elgamal_load_public_key(botan_pubkey_t *pubkey, const pgp_eg_key_t *keydata)
+{
+ bignum_t *p = NULL;
+ bignum_t *g = NULL;
+ bignum_t *y = NULL;
+ bool res = false;
+
+ // Check if provided public key byte size is not greater than ELGAMAL_MAX_P_BYTELEN.
+ if (mpi_bytes(&keydata->p) > ELGAMAL_MAX_P_BYTELEN) {
+ goto done;
+ }
+
+ if (!(p = mpi2bn(&keydata->p)) || !(g = mpi2bn(&keydata->g)) ||
+ !(y = mpi2bn(&keydata->y))) {
+ goto done;
+ }
+
+ res =
+ !botan_pubkey_load_elgamal(pubkey, BN_HANDLE_PTR(p), BN_HANDLE_PTR(g), BN_HANDLE_PTR(y));
+done:
+ bn_free(p);
+ bn_free(g);
+ bn_free(y);
+ return res;
+}
+
+static bool
+elgamal_load_secret_key(botan_privkey_t *seckey, const pgp_eg_key_t *keydata)
+{
+ bignum_t *p = NULL;
+ bignum_t *g = NULL;
+ bignum_t *x = NULL;
+ bool res = false;
+
+ // Check if provided secret key byte size is not greater than ELGAMAL_MAX_P_BYTELEN.
+ if (mpi_bytes(&keydata->p) > ELGAMAL_MAX_P_BYTELEN) {
+ goto done;
+ }
+
+ if (!(p = mpi2bn(&keydata->p)) || !(g = mpi2bn(&keydata->g)) ||
+ !(x = mpi2bn(&keydata->x))) {
+ goto done;
+ }
+
+ res = !botan_privkey_load_elgamal(
+ seckey, BN_HANDLE_PTR(p), BN_HANDLE_PTR(g), BN_HANDLE_PTR(x));
+done:
+ bn_free(p);
+ bn_free(g);
+ bn_free(x);
+ return res;
+}
+
+bool
+elgamal_validate_key(const pgp_eg_key_t *key, bool secret)
+{
+ // Check if provided public key byte size is not greater than ELGAMAL_MAX_P_BYTELEN.
+ if (mpi_bytes(&key->p) > ELGAMAL_MAX_P_BYTELEN) {
+ return false;
+ }
+
+ /* Use custom validation since we added some custom validation, and Botan has slow test for
+ * prime for p */
+ try {
+ Botan::BigInt p(key->p.mpi, key->p.len);
+ Botan::BigInt g(key->g.mpi, key->g.len);
+
+ /* 1 < g < p */
+ if ((g.cmp_word(1) != 1) || (g.cmp(p) != -1)) {
+ return false;
+ }
+ /* g ^ (p - 1) = 1 mod p */
+ if (Botan::power_mod(g, p - 1, p).cmp_word(1)) {
+ return false;
+ }
+ /* check for small order subgroups */
+ Botan::Modular_Reducer reducer(p);
+ Botan::BigInt v = g;
+ for (size_t i = 2; i < (1 << 17); i++) {
+ v = reducer.multiply(v, g);
+ if (!v.cmp_word(1)) {
+ RNP_LOG("Small subgroup detected. Order %zu", i);
+ return false;
+ }
+ }
+ if (!secret) {
+ return true;
+ }
+ /* check that g ^ x = y (mod p) */
+ Botan::BigInt y(key->y.mpi, key->y.len);
+ Botan::BigInt x(key->x.mpi, key->x.len);
+ return Botan::power_mod(g, x, p) == y;
+ } catch (const std::exception &e) {
+ RNP_LOG("%s", e.what());
+ return false;
+ }
+}
+
+rnp_result_t
+elgamal_encrypt_pkcs1(rnp::RNG * rng,
+ pgp_eg_encrypted_t *out,
+ const uint8_t * in,
+ size_t in_len,
+ const pgp_eg_key_t *key)
+{
+ botan_pubkey_t b_key = NULL;
+ botan_pk_op_encrypt_t op_ctx = NULL;
+ rnp_result_t ret = RNP_ERROR_BAD_PARAMETERS;
+ /* Max size of an output len is twice an order of underlying group (p length) */
+ uint8_t enc_buf[ELGAMAL_MAX_P_BYTELEN * 2] = {0};
+ size_t p_len;
+
+ if (!elgamal_load_public_key(&b_key, key)) {
+ RNP_LOG("Failed to load public key");
+ goto end;
+ }
+
+ /* Size of output buffer must be equal to twice the size of key byte len.
+ * as ElGamal encryption outputs concatenation of two components, both
+ * of size equal to size of public key byte len.
+ * Successful call to botan's ElGamal encryption will return output that's
+ * always 2*pubkey size.
+ */
+ p_len = mpi_bytes(&key->p) * 2;
+
+ if (botan_pk_op_encrypt_create(&op_ctx, b_key, "PKCS1v15", 0) ||
+ botan_pk_op_encrypt(op_ctx, rng->handle(), enc_buf, &p_len, in, in_len)) {
+ RNP_LOG("Failed to create operation context");
+ goto end;
+ }
+
+ /*
+ * Botan's ElGamal formats the g^k and msg*(y^k) together into a single byte string.
+ * We have to parse out the two values after encryption, as rnp stores those values
+ * separatelly.
+ *
+ * We don't trim zeros from octet string as it is done before final marshalling
+ * (add_packet_body_mpi)
+ *
+ * We must assume that botan copies even number of bytes to output buffer (to avoid
+ * memory corruption)
+ */
+ p_len /= 2;
+ if (mem2mpi(&out->g, enc_buf, p_len) && mem2mpi(&out->m, enc_buf + p_len, p_len)) {
+ ret = RNP_SUCCESS;
+ }
+end:
+ botan_pk_op_encrypt_destroy(op_ctx);
+ botan_pubkey_destroy(b_key);
+ return ret;
+}
+
+rnp_result_t
+elgamal_decrypt_pkcs1(rnp::RNG * rng,
+ uint8_t * out,
+ size_t * out_len,
+ const pgp_eg_encrypted_t *in,
+ const pgp_eg_key_t * key)
+{
+ botan_privkey_t b_key = NULL;
+ botan_pk_op_decrypt_t op_ctx = NULL;
+ rnp_result_t ret = RNP_ERROR_BAD_PARAMETERS;
+ uint8_t enc_buf[ELGAMAL_MAX_P_BYTELEN * 2] = {0};
+ size_t p_len;
+ size_t g_len;
+ size_t m_len;
+
+ if (!mpi_bytes(&key->x)) {
+ RNP_LOG("empty secret key");
+ goto end;
+ }
+
+ // Check if provided public key byte size is not greater than ELGAMAL_MAX_P_BYTELEN.
+ p_len = mpi_bytes(&key->p);
+ g_len = mpi_bytes(&in->g);
+ m_len = mpi_bytes(&in->m);
+
+ if ((2 * p_len > sizeof(enc_buf)) || (g_len > p_len) || (m_len > p_len)) {
+ RNP_LOG("Unsupported/wrong public key or encrypted data");
+ goto end;
+ }
+
+ if (!elgamal_load_secret_key(&b_key, key)) {
+ RNP_LOG("Failed to load private key");
+ goto end;
+ }
+
+ /* Botan expects ciphertext to be concatenated (g^k | encrypted m). Size must
+ * be equal to twice the byte size of public key, potentially prepended with zeros.
+ */
+ memcpy(&enc_buf[p_len - g_len], in->g.mpi, g_len);
+ memcpy(&enc_buf[2 * p_len - m_len], in->m.mpi, m_len);
+
+ *out_len = p_len;
+ if (botan_pk_op_decrypt_create(&op_ctx, b_key, "PKCS1v15", 0) ||
+ botan_pk_op_decrypt(op_ctx, out, out_len, enc_buf, 2 * p_len)) {
+ RNP_LOG("Decryption failed");
+ goto end;
+ }
+ ret = RNP_SUCCESS;
+end:
+ botan_pk_op_decrypt_destroy(op_ctx);
+ botan_privkey_destroy(b_key);
+ return ret;
+}
+
+rnp_result_t
+elgamal_generate(rnp::RNG *rng, pgp_eg_key_t *key, size_t keybits)
+{
+ if ((keybits < 1024) || (keybits > PGP_MPINT_BITS)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ botan_privkey_t key_priv = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ bignum_t * p = bn_new();
+ bignum_t * g = bn_new();
+ bignum_t * y = bn_new();
+ bignum_t * x = bn_new();
+
+ if (!p || !g || !y || !x) {
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto end;
+ }
+
+start:
+ if (botan_privkey_create_elgamal(&key_priv, rng->handle(), keybits, keybits - 1)) {
+ RNP_LOG("Wrong parameters");
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ goto end;
+ }
+
+ if (botan_privkey_get_field(BN_HANDLE_PTR(y), key_priv, "y")) {
+ RNP_LOG("Failed to obtain public key");
+ goto end;
+ }
+ if (bn_num_bytes(*y) < BITS_TO_BYTES(keybits)) {
+ botan_privkey_destroy(key_priv);
+ goto start;
+ }
+
+ if (botan_privkey_get_field(BN_HANDLE_PTR(p), key_priv, "p") ||
+ botan_privkey_get_field(BN_HANDLE_PTR(g), key_priv, "g") ||
+ botan_privkey_get_field(BN_HANDLE_PTR(y), key_priv, "y") ||
+ botan_privkey_get_field(BN_HANDLE_PTR(x), key_priv, "x")) {
+ RNP_LOG("Botan FFI call failed");
+ ret = RNP_ERROR_GENERIC;
+ goto end;
+ }
+
+ if (bn2mpi(p, &key->p) && bn2mpi(g, &key->g) && bn2mpi(y, &key->y) && bn2mpi(x, &key->x)) {
+ ret = RNP_SUCCESS;
+ }
+end:
+ bn_free(p);
+ bn_free(g);
+ bn_free(y);
+ bn_free(x);
+ botan_privkey_destroy(key_priv);
+ return ret;
+}
diff --git a/src/lib/crypto/elgamal.h b/src/lib/crypto/elgamal.h
new file mode 100644
index 0000000..42d0555
--- /dev/null
+++ b/src/lib/crypto/elgamal.h
@@ -0,0 +1,116 @@
+/*-
+ * Copyright (c) 2017-2022 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef RNP_ELG_H_
+#define RNP_ELG_H_
+
+#include <stdint.h>
+#include "crypto/rng.h"
+#include "crypto/mpi.h"
+
+typedef struct pgp_eg_key_t {
+ pgp_mpi_t p;
+ pgp_mpi_t g;
+ pgp_mpi_t y;
+ /* secret mpi */
+ pgp_mpi_t x;
+} pgp_eg_key_t;
+
+typedef struct pgp_eg_signature_t {
+ /* This is kept only for packet reading. Implementation MUST
+ * not create elgamal signatures */
+ pgp_mpi_t r;
+ pgp_mpi_t s;
+} pgp_eg_signature_t;
+
+typedef struct pgp_eg_encrypted_t {
+ pgp_mpi_t g;
+ pgp_mpi_t m;
+} pgp_eg_encrypted_t;
+
+bool elgamal_validate_key(const pgp_eg_key_t *key, bool secret);
+
+/*
+ * Performs ElGamal encryption
+ * Result of an encryption is composed of two parts - g2k and encm
+ *
+ * @param rng initialized rnp::RNG
+ * @param out encryption result
+ * @param in plaintext to be encrypted
+ * @param in_len length of the plaintext
+ * @param key public key to be used for encryption
+ *
+ * @pre out: must be valid pointer to corresponding structure
+ * @pre in_len: can't be bigger than byte size of `p'
+ *
+ * @return RNP_SUCCESS
+ * RNP_ERROR_OUT_OF_MEMORY allocation failure
+ * RNP_ERROR_BAD_PARAMETERS wrong input provided
+ */
+rnp_result_t elgamal_encrypt_pkcs1(rnp::RNG * rng,
+ pgp_eg_encrypted_t *out,
+ const uint8_t * in,
+ size_t in_len,
+ const pgp_eg_key_t *key);
+
+/*
+ * Performs ElGamal decryption
+ *
+ * @param rng initialized rnp::RNG
+ * @param out decrypted plaintext. Must be capable of storing at least as much bytes as p size
+ * @param out_len number of plaintext bytes written will be put here
+ * @param in encrypted data
+ * @param key private key
+ *
+ * @pre out, in: must be valid pointers
+ * @pre out: length must be long enough to store decrypted data. Max size of
+ * decrypted data is equal to bytes size of `p'
+ *
+ * @return RNP_SUCCESS
+ * RNP_ERROR_OUT_OF_MEMORY allocation failure
+ * RNP_ERROR_BAD_PARAMETERS wrong input provided
+ */
+rnp_result_t elgamal_decrypt_pkcs1(rnp::RNG * rng,
+ uint8_t * out,
+ size_t * out_len,
+ const pgp_eg_encrypted_t *in,
+ const pgp_eg_key_t * key);
+
+/*
+ * Generates ElGamal key
+ *
+ * @param rng pointer to PRNG
+ * @param key generated key
+ * @param keybits key bitlen
+ *
+ * @pre `keybits' > 1024
+ *
+ * @returns RNP_ERROR_BAD_PARAMETERS wrong parameters provided
+ * RNP_ERROR_GENERIC internal error
+ * RNP_SUCCESS key generated and copied to `seckey'
+ */
+rnp_result_t elgamal_generate(rnp::RNG *rng, pgp_eg_key_t *key, size_t keybits);
+#endif
diff --git a/src/lib/crypto/elgamal_ossl.cpp b/src/lib/crypto/elgamal_ossl.cpp
new file mode 100644
index 0000000..f3fa381
--- /dev/null
+++ b/src/lib/crypto/elgamal_ossl.cpp
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 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 <cstdlib>
+#include <string>
+#include <cassert>
+#include <rnp/rnp_def.h>
+#include "elgamal.h"
+#include "dl_ossl.h"
+#include "utils.h"
+#include "bn.h"
+#include "mem.h"
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+// Max supported key byte size
+#define ELGAMAL_MAX_P_BYTELEN BITS_TO_BYTES(PGP_MPINT_BITS)
+
+bool
+elgamal_validate_key(const pgp_eg_key_t *key, bool secret)
+{
+ BN_CTX *ctx = BN_CTX_new();
+ if (!ctx) {
+ RNP_LOG("Allocation failed.");
+ return false;
+ }
+ BN_CTX_start(ctx);
+ bool res = false;
+ bignum_t * p = mpi2bn(&key->p);
+ bignum_t * g = mpi2bn(&key->g);
+ bignum_t * p1 = BN_CTX_get(ctx);
+ bignum_t * r = BN_CTX_get(ctx);
+ bignum_t * y = NULL;
+ bignum_t * x = NULL;
+ BN_RECP_CTX *rctx = NULL;
+
+ if (!p || !g || !p1 || !r) {
+ goto done;
+ }
+
+ /* 1 < g < p */
+ if ((BN_cmp(g, BN_value_one()) != 1) || (BN_cmp(g, p) != -1)) {
+ RNP_LOG("Invalid g value.");
+ goto done;
+ }
+ /* g ^ (p - 1) = 1 mod p */
+ if (!BN_copy(p1, p) || !BN_sub_word(p1, 1) || !BN_mod_exp(r, g, p1, p, ctx)) {
+ RNP_LOG("g exp failed.");
+ goto done;
+ }
+ if (BN_cmp(r, BN_value_one()) != 0) {
+ RNP_LOG("Wrong g exp value.");
+ goto done;
+ }
+ /* check for small order subgroups */
+ rctx = BN_RECP_CTX_new();
+ if (!rctx || !BN_RECP_CTX_set(rctx, p, ctx) || !BN_copy(r, g)) {
+ RNP_LOG("Failed to init RECP context.");
+ goto done;
+ }
+ for (size_t i = 2; i < (1 << 17); i++) {
+ if (!BN_mod_mul_reciprocal(r, r, g, rctx, ctx)) {
+ RNP_LOG("Multiplication failed.");
+ goto done;
+ }
+ if (BN_cmp(r, BN_value_one()) == 0) {
+ RNP_LOG("Small subgroup detected. Order %zu", i);
+ goto done;
+ }
+ }
+ if (!secret) {
+ res = true;
+ goto done;
+ }
+ /* check that g ^ x = y (mod p) */
+ x = mpi2bn(&key->x);
+ y = mpi2bn(&key->y);
+ if (!x || !y) {
+ goto done;
+ }
+ res = BN_mod_exp(r, g, x, p, ctx) && !BN_cmp(r, y);
+done:
+ BN_CTX_free(ctx);
+ BN_RECP_CTX_free(rctx);
+ bn_free(p);
+ bn_free(g);
+ bn_free(y);
+ bn_free(x);
+ return res;
+}
+
+static bool
+pkcs1v15_pad(uint8_t *out, size_t out_len, const uint8_t *in, size_t in_len)
+{
+ assert(out && in);
+ if (out_len < in_len + 11) {
+ return false;
+ }
+ out[0] = 0x00;
+ out[1] = 0x02;
+ size_t rnd = out_len - in_len - 3;
+ out[2 + rnd] = 0x00;
+ if (RAND_bytes(&out[2], rnd) != 1) {
+ return false;
+ }
+ for (size_t i = 2; i < 2 + rnd; i++) {
+ /* we need non-zero bytes */
+ size_t cntr = 16;
+ while (!out[i] && (cntr--) && (RAND_bytes(&out[i], 1) == 1)) {
+ }
+ if (!out[i]) {
+ RNP_LOG("Something is wrong with RNG.");
+ return false;
+ }
+ }
+ memcpy(out + rnd + 3, in, in_len);
+ return true;
+}
+
+static bool
+pkcs1v15_unpad(size_t *padlen, const uint8_t *in, size_t in_len, bool skip0)
+{
+ if (in_len <= (size_t)(11 - skip0)) {
+ return false;
+ }
+ if (!skip0 && in[0]) {
+ return false;
+ }
+ if (in[1 - skip0] != 0x02) {
+ return false;
+ }
+ size_t pad = 2 - skip0;
+ while ((pad < in_len) && in[pad]) {
+ pad++;
+ }
+ if (pad >= in_len) {
+ return false;
+ }
+ *padlen = pad + 1;
+ return true;
+}
+
+rnp_result_t
+elgamal_encrypt_pkcs1(rnp::RNG * rng,
+ pgp_eg_encrypted_t *out,
+ const uint8_t * in,
+ size_t in_len,
+ const pgp_eg_key_t *key)
+{
+ pgp_mpi_t mm = {};
+ mm.len = key->p.len;
+ if (!pkcs1v15_pad(mm.mpi, mm.len, in, in_len)) {
+ RNP_LOG("Failed to add PKCS1 v1.5 padding.");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ BN_CTX * ctx = BN_CTX_new();
+ if (!ctx) {
+ RNP_LOG("Allocation failed.");
+ return RNP_ERROR_OUT_OF_MEMORY;
+ }
+ BN_CTX_start(ctx);
+ BN_MONT_CTX *mctx = BN_MONT_CTX_new();
+ bignum_t * m = mpi2bn(&mm);
+ bignum_t * p = mpi2bn(&key->p);
+ bignum_t * g = mpi2bn(&key->g);
+ bignum_t * y = mpi2bn(&key->y);
+ bignum_t * c1 = BN_CTX_get(ctx);
+ bignum_t * c2 = BN_CTX_get(ctx);
+ bignum_t * k = BN_secure_new();
+ if (!mctx || !m || !p || !g || !y || !c1 || !c2 || !k) {
+ RNP_LOG("Allocation failed.");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ /* initialize Montgomery context */
+ if (BN_MONT_CTX_set(mctx, p, ctx) < 1) {
+ RNP_LOG("Failed to setup Montgomery context.");
+ goto done;
+ }
+ int res;
+ /* must not fail */
+ res = BN_rshift1(c1, p);
+ assert(res == 1);
+ if (res < 1) {
+ RNP_LOG("BN_rshift1 failed.");
+ goto done;
+ }
+ /* generate k */
+ if (BN_rand_range(k, c1) < 1) {
+ RNP_LOG("Failed to generate k.");
+ goto done;
+ }
+ /* calculate c1 = g ^ k (mod p) */
+ if (BN_mod_exp_mont_consttime(c1, g, k, p, ctx, mctx) < 1) {
+ RNP_LOG("Exponentiation 1 failed");
+ goto done;
+ }
+ /* calculate c2 = m * y ^ k (mod p)*/
+ if (BN_mod_exp_mont_consttime(c2, y, k, p, ctx, mctx) < 1) {
+ RNP_LOG("Exponentiation 2 failed");
+ goto done;
+ }
+ if (BN_mod_mul(c2, c2, m, p, ctx) < 1) {
+ RNP_LOG("Multiplication failed");
+ goto done;
+ }
+ res = bn2mpi(c1, &out->g) && bn2mpi(c2, &out->m);
+ assert(res == 1);
+ ret = RNP_SUCCESS;
+done:
+ BN_MONT_CTX_free(mctx);
+ BN_CTX_free(ctx);
+ bn_free(m);
+ bn_free(p);
+ bn_free(g);
+ bn_free(y);
+ bn_free(k);
+ return ret;
+}
+
+rnp_result_t
+elgamal_decrypt_pkcs1(rnp::RNG * rng,
+ uint8_t * out,
+ size_t * out_len,
+ const pgp_eg_encrypted_t *in,
+ const pgp_eg_key_t * key)
+{
+ if (!mpi_bytes(&key->x)) {
+ RNP_LOG("Secret key not set.");
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ BN_CTX *ctx = BN_CTX_new();
+ if (!ctx) {
+ RNP_LOG("Allocation failed.");
+ return RNP_ERROR_OUT_OF_MEMORY;
+ }
+ pgp_mpi_t mm = {};
+ size_t padlen = 0;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ BN_CTX_start(ctx);
+ BN_MONT_CTX *mctx = BN_MONT_CTX_new();
+ bignum_t * p = mpi2bn(&key->p);
+ bignum_t * g = mpi2bn(&key->g);
+ bignum_t * x = mpi2bn(&key->x);
+ bignum_t * c1 = mpi2bn(&in->g);
+ bignum_t * c2 = mpi2bn(&in->m);
+ bignum_t * s = BN_CTX_get(ctx);
+ bignum_t * m = BN_secure_new();
+ if (!mctx || !p || !g || !x || !c1 || !c2 || !m) {
+ RNP_LOG("Allocation failed.");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ /* initialize Montgomery context */
+ if (BN_MONT_CTX_set(mctx, p, ctx) < 1) {
+ RNP_LOG("Failed to setup Montgomery context.");
+ goto done;
+ }
+ /* calculate s = c1 ^ x (mod p) */
+ if (BN_mod_exp_mont_consttime(s, c1, x, p, ctx, mctx) < 1) {
+ RNP_LOG("Exponentiation 1 failed");
+ goto done;
+ }
+ /* calculate s^-1 (mod p) */
+ BN_set_flags(s, BN_FLG_CONSTTIME);
+ if (!BN_mod_inverse(s, s, p, ctx)) {
+ RNP_LOG("Failed to calculate inverse.");
+ goto done;
+ }
+ /* calculate m = c2 * s ^ -1 (mod p)*/
+ if (BN_mod_mul(m, c2, s, p, ctx) < 1) {
+ RNP_LOG("Multiplication failed");
+ goto done;
+ }
+ bool res;
+ res = bn2mpi(m, &mm);
+ assert(res);
+ if (!res) {
+ RNP_LOG("bn2mpi failed.");
+ goto done;
+ }
+ /* unpad, handling skipped leftmost 0 case */
+ if (!pkcs1v15_unpad(&padlen, mm.mpi, mm.len, mm.len == key->p.len - 1)) {
+ RNP_LOG("Unpad failed.");
+ goto done;
+ }
+ *out_len = mm.len - padlen;
+ memcpy(out, &mm.mpi[padlen], *out_len);
+ ret = RNP_SUCCESS;
+done:
+ secure_clear(mm.mpi, PGP_MPINT_SIZE);
+ BN_MONT_CTX_free(mctx);
+ BN_CTX_free(ctx);
+ bn_free(p);
+ bn_free(g);
+ bn_free(x);
+ bn_free(c1);
+ bn_free(c2);
+ bn_free(m);
+ return ret;
+}
+
+rnp_result_t
+elgamal_generate(rnp::RNG *rng, pgp_eg_key_t *key, size_t keybits)
+{
+ if ((keybits < 1024) || (keybits > PGP_MPINT_BITS)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ const DH * dh = NULL;
+ EVP_PKEY * pkey = NULL;
+ EVP_PKEY * parmkey = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+
+ /* Generate DH params, which usable for ElGamal as well */
+ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DH, NULL);
+ if (!ctx) {
+ RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
+ return ret;
+ }
+ if (EVP_PKEY_paramgen_init(ctx) <= 0) {
+ RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, keybits) <= 0) {
+ RNP_LOG("Failed to set key bits: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ /* OpenSSL correctly handles case with g = 5, making sure that g is primitive root of
+ * q-group */
+ if (EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, DH_GENERATOR_5) <= 0) {
+ RNP_LOG("Failed to set key generator: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_paramgen(ctx, &parmkey) <= 0) {
+ RNP_LOG("Failed to generate parameters: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ EVP_PKEY_CTX_free(ctx);
+ /* Generate DH (ElGamal) key */
+start:
+ ctx = EVP_PKEY_CTX_new(parmkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_keygen_init(ctx) <= 0) {
+ RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
+ RNP_LOG("ElGamal keygen failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ dh = EVP_PKEY_get0_DH(pkey);
+ if (!dh) {
+ RNP_LOG("Failed to retrieve DH key: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (BITS_TO_BYTES(BN_num_bits(DH_get0_pub_key(dh))) != BITS_TO_BYTES(keybits)) {
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto start;
+ }
+
+ const bignum_t *p;
+ const bignum_t *g;
+ const bignum_t *y;
+ const bignum_t *x;
+ p = DH_get0_p(dh);
+ g = DH_get0_g(dh);
+ y = DH_get0_pub_key(dh);
+ x = DH_get0_priv_key(dh);
+ if (!p || !g || !y || !x) {
+ ret = RNP_ERROR_BAD_STATE;
+ goto done;
+ }
+ bn2mpi(p, &key->p);
+ bn2mpi(g, &key->g);
+ bn2mpi(y, &key->y);
+ bn2mpi(x, &key->x);
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(parmkey);
+ EVP_PKEY_free(pkey);
+ return ret;
+}
diff --git a/src/lib/crypto/hash.cpp b/src/lib/crypto/hash.cpp
new file mode 100644
index 0000000..250deec
--- /dev/null
+++ b/src/lib/crypto/hash.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2017-2022 Ribose Inc.
+ * 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 HOLDERS 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 "hash_botan.hpp"
+#include "logging.h"
+#include <cassert>
+
+static const id_str_pair botan_alg_map[] = {
+ {PGP_HASH_MD5, "MD5"},
+ {PGP_HASH_SHA1, "SHA-1"},
+ {PGP_HASH_RIPEMD, "RIPEMD-160"},
+ {PGP_HASH_SHA256, "SHA-256"},
+ {PGP_HASH_SHA384, "SHA-384"},
+ {PGP_HASH_SHA512, "SHA-512"},
+ {PGP_HASH_SHA224, "SHA-224"},
+#if defined(ENABLE_SM2)
+ {PGP_HASH_SM3, "SM3"},
+#endif
+ {PGP_HASH_SHA3_256, "SHA-3(256)"},
+ {PGP_HASH_SHA3_512, "SHA-3(512)"},
+ {0, NULL},
+};
+
+namespace rnp {
+
+Hash_Botan::Hash_Botan(pgp_hash_alg_t alg) : Hash(alg)
+{
+ auto name = Hash_Botan::name_backend(alg);
+ if (!name) {
+ throw rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+ }
+
+ fn_ = Botan::HashFunction::create(name);
+ if (!fn_) {
+ RNP_LOG("Error creating hash object for '%s'", name);
+ throw rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+ }
+
+ assert(size_ == fn_->output_length());
+}
+
+Hash_Botan::Hash_Botan(const Hash_Botan &src) : Hash(src.alg_)
+{
+ if (!src.fn_) {
+ throw rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+ }
+ fn_ = src.fn_->copy_state();
+}
+
+Hash_Botan::~Hash_Botan()
+{
+}
+
+std::unique_ptr<Hash_Botan>
+Hash_Botan::create(pgp_hash_alg_t alg)
+{
+ return std::unique_ptr<Hash_Botan>(new Hash_Botan(alg));
+}
+
+std::unique_ptr<Hash>
+Hash_Botan::clone() const
+{
+ return std::unique_ptr<Hash>(new Hash_Botan(*this));
+}
+
+void
+Hash_Botan::add(const void *buf, size_t len)
+{
+ if (!fn_) {
+ throw rnp_exception(RNP_ERROR_NULL_POINTER);
+ }
+ fn_->update(static_cast<const uint8_t *>(buf), len);
+}
+
+size_t
+Hash_Botan::finish(uint8_t *digest)
+{
+ if (!fn_) {
+ return 0;
+ }
+ size_t outlen = size_;
+ if (digest) {
+ fn_->final(digest);
+ }
+ fn_ = nullptr;
+ size_ = 0;
+ return outlen;
+}
+
+const char *
+Hash_Botan::name_backend(pgp_hash_alg_t alg)
+{
+ return id_str_pair::lookup(botan_alg_map, alg);
+}
+
+CRC24_Botan::CRC24_Botan()
+{
+ fn_ = Botan::HashFunction::create("CRC24");
+ if (!fn_) {
+ RNP_LOG("Error creating CRC24 object");
+ throw rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+ }
+ assert(3 == fn_->output_length());
+}
+
+CRC24_Botan::~CRC24_Botan()
+{
+}
+
+std::unique_ptr<CRC24_Botan>
+CRC24_Botan::create()
+{
+ return std::unique_ptr<CRC24_Botan>(new CRC24_Botan());
+}
+
+void
+CRC24_Botan::add(const void *buf, size_t len)
+{
+ if (!fn_) {
+ throw rnp_exception(RNP_ERROR_NULL_POINTER);
+ }
+ fn_->update(static_cast<const uint8_t *>(buf), len);
+}
+
+std::array<uint8_t, 3>
+CRC24_Botan::finish()
+{
+ if (!fn_) {
+ throw rnp_exception(RNP_ERROR_NULL_POINTER);
+ }
+ std::array<uint8_t, 3> crc{};
+ fn_->final(crc.data());
+ fn_ = nullptr;
+ return crc;
+}
+
+} // namespace rnp
diff --git a/src/lib/crypto/hash.hpp b/src/lib/crypto/hash.hpp
new file mode 100644
index 0000000..7fcb817
--- /dev/null
+++ b/src/lib/crypto/hash.hpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017-2022 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef CRYPTO_HASH_H_
+#define CRYPTO_HASH_H_
+
+#include <repgp/repgp_def.h>
+#include "types.h"
+#include "config.h"
+#include <memory>
+#include <vector>
+#include <array>
+
+/**
+ * Output size (in bytes) of biggest supported hash algo
+ */
+#define PGP_MAX_HASH_SIZE (64)
+
+namespace rnp {
+class Hash {
+ protected:
+ pgp_hash_alg_t alg_;
+ size_t size_;
+ Hash(pgp_hash_alg_t alg) : alg_(alg)
+ {
+ size_ = Hash::size(alg);
+ };
+
+ public:
+ pgp_hash_alg_t alg() const;
+ size_t size() const;
+
+ static std::unique_ptr<Hash> create(pgp_hash_alg_t alg);
+ virtual std::unique_ptr<Hash> clone() const = 0;
+
+ virtual void add(const void *buf, size_t len) = 0;
+ virtual void add(uint32_t val);
+ virtual void add(const pgp_mpi_t &mpi);
+ virtual size_t finish(uint8_t *digest = NULL) = 0;
+
+ virtual ~Hash();
+
+ /* Hash algorithm by string representation from cleartext-signed text */
+ static pgp_hash_alg_t alg(const char *name);
+ /* Hash algorithm representation for cleartext-signed text */
+ static const char *name(pgp_hash_alg_t alg);
+ /* Size of the hash algorithm output or 0 if algorithm is unknown */
+ static size_t size(pgp_hash_alg_t alg);
+};
+
+class CRC24 {
+ protected:
+ CRC24(){};
+
+ public:
+ static std::unique_ptr<CRC24> create();
+
+ virtual void add(const void *buf, size_t len) = 0;
+ virtual std::array<uint8_t, 3> finish() = 0;
+
+ virtual ~CRC24(){};
+};
+
+class HashList {
+ public:
+ std::vector<std::unique_ptr<Hash>> hashes;
+
+ void add_alg(pgp_hash_alg_t alg);
+ const Hash *get(pgp_hash_alg_t alg) const;
+ void add(const void *buf, size_t len);
+};
+
+} // namespace rnp
+
+#endif
diff --git a/src/lib/crypto/hash_botan.hpp b/src/lib/crypto/hash_botan.hpp
new file mode 100644
index 0000000..942e3a8
--- /dev/null
+++ b/src/lib/crypto/hash_botan.hpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2022 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef CRYPTO_HASH_BOTAN_HPP_
+#define CRYPTO_HASH_BOTAN_HPP_
+
+#include "hash.hpp"
+#include <botan/hash.h>
+
+namespace rnp {
+class Hash_Botan : public Hash {
+ private:
+ std::unique_ptr<Botan::HashFunction> fn_;
+
+ Hash_Botan(pgp_hash_alg_t alg);
+ Hash_Botan(const Hash_Botan &src);
+
+ public:
+ virtual ~Hash_Botan();
+
+ static std::unique_ptr<Hash_Botan> create(pgp_hash_alg_t alg);
+ std::unique_ptr<Hash> clone() const override;
+
+ void add(const void *buf, size_t len) override;
+ size_t finish(uint8_t *digest = NULL) override;
+
+ static const char *name_backend(pgp_hash_alg_t alg);
+};
+
+class CRC24_Botan : public CRC24 {
+ std::unique_ptr<Botan::HashFunction> fn_;
+ CRC24_Botan();
+
+ public:
+ virtual ~CRC24_Botan();
+
+ static std::unique_ptr<CRC24_Botan> create();
+
+ void add(const void *buf, size_t len) override;
+ std::array<uint8_t, 3> finish() override;
+};
+
+} // namespace rnp
+
+#endif \ No newline at end of file
diff --git a/src/lib/crypto/hash_common.cpp b/src/lib/crypto/hash_common.cpp
new file mode 100644
index 0000000..69b400b
--- /dev/null
+++ b/src/lib/crypto/hash_common.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021-2022 Ribose Inc.
+ * 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 HOLDERS 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 "config.h"
+#include "hash.hpp"
+#include "types.h"
+#include "utils.h"
+#include "str-utils.h"
+#include "hash_sha1cd.hpp"
+#if defined(CRYPTO_BACKEND_BOTAN)
+#include "hash_botan.hpp"
+#endif
+#if defined(CRYPTO_BACKEND_OPENSSL)
+#include "hash_ossl.hpp"
+#include "hash_crc24.hpp"
+#endif
+
+static const struct hash_alg_map_t {
+ pgp_hash_alg_t type;
+ const char * name;
+ size_t len;
+} hash_alg_map[] = {{PGP_HASH_MD5, "MD5", 16},
+ {PGP_HASH_SHA1, "SHA1", 20},
+ {PGP_HASH_RIPEMD, "RIPEMD160", 20},
+ {PGP_HASH_SHA256, "SHA256", 32},
+ {PGP_HASH_SHA384, "SHA384", 48},
+ {PGP_HASH_SHA512, "SHA512", 64},
+ {PGP_HASH_SHA224, "SHA224", 28},
+ {PGP_HASH_SM3, "SM3", 32},
+ {PGP_HASH_SHA3_256, "SHA3-256", 32},
+ {PGP_HASH_SHA3_512, "SHA3-512", 64}};
+
+namespace rnp {
+
+pgp_hash_alg_t
+Hash::alg() const
+{
+ return alg_;
+}
+
+size_t
+Hash::size() const
+{
+ return Hash::size(alg_);
+}
+
+std::unique_ptr<Hash>
+Hash::create(pgp_hash_alg_t alg)
+{
+ if (alg == PGP_HASH_SHA1) {
+ return Hash_SHA1CD::create();
+ }
+#if !defined(ENABLE_SM2)
+ if (alg == PGP_HASH_SM3) {
+ RNP_LOG("SM3 hash is not available.");
+ throw rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+ }
+#endif
+#if defined(CRYPTO_BACKEND_OPENSSL)
+ return Hash_OpenSSL::create(alg);
+#elif defined(CRYPTO_BACKEND_BOTAN)
+ return Hash_Botan::create(alg);
+#else
+#error "Crypto backend not specified"
+#endif
+}
+
+std::unique_ptr<CRC24>
+CRC24::create()
+{
+#if defined(CRYPTO_BACKEND_OPENSSL)
+ return CRC24_RNP::create();
+#elif defined(CRYPTO_BACKEND_BOTAN)
+ return CRC24_Botan::create();
+#else
+#error "Crypto backend not specified"
+#endif
+}
+
+void
+Hash::add(uint32_t val)
+{
+ uint8_t ibuf[4];
+ STORE32BE(ibuf, val);
+ add(ibuf, sizeof(ibuf));
+}
+
+void
+Hash::add(const pgp_mpi_t &val)
+{
+ size_t len = mpi_bytes(&val);
+ size_t idx = 0;
+ while ((idx < len) && (!val.mpi[idx])) {
+ idx++;
+ }
+
+ if (idx >= len) {
+ add(0);
+ return;
+ }
+
+ add(len - idx);
+ if (val.mpi[idx] & 0x80) {
+ uint8_t padbyte = 0;
+ add(&padbyte, 1);
+ }
+ add(val.mpi + idx, len - idx);
+}
+
+Hash::~Hash()
+{
+}
+
+pgp_hash_alg_t
+Hash::alg(const char *name)
+{
+ if (!name) {
+ return PGP_HASH_UNKNOWN;
+ }
+ for (size_t i = 0; i < ARRAY_SIZE(hash_alg_map); i++) {
+ if (rnp::str_case_eq(name, hash_alg_map[i].name)) {
+ return hash_alg_map[i].type;
+ }
+ }
+ return PGP_HASH_UNKNOWN;
+}
+
+const char *
+Hash::name(pgp_hash_alg_t alg)
+{
+ const char *ret = NULL;
+ ARRAY_LOOKUP_BY_ID(hash_alg_map, type, name, alg, ret);
+ return ret;
+}
+
+size_t
+Hash::size(pgp_hash_alg_t alg)
+{
+ size_t val = 0;
+ ARRAY_LOOKUP_BY_ID(hash_alg_map, type, len, alg, val);
+ return val;
+}
+
+void
+HashList::add_alg(pgp_hash_alg_t alg)
+{
+ if (!get(alg)) {
+ hashes.emplace_back(rnp::Hash::create(alg));
+ }
+}
+
+const Hash *
+HashList::get(pgp_hash_alg_t alg) const
+{
+ for (auto &hash : hashes) {
+ if (hash->alg() == alg) {
+ return hash.get();
+ }
+ }
+ return NULL;
+}
+
+void
+HashList::add(const void *buf, size_t len)
+{
+ for (auto &hash : hashes) {
+ hash->add(buf, len);
+ }
+}
+
+} // namespace rnp
diff --git a/src/lib/crypto/hash_crc24.cpp b/src/lib/crypto/hash_crc24.cpp
new file mode 100644
index 0000000..54f4d96
--- /dev/null
+++ b/src/lib/crypto/hash_crc24.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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 <stdint.h>
+#include <stddef.h>
+#include "utils.h"
+#include "hash_crc24.hpp"
+
+static const uint32_t T0[256] = {
+ 0x00000000, 0x00FB4C86, 0x000DD58A, 0x00F6990C, 0x00E1E693, 0x001AAA15, 0x00EC3319,
+ 0x00177F9F, 0x003981A1, 0x00C2CD27, 0x0034542B, 0x00CF18AD, 0x00D86732, 0x00232BB4,
+ 0x00D5B2B8, 0x002EFE3E, 0x00894EC5, 0x00720243, 0x00849B4F, 0x007FD7C9, 0x0068A856,
+ 0x0093E4D0, 0x00657DDC, 0x009E315A, 0x00B0CF64, 0x004B83E2, 0x00BD1AEE, 0x00465668,
+ 0x005129F7, 0x00AA6571, 0x005CFC7D, 0x00A7B0FB, 0x00E9D10C, 0x00129D8A, 0x00E40486,
+ 0x001F4800, 0x0008379F, 0x00F37B19, 0x0005E215, 0x00FEAE93, 0x00D050AD, 0x002B1C2B,
+ 0x00DD8527, 0x0026C9A1, 0x0031B63E, 0x00CAFAB8, 0x003C63B4, 0x00C72F32, 0x00609FC9,
+ 0x009BD34F, 0x006D4A43, 0x009606C5, 0x0081795A, 0x007A35DC, 0x008CACD0, 0x0077E056,
+ 0x00591E68, 0x00A252EE, 0x0054CBE2, 0x00AF8764, 0x00B8F8FB, 0x0043B47D, 0x00B52D71,
+ 0x004E61F7, 0x00D2A319, 0x0029EF9F, 0x00DF7693, 0x00243A15, 0x0033458A, 0x00C8090C,
+ 0x003E9000, 0x00C5DC86, 0x00EB22B8, 0x00106E3E, 0x00E6F732, 0x001DBBB4, 0x000AC42B,
+ 0x00F188AD, 0x000711A1, 0x00FC5D27, 0x005BEDDC, 0x00A0A15A, 0x00563856, 0x00AD74D0,
+ 0x00BA0B4F, 0x004147C9, 0x00B7DEC5, 0x004C9243, 0x00626C7D, 0x009920FB, 0x006FB9F7,
+ 0x0094F571, 0x00838AEE, 0x0078C668, 0x008E5F64, 0x007513E2, 0x003B7215, 0x00C03E93,
+ 0x0036A79F, 0x00CDEB19, 0x00DA9486, 0x0021D800, 0x00D7410C, 0x002C0D8A, 0x0002F3B4,
+ 0x00F9BF32, 0x000F263E, 0x00F46AB8, 0x00E31527, 0x001859A1, 0x00EEC0AD, 0x00158C2B,
+ 0x00B23CD0, 0x00497056, 0x00BFE95A, 0x0044A5DC, 0x0053DA43, 0x00A896C5, 0x005E0FC9,
+ 0x00A5434F, 0x008BBD71, 0x0070F1F7, 0x008668FB, 0x007D247D, 0x006A5BE2, 0x00911764,
+ 0x00678E68, 0x009CC2EE, 0x00A44733, 0x005F0BB5, 0x00A992B9, 0x0052DE3F, 0x0045A1A0,
+ 0x00BEED26, 0x0048742A, 0x00B338AC, 0x009DC692, 0x00668A14, 0x00901318, 0x006B5F9E,
+ 0x007C2001, 0x00876C87, 0x0071F58B, 0x008AB90D, 0x002D09F6, 0x00D64570, 0x0020DC7C,
+ 0x00DB90FA, 0x00CCEF65, 0x0037A3E3, 0x00C13AEF, 0x003A7669, 0x00148857, 0x00EFC4D1,
+ 0x00195DDD, 0x00E2115B, 0x00F56EC4, 0x000E2242, 0x00F8BB4E, 0x0003F7C8, 0x004D963F,
+ 0x00B6DAB9, 0x004043B5, 0x00BB0F33, 0x00AC70AC, 0x00573C2A, 0x00A1A526, 0x005AE9A0,
+ 0x0074179E, 0x008F5B18, 0x0079C214, 0x00828E92, 0x0095F10D, 0x006EBD8B, 0x00982487,
+ 0x00636801, 0x00C4D8FA, 0x003F947C, 0x00C90D70, 0x003241F6, 0x00253E69, 0x00DE72EF,
+ 0x0028EBE3, 0x00D3A765, 0x00FD595B, 0x000615DD, 0x00F08CD1, 0x000BC057, 0x001CBFC8,
+ 0x00E7F34E, 0x00116A42, 0x00EA26C4, 0x0076E42A, 0x008DA8AC, 0x007B31A0, 0x00807D26,
+ 0x009702B9, 0x006C4E3F, 0x009AD733, 0x00619BB5, 0x004F658B, 0x00B4290D, 0x0042B001,
+ 0x00B9FC87, 0x00AE8318, 0x0055CF9E, 0x00A35692, 0x00581A14, 0x00FFAAEF, 0x0004E669,
+ 0x00F27F65, 0x000933E3, 0x001E4C7C, 0x00E500FA, 0x001399F6, 0x00E8D570, 0x00C62B4E,
+ 0x003D67C8, 0x00CBFEC4, 0x0030B242, 0x0027CDDD, 0x00DC815B, 0x002A1857, 0x00D154D1,
+ 0x009F3526, 0x006479A0, 0x0092E0AC, 0x0069AC2A, 0x007ED3B5, 0x00859F33, 0x0073063F,
+ 0x00884AB9, 0x00A6B487, 0x005DF801, 0x00AB610D, 0x00502D8B, 0x00475214, 0x00BC1E92,
+ 0x004A879E, 0x00B1CB18, 0x00167BE3, 0x00ED3765, 0x001BAE69, 0x00E0E2EF, 0x00F79D70,
+ 0x000CD1F6, 0x00FA48FA, 0x0001047C, 0x002FFA42, 0x00D4B6C4, 0x00222FC8, 0x00D9634E,
+ 0x00CE1CD1, 0x00355057, 0x00C3C95B, 0x003885DD,
+};
+
+static const uint32_t T1[256] = {
+ 0x00000000, 0x00488F66, 0x00901ECD, 0x00D891AB, 0x00DB711C, 0x0093FE7A, 0x004B6FD1,
+ 0x0003E0B7, 0x00B6E338, 0x00FE6C5E, 0x0026FDF5, 0x006E7293, 0x006D9224, 0x00251D42,
+ 0x00FD8CE9, 0x00B5038F, 0x006CC771, 0x00244817, 0x00FCD9BC, 0x00B456DA, 0x00B7B66D,
+ 0x00FF390B, 0x0027A8A0, 0x006F27C6, 0x00DA2449, 0x0092AB2F, 0x004A3A84, 0x0002B5E2,
+ 0x00015555, 0x0049DA33, 0x00914B98, 0x00D9C4FE, 0x00D88EE3, 0x00900185, 0x0048902E,
+ 0x00001F48, 0x0003FFFF, 0x004B7099, 0x0093E132, 0x00DB6E54, 0x006E6DDB, 0x0026E2BD,
+ 0x00FE7316, 0x00B6FC70, 0x00B51CC7, 0x00FD93A1, 0x0025020A, 0x006D8D6C, 0x00B44992,
+ 0x00FCC6F4, 0x0024575F, 0x006CD839, 0x006F388E, 0x0027B7E8, 0x00FF2643, 0x00B7A925,
+ 0x0002AAAA, 0x004A25CC, 0x0092B467, 0x00DA3B01, 0x00D9DBB6, 0x009154D0, 0x0049C57B,
+ 0x00014A1D, 0x004B5141, 0x0003DE27, 0x00DB4F8C, 0x0093C0EA, 0x0090205D, 0x00D8AF3B,
+ 0x00003E90, 0x0048B1F6, 0x00FDB279, 0x00B53D1F, 0x006DACB4, 0x002523D2, 0x0026C365,
+ 0x006E4C03, 0x00B6DDA8, 0x00FE52CE, 0x00279630, 0x006F1956, 0x00B788FD, 0x00FF079B,
+ 0x00FCE72C, 0x00B4684A, 0x006CF9E1, 0x00247687, 0x00917508, 0x00D9FA6E, 0x00016BC5,
+ 0x0049E4A3, 0x004A0414, 0x00028B72, 0x00DA1AD9, 0x009295BF, 0x0093DFA2, 0x00DB50C4,
+ 0x0003C16F, 0x004B4E09, 0x0048AEBE, 0x000021D8, 0x00D8B073, 0x00903F15, 0x00253C9A,
+ 0x006DB3FC, 0x00B52257, 0x00FDAD31, 0x00FE4D86, 0x00B6C2E0, 0x006E534B, 0x0026DC2D,
+ 0x00FF18D3, 0x00B797B5, 0x006F061E, 0x00278978, 0x002469CF, 0x006CE6A9, 0x00B47702,
+ 0x00FCF864, 0x0049FBEB, 0x0001748D, 0x00D9E526, 0x00916A40, 0x00928AF7, 0x00DA0591,
+ 0x0002943A, 0x004A1B5C, 0x0096A282, 0x00DE2DE4, 0x0006BC4F, 0x004E3329, 0x004DD39E,
+ 0x00055CF8, 0x00DDCD53, 0x00954235, 0x002041BA, 0x0068CEDC, 0x00B05F77, 0x00F8D011,
+ 0x00FB30A6, 0x00B3BFC0, 0x006B2E6B, 0x0023A10D, 0x00FA65F3, 0x00B2EA95, 0x006A7B3E,
+ 0x0022F458, 0x002114EF, 0x00699B89, 0x00B10A22, 0x00F98544, 0x004C86CB, 0x000409AD,
+ 0x00DC9806, 0x00941760, 0x0097F7D7, 0x00DF78B1, 0x0007E91A, 0x004F667C, 0x004E2C61,
+ 0x0006A307, 0x00DE32AC, 0x0096BDCA, 0x00955D7D, 0x00DDD21B, 0x000543B0, 0x004DCCD6,
+ 0x00F8CF59, 0x00B0403F, 0x0068D194, 0x00205EF2, 0x0023BE45, 0x006B3123, 0x00B3A088,
+ 0x00FB2FEE, 0x0022EB10, 0x006A6476, 0x00B2F5DD, 0x00FA7ABB, 0x00F99A0C, 0x00B1156A,
+ 0x006984C1, 0x00210BA7, 0x00940828, 0x00DC874E, 0x000416E5, 0x004C9983, 0x004F7934,
+ 0x0007F652, 0x00DF67F9, 0x0097E89F, 0x00DDF3C3, 0x00957CA5, 0x004DED0E, 0x00056268,
+ 0x000682DF, 0x004E0DB9, 0x00969C12, 0x00DE1374, 0x006B10FB, 0x00239F9D, 0x00FB0E36,
+ 0x00B38150, 0x00B061E7, 0x00F8EE81, 0x00207F2A, 0x0068F04C, 0x00B134B2, 0x00F9BBD4,
+ 0x00212A7F, 0x0069A519, 0x006A45AE, 0x0022CAC8, 0x00FA5B63, 0x00B2D405, 0x0007D78A,
+ 0x004F58EC, 0x0097C947, 0x00DF4621, 0x00DCA696, 0x009429F0, 0x004CB85B, 0x0004373D,
+ 0x00057D20, 0x004DF246, 0x009563ED, 0x00DDEC8B, 0x00DE0C3C, 0x0096835A, 0x004E12F1,
+ 0x00069D97, 0x00B39E18, 0x00FB117E, 0x002380D5, 0x006B0FB3, 0x0068EF04, 0x00206062,
+ 0x00F8F1C9, 0x00B07EAF, 0x0069BA51, 0x00213537, 0x00F9A49C, 0x00B12BFA, 0x00B2CB4D,
+ 0x00FA442B, 0x0022D580, 0x006A5AE6, 0x00DF5969, 0x0097D60F, 0x004F47A4, 0x0007C8C2,
+ 0x00042875, 0x004CA713, 0x009436B8, 0x00DCB9DE,
+};
+
+static const uint32_t T2[256] = {
+ 0x00000000, 0x00D70983, 0x00555F80, 0x00825603, 0x0051F286, 0x0086FB05, 0x0004AD06,
+ 0x00D3A485, 0x0059A88B, 0x008EA108, 0x000CF70B, 0x00DBFE88, 0x00085A0D, 0x00DF538E,
+ 0x005D058D, 0x008A0C0E, 0x00491C91, 0x009E1512, 0x001C4311, 0x00CB4A92, 0x0018EE17,
+ 0x00CFE794, 0x004DB197, 0x009AB814, 0x0010B41A, 0x00C7BD99, 0x0045EB9A, 0x0092E219,
+ 0x0041469C, 0x00964F1F, 0x0014191C, 0x00C3109F, 0x006974A4, 0x00BE7D27, 0x003C2B24,
+ 0x00EB22A7, 0x00388622, 0x00EF8FA1, 0x006DD9A2, 0x00BAD021, 0x0030DC2F, 0x00E7D5AC,
+ 0x006583AF, 0x00B28A2C, 0x00612EA9, 0x00B6272A, 0x00347129, 0x00E378AA, 0x00206835,
+ 0x00F761B6, 0x007537B5, 0x00A23E36, 0x00719AB3, 0x00A69330, 0x0024C533, 0x00F3CCB0,
+ 0x0079C0BE, 0x00AEC93D, 0x002C9F3E, 0x00FB96BD, 0x00283238, 0x00FF3BBB, 0x007D6DB8,
+ 0x00AA643B, 0x0029A4CE, 0x00FEAD4D, 0x007CFB4E, 0x00ABF2CD, 0x00785648, 0x00AF5FCB,
+ 0x002D09C8, 0x00FA004B, 0x00700C45, 0x00A705C6, 0x002553C5, 0x00F25A46, 0x0021FEC3,
+ 0x00F6F740, 0x0074A143, 0x00A3A8C0, 0x0060B85F, 0x00B7B1DC, 0x0035E7DF, 0x00E2EE5C,
+ 0x00314AD9, 0x00E6435A, 0x00641559, 0x00B31CDA, 0x003910D4, 0x00EE1957, 0x006C4F54,
+ 0x00BB46D7, 0x0068E252, 0x00BFEBD1, 0x003DBDD2, 0x00EAB451, 0x0040D06A, 0x0097D9E9,
+ 0x00158FEA, 0x00C28669, 0x001122EC, 0x00C62B6F, 0x00447D6C, 0x009374EF, 0x001978E1,
+ 0x00CE7162, 0x004C2761, 0x009B2EE2, 0x00488A67, 0x009F83E4, 0x001DD5E7, 0x00CADC64,
+ 0x0009CCFB, 0x00DEC578, 0x005C937B, 0x008B9AF8, 0x00583E7D, 0x008F37FE, 0x000D61FD,
+ 0x00DA687E, 0x00506470, 0x00876DF3, 0x00053BF0, 0x00D23273, 0x000196F6, 0x00D69F75,
+ 0x0054C976, 0x0083C0F5, 0x00A9041B, 0x007E0D98, 0x00FC5B9B, 0x002B5218, 0x00F8F69D,
+ 0x002FFF1E, 0x00ADA91D, 0x007AA09E, 0x00F0AC90, 0x0027A513, 0x00A5F310, 0x0072FA93,
+ 0x00A15E16, 0x00765795, 0x00F40196, 0x00230815, 0x00E0188A, 0x00371109, 0x00B5470A,
+ 0x00624E89, 0x00B1EA0C, 0x0066E38F, 0x00E4B58C, 0x0033BC0F, 0x00B9B001, 0x006EB982,
+ 0x00ECEF81, 0x003BE602, 0x00E84287, 0x003F4B04, 0x00BD1D07, 0x006A1484, 0x00C070BF,
+ 0x0017793C, 0x00952F3F, 0x004226BC, 0x00918239, 0x00468BBA, 0x00C4DDB9, 0x0013D43A,
+ 0x0099D834, 0x004ED1B7, 0x00CC87B4, 0x001B8E37, 0x00C82AB2, 0x001F2331, 0x009D7532,
+ 0x004A7CB1, 0x00896C2E, 0x005E65AD, 0x00DC33AE, 0x000B3A2D, 0x00D89EA8, 0x000F972B,
+ 0x008DC128, 0x005AC8AB, 0x00D0C4A5, 0x0007CD26, 0x00859B25, 0x005292A6, 0x00813623,
+ 0x00563FA0, 0x00D469A3, 0x00036020, 0x0080A0D5, 0x0057A956, 0x00D5FF55, 0x0002F6D6,
+ 0x00D15253, 0x00065BD0, 0x00840DD3, 0x00530450, 0x00D9085E, 0x000E01DD, 0x008C57DE,
+ 0x005B5E5D, 0x0088FAD8, 0x005FF35B, 0x00DDA558, 0x000AACDB, 0x00C9BC44, 0x001EB5C7,
+ 0x009CE3C4, 0x004BEA47, 0x00984EC2, 0x004F4741, 0x00CD1142, 0x001A18C1, 0x009014CF,
+ 0x00471D4C, 0x00C54B4F, 0x001242CC, 0x00C1E649, 0x0016EFCA, 0x0094B9C9, 0x0043B04A,
+ 0x00E9D471, 0x003EDDF2, 0x00BC8BF1, 0x006B8272, 0x00B826F7, 0x006F2F74, 0x00ED7977,
+ 0x003A70F4, 0x00B07CFA, 0x00677579, 0x00E5237A, 0x00322AF9, 0x00E18E7C, 0x003687FF,
+ 0x00B4D1FC, 0x0063D87F, 0x00A0C8E0, 0x0077C163, 0x00F59760, 0x00229EE3, 0x00F13A66,
+ 0x002633E5, 0x00A465E6, 0x00736C65, 0x00F9606B, 0x002E69E8, 0x00AC3FEB, 0x007B3668,
+ 0x00A892ED, 0x007F9B6E, 0x00FDCD6D, 0x002AC4EE,
+};
+
+static const uint32_t T3[256] = {
+ 0x00000000, 0x00520936, 0x00A4126C, 0x00F61B5A, 0x004825D8, 0x001A2CEE, 0x00EC37B4,
+ 0x00BE3E82, 0x006B0636, 0x00390F00, 0x00CF145A, 0x009D1D6C, 0x002323EE, 0x00712AD8,
+ 0x00873182, 0x00D538B4, 0x00D60C6C, 0x0084055A, 0x00721E00, 0x00201736, 0x009E29B4,
+ 0x00CC2082, 0x003A3BD8, 0x006832EE, 0x00BD0A5A, 0x00EF036C, 0x00191836, 0x004B1100,
+ 0x00F52F82, 0x00A726B4, 0x00513DEE, 0x000334D8, 0x00AC19D8, 0x00FE10EE, 0x00080BB4,
+ 0x005A0282, 0x00E43C00, 0x00B63536, 0x00402E6C, 0x0012275A, 0x00C71FEE, 0x009516D8,
+ 0x00630D82, 0x003104B4, 0x008F3A36, 0x00DD3300, 0x002B285A, 0x0079216C, 0x007A15B4,
+ 0x00281C82, 0x00DE07D8, 0x008C0EEE, 0x0032306C, 0x0060395A, 0x00962200, 0x00C42B36,
+ 0x00111382, 0x00431AB4, 0x00B501EE, 0x00E708D8, 0x0059365A, 0x000B3F6C, 0x00FD2436,
+ 0x00AF2D00, 0x00A37F36, 0x00F17600, 0x00076D5A, 0x0055646C, 0x00EB5AEE, 0x00B953D8,
+ 0x004F4882, 0x001D41B4, 0x00C87900, 0x009A7036, 0x006C6B6C, 0x003E625A, 0x00805CD8,
+ 0x00D255EE, 0x00244EB4, 0x00764782, 0x0075735A, 0x00277A6C, 0x00D16136, 0x00836800,
+ 0x003D5682, 0x006F5FB4, 0x009944EE, 0x00CB4DD8, 0x001E756C, 0x004C7C5A, 0x00BA6700,
+ 0x00E86E36, 0x005650B4, 0x00045982, 0x00F242D8, 0x00A04BEE, 0x000F66EE, 0x005D6FD8,
+ 0x00AB7482, 0x00F97DB4, 0x00474336, 0x00154A00, 0x00E3515A, 0x00B1586C, 0x006460D8,
+ 0x003669EE, 0x00C072B4, 0x00927B82, 0x002C4500, 0x007E4C36, 0x0088576C, 0x00DA5E5A,
+ 0x00D96A82, 0x008B63B4, 0x007D78EE, 0x002F71D8, 0x00914F5A, 0x00C3466C, 0x00355D36,
+ 0x00675400, 0x00B26CB4, 0x00E06582, 0x00167ED8, 0x004477EE, 0x00FA496C, 0x00A8405A,
+ 0x005E5B00, 0x000C5236, 0x0046FF6C, 0x0014F65A, 0x00E2ED00, 0x00B0E436, 0x000EDAB4,
+ 0x005CD382, 0x00AAC8D8, 0x00F8C1EE, 0x002DF95A, 0x007FF06C, 0x0089EB36, 0x00DBE200,
+ 0x0065DC82, 0x0037D5B4, 0x00C1CEEE, 0x0093C7D8, 0x0090F300, 0x00C2FA36, 0x0034E16C,
+ 0x0066E85A, 0x00D8D6D8, 0x008ADFEE, 0x007CC4B4, 0x002ECD82, 0x00FBF536, 0x00A9FC00,
+ 0x005FE75A, 0x000DEE6C, 0x00B3D0EE, 0x00E1D9D8, 0x0017C282, 0x0045CBB4, 0x00EAE6B4,
+ 0x00B8EF82, 0x004EF4D8, 0x001CFDEE, 0x00A2C36C, 0x00F0CA5A, 0x0006D100, 0x0054D836,
+ 0x0081E082, 0x00D3E9B4, 0x0025F2EE, 0x0077FBD8, 0x00C9C55A, 0x009BCC6C, 0x006DD736,
+ 0x003FDE00, 0x003CEAD8, 0x006EE3EE, 0x0098F8B4, 0x00CAF182, 0x0074CF00, 0x0026C636,
+ 0x00D0DD6C, 0x0082D45A, 0x0057ECEE, 0x0005E5D8, 0x00F3FE82, 0x00A1F7B4, 0x001FC936,
+ 0x004DC000, 0x00BBDB5A, 0x00E9D26C, 0x00E5805A, 0x00B7896C, 0x00419236, 0x00139B00,
+ 0x00ADA582, 0x00FFACB4, 0x0009B7EE, 0x005BBED8, 0x008E866C, 0x00DC8F5A, 0x002A9400,
+ 0x00789D36, 0x00C6A3B4, 0x0094AA82, 0x0062B1D8, 0x0030B8EE, 0x00338C36, 0x00618500,
+ 0x00979E5A, 0x00C5976C, 0x007BA9EE, 0x0029A0D8, 0x00DFBB82, 0x008DB2B4, 0x00588A00,
+ 0x000A8336, 0x00FC986C, 0x00AE915A, 0x0010AFD8, 0x0042A6EE, 0x00B4BDB4, 0x00E6B482,
+ 0x00499982, 0x001B90B4, 0x00ED8BEE, 0x00BF82D8, 0x0001BC5A, 0x0053B56C, 0x00A5AE36,
+ 0x00F7A700, 0x00229FB4, 0x00709682, 0x00868DD8, 0x00D484EE, 0x006ABA6C, 0x0038B35A,
+ 0x00CEA800, 0x009CA136, 0x009F95EE, 0x00CD9CD8, 0x003B8782, 0x00698EB4, 0x00D7B036,
+ 0x0085B900, 0x0073A25A, 0x0021AB6C, 0x00F493D8, 0x00A69AEE, 0x005081B4, 0x00028882,
+ 0x00BCB600, 0x00EEBF36, 0x0018A46C, 0x004AAD5A};
+
+#define CRC24_FAST_INIT 0xce04b7L
+
+static inline uint32_t
+process8(uint32_t crc, uint8_t data)
+{
+ return (crc >> 8) ^ T0[(crc & 0xff) ^ data];
+}
+
+/*
+ * Process 4 bytes in one go
+ */
+static inline uint32_t
+process32(uint32_t crc, uint32_t word)
+{
+ crc ^= word;
+ crc = T3[crc & 0xff] ^ T2[((crc >> 8) & 0xff)] ^ T1[((crc >> 16) & 0xff)] ^
+ T0[(crc >> 24) & 0xff];
+ return crc;
+}
+
+static uint32_t
+crc24_update(uint32_t crc, const uint8_t *in, size_t length)
+{
+ uint32_t d0, d1, d2, d3;
+
+ while (length >= 16) {
+ LOAD32LE(d0, &in[0]);
+ LOAD32LE(d1, &in[4]);
+ LOAD32LE(d2, &in[8]);
+ LOAD32LE(d3, &in[12]);
+
+ crc = process32(crc, d0);
+ crc = process32(crc, d1);
+ crc = process32(crc, d2);
+ crc = process32(crc, d3);
+
+ in += 16;
+ length -= 16;
+ }
+
+ while (length--) {
+ crc = process8(crc, *in++);
+ }
+
+ return crc & 0xffffff;
+}
+
+/* Swap endianness of 32-bit value */
+#if defined(__GNUC__) || defined(__clang__)
+#define BSWAP32(x) __builtin_bswap32(x)
+#else
+#define BSWAP32(x) \
+ ((x & 0x000000FF) << 24 | (x & 0x0000FF00) << 8 | (x & 0x00FF0000) >> 8 | \
+ (x & 0xFF000000) >> 24)
+#endif
+
+static uint32_t
+crc24_final(uint32_t crc)
+{
+ return (BSWAP32(crc) >> 8);
+}
+
+namespace rnp {
+
+CRC24_RNP::CRC24_RNP()
+{
+ state_ = CRC24_FAST_INIT;
+}
+
+CRC24_RNP::~CRC24_RNP()
+{
+}
+
+std::unique_ptr<CRC24_RNP>
+CRC24_RNP::create()
+{
+ return std::unique_ptr<CRC24_RNP>(new CRC24_RNP());
+}
+
+void
+CRC24_RNP::add(const void *buf, size_t len)
+{
+ state_ = crc24_update(state_, static_cast<const uint8_t *>(buf), len);
+}
+
+std::array<uint8_t, 3>
+CRC24_RNP::finish()
+{
+ uint32_t crc_fin = crc24_final(state_);
+ state_ = 0;
+ std::array<uint8_t, 3> res;
+ res[0] = (crc_fin >> 16) & 0xff;
+ res[1] = (crc_fin >> 8) & 0xff;
+ res[2] = crc_fin & 0xff;
+ return res;
+}
+
+}; // namespace rnp
diff --git a/src/lib/crypto/hash_crc24.hpp b/src/lib/crypto/hash_crc24.hpp
new file mode 100644
index 0000000..a73a466
--- /dev/null
+++ b/src/lib/crypto/hash_crc24.hpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef CRYPTO_HASH_CRC24_HPP_
+#define CRYPTO_HASH_CRC24_HPP_
+
+#include "hash.hpp"
+
+namespace rnp {
+class CRC24_RNP : public CRC24 {
+ uint32_t state_;
+ CRC24_RNP();
+
+ public:
+ virtual ~CRC24_RNP();
+
+ static std::unique_ptr<CRC24_RNP> create();
+
+ void add(const void *buf, size_t len) override;
+ std::array<uint8_t, 3> finish() override;
+};
+} // namespace rnp
+
+#endif \ No newline at end of file
diff --git a/src/lib/crypto/hash_ossl.cpp b/src/lib/crypto/hash_ossl.cpp
new file mode 100644
index 0000000..37b7546
--- /dev/null
+++ b/src/lib/crypto/hash_ossl.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2021-2022 Ribose Inc.
+ * 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 HOLDERS 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 "hash_ossl.hpp"
+#include <stdio.h>
+#include <memory>
+#include <cassert>
+#include <openssl/err.h>
+#include "config.h"
+#include "types.h"
+#include "utils.h"
+#include "str-utils.h"
+#include "defaults.h"
+
+static const id_str_pair openssl_alg_map[] = {
+ {PGP_HASH_MD5, "md5"},
+ {PGP_HASH_SHA1, "sha1"},
+ {PGP_HASH_RIPEMD, "ripemd160"},
+ {PGP_HASH_SHA256, "sha256"},
+ {PGP_HASH_SHA384, "sha384"},
+ {PGP_HASH_SHA512, "sha512"},
+ {PGP_HASH_SHA224, "sha224"},
+ {PGP_HASH_SM3, "sm3"},
+ {PGP_HASH_SHA3_256, "sha3-256"},
+ {PGP_HASH_SHA3_512, "sha3-512"},
+ {0, NULL},
+};
+
+namespace rnp {
+Hash_OpenSSL::Hash_OpenSSL(pgp_hash_alg_t alg) : Hash(alg)
+{
+ const char * hash_name = Hash_OpenSSL::name_backend(alg);
+ const EVP_MD *hash_tp = EVP_get_digestbyname(hash_name);
+ if (!hash_tp) {
+ RNP_LOG("Error creating hash object for '%s'", hash_name);
+ throw rnp_exception(RNP_ERROR_BAD_STATE);
+ }
+ fn_ = EVP_MD_CTX_new();
+ if (!fn_) {
+ RNP_LOG("Allocation failure");
+ throw rnp_exception(RNP_ERROR_OUT_OF_MEMORY);
+ }
+ int res = EVP_DigestInit_ex(fn_, hash_tp, NULL);
+ if (res != 1) {
+ RNP_LOG("Digest initializataion error %d : %lu", res, ERR_peek_last_error());
+ EVP_MD_CTX_free(fn_);
+ throw rnp_exception(RNP_ERROR_BAD_STATE);
+ }
+ assert(size_ == (size_t) EVP_MD_size(hash_tp));
+}
+
+Hash_OpenSSL::Hash_OpenSSL(const Hash_OpenSSL &src) : Hash(src.alg_)
+{
+ if (!src.fn_) {
+ throw rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+ }
+
+ fn_ = EVP_MD_CTX_new();
+ if (!fn_) {
+ RNP_LOG("Allocation failure");
+ throw rnp_exception(RNP_ERROR_OUT_OF_MEMORY);
+ }
+
+ int res = EVP_MD_CTX_copy(fn_, src.fn_);
+ if (res != 1) {
+ RNP_LOG("Digest copying error %d: %lu", res, ERR_peek_last_error());
+ EVP_MD_CTX_free(fn_);
+ throw rnp_exception(RNP_ERROR_BAD_STATE);
+ }
+}
+
+std::unique_ptr<Hash_OpenSSL>
+Hash_OpenSSL::create(pgp_hash_alg_t alg)
+{
+ return std::unique_ptr<Hash_OpenSSL>(new Hash_OpenSSL(alg));
+}
+
+std::unique_ptr<Hash>
+Hash_OpenSSL::clone() const
+{
+ return std::unique_ptr<Hash>(new Hash_OpenSSL(*this));
+}
+
+void
+Hash_OpenSSL::add(const void *buf, size_t len)
+{
+ if (!fn_) {
+ throw rnp_exception(RNP_ERROR_NULL_POINTER);
+ }
+ int res = EVP_DigestUpdate(fn_, buf, len);
+ if (res != 1) {
+ RNP_LOG("Digest updating error %d: %lu", res, ERR_peek_last_error());
+ throw rnp_exception(RNP_ERROR_GENERIC);
+ }
+}
+
+size_t
+Hash_OpenSSL::finish(uint8_t *digest)
+{
+ if (!fn_) {
+ return 0;
+ }
+ int res = digest ? EVP_DigestFinal_ex(fn_, digest, NULL) : 1;
+ EVP_MD_CTX_free(fn_);
+ fn_ = NULL;
+ if (res != 1) {
+ RNP_LOG("Digest finalization error %d: %lu", res, ERR_peek_last_error());
+ return 0;
+ }
+
+ size_t outsz = size_;
+ size_ = 0;
+ return outsz;
+}
+
+Hash_OpenSSL::~Hash_OpenSSL()
+{
+ if (!fn_) {
+ return;
+ }
+ EVP_MD_CTX_free(fn_);
+}
+
+const char *
+Hash_OpenSSL::name_backend(pgp_hash_alg_t alg)
+{
+ return id_str_pair::lookup(openssl_alg_map, alg);
+}
+} // namespace rnp
diff --git a/src/lib/crypto/hash_ossl.hpp b/src/lib/crypto/hash_ossl.hpp
new file mode 100644
index 0000000..95b365b
--- /dev/null
+++ b/src/lib/crypto/hash_ossl.hpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2022 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef CRYPTO_HASH_OSSL_HPP_
+#define CRYPTO_HASH_OSSL_HPP_
+
+#include "hash.hpp"
+#include <openssl/evp.h>
+
+namespace rnp {
+class Hash_OpenSSL : public Hash {
+ private:
+ EVP_MD_CTX *fn_;
+
+ Hash_OpenSSL(pgp_hash_alg_t alg);
+ Hash_OpenSSL(const Hash_OpenSSL &src);
+
+ public:
+ virtual ~Hash_OpenSSL();
+
+ static std::unique_ptr<Hash_OpenSSL> create(pgp_hash_alg_t alg);
+ std::unique_ptr<Hash> clone() const override;
+
+ void add(const void *buf, size_t len) override;
+ size_t finish(uint8_t *digest = NULL) override;
+
+ static const char *name_backend(pgp_hash_alg_t alg);
+};
+
+} // namespace rnp
+
+#endif \ No newline at end of file
diff --git a/src/lib/crypto/hash_sha1cd.cpp b/src/lib/crypto/hash_sha1cd.cpp
new file mode 100644
index 0000000..863591e
--- /dev/null
+++ b/src/lib/crypto/hash_sha1cd.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2021-2022 Ribose Inc.
+ * 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 HOLDERS 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <cassert>
+#include "logging.h"
+#include "hash_sha1cd.hpp"
+
+namespace rnp {
+Hash_SHA1CD::Hash_SHA1CD() : Hash(PGP_HASH_SHA1)
+{
+ assert(size_ == 20);
+ SHA1DCInit(&ctx_);
+}
+
+Hash_SHA1CD::Hash_SHA1CD(const Hash_SHA1CD &src) : Hash(PGP_HASH_SHA1)
+{
+ ctx_ = src.ctx_;
+}
+
+Hash_SHA1CD::~Hash_SHA1CD()
+{
+}
+
+std::unique_ptr<Hash_SHA1CD>
+Hash_SHA1CD::create()
+{
+ return std::unique_ptr<Hash_SHA1CD>(new Hash_SHA1CD());
+}
+
+std::unique_ptr<Hash>
+Hash_SHA1CD::clone() const
+{
+ return std::unique_ptr<Hash>(new Hash_SHA1CD(*this));
+}
+
+/* This produces runtime error: load of misaligned address 0x60d0000030a9 for type 'const
+ * uint32_t' (aka 'const unsigned int'), which requires 4 byte alignment */
+#if defined(__clang__)
+__attribute__((no_sanitize("undefined")))
+#endif
+void
+Hash_SHA1CD::add(const void *buf, size_t len)
+{
+ SHA1DCUpdate(&ctx_, (const char *) buf, len);
+}
+
+#if defined(__clang__)
+__attribute__((no_sanitize("undefined")))
+#endif
+size_t
+Hash_SHA1CD::finish(uint8_t *digest)
+{
+ unsigned char fixed_digest[20];
+ int res = SHA1DCFinal(fixed_digest, &ctx_);
+ if (res && digest) {
+ /* Show warning only if digest is non-null */
+ RNP_LOG("Warning! SHA1 collision detected and mitigated.");
+ }
+ if (res) {
+ throw rnp_exception(RNP_ERROR_BAD_STATE);
+ }
+ if (digest) {
+ memcpy(digest, fixed_digest, 20);
+ }
+ return 20;
+}
+
+} // namespace rnp
diff --git a/src/lib/crypto/hash_sha1cd.hpp b/src/lib/crypto/hash_sha1cd.hpp
new file mode 100644
index 0000000..523bbe0
--- /dev/null
+++ b/src/lib/crypto/hash_sha1cd.hpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2022 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef CRYPTO_HASH_SHA1CD_HPP_
+#define CRYPTO_HASH_SHA1CD_HPP_
+
+#include "hash.hpp"
+#include "sha1cd/sha1.h"
+
+namespace rnp {
+class Hash_SHA1CD : public Hash {
+ private:
+ SHA1_CTX ctx_;
+
+ Hash_SHA1CD();
+ Hash_SHA1CD(const Hash_SHA1CD &src);
+
+ public:
+ virtual ~Hash_SHA1CD();
+
+ static std::unique_ptr<Hash_SHA1CD> create();
+ std::unique_ptr<Hash> clone() const override;
+
+ void add(const void *buf, size_t len) override;
+ size_t finish(uint8_t *digest = NULL) override;
+};
+
+} // namespace rnp
+#endif
diff --git a/src/lib/crypto/mem.cpp b/src/lib/crypto/mem.cpp
new file mode 100644
index 0000000..bf54aa6
--- /dev/null
+++ b/src/lib/crypto/mem.cpp
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2021 Ribose Inc.
+ * 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 HOLDERS 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 <cstdio>
+#include "mem.h"
+#include "logging.h"
+#include <botan/ffi.h>
+
+void
+secure_clear(void *vp, size_t size)
+{
+ botan_scrub_mem(vp, size);
+}
+
+namespace rnp {
+
+bool
+hex_encode(const uint8_t *buf, size_t buf_len, char *hex, size_t hex_len, hex_format_t format)
+{
+ uint32_t flags = format == HEX_LOWERCASE ? BOTAN_FFI_HEX_LOWER_CASE : 0;
+
+ if (hex_len < (buf_len * 2 + 1)) {
+ return false;
+ }
+ hex[buf_len * 2] = '\0';
+ return botan_hex_encode(buf, buf_len, hex, flags) == 0;
+}
+
+size_t
+hex_decode(const char *hex, uint8_t *buf, size_t buf_len)
+{
+ size_t hexlen = strlen(hex);
+
+ /* check for 0x prefix */
+ if ((hexlen >= 2) && (hex[0] == '0') && ((hex[1] == 'x') || (hex[1] == 'X'))) {
+ hex += 2;
+ hexlen -= 2;
+ }
+ if (botan_hex_decode(hex, hexlen, buf, &buf_len) != 0) {
+ RNP_LOG("Hex decode failed on string: %s", hex);
+ return 0;
+ }
+ return buf_len;
+}
+} // namespace rnp \ No newline at end of file
diff --git a/src/lib/crypto/mem.h b/src/lib/crypto/mem.h
new file mode 100644
index 0000000..fe574da
--- /dev/null
+++ b/src/lib/crypto/mem.h
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 2021 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef CRYPTO_MEM_H_
+#define CRYPTO_MEM_H_
+
+#include "config.h"
+#include <array>
+#include <vector>
+#if defined(CRYPTO_BACKEND_BOTAN)
+#include <botan/secmem.h>
+#include <botan/ffi.h>
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+#include <openssl/crypto.h>
+#endif
+
+namespace rnp {
+
+#if defined(CRYPTO_BACKEND_BOTAN)
+template <typename T> using secure_vector = Botan::secure_vector<T>;
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+template <typename T> class ossl_allocator {
+ public:
+ static_assert(std::is_integral<T>::value, "T must be integral type");
+
+ typedef T value_type;
+ typedef std::size_t size_type;
+
+ ossl_allocator() noexcept = default;
+ ossl_allocator(const ossl_allocator &) noexcept = default;
+ ossl_allocator &operator=(const ossl_allocator &) noexcept = default;
+ ~ossl_allocator() noexcept = default;
+
+ template <typename U> ossl_allocator(const ossl_allocator<U> &) noexcept
+ {
+ }
+
+ T *
+ allocate(std::size_t n)
+ {
+ if (!n) {
+ return nullptr;
+ }
+
+ /* attempt to use OpenSSL secure alloc */
+ T *ptr = static_cast<T *>(OPENSSL_secure_zalloc(n * sizeof(T)));
+ if (ptr) {
+ return ptr;
+ }
+ /* fallback to std::alloc if failed */
+ ptr = static_cast<T *>(std::calloc(n, sizeof(T)));
+ if (!ptr)
+ throw std::bad_alloc();
+ return ptr;
+ }
+
+ void
+ deallocate(T *p, std::size_t n)
+ {
+ if (!p) {
+ return;
+ }
+ if (CRYPTO_secure_allocated(p)) {
+ OPENSSL_secure_clear_free(p, n * sizeof(T));
+ return;
+ }
+ OPENSSL_cleanse(p, n * sizeof(T));
+ std::free(p);
+ }
+};
+
+template <typename T> using secure_vector = std::vector<T, ossl_allocator<T> >;
+#else
+#error Unsupported backend.
+#endif
+
+template <typename T, std::size_t N> struct secure_array {
+ private:
+ static_assert(std::is_integral<T>::value, "T must be integer type");
+ std::array<T, N> data_;
+
+ public:
+ secure_array() : data_({0})
+ {
+ }
+
+ T *
+ data()
+ {
+ return &data_[0];
+ }
+
+ std::size_t
+ size() const
+ {
+ return data_.size();
+ }
+
+ T
+ operator[](size_t idx) const
+ {
+ return data_[idx];
+ }
+
+ T &
+ operator[](size_t idx)
+ {
+ return data_[idx];
+ }
+
+ ~secure_array()
+ {
+#if defined(CRYPTO_BACKEND_BOTAN)
+ botan_scrub_mem(&data_[0], sizeof(data_));
+#elif defined(CRYPTO_BACKEND_OPENSSL)
+ OPENSSL_cleanse(&data_[0], sizeof(data_));
+#else
+#error "Unsupported crypto backend."
+#endif
+ }
+};
+
+typedef enum { HEX_LOWERCASE, HEX_UPPERCASE } hex_format_t;
+
+bool hex_encode(const uint8_t *buf,
+ size_t buf_len,
+ char * hex,
+ size_t hex_len,
+ hex_format_t format = HEX_UPPERCASE);
+size_t hex_decode(const char *hex, uint8_t *buf, size_t buf_len);
+} // namespace rnp
+
+void secure_clear(void *vp, size_t size);
+
+#endif // CRYPTO_MEM_H_
diff --git a/src/lib/crypto/mem_ossl.cpp b/src/lib/crypto/mem_ossl.cpp
new file mode 100644
index 0000000..e9d6a93
--- /dev/null
+++ b/src/lib/crypto/mem_ossl.cpp
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2021 Ribose Inc.
+ * 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 HOLDERS 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 <cstdio>
+#include <cstring>
+#include "mem.h"
+#include "logging.h"
+#include <openssl/crypto.h>
+
+void
+secure_clear(void *vp, size_t size)
+{
+ OPENSSL_cleanse(vp, size);
+}
+
+namespace rnp {
+
+bool
+hex_encode(const uint8_t *buf, size_t buf_len, char *hex, size_t hex_len, hex_format_t format)
+{
+ if (hex_len < (buf_len * 2 + 1)) {
+ return false;
+ }
+ static const char *hex_low = "0123456789abcdef";
+ static const char *hex_up = "0123456789ABCDEF";
+ const char * hex_ch = (format == HEX_LOWERCASE) ? hex_low : hex_up;
+ hex[buf_len * 2] = '\0';
+ for (size_t i = 0; i < buf_len; i++) {
+ hex[i << 1] = hex_ch[buf[i] >> 4];
+ hex[(i << 1) + 1] = hex_ch[buf[i] & 0xF];
+ }
+ return true;
+}
+
+static bool
+hex_char_decode(const char hex, uint8_t &res)
+{
+ if ((hex >= '0') && (hex <= '9')) {
+ res = hex - '0';
+ return true;
+ }
+ if (hex >= 'a' && hex <= 'f') {
+ res = hex + 10 - 'a';
+ return true;
+ }
+ if (hex >= 'A' && hex <= 'F') {
+ res = hex + 10 - 'A';
+ return true;
+ }
+ return false;
+}
+
+size_t
+hex_decode(const char *hex, uint8_t *buf, size_t buf_len)
+{
+ size_t hexlen = strlen(hex);
+
+ /* check for 0x prefix */
+ if ((hexlen >= 2) && (hex[0] == '0') && ((hex[1] == 'x') || (hex[1] == 'X'))) {
+ hex += 2;
+ hexlen -= 2;
+ }
+ const char *end = hex + hexlen;
+ uint8_t * buf_st = buf;
+ uint8_t * buf_en = buf + buf_len;
+ while (hex < end) {
+ /* skip whitespaces */
+ if ((*hex < '0') &&
+ ((*hex == ' ') || (*hex == '\t') || (*hex == '\r') || (*hex == '\n'))) {
+ hex++;
+ continue;
+ }
+ if (hexlen < 2) {
+ RNP_LOG("Invalid hex string length.");
+ return 0;
+ }
+ uint8_t lo, hi;
+ if (!hex_char_decode(*hex++, hi) || !hex_char_decode(*hex++, lo)) {
+ RNP_LOG("Hex decode failed on string: %s", hex);
+ return 0;
+ }
+ if (buf == buf_en) {
+ return 0;
+ }
+ *buf++ = (hi << 4) | lo;
+ }
+ return buf - buf_st;
+}
+
+} // namespace rnp
diff --git a/src/lib/crypto/mpi.cpp b/src/lib/crypto/mpi.cpp
new file mode 100644
index 0000000..4df7eea
--- /dev/null
+++ b/src/lib/crypto/mpi.cpp
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2018 Ribose Inc.
+ * 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 HOLDERS 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 <string.h>
+#include <stdlib.h>
+#include "mpi.h"
+#include "mem.h"
+#include "utils.h"
+
+size_t
+mpi_bits(const pgp_mpi_t *val)
+{
+ size_t bits = 0;
+ size_t idx = 0;
+ uint8_t bt;
+
+ for (idx = 0; (idx < val->len) && !val->mpi[idx]; idx++)
+ ;
+
+ if (idx < val->len) {
+ for (bits = (val->len - idx - 1) << 3, bt = val->mpi[idx]; bt; bits++, bt = bt >> 1)
+ ;
+ }
+
+ return bits;
+}
+
+size_t
+mpi_bytes(const pgp_mpi_t *val)
+{
+ return val->len;
+}
+
+bool
+mem2mpi(pgp_mpi_t *val, const void *mem, size_t len)
+{
+ if (len > sizeof(val->mpi)) {
+ return false;
+ }
+
+ memcpy(val->mpi, mem, len);
+ val->len = len;
+ return true;
+}
+
+void
+mpi2mem(const pgp_mpi_t *val, void *mem)
+{
+ memcpy(mem, val->mpi, val->len);
+}
+
+char *
+mpi2hex(const pgp_mpi_t *val)
+{
+ static const char *hexes = "0123456789abcdef";
+ char * out;
+ size_t len;
+ size_t idx = 0;
+
+ len = mpi_bytes(val);
+ out = (char *) malloc(len * 2 + 1);
+
+ if (!out) {
+ return out;
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ out[idx++] = hexes[val->mpi[i] >> 4];
+ out[idx++] = hexes[val->mpi[i] & 0xf];
+ }
+ out[idx] = '\0';
+ return out;
+}
+
+bool
+mpi_equal(const pgp_mpi_t *val1, const pgp_mpi_t *val2)
+{
+ size_t idx1 = 0;
+ size_t idx2 = 0;
+
+ for (idx1 = 0; (idx1 < val1->len) && !val1->mpi[idx1]; idx1++)
+ ;
+
+ for (idx2 = 0; (idx2 < val2->len) && !val2->mpi[idx2]; idx2++)
+ ;
+
+ return ((val1->len - idx1) == (val2->len - idx2) &&
+ !memcmp(val1->mpi + idx1, val2->mpi + idx2, val1->len - idx1));
+}
+
+void
+mpi_forget(pgp_mpi_t *val)
+{
+ secure_clear(val, sizeof(*val));
+ val->len = 0;
+}
diff --git a/src/lib/crypto/mpi.h b/src/lib/crypto/mpi.h
new file mode 100644
index 0000000..f95aeea
--- /dev/null
+++ b/src/lib/crypto/mpi.h
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 2018 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef RNP_MPI_H_
+#define RNP_MPI_H_
+
+#include <cstdint>
+#include <cstdbool>
+#include <cstddef>
+
+/* 16384 bits should be pretty enough for now */
+#define PGP_MPINT_BITS (16384)
+#define PGP_MPINT_SIZE (PGP_MPINT_BITS >> 3)
+
+/** multi-precision integer, used in signatures and public/secret keys */
+typedef struct pgp_mpi_t {
+ uint8_t mpi[PGP_MPINT_SIZE];
+ size_t len;
+} pgp_mpi_t;
+
+bool mem2mpi(pgp_mpi_t *val, const void *mem, size_t len);
+
+void mpi2mem(const pgp_mpi_t *val, void *mem);
+
+char *mpi2hex(const pgp_mpi_t *val);
+
+size_t mpi_bits(const pgp_mpi_t *val);
+
+size_t mpi_bytes(const pgp_mpi_t *val);
+
+bool mpi_equal(const pgp_mpi_t *val1, const pgp_mpi_t *val2);
+
+void mpi_forget(pgp_mpi_t *val);
+
+#endif // MPI_H_
diff --git a/src/lib/crypto/ossl_common.h b/src/lib/crypto/ossl_common.h
new file mode 100644
index 0000000..b6b7067
--- /dev/null
+++ b/src/lib/crypto/ossl_common.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022, [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.
+ */
+
+#ifndef RNP_OSSL_COMMON_H_
+#define RNP_OSSL_COMMON_H_
+
+#include <string>
+#include "config.h"
+#include <openssl/err.h>
+
+inline const char *
+ossl_latest_err()
+{
+ return ERR_error_string(ERR_peek_last_error(), NULL);
+}
+
+#endif
diff --git a/src/lib/crypto/rng.cpp b/src/lib/crypto/rng.cpp
new file mode 100644
index 0000000..bf5bfad
--- /dev/null
+++ b/src/lib/crypto/rng.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 <assert.h>
+#include <botan/ffi.h>
+#include "rng.h"
+#include "types.h"
+
+namespace rnp {
+RNG::RNG(Type type)
+{
+ if (botan_rng_init(&botan_rng, type == Type::DRBG ? "user" : NULL)) {
+ throw rnp::rnp_exception(RNP_ERROR_RNG);
+ }
+}
+
+RNG::~RNG()
+{
+ (void) botan_rng_destroy(botan_rng);
+}
+
+void
+RNG::get(uint8_t *data, size_t len)
+{
+ if (botan_rng_get(botan_rng, data, len)) {
+ // This should never happen
+ throw rnp::rnp_exception(RNP_ERROR_RNG);
+ }
+}
+
+struct botan_rng_struct *
+RNG::handle()
+{
+ return botan_rng;
+}
+} // namespace rnp
diff --git a/src/lib/crypto/rng.h b/src/lib/crypto/rng.h
new file mode 100644
index 0000000..f452bd9
--- /dev/null
+++ b/src/lib/crypto/rng.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef RNP_RNG_H_
+#define RNP_RNG_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "config.h"
+
+#ifdef CRYPTO_BACKEND_BOTAN
+typedef struct botan_rng_struct *botan_rng_t;
+#endif
+
+namespace rnp {
+class RNG {
+ private:
+#ifdef CRYPTO_BACKEND_BOTAN
+ struct botan_rng_struct *botan_rng;
+#endif
+ public:
+ enum Type { DRBG, System };
+ /**
+ * @brief Construct a new RNG object.
+ * Note: OpenSSL uses own global RNG, so this class is not needed there and left
+ * only for code-level compatibility.
+ *
+ * @param type indicates which random generator to initialize.
+ * Possible values for Botan backend:
+ * - DRBG will initialize HMAC_DRBG, this generator is initialized on-demand
+ * (when used for the first time)
+ * - SYSTEM will initialize /dev/(u)random
+ */
+ RNG(Type type = Type::DRBG);
+ ~RNG();
+ /**
+ * @brief Get randoom bytes.
+ *
+ * @param data buffer where data should be stored. Cannot be NULL.
+ * @param len number of bytes required.
+ */
+ void get(uint8_t *data, size_t len);
+#ifdef CRYPTO_BACKEND_BOTAN
+ /**
+ * @brief Returns internal handle to botan rng. Returned
+ * handle is always initialized. In case of
+ * internal error NULL is returned
+ */
+ struct botan_rng_struct *handle();
+#endif
+};
+} // namespace rnp
+
+#endif // RNP_RNG_H_
diff --git a/src/lib/crypto/rng_ossl.cpp b/src/lib/crypto/rng_ossl.cpp
new file mode 100644
index 0000000..4ebcc95
--- /dev/null
+++ b/src/lib/crypto/rng_ossl.cpp
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2021 Ribose Inc.
+ * 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 HOLDERS 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 <assert.h>
+#include <openssl/rand.h>
+#include "rng.h"
+#include "types.h"
+
+namespace rnp {
+RNG::RNG(Type type)
+{
+}
+
+RNG::~RNG()
+{
+}
+
+void
+RNG::get(uint8_t *data, size_t len)
+{
+ if (RAND_bytes(data, len) != 1) {
+ throw rnp::rnp_exception(RNP_ERROR_RNG);
+ }
+}
+} // namespace rnp
diff --git a/src/lib/crypto/rsa.cpp b/src/lib/crypto/rsa.cpp
new file mode 100644
index 0000000..f7ddefe
--- /dev/null
+++ b/src/lib/crypto/rsa.cpp
@@ -0,0 +1,419 @@
+/*-
+ * Copyright (c) 2017-2022 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * 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 HOLDERS 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.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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
+ */
+#include <string>
+#include <cstring>
+#include <botan/ffi.h>
+#include "hash_botan.hpp"
+#include "crypto/rsa.h"
+#include "config.h"
+#include "utils.h"
+#include "bn.h"
+
+rnp_result_t
+rsa_validate_key(rnp::RNG *rng, const pgp_rsa_key_t *key, bool secret)
+{
+ bignum_t * n = NULL;
+ bignum_t * e = NULL;
+ bignum_t * p = NULL;
+ bignum_t * q = NULL;
+ botan_pubkey_t bpkey = NULL;
+ botan_privkey_t bskey = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+
+ /* load and check public key part */
+ if (!(n = mpi2bn(&key->n)) || !(e = mpi2bn(&key->e))) {
+ RNP_LOG("out of memory");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ if (botan_pubkey_load_rsa(&bpkey, BN_HANDLE_PTR(n), BN_HANDLE_PTR(e)) != 0) {
+ goto done;
+ }
+
+ if (botan_pubkey_check_key(bpkey, rng->handle(), 0)) {
+ goto done;
+ }
+
+ if (!secret) {
+ ret = RNP_SUCCESS;
+ goto done;
+ }
+
+ /* load and check secret key part */
+ if (!(p = mpi2bn(&key->p)) || !(q = mpi2bn(&key->q))) {
+ RNP_LOG("out of memory");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ /* p and q are reversed from normal usage in PGP */
+ if (botan_privkey_load_rsa(&bskey, BN_HANDLE_PTR(q), BN_HANDLE_PTR(p), BN_HANDLE_PTR(e))) {
+ goto done;
+ }
+
+ if (botan_privkey_check_key(bskey, rng->handle(), 0)) {
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ botan_pubkey_destroy(bpkey);
+ botan_privkey_destroy(bskey);
+ bn_free(n);
+ bn_free(e);
+ bn_free(p);
+ bn_free(q);
+ return ret;
+}
+
+static bool
+rsa_load_public_key(botan_pubkey_t *bkey, const pgp_rsa_key_t *key)
+{
+ bignum_t *n = NULL;
+ bignum_t *e = NULL;
+ bool res = false;
+
+ *bkey = NULL;
+ n = mpi2bn(&key->n);
+ e = mpi2bn(&key->e);
+
+ if (!n || !e) {
+ RNP_LOG("out of memory");
+ goto done;
+ }
+
+ res = !botan_pubkey_load_rsa(bkey, BN_HANDLE_PTR(n), BN_HANDLE_PTR(e));
+done:
+ bn_free(n);
+ bn_free(e);
+ return res;
+}
+
+static bool
+rsa_load_secret_key(botan_privkey_t *bkey, const pgp_rsa_key_t *key)
+{
+ bignum_t *p = NULL;
+ bignum_t *q = NULL;
+ bignum_t *e = NULL;
+ bool res = false;
+
+ *bkey = NULL;
+ p = mpi2bn(&key->p);
+ q = mpi2bn(&key->q);
+ e = mpi2bn(&key->e);
+
+ if (!p || !q || !e) {
+ RNP_LOG("out of memory");
+ goto done;
+ }
+
+ /* p and q are reversed from normal usage in PGP */
+ res = !botan_privkey_load_rsa(bkey, BN_HANDLE_PTR(q), BN_HANDLE_PTR(p), BN_HANDLE_PTR(e));
+done:
+ bn_free(p);
+ bn_free(q);
+ bn_free(e);
+ return res;
+}
+
+rnp_result_t
+rsa_encrypt_pkcs1(rnp::RNG * rng,
+ pgp_rsa_encrypted_t *out,
+ const uint8_t * in,
+ size_t in_len,
+ const pgp_rsa_key_t *key)
+{
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ botan_pubkey_t rsa_key = NULL;
+ botan_pk_op_encrypt_t enc_op = NULL;
+
+ if (!rsa_load_public_key(&rsa_key, key)) {
+ RNP_LOG("failed to load key");
+ return RNP_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (botan_pk_op_encrypt_create(&enc_op, rsa_key, "PKCS1v15", 0) != 0) {
+ goto done;
+ }
+
+ out->m.len = sizeof(out->m.mpi);
+ if (botan_pk_op_encrypt(enc_op, rng->handle(), out->m.mpi, &out->m.len, in, in_len)) {
+ out->m.len = 0;
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ botan_pk_op_encrypt_destroy(enc_op);
+ botan_pubkey_destroy(rsa_key);
+ return ret;
+}
+
+rnp_result_t
+rsa_verify_pkcs1(const pgp_rsa_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_rsa_key_t * key)
+{
+ char padding_name[64] = {0};
+ botan_pubkey_t rsa_key = NULL;
+ botan_pk_op_verify_t verify_op = NULL;
+ rnp_result_t ret = RNP_ERROR_SIGNATURE_INVALID;
+
+ if (!rsa_load_public_key(&rsa_key, key)) {
+ RNP_LOG("failed to load key");
+ return RNP_ERROR_OUT_OF_MEMORY;
+ }
+
+ snprintf(padding_name,
+ sizeof(padding_name),
+ "EMSA-PKCS1-v1_5(Raw,%s)",
+ rnp::Hash_Botan::name_backend(hash_alg));
+
+ if (botan_pk_op_verify_create(&verify_op, rsa_key, padding_name, 0) != 0) {
+ goto done;
+ }
+
+ if (botan_pk_op_verify_update(verify_op, hash, hash_len) != 0) {
+ goto done;
+ }
+
+ if (botan_pk_op_verify_finish(verify_op, sig->s.mpi, sig->s.len) != 0) {
+ goto done;
+ }
+
+ ret = RNP_SUCCESS;
+done:
+ botan_pk_op_verify_destroy(verify_op);
+ botan_pubkey_destroy(rsa_key);
+ return ret;
+}
+
+rnp_result_t
+rsa_sign_pkcs1(rnp::RNG * rng,
+ pgp_rsa_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_rsa_key_t *key)
+{
+ char padding_name[64] = {0};
+ botan_privkey_t rsa_key;
+ botan_pk_op_sign_t sign_op;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+
+ if (mpi_bytes(&key->q) == 0) {
+ RNP_LOG("private key not set");
+ return ret;
+ }
+
+ if (!rsa_load_secret_key(&rsa_key, key)) {
+ RNP_LOG("failed to load key");
+ return RNP_ERROR_OUT_OF_MEMORY;
+ }
+
+ snprintf(padding_name,
+ sizeof(padding_name),
+ "EMSA-PKCS1-v1_5(Raw,%s)",
+ rnp::Hash_Botan::name_backend(hash_alg));
+
+ if (botan_pk_op_sign_create(&sign_op, rsa_key, padding_name, 0) != 0) {
+ goto done;
+ }
+
+ if (botan_pk_op_sign_update(sign_op, hash, hash_len)) {
+ goto done;
+ }
+
+ sig->s.len = sizeof(sig->s.mpi);
+ if (botan_pk_op_sign_finish(sign_op, rng->handle(), sig->s.mpi, &sig->s.len)) {
+ goto done;
+ }
+
+ ret = RNP_SUCCESS;
+done:
+ botan_pk_op_sign_destroy(sign_op);
+ botan_privkey_destroy(rsa_key);
+ return ret;
+}
+
+rnp_result_t
+rsa_decrypt_pkcs1(rnp::RNG * rng,
+ uint8_t * out,
+ size_t * out_len,
+ const pgp_rsa_encrypted_t *in,
+ const pgp_rsa_key_t * key)
+{
+ botan_privkey_t rsa_key = NULL;
+ botan_pk_op_decrypt_t decrypt_op = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+
+ if (mpi_bytes(&key->q) == 0) {
+ RNP_LOG("private key not set");
+ return ret;
+ }
+
+ if (!rsa_load_secret_key(&rsa_key, key)) {
+ RNP_LOG("failed to load key");
+ return RNP_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (botan_pk_op_decrypt_create(&decrypt_op, rsa_key, "PKCS1v15", 0)) {
+ goto done;
+ }
+
+ *out_len = PGP_MPINT_SIZE;
+ if (botan_pk_op_decrypt(decrypt_op, out, out_len, in->m.mpi, in->m.len)) {
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ botan_privkey_destroy(rsa_key);
+ botan_pk_op_decrypt_destroy(decrypt_op);
+ return ret;
+}
+
+rnp_result_t
+rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits)
+{
+ if ((numbits < 1024) || (numbits > PGP_MPINT_BITS)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ botan_privkey_t rsa_key = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ int cmp;
+ bignum_t * n = bn_new();
+ bignum_t * e = bn_new();
+ bignum_t * p = bn_new();
+ bignum_t * q = bn_new();
+ bignum_t * d = bn_new();
+ bignum_t * u = bn_new();
+
+ if (!n || !e || !p || !q || !d || !u) {
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ if (botan_privkey_create(
+ &rsa_key, "RSA", std::to_string(numbits).c_str(), rng->handle())) {
+ goto end;
+ }
+
+ if (botan_privkey_check_key(rsa_key, rng->handle(), 1) != 0) {
+ goto end;
+ }
+
+ if (botan_privkey_get_field(BN_HANDLE_PTR(n), rsa_key, "n") ||
+ botan_privkey_get_field(BN_HANDLE_PTR(e), rsa_key, "e") ||
+ botan_privkey_get_field(BN_HANDLE_PTR(d), rsa_key, "d") ||
+ botan_privkey_get_field(BN_HANDLE_PTR(p), rsa_key, "p") ||
+ botan_privkey_get_field(BN_HANDLE_PTR(q), rsa_key, "q")) {
+ goto end;
+ }
+
+ /* RFC 4880, 5.5.3 tells that p < q. GnuPG relies on this. */
+ (void) botan_mp_cmp(&cmp, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q));
+ if (cmp > 0) {
+ (void) botan_mp_swap(BN_HANDLE_PTR(p), BN_HANDLE_PTR(q));
+ }
+
+ if (botan_mp_mod_inverse(BN_HANDLE_PTR(u), BN_HANDLE_PTR(p), BN_HANDLE_PTR(q)) != 0) {
+ RNP_LOG("Error computing RSA u param");
+ ret = RNP_ERROR_BAD_STATE;
+ goto end;
+ }
+
+ bn2mpi(n, &key->n);
+ bn2mpi(e, &key->e);
+ bn2mpi(p, &key->p);
+ bn2mpi(q, &key->q);
+ bn2mpi(d, &key->d);
+ bn2mpi(u, &key->u);
+
+ ret = RNP_SUCCESS;
+end:
+ botan_privkey_destroy(rsa_key);
+ bn_free(n);
+ bn_free(e);
+ bn_free(p);
+ bn_free(q);
+ bn_free(d);
+ bn_free(u);
+ return ret;
+}
diff --git a/src/lib/crypto/rsa.h b/src/lib/crypto/rsa.h
new file mode 100644
index 0000000..6b1b615
--- /dev/null
+++ b/src/lib/crypto/rsa.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * This code is originally derived from software contributed to
+ * The NetBSD Foundation by Alistair Crooks (agc@netbsd.org), and
+ * carried further by Ribose Inc (https://www.ribose.com).
+ *
+ * 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 HOLDERS 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.
+ */
+
+#ifndef RNP_RSA_H_
+#define RNP_RSA_H_
+
+#include <rnp/rnp_def.h>
+#include <repgp/repgp_def.h>
+#include "crypto/rng.h"
+#include "crypto/mpi.h"
+
+typedef struct pgp_rsa_key_t {
+ pgp_mpi_t n;
+ pgp_mpi_t e;
+ /* secret mpis */
+ pgp_mpi_t d;
+ pgp_mpi_t p;
+ pgp_mpi_t q;
+ pgp_mpi_t u;
+} pgp_rsa_key_t;
+
+typedef struct pgp_rsa_signature_t {
+ pgp_mpi_t s;
+} pgp_rsa_signature_t;
+
+typedef struct pgp_rsa_encrypted_t {
+ pgp_mpi_t m;
+} pgp_rsa_encrypted_t;
+
+/*
+ * RSA encrypt/decrypt
+ */
+
+rnp_result_t rsa_validate_key(rnp::RNG *rng, const pgp_rsa_key_t *key, bool secret);
+
+rnp_result_t rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits);
+
+rnp_result_t rsa_encrypt_pkcs1(rnp::RNG * rng,
+ pgp_rsa_encrypted_t *out,
+ const uint8_t * in,
+ size_t in_len,
+ const pgp_rsa_key_t *key);
+
+rnp_result_t rsa_decrypt_pkcs1(rnp::RNG * rng,
+ uint8_t * out,
+ size_t * out_len,
+ const pgp_rsa_encrypted_t *in,
+ const pgp_rsa_key_t * key);
+
+rnp_result_t rsa_verify_pkcs1(const pgp_rsa_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_rsa_key_t * key);
+
+rnp_result_t rsa_sign_pkcs1(rnp::RNG * rng,
+ pgp_rsa_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_rsa_key_t *key);
+
+#endif
diff --git a/src/lib/crypto/rsa_ossl.cpp b/src/lib/crypto/rsa_ossl.cpp
new file mode 100644
index 0000000..24cff29
--- /dev/null
+++ b/src/lib/crypto/rsa_ossl.cpp
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2021-2022, [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 <string>
+#include <cstring>
+#include <cassert>
+#include "crypto/rsa.h"
+#include "config.h"
+#include "utils.h"
+#include "bn.h"
+#include "ossl_common.h"
+#include <openssl/rsa.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#ifdef CRYPTO_BACKEND_OPENSSL3
+#include <openssl/param_build.h>
+#include <openssl/core_names.h>
+#endif
+#include "hash_ossl.hpp"
+
+#ifndef CRYPTO_BACKEND_OPENSSL3
+static RSA *
+rsa_load_public_key(const pgp_rsa_key_t *key)
+{
+ RSA * rsa = NULL;
+ bignum_t *n = mpi2bn(&key->n);
+ bignum_t *e = mpi2bn(&key->e);
+
+ if (!n || !e) {
+ RNP_LOG("out of memory");
+ goto done;
+ }
+ rsa = RSA_new();
+ if (!rsa) {
+ RNP_LOG("Out of memory");
+ goto done;
+ }
+ if (RSA_set0_key(rsa, n, e, NULL) != 1) {
+ RNP_LOG("Public key load error: %lu", ERR_peek_last_error());
+ RSA_free(rsa);
+ rsa = NULL;
+ goto done;
+ }
+done:
+ /* OpenSSL set0 function transfers ownership of bignums */
+ if (!rsa) {
+ bn_free(n);
+ bn_free(e);
+ }
+ return rsa;
+}
+
+static RSA *
+rsa_load_secret_key(const pgp_rsa_key_t *key)
+{
+ RSA * rsa = NULL;
+ bignum_t *n = mpi2bn(&key->n);
+ bignum_t *e = mpi2bn(&key->e);
+ bignum_t *p = mpi2bn(&key->p);
+ bignum_t *q = mpi2bn(&key->q);
+ bignum_t *d = mpi2bn(&key->d);
+
+ if (!n || !p || !q || !e || !d) {
+ RNP_LOG("out of memory");
+ goto done;
+ }
+
+ rsa = RSA_new();
+ if (!rsa) {
+ RNP_LOG("Out of memory");
+ goto done;
+ }
+ if (RSA_set0_key(rsa, n, e, d) != 1) {
+ RNP_LOG("Secret key load error: %lu", ERR_peek_last_error());
+ RSA_free(rsa);
+ rsa = NULL;
+ goto done;
+ }
+ /* OpenSSL has p < q, as we do */
+ if (RSA_set0_factors(rsa, p, q) != 1) {
+ RNP_LOG("Factors load error: %lu", ERR_peek_last_error());
+ RSA_free(rsa);
+ rsa = NULL;
+ goto done;
+ }
+done:
+ /* OpenSSL set0 function transfers ownership of bignums */
+ if (!rsa) {
+ bn_free(n);
+ bn_free(p);
+ bn_free(q);
+ bn_free(e);
+ bn_free(d);
+ }
+ return rsa;
+}
+
+static EVP_PKEY_CTX *
+rsa_init_context(const pgp_rsa_key_t *key, bool secret)
+{
+ EVP_PKEY *evpkey = EVP_PKEY_new();
+ if (!evpkey) {
+ RNP_LOG("allocation failed");
+ return NULL;
+ }
+ EVP_PKEY_CTX *ctx = NULL;
+ RSA * rsakey = secret ? rsa_load_secret_key(key) : rsa_load_public_key(key);
+ if (!rsakey) {
+ goto done;
+ }
+ if (EVP_PKEY_set1_RSA(evpkey, rsakey) <= 0) {
+ RNP_LOG("Failed to set key: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ ctx = EVP_PKEY_CTX_new(evpkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ }
+done:
+ RSA_free(rsakey);
+ EVP_PKEY_free(evpkey);
+ return ctx;
+}
+#else
+static OSSL_PARAM *
+rsa_bld_params(const pgp_rsa_key_t *key, bool secret)
+{
+ OSSL_PARAM * params = NULL;
+ OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ bignum_t * n = mpi2bn(&key->n);
+ bignum_t * e = mpi2bn(&key->e);
+ bignum_t * d = NULL;
+ bignum_t * p = NULL;
+ bignum_t * q = NULL;
+ bignum_t * u = NULL;
+ BN_CTX * bnctx = NULL;
+
+ if (!n || !e || !bld) {
+ RNP_LOG("Out of memory");
+ goto done;
+ }
+
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e)) {
+ RNP_LOG("Failed to push RSA params.");
+ goto done;
+ }
+ if (secret) {
+ d = mpi2bn(&key->d);
+ /* As we have u = p^-1 mod q, and qInv = q^-1 mod p, we need to replace one with
+ * another */
+ p = mpi2bn(&key->q);
+ q = mpi2bn(&key->p);
+ u = mpi2bn(&key->u);
+ if (!d || !p || !q || !u) {
+ goto done;
+ }
+ /* We need to calculate exponents manually */
+ bnctx = BN_CTX_new();
+ if (!bnctx) {
+ RNP_LOG("Failed to allocate BN_CTX.");
+ goto done;
+ }
+ bignum_t *p1 = BN_CTX_get(bnctx);
+ bignum_t *q1 = BN_CTX_get(bnctx);
+ bignum_t *dp = BN_CTX_get(bnctx);
+ bignum_t *dq = BN_CTX_get(bnctx);
+ if (!BN_copy(p1, p) || !BN_sub_word(p1, 1) || !BN_copy(q1, q) || !BN_sub_word(q1, 1) ||
+ !BN_mod(dp, d, p1, bnctx) || !BN_mod(dq, d, q1, bnctx)) {
+ RNP_LOG("Failed to calculate dP or dQ.");
+ }
+ /* Push params */
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, p) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, q) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT1, dp) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT2, dq) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, u)) {
+ RNP_LOG("Failed to push RSA secret params.");
+ goto done;
+ }
+ }
+ params = OSSL_PARAM_BLD_to_param(bld);
+ if (!params) {
+ RNP_LOG("Failed to build RSA params: %s.", ossl_latest_err());
+ }
+done:
+ bn_free(n);
+ bn_free(e);
+ bn_free(d);
+ bn_free(p);
+ bn_free(q);
+ bn_free(u);
+ BN_CTX_free(bnctx);
+ OSSL_PARAM_BLD_free(bld);
+ return params;
+}
+
+static EVP_PKEY *
+rsa_load_key(const pgp_rsa_key_t *key, bool secret)
+{
+ /* Build params */
+ OSSL_PARAM *params = rsa_bld_params(key, secret);
+ if (!params) {
+ return NULL;
+ }
+ /* Create context for key creation */
+ EVP_PKEY * res = NULL;
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %s", ossl_latest_err());
+ goto done;
+ }
+ /* Create key */
+ if (EVP_PKEY_fromdata_init(ctx) <= 0) {
+ RNP_LOG("Failed to initialize key creation: %s", ossl_latest_err());
+ goto done;
+ }
+ if (EVP_PKEY_fromdata(
+ ctx, &res, secret ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) <= 0) {
+ RNP_LOG("Failed to create RSA key: %s", ossl_latest_err());
+ }
+done:
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return res;
+}
+
+static EVP_PKEY_CTX *
+rsa_init_context(const pgp_rsa_key_t *key, bool secret)
+{
+ EVP_PKEY *pkey = rsa_load_key(key, secret);
+ if (!pkey) {
+ return NULL;
+ }
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
+ if (!ctx) {
+ RNP_LOG("Context allocation failed: %s", ossl_latest_err());
+ }
+ EVP_PKEY_free(pkey);
+ return ctx;
+}
+#endif
+
+rnp_result_t
+rsa_validate_key(rnp::RNG *rng, const pgp_rsa_key_t *key, bool secret)
+{
+#ifdef CRYPTO_BACKEND_OPENSSL3
+ EVP_PKEY_CTX *ctx = rsa_init_context(key, secret);
+ if (!ctx) {
+ RNP_LOG("Failed to init context: %s", ossl_latest_err());
+ return RNP_ERROR_GENERIC;
+ }
+ int res = secret ? EVP_PKEY_pairwise_check(ctx) : EVP_PKEY_public_check(ctx);
+ if (res <= 0) {
+ RNP_LOG("Key validation error: %s", ossl_latest_err());
+ }
+ EVP_PKEY_CTX_free(ctx);
+ return res > 0 ? RNP_SUCCESS : RNP_ERROR_GENERIC;
+#else
+ if (secret) {
+ EVP_PKEY_CTX *ctx = rsa_init_context(key, secret);
+ if (!ctx) {
+ RNP_LOG("Failed to init context: %s", ossl_latest_err());
+ return RNP_ERROR_GENERIC;
+ }
+ int res = EVP_PKEY_check(ctx);
+ if (res <= 0) {
+ RNP_LOG("Key validation error: %s", ossl_latest_err());
+ }
+ EVP_PKEY_CTX_free(ctx);
+ return res > 0 ? RNP_SUCCESS : RNP_ERROR_GENERIC;
+ }
+
+ /* OpenSSL 1.1.1 doesn't have RSA public key check function, so let's do some checks */
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ bignum_t * n = mpi2bn(&key->n);
+ bignum_t * e = mpi2bn(&key->e);
+ if (!n || !e) {
+ RNP_LOG("out of memory");
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ if ((BN_num_bits(n) < 512) || !BN_is_odd(n) || (BN_num_bits(e) < 2) || !BN_is_odd(e)) {
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ bn_free(n);
+ bn_free(e);
+ return ret;
+#endif
+}
+
+static bool
+rsa_setup_context(EVP_PKEY_CTX *ctx)
+{
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) {
+ RNP_LOG("Failed to set padding: %lu", ERR_peek_last_error());
+ return false;
+ }
+ return true;
+}
+
+static const uint8_t PKCS1_SHA1_ENCODING[15] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14};
+
+static bool
+rsa_setup_signature_hash(EVP_PKEY_CTX * ctx,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t *&enc,
+ size_t & enc_size)
+{
+ const char *hash_name = rnp::Hash_OpenSSL::name(hash_alg);
+ if (!hash_name) {
+ RNP_LOG("Unknown hash: %d", (int) hash_alg);
+ return false;
+ }
+ const EVP_MD *hash_tp = EVP_get_digestbyname(hash_name);
+ if (!hash_tp) {
+ RNP_LOG("Error creating hash object for '%s'", hash_name);
+ return false;
+ }
+ if (EVP_PKEY_CTX_set_signature_md(ctx, hash_tp) <= 0) {
+ if ((hash_alg != PGP_HASH_SHA1)) {
+ RNP_LOG("Failed to set digest %s: %s", hash_name, ossl_latest_err());
+ return false;
+ }
+ enc = &PKCS1_SHA1_ENCODING[0];
+ enc_size = sizeof(PKCS1_SHA1_ENCODING);
+ } else {
+ enc = NULL;
+ enc_size = 0;
+ }
+ return true;
+}
+
+rnp_result_t
+rsa_encrypt_pkcs1(rnp::RNG * rng,
+ pgp_rsa_encrypted_t *out,
+ const uint8_t * in,
+ size_t in_len,
+ const pgp_rsa_key_t *key)
+{
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ EVP_PKEY_CTX *ctx = rsa_init_context(key, false);
+ if (!ctx) {
+ return ret;
+ }
+ if (EVP_PKEY_encrypt_init(ctx) <= 0) {
+ RNP_LOG("Failed to initialize encryption: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (!rsa_setup_context(ctx)) {
+ goto done;
+ }
+ out->m.len = sizeof(out->m.mpi);
+ if (EVP_PKEY_encrypt(ctx, out->m.mpi, &out->m.len, in, in_len) <= 0) {
+ RNP_LOG("Encryption failed: %lu", ERR_peek_last_error());
+ out->m.len = 0;
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+rnp_result_t
+rsa_verify_pkcs1(const pgp_rsa_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_rsa_key_t * key)
+{
+ rnp_result_t ret = RNP_ERROR_SIGNATURE_INVALID;
+ EVP_PKEY_CTX *ctx = rsa_init_context(key, false);
+ if (!ctx) {
+ return ret;
+ }
+ const uint8_t *hash_enc = NULL;
+ size_t hash_enc_size = 0;
+ uint8_t hash_enc_buf[PGP_MAX_HASH_SIZE + 32] = {0};
+ assert(hash_len + hash_enc_size <= sizeof(hash_enc_buf));
+
+ if (EVP_PKEY_verify_init(ctx) <= 0) {
+ RNP_LOG("Failed to initialize verification: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (!rsa_setup_context(ctx) ||
+ !rsa_setup_signature_hash(ctx, hash_alg, hash_enc, hash_enc_size)) {
+ goto done;
+ }
+ /* Check whether we need to workaround on unsupported SHA1 for RSA signature verification
+ */
+ if (hash_enc_size) {
+ memcpy(hash_enc_buf, hash_enc, hash_enc_size);
+ memcpy(&hash_enc_buf[hash_enc_size], hash, hash_len);
+ hash = hash_enc_buf;
+ hash_len += hash_enc_size;
+ }
+ int res;
+ if (sig->s.len < key->n.len) {
+ /* OpenSSL doesn't like signatures smaller then N */
+ pgp_mpi_t sn;
+ sn.len = key->n.len;
+ size_t diff = key->n.len - sig->s.len;
+ memset(sn.mpi, 0, diff);
+ memcpy(&sn.mpi[diff], sig->s.mpi, sig->s.len);
+ res = EVP_PKEY_verify(ctx, sn.mpi, sn.len, hash, hash_len);
+ } else {
+ res = EVP_PKEY_verify(ctx, sig->s.mpi, sig->s.len, hash, hash_len);
+ }
+ if (res > 0) {
+ ret = RNP_SUCCESS;
+ } else {
+ RNP_LOG("RSA verification failure: %s", ossl_latest_err());
+ }
+done:
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+rnp_result_t
+rsa_sign_pkcs1(rnp::RNG * rng,
+ pgp_rsa_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_rsa_key_t *key)
+{
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ if (mpi_bytes(&key->q) == 0) {
+ RNP_LOG("private key not set");
+ return ret;
+ }
+ EVP_PKEY_CTX *ctx = rsa_init_context(key, true);
+ if (!ctx) {
+ return ret;
+ }
+ const uint8_t *hash_enc = NULL;
+ size_t hash_enc_size = 0;
+ uint8_t hash_enc_buf[PGP_MAX_HASH_SIZE + 32] = {0};
+ assert(hash_len + hash_enc_size <= sizeof(hash_enc_buf));
+ if (EVP_PKEY_sign_init(ctx) <= 0) {
+ RNP_LOG("Failed to initialize signing: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (!rsa_setup_context(ctx) ||
+ !rsa_setup_signature_hash(ctx, hash_alg, hash_enc, hash_enc_size)) {
+ goto done;
+ }
+ /* Check whether we need to workaround on unsupported SHA1 for RSA signature verification
+ */
+ if (hash_enc_size) {
+ memcpy(hash_enc_buf, hash_enc, hash_enc_size);
+ memcpy(&hash_enc_buf[hash_enc_size], hash, hash_len);
+ hash = hash_enc_buf;
+ hash_len += hash_enc_size;
+ }
+ sig->s.len = PGP_MPINT_SIZE;
+ if (EVP_PKEY_sign(ctx, sig->s.mpi, &sig->s.len, hash, hash_len) <= 0) {
+ RNP_LOG("Encryption failed: %lu", ERR_peek_last_error());
+ sig->s.len = 0;
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+rnp_result_t
+rsa_decrypt_pkcs1(rnp::RNG * rng,
+ uint8_t * out,
+ size_t * out_len,
+ const pgp_rsa_encrypted_t *in,
+ const pgp_rsa_key_t * key)
+{
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ if (mpi_bytes(&key->q) == 0) {
+ RNP_LOG("private key not set");
+ return ret;
+ }
+ EVP_PKEY_CTX *ctx = rsa_init_context(key, true);
+ if (!ctx) {
+ return ret;
+ }
+ if (EVP_PKEY_decrypt_init(ctx) <= 0) {
+ RNP_LOG("Failed to initialize encryption: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (!rsa_setup_context(ctx)) {
+ goto done;
+ }
+ *out_len = PGP_MPINT_SIZE;
+ if (EVP_PKEY_decrypt(ctx, out, out_len, in->m.mpi, in->m.len) <= 0) {
+ RNP_LOG("Encryption failed: %lu", ERR_peek_last_error());
+ *out_len = 0;
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+rnp_result_t
+rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits)
+{
+ if ((numbits < 1024) || (numbits > PGP_MPINT_BITS)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ const RSA * rsa = NULL;
+ EVP_PKEY * pkey = NULL;
+ EVP_PKEY_CTX * ctx = NULL;
+ const bignum_t *u = NULL;
+ BN_CTX * bnctx = NULL;
+
+ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+ if (!ctx) {
+ RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
+ return ret;
+ }
+ if (EVP_PKEY_keygen_init(ctx) <= 0) {
+ RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, numbits) <= 0) {
+ RNP_LOG("Failed to set rsa bits: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
+ RNP_LOG("RSA keygen failed: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ rsa = EVP_PKEY_get0_RSA(pkey);
+ if (!rsa) {
+ RNP_LOG("Failed to retrieve RSA key: %lu", ERR_peek_last_error());
+ goto done;
+ }
+ if (RSA_check_key(rsa) != 1) {
+ RNP_LOG("Key validation error: %lu", ERR_peek_last_error());
+ goto done;
+ }
+
+ const bignum_t *n;
+ const bignum_t *e;
+ const bignum_t *p;
+ const bignum_t *q;
+ const bignum_t *d;
+ n = RSA_get0_n(rsa);
+ e = RSA_get0_e(rsa);
+ d = RSA_get0_d(rsa);
+ p = RSA_get0_p(rsa);
+ q = RSA_get0_q(rsa);
+ if (!n || !e || !d || !p || !q) {
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ /* OpenSSL doesn't care whether p < q */
+ if (BN_cmp(p, q) > 0) {
+ /* In this case we have u, as iqmp is inverse of q mod p, and we exchange them */
+ const bignum_t *tmp = p;
+ p = q;
+ q = tmp;
+ u = RSA_get0_iqmp(rsa);
+ } else {
+ /* we need to calculate u, since we need inverse of p mod q, while OpenSSL has inverse
+ * of q mod p, and doesn't care of p < q */
+ bnctx = BN_CTX_new();
+ if (!bnctx) {
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ BN_CTX_start(bnctx);
+ bignum_t *nu = BN_CTX_get(bnctx);
+ bignum_t *nq = BN_CTX_get(bnctx);
+ if (!nu || !nq) {
+ ret = RNP_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ BN_with_flags(nq, q, BN_FLG_CONSTTIME);
+ /* calculate inverse of p mod q */
+ if (!BN_mod_inverse(nu, p, nq, bnctx)) {
+ RNP_LOG("Failed to calculate u");
+ ret = RNP_ERROR_BAD_STATE;
+ goto done;
+ }
+ u = nu;
+ }
+ bn2mpi(n, &key->n);
+ bn2mpi(e, &key->e);
+ bn2mpi(p, &key->p);
+ bn2mpi(q, &key->q);
+ bn2mpi(d, &key->d);
+ bn2mpi(u, &key->u);
+ ret = RNP_SUCCESS;
+done:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(pkey);
+ BN_CTX_free(bnctx);
+ return ret;
+}
diff --git a/src/lib/crypto/s2k.cpp b/src/lib/crypto/s2k.cpp
new file mode 100644
index 0000000..ede7965
--- /dev/null
+++ b/src/lib/crypto/s2k.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2017-2022 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * This code is originally derived from software contributed to
+ * The NetBSD Foundation by Alistair Crooks (agc@netbsd.org), and
+ * carried further by Ribose Inc (https://www.ribose.com).
+ *
+ * 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 HOLDERS 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 <stdio.h>
+#include "config.h"
+#ifndef _MSC_VER
+#include <sys/time.h>
+#else
+#include "uniwin.h"
+#endif
+
+#include "crypto/s2k.h"
+#include "defaults.h"
+#include "rnp.h"
+#include "types.h"
+#include "utils.h"
+#ifdef CRYPTO_BACKEND_BOTAN
+#include <botan/ffi.h>
+#include "hash_botan.hpp"
+#endif
+
+bool
+pgp_s2k_derive_key(pgp_s2k_t *s2k, const char *password, uint8_t *key, int keysize)
+{
+ uint8_t *saltptr = NULL;
+ unsigned iterations = 1;
+
+ switch (s2k->specifier) {
+ case PGP_S2KS_SIMPLE:
+ break;
+ case PGP_S2KS_SALTED:
+ saltptr = s2k->salt;
+ break;
+ case PGP_S2KS_ITERATED_AND_SALTED:
+ saltptr = s2k->salt;
+ if (s2k->iterations < 256) {
+ iterations = pgp_s2k_decode_iterations(s2k->iterations);
+ } else {
+ iterations = s2k->iterations;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ if (pgp_s2k_iterated(s2k->hash_alg, key, keysize, password, saltptr, iterations)) {
+ RNP_LOG("s2k failed");
+ return false;
+ }
+
+ return true;
+}
+
+#ifdef CRYPTO_BACKEND_BOTAN
+int
+pgp_s2k_iterated(pgp_hash_alg_t alg,
+ uint8_t * out,
+ size_t output_len,
+ const char * password,
+ const uint8_t *salt,
+ size_t iterations)
+{
+ char s2k_algo_str[128];
+ snprintf(s2k_algo_str,
+ sizeof(s2k_algo_str),
+ "OpenPGP-S2K(%s)",
+ rnp::Hash_Botan::name_backend(alg));
+
+ return botan_pwdhash(s2k_algo_str,
+ iterations,
+ 0,
+ 0,
+ out,
+ output_len,
+ password,
+ 0,
+ salt,
+ salt ? PGP_SALT_SIZE : 0);
+}
+#endif
+
+size_t
+pgp_s2k_decode_iterations(uint8_t c)
+{
+ // See RFC 4880 section 3.7.1.3
+ return (16 + (c & 0x0F)) << ((c >> 4) + 6);
+}
+
+size_t
+pgp_s2k_round_iterations(size_t iterations)
+{
+ return pgp_s2k_decode_iterations(pgp_s2k_encode_iterations(iterations));
+}
+
+uint8_t
+pgp_s2k_encode_iterations(size_t iterations)
+{
+ /* For compatibility, when an S2K specifier is used, the special value
+ * 254 or 255 is stored in the position where the hash algorithm octet
+ * would have been in the old data structure. This is then followed
+ * immediately by a one-octet algorithm identifier, and then by the S2K
+ * specifier as encoded above.
+ * 0: secret data is unencrypted (no password)
+ * 255 or 254: followed by algorithm octet and S2K specifier
+ * Cipher alg: use Simple S2K algorithm using MD5 hash
+ * For more info refer to rfc 4880 section 3.7.2.1.
+ */
+ for (uint16_t c = 0; c < 256; ++c) {
+ // This could be a binary search
+ if (pgp_s2k_decode_iterations(c) >= iterations) {
+ return c;
+ }
+ }
+ return 255;
+}
+
+/// Should this function be elsewhere?
+static uint64_t
+get_timestamp_usec()
+{
+#ifndef _MSC_VER
+ // TODO: Consider clock_gettime
+ struct timeval tv;
+ ::gettimeofday(&tv, NULL);
+ return (static_cast<uint64_t>(tv.tv_sec) * 1000000) + static_cast<uint64_t>(tv.tv_usec);
+#else
+ return GetTickCount64() * 1000;
+#endif
+}
+
+size_t
+pgp_s2k_compute_iters(pgp_hash_alg_t alg, size_t desired_msec, size_t trial_msec)
+{
+ if (desired_msec == 0) {
+ desired_msec = DEFAULT_S2K_MSEC;
+ }
+ if (trial_msec == 0) {
+ trial_msec = DEFAULT_S2K_TUNE_MSEC;
+ }
+
+ // number of iterations to estimate the number of iterations
+ // (sorry, cannot tell it better)
+ const uint8_t NUM_ITERATIONS = 16;
+ uint64_t duration = 0;
+ size_t bytes = 0;
+ try {
+ for (uint8_t i = 0; i < NUM_ITERATIONS; i++) {
+ uint64_t start = get_timestamp_usec();
+ uint64_t end = start;
+ auto hash = rnp::Hash::create(alg);
+ uint8_t buf[8192] = {0};
+ while (end - start < trial_msec * 1000ull) {
+ hash->add(buf, sizeof(buf));
+ bytes += sizeof(buf);
+ end = get_timestamp_usec();
+ }
+ hash->finish(buf);
+ duration += (end - start);
+ }
+ } catch (const std::exception &e) {
+ RNP_LOG("Failed to hash data: %s", e.what());
+ return 0;
+ }
+
+ const uint8_t MIN_ITERS = 96;
+ if (duration == 0) {
+ return pgp_s2k_decode_iterations(MIN_ITERS);
+ }
+
+ const double bytes_per_usec = static_cast<double>(bytes) / duration;
+ const double desired_usec = desired_msec * 1000.0;
+ const double bytes_for_target = bytes_per_usec * desired_usec;
+ const uint8_t iters = pgp_s2k_encode_iterations(bytes_for_target);
+
+ return pgp_s2k_decode_iterations((iters > MIN_ITERS) ? iters : MIN_ITERS);
+}
diff --git a/src/lib/crypto/s2k.h b/src/lib/crypto/s2k.h
new file mode 100644
index 0000000..c67a773
--- /dev/null
+++ b/src/lib/crypto/s2k.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * This code is originally derived from software contributed to
+ * The NetBSD Foundation by Alistair Crooks (agc@netbsd.org), and
+ * carried further by Ribose Inc (https://www.ribose.com).
+ *
+ * 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 HOLDERS 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.
+ */
+
+#ifndef RNP_S2K_H_
+#define RNP_S2K_H_
+
+#include <cstdint>
+#include "repgp/repgp_def.h"
+
+typedef struct pgp_s2k_t pgp_s2k_t;
+
+int pgp_s2k_iterated(pgp_hash_alg_t alg,
+ uint8_t * out,
+ size_t output_len,
+ const char * password,
+ const uint8_t *salt,
+ size_t iterations);
+
+size_t pgp_s2k_decode_iterations(uint8_t encoded_iter);
+
+uint8_t pgp_s2k_encode_iterations(size_t iterations);
+
+// Round iterations to nearest representable value
+size_t pgp_s2k_round_iterations(size_t iterations);
+
+size_t pgp_s2k_compute_iters(pgp_hash_alg_t alg, size_t desired_msec, size_t trial_msec);
+
+/** @brief Derive key from password using the information stored in s2k structure
+ * @param s2k pointer to s2k structure, filled according to RFC 4880.
+ * Iterations field may contain encoded ( < 256) or decoded ( > 256) value.
+ * @param password NULL-terminated password
+ * @param key buffer to store the derived key, must have at least keysize bytes
+ * @param keysize number of bytes in the key.
+ * @return true on success or false otherwise
+ */
+bool pgp_s2k_derive_key(pgp_s2k_t *s2k, const char *password, uint8_t *key, int keysize);
+
+#endif
diff --git a/src/lib/crypto/s2k_ossl.cpp b/src/lib/crypto/s2k_ossl.cpp
new file mode 100644
index 0000000..acf1ca9
--- /dev/null
+++ b/src/lib/crypto/s2k_ossl.cpp
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 2021 Ribose Inc.
+ * 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 HOLDERS 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 <cstdint>
+#include <vector>
+#include <algorithm>
+#include <openssl/evp.h>
+#include "hash.hpp"
+#include "s2k.h"
+#include "mem.h"
+#include "logging.h"
+
+int
+pgp_s2k_iterated(pgp_hash_alg_t alg,
+ uint8_t * out,
+ size_t output_len,
+ const char * password,
+ const uint8_t *salt,
+ size_t iterations)
+{
+ if ((iterations > 1) && !salt) {
+ RNP_LOG("Iterated S2K mus be salted as well.");
+ return 1;
+ }
+ size_t hash_len = rnp::Hash::size(alg);
+ if (!hash_len) {
+ RNP_LOG("Unknown digest: %d", (int) alg);
+ return 1;
+ }
+ try {
+ size_t pswd_len = strlen(password);
+ size_t salt_len = salt ? PGP_SALT_SIZE : 0;
+
+ rnp::secure_vector<uint8_t> data(salt_len + pswd_len);
+ if (salt_len) {
+ memcpy(data.data(), salt, PGP_SALT_SIZE);
+ }
+ memcpy(data.data() + salt_len, password, pswd_len);
+ size_t zeroes = 0;
+
+ while (output_len) {
+ /* create hash context */
+ auto hash = rnp::Hash::create(alg);
+ /* add leading zeroes */
+ for (size_t z = 0; z < zeroes; z++) {
+ uint8_t zero = 0;
+ hash->add(&zero, 1);
+ }
+ if (!data.empty()) {
+ /* if iteration is 1 then still hash the whole data chunk */
+ size_t left = std::max(data.size(), iterations);
+ while (left) {
+ size_t to_hash = std::min(left, data.size());
+ hash->add(data.data(), to_hash);
+ left -= to_hash;
+ }
+ }
+ rnp::secure_vector<uint8_t> dgst(hash_len);
+ size_t out_cpy = std::min(dgst.size(), output_len);
+ if (hash->finish(dgst.data()) != dgst.size()) {
+ RNP_LOG("Unexpected digest size.");
+ return 1;
+ }
+ memcpy(out, dgst.data(), out_cpy);
+ output_len -= out_cpy;
+ out += out_cpy;
+ zeroes++;
+ }
+ return 0;
+ } catch (const std::exception &e) {
+ RNP_LOG("s2k failed: %s", e.what());
+ return 1;
+ }
+}
diff --git a/src/lib/crypto/sha1cd/sha1.c b/src/lib/crypto/sha1cd/sha1.c
new file mode 100644
index 0000000..d90bc41
--- /dev/null
+++ b/src/lib/crypto/sha1cd/sha1.c
@@ -0,0 +1,2162 @@
+/***
+ * Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow (danshu@microsoft.com)
+ * Distributed under the MIT Software License.
+ * See accompanying file LICENSE.txt or copy at
+ * https://opensource.org/licenses/MIT
+ ***/
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <string.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef __unix__
+#include <sys/types.h> /* make sure macros like _BIG_ENDIAN visible */
+#endif
+#endif
+
+#ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C
+#include SHA1DC_CUSTOM_INCLUDE_SHA1_C
+#endif
+
+#ifndef SHA1DC_INIT_SAFE_HASH_DEFAULT
+#define SHA1DC_INIT_SAFE_HASH_DEFAULT 1
+#endif
+
+#include "sha1.h"
+#include "ubc_check.h"
+
+#if (defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \
+ defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || \
+ defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \
+ defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \
+ defined(__386) || defined(_M_X64) || defined(_M_AMD64))
+#define SHA1DC_ON_INTEL_LIKE_PROCESSOR
+#endif
+
+/*
+ Because Little-Endian architectures are most common,
+ we only set SHA1DC_BIGENDIAN if one of these conditions is met.
+ Note that all MSFT platforms are little endian,
+ so none of these will be defined under the MSC compiler.
+ If you are compiling on a big endian platform and your compiler does not define one of
+ these, you will have to add whatever macros your tool chain defines to indicate
+ Big-Endianness.
+ */
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
+/*
+ * Should detect Big Endian under GCC since at least 4.6.0 (gcc svn
+ * rev #165881). See
+ * https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
+ *
+ * This also works under clang since 3.2, it copied the GCC-ism. See
+ * clang.git's 3b198a97d2 ("Preprocessor: add __BYTE_ORDER__
+ * predefined macro", 2012-07-27)
+ */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define SHA1DC_BIGENDIAN
+#endif
+
+/* Not under GCC-alike */
+#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN)
+/*
+ * Should detect Big Endian under glibc.git since 14245eb70e ("entered
+ * into RCS", 1992-11-25). Defined in <endian.h> which will have been
+ * brought in by standard headers. See glibc.git and
+ * https://sourceforge.net/p/predef/wiki/Endianness/
+ */
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define SHA1DC_BIGENDIAN
+#endif
+
+/* Not under GCC-alike or glibc */
+#elif defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN)
+/*
+ * *BSD and newlib (embedded linux, cygwin, etc).
+ * the defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN) part prevents
+ * this condition from matching with Solaris/sparc.
+ * (Solaris defines only one endian macro)
+ */
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define SHA1DC_BIGENDIAN
+#endif
+
+/* Not under GCC-alike or glibc or *BSD or newlib */
+#elif (defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
+ defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(__sparc))
+/*
+ * Should define Big Endian for a whitelist of known processors. See
+ * https://sourceforge.net/p/predef/wiki/Endianness/ and
+ * http://www.oracle.com/technetwork/server-storage/solaris/portingtosolaris-138514.html
+ */
+#define SHA1DC_BIGENDIAN
+
+/* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> */
+#elif (defined(_AIX) || defined(__hpux))
+
+/*
+ * Defines Big Endian on a whitelist of OSs that are known to be Big
+ * Endian-only. See
+ * https://public-inbox.org/git/93056823-2740-d072-1ebd-46b440b33d7e@felt.demon.nl/
+ */
+#define SHA1DC_BIGENDIAN
+
+/* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> or <os whitelist> */
+#elif defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR)
+/*
+ * As a last resort before we do anything else we're not 100% sure
+ * about below, we blacklist specific processors here. We could add
+ * more, see e.g. https://wiki.debian.org/ArchitectureSpecificsMemo
+ */
+#else /* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> or <os \
+ whitelist> or <processor blacklist> */
+
+/* We do nothing more here for now */
+/*#error "Uncomment this to see if you fall through all the detection"*/
+
+#endif /* Big Endian detection */
+
+#if (defined(SHA1DC_FORCE_LITTLEENDIAN) && defined(SHA1DC_BIGENDIAN))
+#undef SHA1DC_BIGENDIAN
+#endif
+#if (defined(SHA1DC_FORCE_BIGENDIAN) && !defined(SHA1DC_BIGENDIAN))
+#define SHA1DC_BIGENDIAN
+#endif
+/*ENDIANNESS SELECTION*/
+
+#ifndef SHA1DC_FORCE_ALIGNED_ACCESS
+#if defined(SHA1DC_FORCE_UNALIGNED_ACCESS) || defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR)
+#define SHA1DC_ALLOW_UNALIGNED_ACCESS
+#endif /*UNALIGNED ACCESS DETECTION*/
+#endif /*FORCE ALIGNED ACCESS*/
+
+#define rotate_right(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
+#define rotate_left(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+#define sha1_bswap32(x) \
+ { \
+ x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); \
+ x = (x << 16) | (x >> 16); \
+ }
+
+#define sha1_mix(W, t) (rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1))
+
+#ifdef SHA1DC_BIGENDIAN
+#define sha1_load(m, t, temp) \
+ { \
+ temp = m[t]; \
+ }
+#else
+#define sha1_load(m, t, temp) \
+ { \
+ temp = m[t]; \
+ sha1_bswap32(temp); \
+ }
+#endif
+
+#define sha1_store(W, t, x) *(volatile uint32_t *) &W[t] = x
+
+#define sha1_f1(b, c, d) ((d) ^ ((b) & ((c) ^ (d))))
+#define sha1_f2(b, c, d) ((b) ^ (c) ^ (d))
+#define sha1_f3(b, c, d) (((b) & (c)) + ((d) & ((b) ^ (c))))
+#define sha1_f4(b, c, d) ((b) ^ (c) ^ (d))
+
+#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, m, t) \
+ { \
+ e += rotate_left(a, 5) + sha1_f1(b, c, d) + 0x5A827999 + m[t]; \
+ b = rotate_left(b, 30); \
+ }
+#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, m, t) \
+ { \
+ e += rotate_left(a, 5) + sha1_f2(b, c, d) + 0x6ED9EBA1 + m[t]; \
+ b = rotate_left(b, 30); \
+ }
+#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, m, t) \
+ { \
+ e += rotate_left(a, 5) + sha1_f3(b, c, d) + 0x8F1BBCDC + m[t]; \
+ b = rotate_left(b, 30); \
+ }
+#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, m, t) \
+ { \
+ e += rotate_left(a, 5) + sha1_f4(b, c, d) + 0xCA62C1D6 + m[t]; \
+ b = rotate_left(b, 30); \
+ }
+
+#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, m, t) \
+ { \
+ b = rotate_right(b, 30); \
+ e -= rotate_left(a, 5) + sha1_f1(b, c, d) + 0x5A827999 + m[t]; \
+ }
+#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, m, t) \
+ { \
+ b = rotate_right(b, 30); \
+ e -= rotate_left(a, 5) + sha1_f2(b, c, d) + 0x6ED9EBA1 + m[t]; \
+ }
+#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, m, t) \
+ { \
+ b = rotate_right(b, 30); \
+ e -= rotate_left(a, 5) + sha1_f3(b, c, d) + 0x8F1BBCDC + m[t]; \
+ }
+#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, m, t) \
+ { \
+ b = rotate_right(b, 30); \
+ e -= rotate_left(a, 5) + sha1_f4(b, c, d) + 0xCA62C1D6 + m[t]; \
+ }
+
+#define SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, t, temp) \
+ { \
+ sha1_load(m, t, temp); \
+ sha1_store(W, t, temp); \
+ e += temp + rotate_left(a, 5) + sha1_f1(b, c, d) + 0x5A827999; \
+ b = rotate_left(b, 30); \
+ }
+
+#define SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(a, b, c, d, e, W, t, temp) \
+ { \
+ temp = sha1_mix(W, t); \
+ sha1_store(W, t, temp); \
+ e += temp + rotate_left(a, 5) + sha1_f1(b, c, d) + 0x5A827999; \
+ b = rotate_left(b, 30); \
+ }
+
+#define SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, t, temp) \
+ { \
+ temp = sha1_mix(W, t); \
+ sha1_store(W, t, temp); \
+ e += temp + rotate_left(a, 5) + sha1_f2(b, c, d) + 0x6ED9EBA1; \
+ b = rotate_left(b, 30); \
+ }
+
+#define SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, t, temp) \
+ { \
+ temp = sha1_mix(W, t); \
+ sha1_store(W, t, temp); \
+ e += temp + rotate_left(a, 5) + sha1_f3(b, c, d) + 0x8F1BBCDC; \
+ b = rotate_left(b, 30); \
+ }
+
+#define SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, t, temp) \
+ { \
+ temp = sha1_mix(W, t); \
+ sha1_store(W, t, temp); \
+ e += temp + rotate_left(a, 5) + sha1_f4(b, c, d) + 0xCA62C1D6; \
+ b = rotate_left(b, 30); \
+ }
+
+#define SHA1_STORE_STATE(i) \
+ states[i][0] = a; \
+ states[i][1] = b; \
+ states[i][2] = c; \
+ states[i][3] = d; \
+ states[i][4] = e;
+
+#ifdef BUILDNOCOLLDETECTSHA1COMPRESSION
+void
+sha1_compression(uint32_t ihv[5], const uint32_t m[16])
+{
+ uint32_t W[80];
+ uint32_t a, b, c, d, e;
+ unsigned i;
+
+ memcpy(W, m, 16 * 4);
+ for (i = 16; i < 80; ++i)
+ W[i] = sha1_mix(W, i);
+
+ a = ihv[0];
+ b = ihv[1];
+ c = ihv[2];
+ d = ihv[3];
+ e = ihv[4];
+
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19);
+
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39);
+
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59);
+
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79);
+
+ ihv[0] += a;
+ ihv[1] += b;
+ ihv[2] += c;
+ ihv[3] += d;
+ ihv[4] += e;
+}
+#endif /*BUILDNOCOLLDETECTSHA1COMPRESSION*/
+
+static void
+sha1_compression_W(uint32_t ihv[5], const uint32_t W[80])
+{
+ uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4];
+
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19);
+
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39);
+
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59);
+
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79);
+
+ ihv[0] += a;
+ ihv[1] += b;
+ ihv[2] += c;
+ ihv[3] += d;
+ ihv[4] += e;
+}
+
+void
+sha1_compression_states(uint32_t ihv[5],
+ const uint32_t m[16],
+ uint32_t W[80],
+ uint32_t states[80][5])
+{
+ uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4];
+ uint32_t temp;
+
+#ifdef DOSTORESTATE00
+ SHA1_STORE_STATE(0)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 0, temp);
+
+#ifdef DOSTORESTATE01
+ SHA1_STORE_STATE(1)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 1, temp);
+
+#ifdef DOSTORESTATE02
+ SHA1_STORE_STATE(2)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 2, temp);
+
+#ifdef DOSTORESTATE03
+ SHA1_STORE_STATE(3)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 3, temp);
+
+#ifdef DOSTORESTATE04
+ SHA1_STORE_STATE(4)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 4, temp);
+
+#ifdef DOSTORESTATE05
+ SHA1_STORE_STATE(5)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 5, temp);
+
+#ifdef DOSTORESTATE06
+ SHA1_STORE_STATE(6)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 6, temp);
+
+#ifdef DOSTORESTATE07
+ SHA1_STORE_STATE(7)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 7, temp);
+
+#ifdef DOSTORESTATE08
+ SHA1_STORE_STATE(8)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 8, temp);
+
+#ifdef DOSTORESTATE09
+ SHA1_STORE_STATE(9)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 9, temp);
+
+#ifdef DOSTORESTATE10
+ SHA1_STORE_STATE(10)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 10, temp);
+
+#ifdef DOSTORESTATE11
+ SHA1_STORE_STATE(11)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 11, temp);
+
+#ifdef DOSTORESTATE12
+ SHA1_STORE_STATE(12)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 12, temp);
+
+#ifdef DOSTORESTATE13
+ SHA1_STORE_STATE(13)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 13, temp);
+
+#ifdef DOSTORESTATE14
+ SHA1_STORE_STATE(14)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 14, temp);
+
+#ifdef DOSTORESTATE15
+ SHA1_STORE_STATE(15)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 15, temp);
+
+#ifdef DOSTORESTATE16
+ SHA1_STORE_STATE(16)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(e, a, b, c, d, W, 16, temp);
+
+#ifdef DOSTORESTATE17
+ SHA1_STORE_STATE(17)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(d, e, a, b, c, W, 17, temp);
+
+#ifdef DOSTORESTATE18
+ SHA1_STORE_STATE(18)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(c, d, e, a, b, W, 18, temp);
+
+#ifdef DOSTORESTATE19
+ SHA1_STORE_STATE(19)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(b, c, d, e, a, W, 19, temp);
+
+#ifdef DOSTORESTATE20
+ SHA1_STORE_STATE(20)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 20, temp);
+
+#ifdef DOSTORESTATE21
+ SHA1_STORE_STATE(21)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 21, temp);
+
+#ifdef DOSTORESTATE22
+ SHA1_STORE_STATE(22)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 22, temp);
+
+#ifdef DOSTORESTATE23
+ SHA1_STORE_STATE(23)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 23, temp);
+
+#ifdef DOSTORESTATE24
+ SHA1_STORE_STATE(24)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 24, temp);
+
+#ifdef DOSTORESTATE25
+ SHA1_STORE_STATE(25)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 25, temp);
+
+#ifdef DOSTORESTATE26
+ SHA1_STORE_STATE(26)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 26, temp);
+
+#ifdef DOSTORESTATE27
+ SHA1_STORE_STATE(27)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 27, temp);
+
+#ifdef DOSTORESTATE28
+ SHA1_STORE_STATE(28)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 28, temp);
+
+#ifdef DOSTORESTATE29
+ SHA1_STORE_STATE(29)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 29, temp);
+
+#ifdef DOSTORESTATE30
+ SHA1_STORE_STATE(30)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 30, temp);
+
+#ifdef DOSTORESTATE31
+ SHA1_STORE_STATE(31)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 31, temp);
+
+#ifdef DOSTORESTATE32
+ SHA1_STORE_STATE(32)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 32, temp);
+
+#ifdef DOSTORESTATE33
+ SHA1_STORE_STATE(33)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 33, temp);
+
+#ifdef DOSTORESTATE34
+ SHA1_STORE_STATE(34)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 34, temp);
+
+#ifdef DOSTORESTATE35
+ SHA1_STORE_STATE(35)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 35, temp);
+
+#ifdef DOSTORESTATE36
+ SHA1_STORE_STATE(36)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 36, temp);
+
+#ifdef DOSTORESTATE37
+ SHA1_STORE_STATE(37)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 37, temp);
+
+#ifdef DOSTORESTATE38
+ SHA1_STORE_STATE(38)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 38, temp);
+
+#ifdef DOSTORESTATE39
+ SHA1_STORE_STATE(39)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 39, temp);
+
+#ifdef DOSTORESTATE40
+ SHA1_STORE_STATE(40)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 40, temp);
+
+#ifdef DOSTORESTATE41
+ SHA1_STORE_STATE(41)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 41, temp);
+
+#ifdef DOSTORESTATE42
+ SHA1_STORE_STATE(42)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 42, temp);
+
+#ifdef DOSTORESTATE43
+ SHA1_STORE_STATE(43)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 43, temp);
+
+#ifdef DOSTORESTATE44
+ SHA1_STORE_STATE(44)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 44, temp);
+
+#ifdef DOSTORESTATE45
+ SHA1_STORE_STATE(45)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 45, temp);
+
+#ifdef DOSTORESTATE46
+ SHA1_STORE_STATE(46)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 46, temp);
+
+#ifdef DOSTORESTATE47
+ SHA1_STORE_STATE(47)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 47, temp);
+
+#ifdef DOSTORESTATE48
+ SHA1_STORE_STATE(48)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 48, temp);
+
+#ifdef DOSTORESTATE49
+ SHA1_STORE_STATE(49)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 49, temp);
+
+#ifdef DOSTORESTATE50
+ SHA1_STORE_STATE(50)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 50, temp);
+
+#ifdef DOSTORESTATE51
+ SHA1_STORE_STATE(51)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 51, temp);
+
+#ifdef DOSTORESTATE52
+ SHA1_STORE_STATE(52)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 52, temp);
+
+#ifdef DOSTORESTATE53
+ SHA1_STORE_STATE(53)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 53, temp);
+
+#ifdef DOSTORESTATE54
+ SHA1_STORE_STATE(54)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 54, temp);
+
+#ifdef DOSTORESTATE55
+ SHA1_STORE_STATE(55)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 55, temp);
+
+#ifdef DOSTORESTATE56
+ SHA1_STORE_STATE(56)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 56, temp);
+
+#ifdef DOSTORESTATE57
+ SHA1_STORE_STATE(57)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 57, temp);
+
+#ifdef DOSTORESTATE58
+ SHA1_STORE_STATE(58)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 58, temp);
+
+#ifdef DOSTORESTATE59
+ SHA1_STORE_STATE(59)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 59, temp);
+
+#ifdef DOSTORESTATE60
+ SHA1_STORE_STATE(60)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 60, temp);
+
+#ifdef DOSTORESTATE61
+ SHA1_STORE_STATE(61)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 61, temp);
+
+#ifdef DOSTORESTATE62
+ SHA1_STORE_STATE(62)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 62, temp);
+
+#ifdef DOSTORESTATE63
+ SHA1_STORE_STATE(63)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 63, temp);
+
+#ifdef DOSTORESTATE64
+ SHA1_STORE_STATE(64)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 64, temp);
+
+#ifdef DOSTORESTATE65
+ SHA1_STORE_STATE(65)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 65, temp);
+
+#ifdef DOSTORESTATE66
+ SHA1_STORE_STATE(66)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 66, temp);
+
+#ifdef DOSTORESTATE67
+ SHA1_STORE_STATE(67)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 67, temp);
+
+#ifdef DOSTORESTATE68
+ SHA1_STORE_STATE(68)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 68, temp);
+
+#ifdef DOSTORESTATE69
+ SHA1_STORE_STATE(69)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 69, temp);
+
+#ifdef DOSTORESTATE70
+ SHA1_STORE_STATE(70)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 70, temp);
+
+#ifdef DOSTORESTATE71
+ SHA1_STORE_STATE(71)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 71, temp);
+
+#ifdef DOSTORESTATE72
+ SHA1_STORE_STATE(72)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 72, temp);
+
+#ifdef DOSTORESTATE73
+ SHA1_STORE_STATE(73)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 73, temp);
+
+#ifdef DOSTORESTATE74
+ SHA1_STORE_STATE(74)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 74, temp);
+
+#ifdef DOSTORESTATE75
+ SHA1_STORE_STATE(75)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 75, temp);
+
+#ifdef DOSTORESTATE76
+ SHA1_STORE_STATE(76)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 76, temp);
+
+#ifdef DOSTORESTATE77
+ SHA1_STORE_STATE(77)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 77, temp);
+
+#ifdef DOSTORESTATE78
+ SHA1_STORE_STATE(78)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 78, temp);
+
+#ifdef DOSTORESTATE79
+ SHA1_STORE_STATE(79)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 79, temp);
+
+ ihv[0] += a;
+ ihv[1] += b;
+ ihv[2] += c;
+ ihv[3] += d;
+ ihv[4] += e;
+}
+
+#define SHA1_RECOMPRESS(t) \
+ static void sha1recompress_fast_##t( \
+ uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) \
+ { \
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; \
+ if (t > 79) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 79); \
+ if (t > 78) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 78); \
+ if (t > 77) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 77); \
+ if (t > 76) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 76); \
+ if (t > 75) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 75); \
+ if (t > 74) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 74); \
+ if (t > 73) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 73); \
+ if (t > 72) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 72); \
+ if (t > 71) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 71); \
+ if (t > 70) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 70); \
+ if (t > 69) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 69); \
+ if (t > 68) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 68); \
+ if (t > 67) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 67); \
+ if (t > 66) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 66); \
+ if (t > 65) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 65); \
+ if (t > 64) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 64); \
+ if (t > 63) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 63); \
+ if (t > 62) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 62); \
+ if (t > 61) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 61); \
+ if (t > 60) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 60); \
+ if (t > 59) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 59); \
+ if (t > 58) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 58); \
+ if (t > 57) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 57); \
+ if (t > 56) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 56); \
+ if (t > 55) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 55); \
+ if (t > 54) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 54); \
+ if (t > 53) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 53); \
+ if (t > 52) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 52); \
+ if (t > 51) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 51); \
+ if (t > 50) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 50); \
+ if (t > 49) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 49); \
+ if (t > 48) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 48); \
+ if (t > 47) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 47); \
+ if (t > 46) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 46); \
+ if (t > 45) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 45); \
+ if (t > 44) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 44); \
+ if (t > 43) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 43); \
+ if (t > 42) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 42); \
+ if (t > 41) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 41); \
+ if (t > 40) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 40); \
+ if (t > 39) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 39); \
+ if (t > 38) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 38); \
+ if (t > 37) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 37); \
+ if (t > 36) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 36); \
+ if (t > 35) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 35); \
+ if (t > 34) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 34); \
+ if (t > 33) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 33); \
+ if (t > 32) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 32); \
+ if (t > 31) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 31); \
+ if (t > 30) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 30); \
+ if (t > 29) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 29); \
+ if (t > 28) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 28); \
+ if (t > 27) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 27); \
+ if (t > 26) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 26); \
+ if (t > 25) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 25); \
+ if (t > 24) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 24); \
+ if (t > 23) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 23); \
+ if (t > 22) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 22); \
+ if (t > 21) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 21); \
+ if (t > 20) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 20); \
+ if (t > 19) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 19); \
+ if (t > 18) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 18); \
+ if (t > 17) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 17); \
+ if (t > 16) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 16); \
+ if (t > 15) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 15); \
+ if (t > 14) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 14); \
+ if (t > 13) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 13); \
+ if (t > 12) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 12); \
+ if (t > 11) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 11); \
+ if (t > 10) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 10); \
+ if (t > 9) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 9); \
+ if (t > 8) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 8); \
+ if (t > 7) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 7); \
+ if (t > 6) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 6); \
+ if (t > 5) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 5); \
+ if (t > 4) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 4); \
+ if (t > 3) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 3); \
+ if (t > 2) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 2); \
+ if (t > 1) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 1); \
+ if (t > 0) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 0); \
+ ihvin[0] = a; \
+ ihvin[1] = b; \
+ ihvin[2] = c; \
+ ihvin[3] = d; \
+ ihvin[4] = e; \
+ a = state[0]; \
+ b = state[1]; \
+ c = state[2]; \
+ d = state[3]; \
+ e = state[4]; \
+ if (t <= 0) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 0); \
+ if (t <= 1) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 1); \
+ if (t <= 2) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 2); \
+ if (t <= 3) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 3); \
+ if (t <= 4) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 4); \
+ if (t <= 5) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 5); \
+ if (t <= 6) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 6); \
+ if (t <= 7) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 7); \
+ if (t <= 8) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 8); \
+ if (t <= 9) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 9); \
+ if (t <= 10) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 10); \
+ if (t <= 11) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 11); \
+ if (t <= 12) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 12); \
+ if (t <= 13) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 13); \
+ if (t <= 14) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 14); \
+ if (t <= 15) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 15); \
+ if (t <= 16) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 16); \
+ if (t <= 17) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 17); \
+ if (t <= 18) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 18); \
+ if (t <= 19) \
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 19); \
+ if (t <= 20) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 20); \
+ if (t <= 21) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 21); \
+ if (t <= 22) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 22); \
+ if (t <= 23) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 23); \
+ if (t <= 24) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 24); \
+ if (t <= 25) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 25); \
+ if (t <= 26) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 26); \
+ if (t <= 27) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 27); \
+ if (t <= 28) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 28); \
+ if (t <= 29) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 29); \
+ if (t <= 30) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 30); \
+ if (t <= 31) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 31); \
+ if (t <= 32) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 32); \
+ if (t <= 33) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 33); \
+ if (t <= 34) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 34); \
+ if (t <= 35) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 35); \
+ if (t <= 36) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 36); \
+ if (t <= 37) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 37); \
+ if (t <= 38) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 38); \
+ if (t <= 39) \
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 39); \
+ if (t <= 40) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 40); \
+ if (t <= 41) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 41); \
+ if (t <= 42) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 42); \
+ if (t <= 43) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 43); \
+ if (t <= 44) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 44); \
+ if (t <= 45) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 45); \
+ if (t <= 46) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 46); \
+ if (t <= 47) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 47); \
+ if (t <= 48) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 48); \
+ if (t <= 49) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 49); \
+ if (t <= 50) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 50); \
+ if (t <= 51) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 51); \
+ if (t <= 52) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 52); \
+ if (t <= 53) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 53); \
+ if (t <= 54) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 54); \
+ if (t <= 55) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 55); \
+ if (t <= 56) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 56); \
+ if (t <= 57) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 57); \
+ if (t <= 58) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 58); \
+ if (t <= 59) \
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 59); \
+ if (t <= 60) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 60); \
+ if (t <= 61) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 61); \
+ if (t <= 62) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 62); \
+ if (t <= 63) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 63); \
+ if (t <= 64) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 64); \
+ if (t <= 65) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 65); \
+ if (t <= 66) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 66); \
+ if (t <= 67) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 67); \
+ if (t <= 68) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 68); \
+ if (t <= 69) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 69); \
+ if (t <= 70) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 70); \
+ if (t <= 71) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 71); \
+ if (t <= 72) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 72); \
+ if (t <= 73) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 73); \
+ if (t <= 74) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 74); \
+ if (t <= 75) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 75); \
+ if (t <= 76) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 76); \
+ if (t <= 77) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 77); \
+ if (t <= 78) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 78); \
+ if (t <= 79) \
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 79); \
+ ihvout[0] = ihvin[0] + a; \
+ ihvout[1] = ihvin[1] + b; \
+ ihvout[2] = ihvin[2] + c; \
+ ihvout[3] = ihvin[3] + d; \
+ ihvout[4] = ihvin[4] + e; \
+ }
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4127) /* Compiler complains about the checks in the above macro \
+ being constant. */
+#endif
+
+#ifdef DOSTORESTATE0
+SHA1_RECOMPRESS(0)
+#endif
+
+#ifdef DOSTORESTATE1
+SHA1_RECOMPRESS(1)
+#endif
+
+#ifdef DOSTORESTATE2
+SHA1_RECOMPRESS(2)
+#endif
+
+#ifdef DOSTORESTATE3
+SHA1_RECOMPRESS(3)
+#endif
+
+#ifdef DOSTORESTATE4
+SHA1_RECOMPRESS(4)
+#endif
+
+#ifdef DOSTORESTATE5
+SHA1_RECOMPRESS(5)
+#endif
+
+#ifdef DOSTORESTATE6
+SHA1_RECOMPRESS(6)
+#endif
+
+#ifdef DOSTORESTATE7
+SHA1_RECOMPRESS(7)
+#endif
+
+#ifdef DOSTORESTATE8
+SHA1_RECOMPRESS(8)
+#endif
+
+#ifdef DOSTORESTATE9
+SHA1_RECOMPRESS(9)
+#endif
+
+#ifdef DOSTORESTATE10
+SHA1_RECOMPRESS(10)
+#endif
+
+#ifdef DOSTORESTATE11
+SHA1_RECOMPRESS(11)
+#endif
+
+#ifdef DOSTORESTATE12
+SHA1_RECOMPRESS(12)
+#endif
+
+#ifdef DOSTORESTATE13
+SHA1_RECOMPRESS(13)
+#endif
+
+#ifdef DOSTORESTATE14
+SHA1_RECOMPRESS(14)
+#endif
+
+#ifdef DOSTORESTATE15
+SHA1_RECOMPRESS(15)
+#endif
+
+#ifdef DOSTORESTATE16
+SHA1_RECOMPRESS(16)
+#endif
+
+#ifdef DOSTORESTATE17
+SHA1_RECOMPRESS(17)
+#endif
+
+#ifdef DOSTORESTATE18
+SHA1_RECOMPRESS(18)
+#endif
+
+#ifdef DOSTORESTATE19
+SHA1_RECOMPRESS(19)
+#endif
+
+#ifdef DOSTORESTATE20
+SHA1_RECOMPRESS(20)
+#endif
+
+#ifdef DOSTORESTATE21
+SHA1_RECOMPRESS(21)
+#endif
+
+#ifdef DOSTORESTATE22
+SHA1_RECOMPRESS(22)
+#endif
+
+#ifdef DOSTORESTATE23
+SHA1_RECOMPRESS(23)
+#endif
+
+#ifdef DOSTORESTATE24
+SHA1_RECOMPRESS(24)
+#endif
+
+#ifdef DOSTORESTATE25
+SHA1_RECOMPRESS(25)
+#endif
+
+#ifdef DOSTORESTATE26
+SHA1_RECOMPRESS(26)
+#endif
+
+#ifdef DOSTORESTATE27
+SHA1_RECOMPRESS(27)
+#endif
+
+#ifdef DOSTORESTATE28
+SHA1_RECOMPRESS(28)
+#endif
+
+#ifdef DOSTORESTATE29
+SHA1_RECOMPRESS(29)
+#endif
+
+#ifdef DOSTORESTATE30
+SHA1_RECOMPRESS(30)
+#endif
+
+#ifdef DOSTORESTATE31
+SHA1_RECOMPRESS(31)
+#endif
+
+#ifdef DOSTORESTATE32
+SHA1_RECOMPRESS(32)
+#endif
+
+#ifdef DOSTORESTATE33
+SHA1_RECOMPRESS(33)
+#endif
+
+#ifdef DOSTORESTATE34
+SHA1_RECOMPRESS(34)
+#endif
+
+#ifdef DOSTORESTATE35
+SHA1_RECOMPRESS(35)
+#endif
+
+#ifdef DOSTORESTATE36
+SHA1_RECOMPRESS(36)
+#endif
+
+#ifdef DOSTORESTATE37
+SHA1_RECOMPRESS(37)
+#endif
+
+#ifdef DOSTORESTATE38
+SHA1_RECOMPRESS(38)
+#endif
+
+#ifdef DOSTORESTATE39
+SHA1_RECOMPRESS(39)
+#endif
+
+#ifdef DOSTORESTATE40
+SHA1_RECOMPRESS(40)
+#endif
+
+#ifdef DOSTORESTATE41
+SHA1_RECOMPRESS(41)
+#endif
+
+#ifdef DOSTORESTATE42
+SHA1_RECOMPRESS(42)
+#endif
+
+#ifdef DOSTORESTATE43
+SHA1_RECOMPRESS(43)
+#endif
+
+#ifdef DOSTORESTATE44
+SHA1_RECOMPRESS(44)
+#endif
+
+#ifdef DOSTORESTATE45
+SHA1_RECOMPRESS(45)
+#endif
+
+#ifdef DOSTORESTATE46
+SHA1_RECOMPRESS(46)
+#endif
+
+#ifdef DOSTORESTATE47
+SHA1_RECOMPRESS(47)
+#endif
+
+#ifdef DOSTORESTATE48
+SHA1_RECOMPRESS(48)
+#endif
+
+#ifdef DOSTORESTATE49
+SHA1_RECOMPRESS(49)
+#endif
+
+#ifdef DOSTORESTATE50
+SHA1_RECOMPRESS(50)
+#endif
+
+#ifdef DOSTORESTATE51
+SHA1_RECOMPRESS(51)
+#endif
+
+#ifdef DOSTORESTATE52
+SHA1_RECOMPRESS(52)
+#endif
+
+#ifdef DOSTORESTATE53
+SHA1_RECOMPRESS(53)
+#endif
+
+#ifdef DOSTORESTATE54
+SHA1_RECOMPRESS(54)
+#endif
+
+#ifdef DOSTORESTATE55
+SHA1_RECOMPRESS(55)
+#endif
+
+#ifdef DOSTORESTATE56
+SHA1_RECOMPRESS(56)
+#endif
+
+#ifdef DOSTORESTATE57
+SHA1_RECOMPRESS(57)
+#endif
+
+#ifdef DOSTORESTATE58
+SHA1_RECOMPRESS(58)
+#endif
+
+#ifdef DOSTORESTATE59
+SHA1_RECOMPRESS(59)
+#endif
+
+#ifdef DOSTORESTATE60
+SHA1_RECOMPRESS(60)
+#endif
+
+#ifdef DOSTORESTATE61
+SHA1_RECOMPRESS(61)
+#endif
+
+#ifdef DOSTORESTATE62
+SHA1_RECOMPRESS(62)
+#endif
+
+#ifdef DOSTORESTATE63
+SHA1_RECOMPRESS(63)
+#endif
+
+#ifdef DOSTORESTATE64
+SHA1_RECOMPRESS(64)
+#endif
+
+#ifdef DOSTORESTATE65
+SHA1_RECOMPRESS(65)
+#endif
+
+#ifdef DOSTORESTATE66
+SHA1_RECOMPRESS(66)
+#endif
+
+#ifdef DOSTORESTATE67
+SHA1_RECOMPRESS(67)
+#endif
+
+#ifdef DOSTORESTATE68
+SHA1_RECOMPRESS(68)
+#endif
+
+#ifdef DOSTORESTATE69
+SHA1_RECOMPRESS(69)
+#endif
+
+#ifdef DOSTORESTATE70
+SHA1_RECOMPRESS(70)
+#endif
+
+#ifdef DOSTORESTATE71
+SHA1_RECOMPRESS(71)
+#endif
+
+#ifdef DOSTORESTATE72
+SHA1_RECOMPRESS(72)
+#endif
+
+#ifdef DOSTORESTATE73
+SHA1_RECOMPRESS(73)
+#endif
+
+#ifdef DOSTORESTATE74
+SHA1_RECOMPRESS(74)
+#endif
+
+#ifdef DOSTORESTATE75
+SHA1_RECOMPRESS(75)
+#endif
+
+#ifdef DOSTORESTATE76
+SHA1_RECOMPRESS(76)
+#endif
+
+#ifdef DOSTORESTATE77
+SHA1_RECOMPRESS(77)
+#endif
+
+#ifdef DOSTORESTATE78
+SHA1_RECOMPRESS(78)
+#endif
+
+#ifdef DOSTORESTATE79
+SHA1_RECOMPRESS(79)
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+static void
+sha1_recompression_step(uint32_t step,
+ uint32_t ihvin[5],
+ uint32_t ihvout[5],
+ const uint32_t me2[80],
+ const uint32_t state[5])
+{
+ switch (step) {
+#ifdef DOSTORESTATE0
+ case 0:
+ sha1recompress_fast_0(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE1
+ case 1:
+ sha1recompress_fast_1(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE2
+ case 2:
+ sha1recompress_fast_2(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE3
+ case 3:
+ sha1recompress_fast_3(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE4
+ case 4:
+ sha1recompress_fast_4(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE5
+ case 5:
+ sha1recompress_fast_5(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE6
+ case 6:
+ sha1recompress_fast_6(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE7
+ case 7:
+ sha1recompress_fast_7(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE8
+ case 8:
+ sha1recompress_fast_8(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE9
+ case 9:
+ sha1recompress_fast_9(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE10
+ case 10:
+ sha1recompress_fast_10(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE11
+ case 11:
+ sha1recompress_fast_11(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE12
+ case 12:
+ sha1recompress_fast_12(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE13
+ case 13:
+ sha1recompress_fast_13(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE14
+ case 14:
+ sha1recompress_fast_14(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE15
+ case 15:
+ sha1recompress_fast_15(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE16
+ case 16:
+ sha1recompress_fast_16(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE17
+ case 17:
+ sha1recompress_fast_17(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE18
+ case 18:
+ sha1recompress_fast_18(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE19
+ case 19:
+ sha1recompress_fast_19(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE20
+ case 20:
+ sha1recompress_fast_20(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE21
+ case 21:
+ sha1recompress_fast_21(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE22
+ case 22:
+ sha1recompress_fast_22(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE23
+ case 23:
+ sha1recompress_fast_23(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE24
+ case 24:
+ sha1recompress_fast_24(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE25
+ case 25:
+ sha1recompress_fast_25(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE26
+ case 26:
+ sha1recompress_fast_26(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE27
+ case 27:
+ sha1recompress_fast_27(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE28
+ case 28:
+ sha1recompress_fast_28(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE29
+ case 29:
+ sha1recompress_fast_29(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE30
+ case 30:
+ sha1recompress_fast_30(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE31
+ case 31:
+ sha1recompress_fast_31(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE32
+ case 32:
+ sha1recompress_fast_32(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE33
+ case 33:
+ sha1recompress_fast_33(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE34
+ case 34:
+ sha1recompress_fast_34(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE35
+ case 35:
+ sha1recompress_fast_35(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE36
+ case 36:
+ sha1recompress_fast_36(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE37
+ case 37:
+ sha1recompress_fast_37(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE38
+ case 38:
+ sha1recompress_fast_38(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE39
+ case 39:
+ sha1recompress_fast_39(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE40
+ case 40:
+ sha1recompress_fast_40(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE41
+ case 41:
+ sha1recompress_fast_41(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE42
+ case 42:
+ sha1recompress_fast_42(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE43
+ case 43:
+ sha1recompress_fast_43(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE44
+ case 44:
+ sha1recompress_fast_44(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE45
+ case 45:
+ sha1recompress_fast_45(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE46
+ case 46:
+ sha1recompress_fast_46(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE47
+ case 47:
+ sha1recompress_fast_47(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE48
+ case 48:
+ sha1recompress_fast_48(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE49
+ case 49:
+ sha1recompress_fast_49(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE50
+ case 50:
+ sha1recompress_fast_50(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE51
+ case 51:
+ sha1recompress_fast_51(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE52
+ case 52:
+ sha1recompress_fast_52(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE53
+ case 53:
+ sha1recompress_fast_53(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE54
+ case 54:
+ sha1recompress_fast_54(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE55
+ case 55:
+ sha1recompress_fast_55(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE56
+ case 56:
+ sha1recompress_fast_56(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE57
+ case 57:
+ sha1recompress_fast_57(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE58
+ case 58:
+ sha1recompress_fast_58(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE59
+ case 59:
+ sha1recompress_fast_59(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE60
+ case 60:
+ sha1recompress_fast_60(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE61
+ case 61:
+ sha1recompress_fast_61(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE62
+ case 62:
+ sha1recompress_fast_62(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE63
+ case 63:
+ sha1recompress_fast_63(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE64
+ case 64:
+ sha1recompress_fast_64(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE65
+ case 65:
+ sha1recompress_fast_65(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE66
+ case 66:
+ sha1recompress_fast_66(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE67
+ case 67:
+ sha1recompress_fast_67(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE68
+ case 68:
+ sha1recompress_fast_68(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE69
+ case 69:
+ sha1recompress_fast_69(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE70
+ case 70:
+ sha1recompress_fast_70(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE71
+ case 71:
+ sha1recompress_fast_71(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE72
+ case 72:
+ sha1recompress_fast_72(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE73
+ case 73:
+ sha1recompress_fast_73(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE74
+ case 74:
+ sha1recompress_fast_74(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE75
+ case 75:
+ sha1recompress_fast_75(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE76
+ case 76:
+ sha1recompress_fast_76(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE77
+ case 77:
+ sha1recompress_fast_77(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE78
+ case 78:
+ sha1recompress_fast_78(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE79
+ case 79:
+ sha1recompress_fast_79(ihvin, ihvout, me2, state);
+ break;
+#endif
+ default:
+ abort();
+ }
+}
+
+static void
+sha1_process(SHA1_CTX *ctx, const uint32_t block[16])
+{
+ unsigned i, j;
+ uint32_t ubc_dv_mask[DVMASKSIZE] = {0xFFFFFFFF};
+ uint32_t ihvtmp[5];
+
+ ctx->ihv1[0] = ctx->ihv[0];
+ ctx->ihv1[1] = ctx->ihv[1];
+ ctx->ihv1[2] = ctx->ihv[2];
+ ctx->ihv1[3] = ctx->ihv[3];
+ ctx->ihv1[4] = ctx->ihv[4];
+
+ sha1_compression_states(ctx->ihv, block, ctx->m1, ctx->states);
+
+ if (ctx->detect_coll) {
+ if (ctx->ubc_check) {
+ ubc_check(ctx->m1, ubc_dv_mask);
+ }
+
+ if (ubc_dv_mask[0] != 0) {
+ for (i = 0; sha1_dvs[i].dvType != 0; ++i) {
+ if (ubc_dv_mask[0] & ((uint32_t)(1) << sha1_dvs[i].maskb)) {
+ for (j = 0; j < 80; ++j)
+ ctx->m2[j] = ctx->m1[j] ^ sha1_dvs[i].dm[j];
+
+ sha1_recompression_step(sha1_dvs[i].testt,
+ ctx->ihv2,
+ ihvtmp,
+ ctx->m2,
+ ctx->states[sha1_dvs[i].testt]);
+
+ /* to verify SHA-1 collision detection code with collisions for
+ * reduced-step SHA-1 */
+ if ((0 == ((ihvtmp[0] ^ ctx->ihv[0]) | (ihvtmp[1] ^ ctx->ihv[1]) |
+ (ihvtmp[2] ^ ctx->ihv[2]) | (ihvtmp[3] ^ ctx->ihv[3]) |
+ (ihvtmp[4] ^ ctx->ihv[4]))) ||
+ (ctx->reduced_round_coll &&
+ 0 == ((ctx->ihv1[0] ^ ctx->ihv2[0]) | (ctx->ihv1[1] ^ ctx->ihv2[1]) |
+ (ctx->ihv1[2] ^ ctx->ihv2[2]) | (ctx->ihv1[3] ^ ctx->ihv2[3]) |
+ (ctx->ihv1[4] ^ ctx->ihv2[4])))) {
+ ctx->found_collision = 1;
+
+ if (ctx->safe_hash) {
+ sha1_compression_W(ctx->ihv, ctx->m1);
+ sha1_compression_W(ctx->ihv, ctx->m1);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+SHA1DCInit(SHA1_CTX *ctx)
+{
+ ctx->total = 0;
+ ctx->ihv[0] = 0x67452301;
+ ctx->ihv[1] = 0xEFCDAB89;
+ ctx->ihv[2] = 0x98BADCFE;
+ ctx->ihv[3] = 0x10325476;
+ ctx->ihv[4] = 0xC3D2E1F0;
+ ctx->found_collision = 0;
+ ctx->safe_hash = SHA1DC_INIT_SAFE_HASH_DEFAULT;
+ ctx->ubc_check = 1;
+ ctx->detect_coll = 1;
+ ctx->reduced_round_coll = 0;
+ ctx->callback = NULL;
+}
+
+void
+SHA1DCSetSafeHash(SHA1_CTX *ctx, int safehash)
+{
+ if (safehash)
+ ctx->safe_hash = 1;
+ else
+ ctx->safe_hash = 0;
+}
+
+void
+SHA1DCSetUseUBC(SHA1_CTX *ctx, int ubc_check)
+{
+ if (ubc_check)
+ ctx->ubc_check = 1;
+ else
+ ctx->ubc_check = 0;
+}
+
+void
+SHA1DCSetUseDetectColl(SHA1_CTX *ctx, int detect_coll)
+{
+ if (detect_coll)
+ ctx->detect_coll = 1;
+ else
+ ctx->detect_coll = 0;
+}
+
+void
+SHA1DCSetDetectReducedRoundCollision(SHA1_CTX *ctx, int reduced_round_coll)
+{
+ if (reduced_round_coll)
+ ctx->reduced_round_coll = 1;
+ else
+ ctx->reduced_round_coll = 0;
+}
+
+void
+SHA1DCSetCallback(SHA1_CTX *ctx, collision_block_callback callback)
+{
+ ctx->callback = callback;
+}
+
+void
+SHA1DCUpdate(SHA1_CTX *ctx, const char *buf, size_t len)
+{
+ unsigned left, fill;
+
+ if (len == 0)
+ return;
+
+ left = ctx->total & 63;
+ fill = 64 - left;
+
+ if (left && len >= fill) {
+ ctx->total += fill;
+ memcpy(ctx->buffer + left, buf, fill);
+ sha1_process(ctx, (uint32_t *) (ctx->buffer));
+ buf += fill;
+ len -= fill;
+ left = 0;
+ }
+ while (len >= 64) {
+ ctx->total += 64;
+
+#if defined(SHA1DC_ALLOW_UNALIGNED_ACCESS)
+ sha1_process(ctx, (uint32_t *) (buf));
+#else
+ memcpy(ctx->buffer, buf, 64);
+ sha1_process(ctx, (uint32_t *) (ctx->buffer));
+#endif /* defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) */
+ buf += 64;
+ len -= 64;
+ }
+ if (len > 0) {
+ ctx->total += len;
+ memcpy(ctx->buffer + left, buf, len);
+ }
+}
+
+static const unsigned char sha1_padding[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+int
+SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx)
+{
+ uint32_t last = ctx->total & 63;
+ uint32_t padn = (last < 56) ? (56 - last) : (120 - last);
+ uint64_t total;
+ SHA1DCUpdate(ctx, (const char *) (sha1_padding), padn);
+
+ total = ctx->total - padn;
+ total <<= 3;
+ ctx->buffer[56] = (unsigned char) (total >> 56);
+ ctx->buffer[57] = (unsigned char) (total >> 48);
+ ctx->buffer[58] = (unsigned char) (total >> 40);
+ ctx->buffer[59] = (unsigned char) (total >> 32);
+ ctx->buffer[60] = (unsigned char) (total >> 24);
+ ctx->buffer[61] = (unsigned char) (total >> 16);
+ ctx->buffer[62] = (unsigned char) (total >> 8);
+ ctx->buffer[63] = (unsigned char) (total);
+ sha1_process(ctx, (uint32_t *) (ctx->buffer));
+ output[0] = (unsigned char) (ctx->ihv[0] >> 24);
+ output[1] = (unsigned char) (ctx->ihv[0] >> 16);
+ output[2] = (unsigned char) (ctx->ihv[0] >> 8);
+ output[3] = (unsigned char) (ctx->ihv[0]);
+ output[4] = (unsigned char) (ctx->ihv[1] >> 24);
+ output[5] = (unsigned char) (ctx->ihv[1] >> 16);
+ output[6] = (unsigned char) (ctx->ihv[1] >> 8);
+ output[7] = (unsigned char) (ctx->ihv[1]);
+ output[8] = (unsigned char) (ctx->ihv[2] >> 24);
+ output[9] = (unsigned char) (ctx->ihv[2] >> 16);
+ output[10] = (unsigned char) (ctx->ihv[2] >> 8);
+ output[11] = (unsigned char) (ctx->ihv[2]);
+ output[12] = (unsigned char) (ctx->ihv[3] >> 24);
+ output[13] = (unsigned char) (ctx->ihv[3] >> 16);
+ output[14] = (unsigned char) (ctx->ihv[3] >> 8);
+ output[15] = (unsigned char) (ctx->ihv[3]);
+ output[16] = (unsigned char) (ctx->ihv[4] >> 24);
+ output[17] = (unsigned char) (ctx->ihv[4] >> 16);
+ output[18] = (unsigned char) (ctx->ihv[4] >> 8);
+ output[19] = (unsigned char) (ctx->ihv[4]);
+ return ctx->found_collision;
+}
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C
+#endif
diff --git a/src/lib/crypto/sha1cd/sha1.h b/src/lib/crypto/sha1cd/sha1.h
new file mode 100644
index 0000000..5bc0925
--- /dev/null
+++ b/src/lib/crypto/sha1cd/sha1.h
@@ -0,0 +1,122 @@
+/***
+ * Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+ * Distributed under the MIT Software License.
+ * See accompanying file LICENSE.txt or copy at
+ * https://opensource.org/licenses/MIT
+ ***/
+
+#ifndef SHA1DC_SHA1_H
+#define SHA1DC_SHA1_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+
+/* sha-1 compression function that takes an already expanded message, and additionally store
+ * intermediate states */
+/* only stores states ii (the state between step ii-1 and step ii) when DOSTORESTATEii is
+ * defined in ubc_check.h */
+void sha1_compression_states(uint32_t[5], const uint32_t[16], uint32_t[80], uint32_t[80][5]);
+
+/*
+// Function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const
+uint32_t me2[80], const uint32_t state[5]).
+// Where 0 <= T < 80
+// me2 is an expanded message (the expansion of an original message block XOR'ed with a
+disturbance vector's message block difference.)
+// state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression
+function while processing the original message block.
+// The function will return:
+// ihvin: The reconstructed input chaining value.
+// ihvout: The reconstructed output chaining value.
+*/
+typedef void (*sha1_recompression_type)(uint32_t *,
+ uint32_t *,
+ const uint32_t *,
+ const uint32_t *);
+
+/* A callback function type that can be set to be called when a collision block has been found:
+ */
+/* void collision_block_callback(uint64_t byteoffset, const uint32_t ihvin1[5], const uint32_t
+ * ihvin2[5], const uint32_t m1[80], const uint32_t m2[80]) */
+typedef void (*collision_block_callback)(
+ uint64_t, const uint32_t *, const uint32_t *, const uint32_t *, const uint32_t *);
+
+/* The SHA-1 context. */
+typedef struct {
+ uint64_t total;
+ uint32_t ihv[5];
+ unsigned char buffer[64];
+ int found_collision;
+ int safe_hash;
+ int detect_coll;
+ int ubc_check;
+ int reduced_round_coll;
+ collision_block_callback callback;
+
+ uint32_t ihv1[5];
+ uint32_t ihv2[5];
+ uint32_t m1[80];
+ uint32_t m2[80];
+ uint32_t states[80][5];
+} SHA1_CTX;
+
+/* Initialize SHA-1 context. */
+void SHA1DCInit(SHA1_CTX *);
+
+/*
+ Function to enable safe SHA-1 hashing:
+ Collision attacks are thwarted by hashing a detected near-collision block 3 times.
+ Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks:
+ The best collision attacks against SHA-1 have complexity about 2^60,
+ thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be
+ 2^180. An attacker would be better off using a generic birthday search of complexity 2^80.
+
+ Enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no
+ collision attack was detected, but it will result in a different SHA-1 hash for messages
+ where a collision attack was detected. This will automatically invalidate SHA-1 based
+ digital signature forgeries. Enabled by default.
+*/
+void SHA1DCSetSafeHash(SHA1_CTX *, int);
+
+/*
+ Function to disable or enable the use of Unavoidable Bitconditions (provides a significant
+ speed up). Enabled by default
+ */
+void SHA1DCSetUseUBC(SHA1_CTX *, int);
+
+/*
+ Function to disable or enable the use of Collision Detection.
+ Enabled by default.
+ */
+void SHA1DCSetUseDetectColl(SHA1_CTX *, int);
+
+/* function to disable or enable the detection of reduced-round SHA-1 collisions */
+/* disabled by default */
+void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX *, int);
+
+/* function to set a callback function, pass NULL to disable */
+/* by default no callback set */
+void SHA1DCSetCallback(SHA1_CTX *, collision_block_callback);
+
+/* update SHA-1 context with buffer contents */
+void SHA1DCUpdate(SHA1_CTX *, const char *, size_t);
+
+/* obtain SHA-1 hash from SHA-1 context */
+/* returns: 0 = no collision detected, otherwise = collision found => warn user for active
+ * attack */
+int SHA1DCFinal(unsigned char[20], SHA1_CTX *);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H
+#endif
+
+#endif
diff --git a/src/lib/crypto/sha1cd/ubc_check.c b/src/lib/crypto/sha1cd/ubc_check.c
new file mode 100644
index 0000000..c44c53d
--- /dev/null
+++ b/src/lib/crypto/sha1cd/ubc_check.c
@@ -0,0 +1,908 @@
+/***
+ * Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+ * Distributed under the MIT Software License.
+ * See accompanying file LICENSE.txt or copy at
+ * https://opensource.org/licenses/MIT
+ ***/
+
+/*
+// this file was generated by the 'parse_bitrel' program in the tools section
+// using the data files from directory 'tools/data/3565'
+//
+// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check
+// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper)
+// dm[80] is the expanded message block XOR-difference defined by the DV
+// testt is the step to do the recompression from for collision detection
+// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check
+//
+// ubc_check takes as input an expanded message block and verifies the unavoidable
+bitconditions for all listed DVs
+// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions
+for that DV have been met
+// thus one needs to do the recompression check for each DV that has its bit set
+//
+// ubc_check is programmatically generated and the unavoidable bitconditions have been
+hardcoded
+// a directly verifiable version named ubc_check_verify can be found in ubc_check_verify.c
+// ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in
+the tools section
+*/
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+#ifdef SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C
+#include SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C
+#endif
+#include "ubc_check.h"
+
+static const uint32_t DV_I_43_0_bit = (uint32_t)(1) << 0;
+static const uint32_t DV_I_44_0_bit = (uint32_t)(1) << 1;
+static const uint32_t DV_I_45_0_bit = (uint32_t)(1) << 2;
+static const uint32_t DV_I_46_0_bit = (uint32_t)(1) << 3;
+static const uint32_t DV_I_46_2_bit = (uint32_t)(1) << 4;
+static const uint32_t DV_I_47_0_bit = (uint32_t)(1) << 5;
+static const uint32_t DV_I_47_2_bit = (uint32_t)(1) << 6;
+static const uint32_t DV_I_48_0_bit = (uint32_t)(1) << 7;
+static const uint32_t DV_I_48_2_bit = (uint32_t)(1) << 8;
+static const uint32_t DV_I_49_0_bit = (uint32_t)(1) << 9;
+static const uint32_t DV_I_49_2_bit = (uint32_t)(1) << 10;
+static const uint32_t DV_I_50_0_bit = (uint32_t)(1) << 11;
+static const uint32_t DV_I_50_2_bit = (uint32_t)(1) << 12;
+static const uint32_t DV_I_51_0_bit = (uint32_t)(1) << 13;
+static const uint32_t DV_I_51_2_bit = (uint32_t)(1) << 14;
+static const uint32_t DV_I_52_0_bit = (uint32_t)(1) << 15;
+static const uint32_t DV_II_45_0_bit = (uint32_t)(1) << 16;
+static const uint32_t DV_II_46_0_bit = (uint32_t)(1) << 17;
+static const uint32_t DV_II_46_2_bit = (uint32_t)(1) << 18;
+static const uint32_t DV_II_47_0_bit = (uint32_t)(1) << 19;
+static const uint32_t DV_II_48_0_bit = (uint32_t)(1) << 20;
+static const uint32_t DV_II_49_0_bit = (uint32_t)(1) << 21;
+static const uint32_t DV_II_49_2_bit = (uint32_t)(1) << 22;
+static const uint32_t DV_II_50_0_bit = (uint32_t)(1) << 23;
+static const uint32_t DV_II_50_2_bit = (uint32_t)(1) << 24;
+static const uint32_t DV_II_51_0_bit = (uint32_t)(1) << 25;
+static const uint32_t DV_II_51_2_bit = (uint32_t)(1) << 26;
+static const uint32_t DV_II_52_0_bit = (uint32_t)(1) << 27;
+static const uint32_t DV_II_53_0_bit = (uint32_t)(1) << 28;
+static const uint32_t DV_II_54_0_bit = (uint32_t)(1) << 29;
+static const uint32_t DV_II_55_0_bit = (uint32_t)(1) << 30;
+static const uint32_t DV_II_56_0_bit = (uint32_t)(1) << 31;
+
+dv_info_t sha1_dvs[] = {
+ {1, 43, 0, 58, 0, 0, {0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000,
+ 0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014,
+ 0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010,
+ 0xf0000010, 0x90000008, 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008,
+ 0x40000000, 0x90000000, 0xf0000010, 0x90000018, 0x60000000, 0x90000010,
+ 0x90000010, 0x90000000, 0x80000000, 0x00000010, 0xa0000000, 0x20000000,
+ 0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+ 0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+ 0x00000001, 0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080,
+ 0x80000006, 0x00000049, 0x00000103, 0x80000009, 0x80000012, 0x80000202,
+ 0x00000018, 0x00000164, 0x00000408, 0x800000e6, 0x8000004c, 0x00000803,
+ 0x80000161, 0x80000599}},
+ {1, 44, 0, 58, 0, 1, {0xb4000008, 0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010,
+ 0x98000000, 0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010,
+ 0xb8000014, 0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000,
+ 0x90000010, 0xf0000010, 0x90000008, 0xc0000000, 0x90000010, 0xf0000010,
+ 0xb0000008, 0x40000000, 0x90000000, 0xf0000010, 0x90000018, 0x60000000,
+ 0x90000010, 0x90000010, 0x90000000, 0x80000000, 0x00000010, 0xa0000000,
+ 0x20000000, 0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000,
+ 0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000,
+ 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+ 0x00000020, 0x00000001, 0x40000002, 0x40000040, 0x40000002, 0x80000004,
+ 0x80000080, 0x80000006, 0x00000049, 0x00000103, 0x80000009, 0x80000012,
+ 0x80000202, 0x00000018, 0x00000164, 0x00000408, 0x800000e6, 0x8000004c,
+ 0x00000803, 0x80000161}},
+ {1, 45, 0, 58, 0, 2, {0xf4000014, 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010, 0x08000010,
+ 0xb8000010, 0x98000000, 0x60000000, 0x00000008, 0xc0000000, 0x90000014,
+ 0x10000010, 0xb8000014, 0x28000000, 0x20000010, 0x48000000, 0x08000018,
+ 0x60000000, 0x90000010, 0xf0000010, 0x90000008, 0xc0000000, 0x90000010,
+ 0xf0000010, 0xb0000008, 0x40000000, 0x90000000, 0xf0000010, 0x90000018,
+ 0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000, 0x00000010,
+ 0xa0000000, 0x20000000, 0xa0000000, 0x20000010, 0x00000000, 0x20000010,
+ 0x20000000, 0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000,
+ 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000040, 0x40000002,
+ 0x80000004, 0x80000080, 0x80000006, 0x00000049, 0x00000103, 0x80000009,
+ 0x80000012, 0x80000202, 0x00000018, 0x00000164, 0x00000408, 0x800000e6,
+ 0x8000004c, 0x00000803}},
+ {1, 46, 0, 58, 0, 3, {0x2c000010, 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010,
+ 0x08000010, 0xb8000010, 0x98000000, 0x60000000, 0x00000008, 0xc0000000,
+ 0x90000014, 0x10000010, 0xb8000014, 0x28000000, 0x20000010, 0x48000000,
+ 0x08000018, 0x60000000, 0x90000010, 0xf0000010, 0x90000008, 0xc0000000,
+ 0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000, 0xf0000010,
+ 0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000,
+ 0x00000010, 0xa0000000, 0x20000000, 0xa0000000, 0x20000010, 0x00000000,
+ 0x20000010, 0x20000000, 0x00000010, 0x20000000, 0x00000010, 0xa0000000,
+ 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000040,
+ 0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049, 0x00000103,
+ 0x80000009, 0x80000012, 0x80000202, 0x00000018, 0x00000164, 0x00000408,
+ 0x800000e6, 0x8000004c}},
+ {1, 46, 2, 58, 0, 4, {0xb0000040, 0xd0000053, 0xd0000022, 0x20000000, 0x60000032, 0x60000043,
+ 0x20000040, 0xe0000042, 0x60000002, 0x80000001, 0x00000020, 0x00000003,
+ 0x40000052, 0x40000040, 0xe0000052, 0xa0000000, 0x80000040, 0x20000001,
+ 0x20000060, 0x80000001, 0x40000042, 0xc0000043, 0x40000022, 0x00000003,
+ 0x40000042, 0xc0000043, 0xc0000022, 0x00000001, 0x40000002, 0xc0000043,
+ 0x40000062, 0x80000001, 0x40000042, 0x40000042, 0x40000002, 0x00000002,
+ 0x00000040, 0x80000002, 0x80000000, 0x80000002, 0x80000040, 0x00000000,
+ 0x80000040, 0x80000000, 0x00000040, 0x80000000, 0x00000040, 0x80000002,
+ 0x00000000, 0x80000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000004, 0x00000080, 0x00000004, 0x00000009, 0x00000101,
+ 0x00000009, 0x00000012, 0x00000202, 0x0000001a, 0x00000124, 0x0000040c,
+ 0x00000026, 0x0000004a, 0x0000080a, 0x00000060, 0x00000590, 0x00001020,
+ 0x0000039a, 0x00000132}},
+ {1, 47, 0, 58, 0, 5, {0xc8000010, 0x2c000010, 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c,
+ 0xd8000010, 0x08000010, 0xb8000010, 0x98000000, 0x60000000, 0x00000008,
+ 0xc0000000, 0x90000014, 0x10000010, 0xb8000014, 0x28000000, 0x20000010,
+ 0x48000000, 0x08000018, 0x60000000, 0x90000010, 0xf0000010, 0x90000008,
+ 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000,
+ 0xf0000010, 0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000,
+ 0x80000000, 0x00000010, 0xa0000000, 0x20000000, 0xa0000000, 0x20000010,
+ 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x20000000, 0x00000010,
+ 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+ 0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049,
+ 0x00000103, 0x80000009, 0x80000012, 0x80000202, 0x00000018, 0x00000164,
+ 0x00000408, 0x800000e6}},
+ {1, 47, 2, 58, 0, 6, {0x20000043, 0xb0000040, 0xd0000053, 0xd0000022, 0x20000000, 0x60000032,
+ 0x60000043, 0x20000040, 0xe0000042, 0x60000002, 0x80000001, 0x00000020,
+ 0x00000003, 0x40000052, 0x40000040, 0xe0000052, 0xa0000000, 0x80000040,
+ 0x20000001, 0x20000060, 0x80000001, 0x40000042, 0xc0000043, 0x40000022,
+ 0x00000003, 0x40000042, 0xc0000043, 0xc0000022, 0x00000001, 0x40000002,
+ 0xc0000043, 0x40000062, 0x80000001, 0x40000042, 0x40000042, 0x40000002,
+ 0x00000002, 0x00000040, 0x80000002, 0x80000000, 0x80000002, 0x80000040,
+ 0x00000000, 0x80000040, 0x80000000, 0x00000040, 0x80000000, 0x00000040,
+ 0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000004, 0x00000080, 0x00000004, 0x00000009,
+ 0x00000101, 0x00000009, 0x00000012, 0x00000202, 0x0000001a, 0x00000124,
+ 0x0000040c, 0x00000026, 0x0000004a, 0x0000080a, 0x00000060, 0x00000590,
+ 0x00001020, 0x0000039a}},
+ {1, 48, 0, 58, 0, 7, {0xb800000a, 0xc8000010, 0x2c000010, 0xf4000014, 0xb4000008, 0x08000000,
+ 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000, 0x60000000,
+ 0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014, 0x28000000,
+ 0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010, 0xf0000010,
+ 0x90000008, 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008, 0x40000000,
+ 0x90000000, 0xf0000010, 0x90000018, 0x60000000, 0x90000010, 0x90000010,
+ 0x90000000, 0x80000000, 0x00000010, 0xa0000000, 0x20000000, 0xa0000000,
+ 0x20000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x20000000,
+ 0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+ 0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006,
+ 0x00000049, 0x00000103, 0x80000009, 0x80000012, 0x80000202, 0x00000018,
+ 0x00000164, 0x00000408}},
+ {1, 48, 2, 58, 0, 8, {0xe000002a, 0x20000043, 0xb0000040, 0xd0000053, 0xd0000022, 0x20000000,
+ 0x60000032, 0x60000043, 0x20000040, 0xe0000042, 0x60000002, 0x80000001,
+ 0x00000020, 0x00000003, 0x40000052, 0x40000040, 0xe0000052, 0xa0000000,
+ 0x80000040, 0x20000001, 0x20000060, 0x80000001, 0x40000042, 0xc0000043,
+ 0x40000022, 0x00000003, 0x40000042, 0xc0000043, 0xc0000022, 0x00000001,
+ 0x40000002, 0xc0000043, 0x40000062, 0x80000001, 0x40000042, 0x40000042,
+ 0x40000002, 0x00000002, 0x00000040, 0x80000002, 0x80000000, 0x80000002,
+ 0x80000040, 0x00000000, 0x80000040, 0x80000000, 0x00000040, 0x80000000,
+ 0x00000040, 0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000080, 0x00000004,
+ 0x00000009, 0x00000101, 0x00000009, 0x00000012, 0x00000202, 0x0000001a,
+ 0x00000124, 0x0000040c, 0x00000026, 0x0000004a, 0x0000080a, 0x00000060,
+ 0x00000590, 0x00001020}},
+ {1, 49, 0, 58, 0, 9, {0x18000000, 0xb800000a, 0xc8000010, 0x2c000010, 0xf4000014, 0xb4000008,
+ 0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000,
+ 0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014,
+ 0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010,
+ 0xf0000010, 0x90000008, 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008,
+ 0x40000000, 0x90000000, 0xf0000010, 0x90000018, 0x60000000, 0x90000010,
+ 0x90000010, 0x90000000, 0x80000000, 0x00000010, 0xa0000000, 0x20000000,
+ 0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+ 0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+ 0x00000001, 0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080,
+ 0x80000006, 0x00000049, 0x00000103, 0x80000009, 0x80000012, 0x80000202,
+ 0x00000018, 0x00000164}},
+ {1, 49, 2, 58, 0, 10, {0x60000000, 0xe000002a, 0x20000043, 0xb0000040, 0xd0000053,
+ 0xd0000022, 0x20000000, 0x60000032, 0x60000043, 0x20000040,
+ 0xe0000042, 0x60000002, 0x80000001, 0x00000020, 0x00000003,
+ 0x40000052, 0x40000040, 0xe0000052, 0xa0000000, 0x80000040,
+ 0x20000001, 0x20000060, 0x80000001, 0x40000042, 0xc0000043,
+ 0x40000022, 0x00000003, 0x40000042, 0xc0000043, 0xc0000022,
+ 0x00000001, 0x40000002, 0xc0000043, 0x40000062, 0x80000001,
+ 0x40000042, 0x40000042, 0x40000002, 0x00000002, 0x00000040,
+ 0x80000002, 0x80000000, 0x80000002, 0x80000040, 0x00000000,
+ 0x80000040, 0x80000000, 0x00000040, 0x80000000, 0x00000040,
+ 0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004,
+ 0x00000080, 0x00000004, 0x00000009, 0x00000101, 0x00000009,
+ 0x00000012, 0x00000202, 0x0000001a, 0x00000124, 0x0000040c,
+ 0x00000026, 0x0000004a, 0x0000080a, 0x00000060, 0x00000590}},
+ {1, 50, 0, 65, 0, 11, {0x0800000c, 0x18000000, 0xb800000a, 0xc8000010, 0x2c000010,
+ 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010,
+ 0x08000010, 0xb8000010, 0x98000000, 0x60000000, 0x00000008,
+ 0xc0000000, 0x90000014, 0x10000010, 0xb8000014, 0x28000000,
+ 0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010,
+ 0xf0000010, 0x90000008, 0xc0000000, 0x90000010, 0xf0000010,
+ 0xb0000008, 0x40000000, 0x90000000, 0xf0000010, 0x90000018,
+ 0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000,
+ 0x00000010, 0xa0000000, 0x20000000, 0xa0000000, 0x20000010,
+ 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x20000000,
+ 0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000040,
+ 0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049,
+ 0x00000103, 0x80000009, 0x80000012, 0x80000202, 0x00000018}},
+ {1, 50, 2, 65, 0, 12, {0x20000030, 0x60000000, 0xe000002a, 0x20000043, 0xb0000040,
+ 0xd0000053, 0xd0000022, 0x20000000, 0x60000032, 0x60000043,
+ 0x20000040, 0xe0000042, 0x60000002, 0x80000001, 0x00000020,
+ 0x00000003, 0x40000052, 0x40000040, 0xe0000052, 0xa0000000,
+ 0x80000040, 0x20000001, 0x20000060, 0x80000001, 0x40000042,
+ 0xc0000043, 0x40000022, 0x00000003, 0x40000042, 0xc0000043,
+ 0xc0000022, 0x00000001, 0x40000002, 0xc0000043, 0x40000062,
+ 0x80000001, 0x40000042, 0x40000042, 0x40000002, 0x00000002,
+ 0x00000040, 0x80000002, 0x80000000, 0x80000002, 0x80000040,
+ 0x00000000, 0x80000040, 0x80000000, 0x00000040, 0x80000000,
+ 0x00000040, 0x80000002, 0x00000000, 0x80000000, 0x80000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000004, 0x00000080, 0x00000004, 0x00000009, 0x00000101,
+ 0x00000009, 0x00000012, 0x00000202, 0x0000001a, 0x00000124,
+ 0x0000040c, 0x00000026, 0x0000004a, 0x0000080a, 0x00000060}},
+ {1, 51, 0, 65, 0, 13, {0xe8000000, 0x0800000c, 0x18000000, 0xb800000a, 0xc8000010,
+ 0x2c000010, 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c,
+ 0xd8000010, 0x08000010, 0xb8000010, 0x98000000, 0x60000000,
+ 0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014,
+ 0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000,
+ 0x90000010, 0xf0000010, 0x90000008, 0xc0000000, 0x90000010,
+ 0xf0000010, 0xb0000008, 0x40000000, 0x90000000, 0xf0000010,
+ 0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000,
+ 0x80000000, 0x00000010, 0xa0000000, 0x20000000, 0xa0000000,
+ 0x20000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+ 0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000,
+ 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+ 0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006,
+ 0x00000049, 0x00000103, 0x80000009, 0x80000012, 0x80000202}},
+ {1, 51, 2, 65, 0, 14, {0xa0000003, 0x20000030, 0x60000000, 0xe000002a, 0x20000043,
+ 0xb0000040, 0xd0000053, 0xd0000022, 0x20000000, 0x60000032,
+ 0x60000043, 0x20000040, 0xe0000042, 0x60000002, 0x80000001,
+ 0x00000020, 0x00000003, 0x40000052, 0x40000040, 0xe0000052,
+ 0xa0000000, 0x80000040, 0x20000001, 0x20000060, 0x80000001,
+ 0x40000042, 0xc0000043, 0x40000022, 0x00000003, 0x40000042,
+ 0xc0000043, 0xc0000022, 0x00000001, 0x40000002, 0xc0000043,
+ 0x40000062, 0x80000001, 0x40000042, 0x40000042, 0x40000002,
+ 0x00000002, 0x00000040, 0x80000002, 0x80000000, 0x80000002,
+ 0x80000040, 0x00000000, 0x80000040, 0x80000000, 0x00000040,
+ 0x80000000, 0x00000040, 0x80000002, 0x00000000, 0x80000000,
+ 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000004, 0x00000080, 0x00000004, 0x00000009,
+ 0x00000101, 0x00000009, 0x00000012, 0x00000202, 0x0000001a,
+ 0x00000124, 0x0000040c, 0x00000026, 0x0000004a, 0x0000080a}},
+ {1, 52, 0, 65, 0, 15, {0x04000010, 0xe8000000, 0x0800000c, 0x18000000, 0xb800000a,
+ 0xc8000010, 0x2c000010, 0xf4000014, 0xb4000008, 0x08000000,
+ 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000,
+ 0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010,
+ 0xb8000014, 0x28000000, 0x20000010, 0x48000000, 0x08000018,
+ 0x60000000, 0x90000010, 0xf0000010, 0x90000008, 0xc0000000,
+ 0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000,
+ 0xf0000010, 0x90000018, 0x60000000, 0x90000010, 0x90000010,
+ 0x90000000, 0x80000000, 0x00000010, 0xa0000000, 0x20000000,
+ 0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000,
+ 0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000,
+ 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+ 0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080,
+ 0x80000006, 0x00000049, 0x00000103, 0x80000009, 0x80000012}},
+ {2, 45, 0, 58, 0, 16, {0xec000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004,
+ 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018,
+ 0x78000010, 0x08000014, 0x70000010, 0xb800001c, 0xe8000000,
+ 0xb0000004, 0x58000010, 0xb000000c, 0x48000000, 0xb0000000,
+ 0xb8000010, 0x98000010, 0xa0000000, 0x00000000, 0x00000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0x20000000, 0x00000010, 0x60000000, 0x00000018, 0xe0000000,
+ 0x90000000, 0x30000010, 0xb0000000, 0x20000000, 0x20000000,
+ 0xa0000000, 0x00000010, 0x80000000, 0x20000000, 0x20000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000041,
+ 0x40000022, 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b,
+ 0x80000107, 0x00000089, 0x00000014, 0x8000024b, 0x0000011b,
+ 0x8000016d, 0x8000041a, 0x000002e4, 0x80000054, 0x00000967}},
+ {2, 46, 0, 58, 0, 17, {0x2400001c, 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c,
+ 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010,
+ 0x08000018, 0x78000010, 0x08000014, 0x70000010, 0xb800001c,
+ 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c, 0x48000000,
+ 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000, 0x00000000,
+ 0x00000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0x20000000, 0x00000010, 0x60000000, 0x00000018,
+ 0xe0000000, 0x90000000, 0x30000010, 0xb0000000, 0x20000000,
+ 0x20000000, 0xa0000000, 0x00000010, 0x80000000, 0x20000000,
+ 0x20000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+ 0x40000041, 0x40000022, 0x80000005, 0xc0000082, 0xc0000046,
+ 0x4000004b, 0x80000107, 0x00000089, 0x00000014, 0x8000024b,
+ 0x0000011b, 0x8000016d, 0x8000041a, 0x000002e4, 0x80000054}},
+ {2, 46, 2, 58, 0, 18, {0x90000070, 0xb0000053, 0x30000008, 0x00000043, 0xd0000072,
+ 0xb0000010, 0xf0000062, 0xc0000042, 0x00000030, 0xe0000042,
+ 0x20000060, 0xe0000041, 0x20000050, 0xc0000041, 0xe0000072,
+ 0xa0000003, 0xc0000012, 0x60000041, 0xc0000032, 0x20000001,
+ 0xc0000002, 0xe0000042, 0x60000042, 0x80000002, 0x00000000,
+ 0x00000000, 0x80000000, 0x00000002, 0x00000040, 0x00000000,
+ 0x80000040, 0x80000000, 0x00000040, 0x80000001, 0x00000060,
+ 0x80000003, 0x40000002, 0xc0000040, 0xc0000002, 0x80000000,
+ 0x80000000, 0x80000002, 0x00000040, 0x00000002, 0x80000000,
+ 0x80000000, 0x80000000, 0x00000002, 0x00000040, 0x00000000,
+ 0x80000040, 0x80000002, 0x00000000, 0x80000000, 0x80000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000004, 0x00000080, 0x00000004, 0x00000009,
+ 0x00000105, 0x00000089, 0x00000016, 0x0000020b, 0x0000011b,
+ 0x0000012d, 0x0000041e, 0x00000224, 0x00000050, 0x0000092e,
+ 0x0000046c, 0x000005b6, 0x0000106a, 0x00000b90, 0x00000152}},
+ {2, 47, 0, 58, 0, 19, {0x20000010, 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010,
+ 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c,
+ 0xb8000010, 0x08000018, 0x78000010, 0x08000014, 0x70000010,
+ 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c,
+ 0x48000000, 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000,
+ 0x00000000, 0x00000000, 0x20000000, 0x80000000, 0x00000010,
+ 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x60000000,
+ 0x00000018, 0xe0000000, 0x90000000, 0x30000010, 0xb0000000,
+ 0x20000000, 0x20000000, 0xa0000000, 0x00000010, 0x80000000,
+ 0x20000000, 0x20000000, 0x20000000, 0x80000000, 0x00000010,
+ 0x00000000, 0x20000010, 0xa0000000, 0x00000000, 0x20000000,
+ 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+ 0x40000002, 0x40000041, 0x40000022, 0x80000005, 0xc0000082,
+ 0xc0000046, 0x4000004b, 0x80000107, 0x00000089, 0x00000014,
+ 0x8000024b, 0x0000011b, 0x8000016d, 0x8000041a, 0x000002e4}},
+ {2, 48, 0, 58, 0, 20, {0xbc00001a, 0x20000010, 0x2400001c, 0xec000014, 0x0c000002,
+ 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010,
+ 0x0000000c, 0xb8000010, 0x08000018, 0x78000010, 0x08000014,
+ 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010,
+ 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010, 0x98000010,
+ 0xa0000000, 0x00000000, 0x00000000, 0x20000000, 0x80000000,
+ 0x00000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+ 0x60000000, 0x00000018, 0xe0000000, 0x90000000, 0x30000010,
+ 0xb0000000, 0x20000000, 0x20000000, 0xa0000000, 0x00000010,
+ 0x80000000, 0x20000000, 0x20000000, 0x20000000, 0x80000000,
+ 0x00000010, 0x00000000, 0x20000010, 0xa0000000, 0x00000000,
+ 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+ 0x00000001, 0x40000002, 0x40000041, 0x40000022, 0x80000005,
+ 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107, 0x00000089,
+ 0x00000014, 0x8000024b, 0x0000011b, 0x8000016d, 0x8000041a}},
+ {2, 49, 0, 58, 0, 21, {0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014,
+ 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018,
+ 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018, 0x78000010,
+ 0x08000014, 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004,
+ 0x58000010, 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010,
+ 0x98000010, 0xa0000000, 0x00000000, 0x00000000, 0x20000000,
+ 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0x20000000,
+ 0x00000010, 0x60000000, 0x00000018, 0xe0000000, 0x90000000,
+ 0x30000010, 0xb0000000, 0x20000000, 0x20000000, 0xa0000000,
+ 0x00000010, 0x80000000, 0x20000000, 0x20000000, 0x20000000,
+ 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0xa0000000,
+ 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+ 0x00000020, 0x00000001, 0x40000002, 0x40000041, 0x40000022,
+ 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107,
+ 0x00000089, 0x00000014, 0x8000024b, 0x0000011b, 0x8000016d}},
+ {2, 49, 2, 58, 0, 22, {0xf0000010, 0xf000006a, 0x80000040, 0x90000070, 0xb0000053,
+ 0x30000008, 0x00000043, 0xd0000072, 0xb0000010, 0xf0000062,
+ 0xc0000042, 0x00000030, 0xe0000042, 0x20000060, 0xe0000041,
+ 0x20000050, 0xc0000041, 0xe0000072, 0xa0000003, 0xc0000012,
+ 0x60000041, 0xc0000032, 0x20000001, 0xc0000002, 0xe0000042,
+ 0x60000042, 0x80000002, 0x00000000, 0x00000000, 0x80000000,
+ 0x00000002, 0x00000040, 0x00000000, 0x80000040, 0x80000000,
+ 0x00000040, 0x80000001, 0x00000060, 0x80000003, 0x40000002,
+ 0xc0000040, 0xc0000002, 0x80000000, 0x80000000, 0x80000002,
+ 0x00000040, 0x00000002, 0x80000000, 0x80000000, 0x80000000,
+ 0x00000002, 0x00000040, 0x00000000, 0x80000040, 0x80000002,
+ 0x00000000, 0x80000000, 0x80000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004,
+ 0x00000080, 0x00000004, 0x00000009, 0x00000105, 0x00000089,
+ 0x00000016, 0x0000020b, 0x0000011b, 0x0000012d, 0x0000041e,
+ 0x00000224, 0x00000050, 0x0000092e, 0x0000046c, 0x000005b6}},
+ {2, 50, 0, 65, 0, 23, {0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c,
+ 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004,
+ 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018,
+ 0x78000010, 0x08000014, 0x70000010, 0xb800001c, 0xe8000000,
+ 0xb0000004, 0x58000010, 0xb000000c, 0x48000000, 0xb0000000,
+ 0xb8000010, 0x98000010, 0xa0000000, 0x00000000, 0x00000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0x20000000, 0x00000010, 0x60000000, 0x00000018, 0xe0000000,
+ 0x90000000, 0x30000010, 0xb0000000, 0x20000000, 0x20000000,
+ 0xa0000000, 0x00000010, 0x80000000, 0x20000000, 0x20000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000041,
+ 0x40000022, 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b,
+ 0x80000107, 0x00000089, 0x00000014, 0x8000024b, 0x0000011b}},
+ {2, 50, 2, 65, 0, 24, {0xd0000072, 0xf0000010, 0xf000006a, 0x80000040, 0x90000070,
+ 0xb0000053, 0x30000008, 0x00000043, 0xd0000072, 0xb0000010,
+ 0xf0000062, 0xc0000042, 0x00000030, 0xe0000042, 0x20000060,
+ 0xe0000041, 0x20000050, 0xc0000041, 0xe0000072, 0xa0000003,
+ 0xc0000012, 0x60000041, 0xc0000032, 0x20000001, 0xc0000002,
+ 0xe0000042, 0x60000042, 0x80000002, 0x00000000, 0x00000000,
+ 0x80000000, 0x00000002, 0x00000040, 0x00000000, 0x80000040,
+ 0x80000000, 0x00000040, 0x80000001, 0x00000060, 0x80000003,
+ 0x40000002, 0xc0000040, 0xc0000002, 0x80000000, 0x80000000,
+ 0x80000002, 0x00000040, 0x00000002, 0x80000000, 0x80000000,
+ 0x80000000, 0x00000002, 0x00000040, 0x00000000, 0x80000040,
+ 0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000004, 0x00000080, 0x00000004, 0x00000009, 0x00000105,
+ 0x00000089, 0x00000016, 0x0000020b, 0x0000011b, 0x0000012d,
+ 0x0000041e, 0x00000224, 0x00000050, 0x0000092e, 0x0000046c}},
+ {2, 51, 0, 65, 0, 25, {0xc0000010, 0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010,
+ 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c,
+ 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010,
+ 0x08000018, 0x78000010, 0x08000014, 0x70000010, 0xb800001c,
+ 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c, 0x48000000,
+ 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000, 0x00000000,
+ 0x00000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0x20000000, 0x00000010, 0x60000000, 0x00000018,
+ 0xe0000000, 0x90000000, 0x30000010, 0xb0000000, 0x20000000,
+ 0x20000000, 0xa0000000, 0x00000010, 0x80000000, 0x20000000,
+ 0x20000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+ 0x40000041, 0x40000022, 0x80000005, 0xc0000082, 0xc0000046,
+ 0x4000004b, 0x80000107, 0x00000089, 0x00000014, 0x8000024b}},
+ {2, 51, 2, 65, 0, 26, {0x00000043, 0xd0000072, 0xf0000010, 0xf000006a, 0x80000040,
+ 0x90000070, 0xb0000053, 0x30000008, 0x00000043, 0xd0000072,
+ 0xb0000010, 0xf0000062, 0xc0000042, 0x00000030, 0xe0000042,
+ 0x20000060, 0xe0000041, 0x20000050, 0xc0000041, 0xe0000072,
+ 0xa0000003, 0xc0000012, 0x60000041, 0xc0000032, 0x20000001,
+ 0xc0000002, 0xe0000042, 0x60000042, 0x80000002, 0x00000000,
+ 0x00000000, 0x80000000, 0x00000002, 0x00000040, 0x00000000,
+ 0x80000040, 0x80000000, 0x00000040, 0x80000001, 0x00000060,
+ 0x80000003, 0x40000002, 0xc0000040, 0xc0000002, 0x80000000,
+ 0x80000000, 0x80000002, 0x00000040, 0x00000002, 0x80000000,
+ 0x80000000, 0x80000000, 0x00000002, 0x00000040, 0x00000000,
+ 0x80000040, 0x80000002, 0x00000000, 0x80000000, 0x80000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000004, 0x00000080, 0x00000004, 0x00000009,
+ 0x00000105, 0x00000089, 0x00000016, 0x0000020b, 0x0000011b,
+ 0x0000012d, 0x0000041e, 0x00000224, 0x00000050, 0x0000092e}},
+ {2, 52, 0, 65, 0, 27, {0x0c000002, 0xc0000010, 0xb400001c, 0x3c000004, 0xbc00001a,
+ 0x20000010, 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010,
+ 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c,
+ 0xb8000010, 0x08000018, 0x78000010, 0x08000014, 0x70000010,
+ 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c,
+ 0x48000000, 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000,
+ 0x00000000, 0x00000000, 0x20000000, 0x80000000, 0x00000010,
+ 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x60000000,
+ 0x00000018, 0xe0000000, 0x90000000, 0x30000010, 0xb0000000,
+ 0x20000000, 0x20000000, 0xa0000000, 0x00000010, 0x80000000,
+ 0x20000000, 0x20000000, 0x20000000, 0x80000000, 0x00000010,
+ 0x00000000, 0x20000010, 0xa0000000, 0x00000000, 0x20000000,
+ 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+ 0x40000002, 0x40000041, 0x40000022, 0x80000005, 0xc0000082,
+ 0xc0000046, 0x4000004b, 0x80000107, 0x00000089, 0x00000014}},
+ {2, 53, 0, 65, 0, 28, {0xcc000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x3c000004,
+ 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014, 0x0c000002,
+ 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010,
+ 0x0000000c, 0xb8000010, 0x08000018, 0x78000010, 0x08000014,
+ 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010,
+ 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010, 0x98000010,
+ 0xa0000000, 0x00000000, 0x00000000, 0x20000000, 0x80000000,
+ 0x00000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+ 0x60000000, 0x00000018, 0xe0000000, 0x90000000, 0x30000010,
+ 0xb0000000, 0x20000000, 0x20000000, 0xa0000000, 0x00000010,
+ 0x80000000, 0x20000000, 0x20000000, 0x20000000, 0x80000000,
+ 0x00000010, 0x00000000, 0x20000010, 0xa0000000, 0x00000000,
+ 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+ 0x00000001, 0x40000002, 0x40000041, 0x40000022, 0x80000005,
+ 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107, 0x00000089}},
+ {2, 54, 0, 65, 0, 29, {0x0400001c, 0xcc000014, 0x0c000002, 0xc0000010, 0xb400001c,
+ 0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014,
+ 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018,
+ 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018, 0x78000010,
+ 0x08000014, 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004,
+ 0x58000010, 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010,
+ 0x98000010, 0xa0000000, 0x00000000, 0x00000000, 0x20000000,
+ 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0x20000000,
+ 0x00000010, 0x60000000, 0x00000018, 0xe0000000, 0x90000000,
+ 0x30000010, 0xb0000000, 0x20000000, 0x20000000, 0xa0000000,
+ 0x00000010, 0x80000000, 0x20000000, 0x20000000, 0x20000000,
+ 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0xa0000000,
+ 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+ 0x00000020, 0x00000001, 0x40000002, 0x40000041, 0x40000022,
+ 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107}},
+ {2, 55, 0, 65, 0, 30, {0x00000010, 0x0400001c, 0xcc000014, 0x0c000002, 0xc0000010,
+ 0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c,
+ 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004,
+ 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018,
+ 0x78000010, 0x08000014, 0x70000010, 0xb800001c, 0xe8000000,
+ 0xb0000004, 0x58000010, 0xb000000c, 0x48000000, 0xb0000000,
+ 0xb8000010, 0x98000010, 0xa0000000, 0x00000000, 0x00000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0x20000000, 0x00000010, 0x60000000, 0x00000018, 0xe0000000,
+ 0x90000000, 0x30000010, 0xb0000000, 0x20000000, 0x20000000,
+ 0xa0000000, 0x00000010, 0x80000000, 0x20000000, 0x20000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000041,
+ 0x40000022, 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b}},
+ {2, 56, 0, 65, 0, 31, {0x2600001a, 0x00000010, 0x0400001c, 0xcc000014, 0x0c000002,
+ 0xc0000010, 0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010,
+ 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c,
+ 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010,
+ 0x08000018, 0x78000010, 0x08000014, 0x70000010, 0xb800001c,
+ 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c, 0x48000000,
+ 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000, 0x00000000,
+ 0x00000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0x20000000, 0x00000010, 0x60000000, 0x00000018,
+ 0xe0000000, 0x90000000, 0x30000010, 0xb0000000, 0x20000000,
+ 0x20000000, 0xa0000000, 0x00000010, 0x80000000, 0x20000000,
+ 0x20000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+ 0x40000041, 0x40000022, 0x80000005, 0xc0000082, 0xc0000046}},
+ {0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}};
+void
+ubc_check(const uint32_t W[80], uint32_t dvmask[1])
+{
+ uint32_t mask = ~((uint32_t)(0));
+ mask &= (((((W[44] ^ W[45]) >> 29) & 1) - 1) |
+ ~(DV_I_48_0_bit | DV_I_51_0_bit | DV_I_52_0_bit | DV_II_45_0_bit |
+ DV_II_46_0_bit | DV_II_50_0_bit | DV_II_51_0_bit));
+ mask &= (((((W[49] ^ W[50]) >> 29) & 1) - 1) |
+ ~(DV_I_46_0_bit | DV_II_45_0_bit | DV_II_50_0_bit | DV_II_51_0_bit |
+ DV_II_55_0_bit | DV_II_56_0_bit));
+ mask &= (((((W[48] ^ W[49]) >> 29) & 1) - 1) |
+ ~(DV_I_45_0_bit | DV_I_52_0_bit | DV_II_49_0_bit | DV_II_50_0_bit |
+ DV_II_54_0_bit | DV_II_55_0_bit));
+ mask &= ((((W[47] ^ (W[50] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit |
+ DV_II_51_0_bit | DV_II_56_0_bit));
+ mask &= (((((W[47] ^ W[48]) >> 29) & 1) - 1) |
+ ~(DV_I_44_0_bit | DV_I_51_0_bit | DV_II_48_0_bit | DV_II_49_0_bit |
+ DV_II_53_0_bit | DV_II_54_0_bit));
+ mask &= (((((W[46] >> 4) ^ (W[49] >> 29)) & 1) - 1) |
+ ~(DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit | DV_II_50_0_bit |
+ DV_II_55_0_bit));
+ mask &= (((((W[46] ^ W[47]) >> 29) & 1) - 1) |
+ ~(DV_I_43_0_bit | DV_I_50_0_bit | DV_II_47_0_bit | DV_II_48_0_bit |
+ DV_II_52_0_bit | DV_II_53_0_bit));
+ mask &= (((((W[45] >> 4) ^ (W[48] >> 29)) & 1) - 1) |
+ ~(DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit | DV_II_49_0_bit |
+ DV_II_54_0_bit));
+ mask &= (((((W[45] ^ W[46]) >> 29) & 1) - 1) |
+ ~(DV_I_49_0_bit | DV_I_52_0_bit | DV_II_46_0_bit | DV_II_47_0_bit |
+ DV_II_51_0_bit | DV_II_52_0_bit));
+ mask &= (((((W[44] >> 4) ^ (W[47] >> 29)) & 1) - 1) |
+ ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit | DV_II_48_0_bit |
+ DV_II_53_0_bit));
+ mask &= (((((W[43] >> 4) ^ (W[46] >> 29)) & 1) - 1) |
+ ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit | DV_II_47_0_bit |
+ DV_II_52_0_bit));
+ mask &= (((((W[43] ^ W[44]) >> 29) & 1) - 1) |
+ ~(DV_I_47_0_bit | DV_I_50_0_bit | DV_I_51_0_bit | DV_II_45_0_bit |
+ DV_II_49_0_bit | DV_II_50_0_bit));
+ mask &= (((((W[42] >> 4) ^ (W[45] >> 29)) & 1) - 1) |
+ ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit | DV_I_52_0_bit | DV_II_46_0_bit |
+ DV_II_51_0_bit));
+ mask &= (((((W[41] >> 4) ^ (W[44] >> 29)) & 1) - 1) |
+ ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit | DV_I_51_0_bit | DV_II_45_0_bit |
+ DV_II_50_0_bit));
+ mask &= (((((W[40] ^ W[41]) >> 29) & 1) - 1) |
+ ~(DV_I_44_0_bit | DV_I_47_0_bit | DV_I_48_0_bit | DV_II_46_0_bit |
+ DV_II_47_0_bit | DV_II_56_0_bit));
+ mask &=
+ (((((W[54] ^ W[55]) >> 29) & 1) - 1) |
+ ~(DV_I_51_0_bit | DV_II_47_0_bit | DV_II_50_0_bit | DV_II_55_0_bit | DV_II_56_0_bit));
+ mask &=
+ (((((W[53] ^ W[54]) >> 29) & 1) - 1) |
+ ~(DV_I_50_0_bit | DV_II_46_0_bit | DV_II_49_0_bit | DV_II_54_0_bit | DV_II_55_0_bit));
+ mask &=
+ (((((W[52] ^ W[53]) >> 29) & 1) - 1) |
+ ~(DV_I_49_0_bit | DV_II_45_0_bit | DV_II_48_0_bit | DV_II_53_0_bit | DV_II_54_0_bit));
+ mask &=
+ ((((W[50] ^ (W[53] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_I_50_0_bit | DV_I_52_0_bit | DV_II_46_0_bit | DV_II_48_0_bit | DV_II_54_0_bit));
+ mask &=
+ (((((W[50] ^ W[51]) >> 29) & 1) - 1) |
+ ~(DV_I_47_0_bit | DV_II_46_0_bit | DV_II_51_0_bit | DV_II_52_0_bit | DV_II_56_0_bit));
+ mask &=
+ ((((W[49] ^ (W[52] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit | DV_II_47_0_bit | DV_II_53_0_bit));
+ mask &=
+ ((((W[48] ^ (W[51] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit | DV_II_46_0_bit | DV_II_52_0_bit));
+ mask &=
+ (((((W[42] ^ W[43]) >> 29) & 1) - 1) |
+ ~(DV_I_46_0_bit | DV_I_49_0_bit | DV_I_50_0_bit | DV_II_48_0_bit | DV_II_49_0_bit));
+ mask &=
+ (((((W[41] ^ W[42]) >> 29) & 1) - 1) |
+ ~(DV_I_45_0_bit | DV_I_48_0_bit | DV_I_49_0_bit | DV_II_47_0_bit | DV_II_48_0_bit));
+ mask &=
+ (((((W[40] >> 4) ^ (W[43] >> 29)) & 1) - 1) |
+ ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_50_0_bit | DV_II_49_0_bit | DV_II_56_0_bit));
+ mask &=
+ (((((W[39] >> 4) ^ (W[42] >> 29)) & 1) - 1) |
+ ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_49_0_bit | DV_II_48_0_bit | DV_II_55_0_bit));
+ if (mask &
+ (DV_I_44_0_bit | DV_I_48_0_bit | DV_II_47_0_bit | DV_II_54_0_bit | DV_II_56_0_bit))
+ mask &= (((((W[38] >> 4) ^ (W[41] >> 29)) & 1) - 1) |
+ ~(DV_I_44_0_bit | DV_I_48_0_bit | DV_II_47_0_bit | DV_II_54_0_bit |
+ DV_II_56_0_bit));
+ mask &=
+ (((((W[37] >> 4) ^ (W[40] >> 29)) & 1) - 1) |
+ ~(DV_I_43_0_bit | DV_I_47_0_bit | DV_II_46_0_bit | DV_II_53_0_bit | DV_II_55_0_bit));
+ if (mask & (DV_I_52_0_bit | DV_II_48_0_bit | DV_II_51_0_bit | DV_II_56_0_bit))
+ mask &= (((((W[55] ^ W[56]) >> 29) & 1) - 1) |
+ ~(DV_I_52_0_bit | DV_II_48_0_bit | DV_II_51_0_bit | DV_II_56_0_bit));
+ if (mask & (DV_I_52_0_bit | DV_II_48_0_bit | DV_II_50_0_bit | DV_II_56_0_bit))
+ mask &= ((((W[52] ^ (W[55] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_I_52_0_bit | DV_II_48_0_bit | DV_II_50_0_bit | DV_II_56_0_bit));
+ if (mask & (DV_I_51_0_bit | DV_II_47_0_bit | DV_II_49_0_bit | DV_II_55_0_bit))
+ mask &= ((((W[51] ^ (W[54] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_I_51_0_bit | DV_II_47_0_bit | DV_II_49_0_bit | DV_II_55_0_bit));
+ if (mask & (DV_I_48_0_bit | DV_II_47_0_bit | DV_II_52_0_bit | DV_II_53_0_bit))
+ mask &= (((((W[51] ^ W[52]) >> 29) & 1) - 1) |
+ ~(DV_I_48_0_bit | DV_II_47_0_bit | DV_II_52_0_bit | DV_II_53_0_bit));
+ if (mask & (DV_I_46_0_bit | DV_I_49_0_bit | DV_II_45_0_bit | DV_II_48_0_bit))
+ mask &= (((((W[36] >> 4) ^ (W[40] >> 29)) & 1) - 1) |
+ ~(DV_I_46_0_bit | DV_I_49_0_bit | DV_II_45_0_bit | DV_II_48_0_bit));
+ if (mask & (DV_I_52_0_bit | DV_II_48_0_bit | DV_II_49_0_bit))
+ mask &= ((0 - (((W[53] ^ W[56]) >> 29) & 1)) |
+ ~(DV_I_52_0_bit | DV_II_48_0_bit | DV_II_49_0_bit));
+ if (mask & (DV_I_50_0_bit | DV_II_46_0_bit | DV_II_47_0_bit))
+ mask &= ((0 - (((W[51] ^ W[54]) >> 29) & 1)) |
+ ~(DV_I_50_0_bit | DV_II_46_0_bit | DV_II_47_0_bit));
+ if (mask & (DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit))
+ mask &= ((0 - (((W[50] ^ W[52]) >> 29) & 1)) |
+ ~(DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit));
+ if (mask & (DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit))
+ mask &= ((0 - (((W[49] ^ W[51]) >> 29) & 1)) |
+ ~(DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit));
+ if (mask & (DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit))
+ mask &= ((0 - (((W[48] ^ W[50]) >> 29) & 1)) |
+ ~(DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit));
+ if (mask & (DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit))
+ mask &= ((0 - (((W[47] ^ W[49]) >> 29) & 1)) |
+ ~(DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit));
+ if (mask & (DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit))
+ mask &= ((0 - (((W[46] ^ W[48]) >> 29) & 1)) |
+ ~(DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit));
+ mask &= ((((W[45] ^ W[47]) & (1 << 6)) - (1 << 6)) |
+ ~(DV_I_47_2_bit | DV_I_49_2_bit | DV_I_51_2_bit));
+ if (mask & (DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit))
+ mask &= ((0 - (((W[45] ^ W[47]) >> 29) & 1)) |
+ ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit));
+ mask &=
+ (((((W[44] ^ W[46]) >> 6) & 1) - 1) | ~(DV_I_46_2_bit | DV_I_48_2_bit | DV_I_50_2_bit));
+ if (mask & (DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit))
+ mask &= ((0 - (((W[44] ^ W[46]) >> 29) & 1)) |
+ ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit));
+ mask &= ((0 - ((W[41] ^ (W[42] >> 5)) & (1 << 1))) |
+ ~(DV_I_48_2_bit | DV_II_46_2_bit | DV_II_51_2_bit));
+ mask &= ((0 - ((W[40] ^ (W[41] >> 5)) & (1 << 1))) |
+ ~(DV_I_47_2_bit | DV_I_51_2_bit | DV_II_50_2_bit));
+ if (mask & (DV_I_44_0_bit | DV_I_46_0_bit | DV_II_56_0_bit))
+ mask &= ((0 - (((W[40] ^ W[42]) >> 4) & 1)) |
+ ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_II_56_0_bit));
+ mask &= ((0 - ((W[39] ^ (W[40] >> 5)) & (1 << 1))) |
+ ~(DV_I_46_2_bit | DV_I_50_2_bit | DV_II_49_2_bit));
+ if (mask & (DV_I_43_0_bit | DV_I_45_0_bit | DV_II_55_0_bit))
+ mask &= ((0 - (((W[39] ^ W[41]) >> 4) & 1)) |
+ ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_II_55_0_bit));
+ if (mask & (DV_I_44_0_bit | DV_II_54_0_bit | DV_II_56_0_bit))
+ mask &= ((0 - (((W[38] ^ W[40]) >> 4) & 1)) |
+ ~(DV_I_44_0_bit | DV_II_54_0_bit | DV_II_56_0_bit));
+ if (mask & (DV_I_43_0_bit | DV_II_53_0_bit | DV_II_55_0_bit))
+ mask &= ((0 - (((W[37] ^ W[39]) >> 4) & 1)) |
+ ~(DV_I_43_0_bit | DV_II_53_0_bit | DV_II_55_0_bit));
+ mask &= ((0 - ((W[36] ^ (W[37] >> 5)) & (1 << 1))) |
+ ~(DV_I_47_2_bit | DV_I_50_2_bit | DV_II_46_2_bit));
+ if (mask & (DV_I_45_0_bit | DV_I_48_0_bit | DV_II_47_0_bit))
+ mask &= (((((W[35] >> 4) ^ (W[39] >> 29)) & 1) - 1) |
+ ~(DV_I_45_0_bit | DV_I_48_0_bit | DV_II_47_0_bit));
+ if (mask & (DV_I_48_0_bit | DV_II_48_0_bit))
+ mask &=
+ ((0 - ((W[63] ^ (W[64] >> 5)) & (1 << 0))) | ~(DV_I_48_0_bit | DV_II_48_0_bit));
+ if (mask & (DV_I_45_0_bit | DV_II_45_0_bit))
+ mask &=
+ ((0 - ((W[63] ^ (W[64] >> 5)) & (1 << 1))) | ~(DV_I_45_0_bit | DV_II_45_0_bit));
+ if (mask & (DV_I_47_0_bit | DV_II_47_0_bit))
+ mask &=
+ ((0 - ((W[62] ^ (W[63] >> 5)) & (1 << 0))) | ~(DV_I_47_0_bit | DV_II_47_0_bit));
+ if (mask & (DV_I_46_0_bit | DV_II_46_0_bit))
+ mask &=
+ ((0 - ((W[61] ^ (W[62] >> 5)) & (1 << 0))) | ~(DV_I_46_0_bit | DV_II_46_0_bit));
+ mask &= ((0 - ((W[61] ^ (W[62] >> 5)) & (1 << 2))) | ~(DV_I_46_2_bit | DV_II_46_2_bit));
+ if (mask & (DV_I_45_0_bit | DV_II_45_0_bit))
+ mask &=
+ ((0 - ((W[60] ^ (W[61] >> 5)) & (1 << 0))) | ~(DV_I_45_0_bit | DV_II_45_0_bit));
+ if (mask & (DV_II_51_0_bit | DV_II_54_0_bit))
+ mask &= (((((W[58] ^ W[59]) >> 29) & 1) - 1) | ~(DV_II_51_0_bit | DV_II_54_0_bit));
+ if (mask & (DV_II_50_0_bit | DV_II_53_0_bit))
+ mask &= (((((W[57] ^ W[58]) >> 29) & 1) - 1) | ~(DV_II_50_0_bit | DV_II_53_0_bit));
+ if (mask & (DV_II_52_0_bit | DV_II_54_0_bit))
+ mask &= ((((W[56] ^ (W[59] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_II_52_0_bit | DV_II_54_0_bit));
+ if (mask & (DV_II_51_0_bit | DV_II_52_0_bit))
+ mask &= ((0 - (((W[56] ^ W[59]) >> 29) & 1)) | ~(DV_II_51_0_bit | DV_II_52_0_bit));
+ if (mask & (DV_II_49_0_bit | DV_II_52_0_bit))
+ mask &= (((((W[56] ^ W[57]) >> 29) & 1) - 1) | ~(DV_II_49_0_bit | DV_II_52_0_bit));
+ if (mask & (DV_II_51_0_bit | DV_II_53_0_bit))
+ mask &= ((((W[55] ^ (W[58] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_II_51_0_bit | DV_II_53_0_bit));
+ if (mask & (DV_II_50_0_bit | DV_II_52_0_bit))
+ mask &= ((((W[54] ^ (W[57] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_II_50_0_bit | DV_II_52_0_bit));
+ if (mask & (DV_II_49_0_bit | DV_II_51_0_bit))
+ mask &= ((((W[53] ^ (W[56] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_II_49_0_bit | DV_II_51_0_bit));
+ mask &=
+ ((((W[51] ^ (W[50] >> 5)) & (1 << 1)) - (1 << 1)) | ~(DV_I_50_2_bit | DV_II_46_2_bit));
+ mask &= ((((W[48] ^ W[50]) & (1 << 6)) - (1 << 6)) | ~(DV_I_50_2_bit | DV_II_46_2_bit));
+ if (mask & (DV_I_51_0_bit | DV_I_52_0_bit))
+ mask &= ((0 - (((W[48] ^ W[55]) >> 29) & 1)) | ~(DV_I_51_0_bit | DV_I_52_0_bit));
+ mask &= ((((W[47] ^ W[49]) & (1 << 6)) - (1 << 6)) | ~(DV_I_49_2_bit | DV_I_51_2_bit));
+ mask &=
+ ((((W[48] ^ (W[47] >> 5)) & (1 << 1)) - (1 << 1)) | ~(DV_I_47_2_bit | DV_II_51_2_bit));
+ mask &= ((((W[46] ^ W[48]) & (1 << 6)) - (1 << 6)) | ~(DV_I_48_2_bit | DV_I_50_2_bit));
+ mask &=
+ ((((W[47] ^ (W[46] >> 5)) & (1 << 1)) - (1 << 1)) | ~(DV_I_46_2_bit | DV_II_50_2_bit));
+ mask &= ((0 - ((W[44] ^ (W[45] >> 5)) & (1 << 1))) | ~(DV_I_51_2_bit | DV_II_49_2_bit));
+ mask &= ((((W[43] ^ W[45]) & (1 << 6)) - (1 << 6)) | ~(DV_I_47_2_bit | DV_I_49_2_bit));
+ mask &= (((((W[42] ^ W[44]) >> 6) & 1) - 1) | ~(DV_I_46_2_bit | DV_I_48_2_bit));
+ mask &=
+ ((((W[43] ^ (W[42] >> 5)) & (1 << 1)) - (1 << 1)) | ~(DV_II_46_2_bit | DV_II_51_2_bit));
+ mask &=
+ ((((W[42] ^ (W[41] >> 5)) & (1 << 1)) - (1 << 1)) | ~(DV_I_51_2_bit | DV_II_50_2_bit));
+ mask &=
+ ((((W[41] ^ (W[40] >> 5)) & (1 << 1)) - (1 << 1)) | ~(DV_I_50_2_bit | DV_II_49_2_bit));
+ if (mask & (DV_I_52_0_bit | DV_II_51_0_bit))
+ mask &= ((((W[39] ^ (W[43] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_I_52_0_bit | DV_II_51_0_bit));
+ if (mask & (DV_I_51_0_bit | DV_II_50_0_bit))
+ mask &= ((((W[38] ^ (W[42] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_I_51_0_bit | DV_II_50_0_bit));
+ if (mask & (DV_I_48_2_bit | DV_I_51_2_bit))
+ mask &= ((0 - ((W[37] ^ (W[38] >> 5)) & (1 << 1))) | ~(DV_I_48_2_bit | DV_I_51_2_bit));
+ if (mask & (DV_I_50_0_bit | DV_II_49_0_bit))
+ mask &= ((((W[37] ^ (W[41] >> 25)) & (1 << 4)) - (1 << 4)) |
+ ~(DV_I_50_0_bit | DV_II_49_0_bit));
+ if (mask & (DV_II_52_0_bit | DV_II_54_0_bit))
+ mask &= ((0 - ((W[36] ^ W[38]) & (1 << 4))) | ~(DV_II_52_0_bit | DV_II_54_0_bit));
+ mask &= ((0 - ((W[35] ^ (W[36] >> 5)) & (1 << 1))) | ~(DV_I_46_2_bit | DV_I_49_2_bit));
+ if (mask & (DV_I_51_0_bit | DV_II_47_0_bit))
+ mask &= ((((W[35] ^ (W[39] >> 25)) & (1 << 3)) - (1 << 3)) |
+ ~(DV_I_51_0_bit | DV_II_47_0_bit));
+ if (mask) {
+ if (mask & DV_I_43_0_bit)
+ if (!((W[61] ^ (W[62] >> 5)) & (1 << 1)) ||
+ !(!((W[59] ^ (W[63] >> 25)) & (1 << 5))) ||
+ !((W[58] ^ (W[63] >> 30)) & (1 << 0)))
+ mask &= ~DV_I_43_0_bit;
+ if (mask & DV_I_44_0_bit)
+ if (!((W[62] ^ (W[63] >> 5)) & (1 << 1)) ||
+ !(!((W[60] ^ (W[64] >> 25)) & (1 << 5))) ||
+ !((W[59] ^ (W[64] >> 30)) & (1 << 0)))
+ mask &= ~DV_I_44_0_bit;
+ if (mask & DV_I_46_2_bit)
+ mask &= ((~((W[40] ^ W[42]) >> 2)) | ~DV_I_46_2_bit);
+ if (mask & DV_I_47_2_bit)
+ if (!((W[62] ^ (W[63] >> 5)) & (1 << 2)) || !(!((W[41] ^ W[43]) & (1 << 6))))
+ mask &= ~DV_I_47_2_bit;
+ if (mask & DV_I_48_2_bit)
+ if (!((W[63] ^ (W[64] >> 5)) & (1 << 2)) ||
+ !(!((W[48] ^ (W[49] << 5)) & (1 << 6))))
+ mask &= ~DV_I_48_2_bit;
+ if (mask & DV_I_49_2_bit)
+ if (!(!((W[49] ^ (W[50] << 5)) & (1 << 6))) || !((W[42] ^ W[50]) & (1 << 1)) ||
+ !(!((W[39] ^ (W[40] << 5)) & (1 << 6))) || !((W[38] ^ W[40]) & (1 << 1)))
+ mask &= ~DV_I_49_2_bit;
+ if (mask & DV_I_50_0_bit)
+ mask &= ((((W[36] ^ W[37]) << 7)) | ~DV_I_50_0_bit);
+ if (mask & DV_I_50_2_bit)
+ mask &= ((((W[43] ^ W[51]) << 11)) | ~DV_I_50_2_bit);
+ if (mask & DV_I_51_0_bit)
+ mask &= ((((W[37] ^ W[38]) << 9)) | ~DV_I_51_0_bit);
+ if (mask & DV_I_51_2_bit)
+ if (!(!((W[51] ^ (W[52] << 5)) & (1 << 6))) || !(!((W[49] ^ W[51]) & (1 << 6))) ||
+ !(!((W[37] ^ (W[37] >> 5)) & (1 << 1))) ||
+ !(!((W[35] ^ (W[39] >> 25)) & (1 << 5))))
+ mask &= ~DV_I_51_2_bit;
+ if (mask & DV_I_52_0_bit)
+ mask &= ((((W[38] ^ W[39]) << 11)) | ~DV_I_52_0_bit);
+ if (mask & DV_II_46_2_bit)
+ mask &= ((((W[47] ^ W[51]) << 17)) | ~DV_II_46_2_bit);
+ if (mask & DV_II_48_0_bit)
+ if (!(!((W[36] ^ (W[40] >> 25)) & (1 << 3))) ||
+ !((W[35] ^ (W[40] << 2)) & (1 << 30)))
+ mask &= ~DV_II_48_0_bit;
+ if (mask & DV_II_49_0_bit)
+ if (!(!((W[37] ^ (W[41] >> 25)) & (1 << 3))) ||
+ !((W[36] ^ (W[41] << 2)) & (1 << 30)))
+ mask &= ~DV_II_49_0_bit;
+ if (mask & DV_II_49_2_bit)
+ if (!(!((W[53] ^ (W[54] << 5)) & (1 << 6))) || !(!((W[51] ^ W[53]) & (1 << 6))) ||
+ !((W[50] ^ W[54]) & (1 << 1)) || !(!((W[45] ^ (W[46] << 5)) & (1 << 6))) ||
+ !(!((W[37] ^ (W[41] >> 25)) & (1 << 5))) ||
+ !((W[36] ^ (W[41] >> 30)) & (1 << 0)))
+ mask &= ~DV_II_49_2_bit;
+ if (mask & DV_II_50_0_bit)
+ if (!((W[55] ^ W[58]) & (1 << 29)) || !(!((W[38] ^ (W[42] >> 25)) & (1 << 3))) ||
+ !((W[37] ^ (W[42] << 2)) & (1 << 30)))
+ mask &= ~DV_II_50_0_bit;
+ if (mask & DV_II_50_2_bit)
+ if (!(!((W[54] ^ (W[55] << 5)) & (1 << 6))) || !(!((W[52] ^ W[54]) & (1 << 6))) ||
+ !((W[51] ^ W[55]) & (1 << 1)) || !((W[45] ^ W[47]) & (1 << 1)) ||
+ !(!((W[38] ^ (W[42] >> 25)) & (1 << 5))) ||
+ !((W[37] ^ (W[42] >> 30)) & (1 << 0)))
+ mask &= ~DV_II_50_2_bit;
+ if (mask & DV_II_51_0_bit)
+ if (!(!((W[39] ^ (W[43] >> 25)) & (1 << 3))) ||
+ !((W[38] ^ (W[43] << 2)) & (1 << 30)))
+ mask &= ~DV_II_51_0_bit;
+ if (mask & DV_II_51_2_bit)
+ if (!(!((W[55] ^ (W[56] << 5)) & (1 << 6))) || !(!((W[53] ^ W[55]) & (1 << 6))) ||
+ !((W[52] ^ W[56]) & (1 << 1)) || !((W[46] ^ W[48]) & (1 << 1)) ||
+ !(!((W[39] ^ (W[43] >> 25)) & (1 << 5))) ||
+ !((W[38] ^ (W[43] >> 30)) & (1 << 0)))
+ mask &= ~DV_II_51_2_bit;
+ if (mask & DV_II_52_0_bit)
+ if (!(!((W[59] ^ W[60]) & (1 << 29))) ||
+ !(!((W[40] ^ (W[44] >> 25)) & (1 << 3))) ||
+ !(!((W[40] ^ (W[44] >> 25)) & (1 << 4))) ||
+ !((W[39] ^ (W[44] << 2)) & (1 << 30)))
+ mask &= ~DV_II_52_0_bit;
+ if (mask & DV_II_53_0_bit)
+ if (!((W[58] ^ W[61]) & (1 << 29)) || !(!((W[57] ^ (W[61] >> 25)) & (1 << 4))) ||
+ !(!((W[41] ^ (W[45] >> 25)) & (1 << 3))) ||
+ !(!((W[41] ^ (W[45] >> 25)) & (1 << 4))))
+ mask &= ~DV_II_53_0_bit;
+ if (mask & DV_II_54_0_bit)
+ if (!(!((W[58] ^ (W[62] >> 25)) & (1 << 4))) ||
+ !(!((W[42] ^ (W[46] >> 25)) & (1 << 3))) ||
+ !(!((W[42] ^ (W[46] >> 25)) & (1 << 4))))
+ mask &= ~DV_II_54_0_bit;
+ if (mask & DV_II_55_0_bit)
+ if (!(!((W[59] ^ (W[63] >> 25)) & (1 << 4))) ||
+ !(!((W[57] ^ (W[59] >> 25)) & (1 << 4))) ||
+ !(!((W[43] ^ (W[47] >> 25)) & (1 << 3))) ||
+ !(!((W[43] ^ (W[47] >> 25)) & (1 << 4))))
+ mask &= ~DV_II_55_0_bit;
+ if (mask & DV_II_56_0_bit)
+ if (!(!((W[60] ^ (W[64] >> 25)) & (1 << 4))) ||
+ !(!((W[44] ^ (W[48] >> 25)) & (1 << 3))) ||
+ !(!((W[44] ^ (W[48] >> 25)) & (1 << 4))))
+ mask &= ~DV_II_56_0_bit;
+ }
+
+ dvmask[0] = mask;
+}
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C
+#endif
diff --git a/src/lib/crypto/sha1cd/ubc_check.h b/src/lib/crypto/sha1cd/ubc_check.h
new file mode 100644
index 0000000..a43c7b6
--- /dev/null
+++ b/src/lib/crypto/sha1cd/ubc_check.h
@@ -0,0 +1,62 @@
+/***
+ * Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+ * Distributed under the MIT Software License.
+ * See accompanying file LICENSE.txt or copy at
+ * https://opensource.org/licenses/MIT
+ ***/
+
+/*
+// this file was generated by the 'parse_bitrel' program in the tools section
+// using the data files from directory 'tools/data/3565'
+//
+// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check
+// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper)
+// dm[80] is the expanded message block XOR-difference defined by the DV
+// testt is the step to do the recompression from for collision detection
+// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check
+//
+// ubc_check takes as input an expanded message block and verifies the unavoidable
+bitconditions for all listed DVs
+// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions
+for that DV have been met
+// thus one needs to do the recompression check for each DV that has its bit set
+*/
+
+#ifndef SHA1DC_UBC_CHECK_H
+#define SHA1DC_UBC_CHECK_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+
+#define DVMASKSIZE 1
+typedef struct {
+ int dvType;
+ int dvK;
+ int dvB;
+ int testt;
+ int maski;
+ int maskb;
+ uint32_t dm[80];
+} dv_info_t;
+extern dv_info_t sha1_dvs[];
+void ubc_check(const uint32_t W[80], uint32_t dvmask[DVMASKSIZE]);
+
+#define DOSTORESTATE58
+#define DOSTORESTATE65
+
+#define CHECK_DVMASK(_DVMASK) (0 != _DVMASK[0])
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H
+#endif
+
+#endif
diff --git a/src/lib/crypto/signatures.cpp b/src/lib/crypto/signatures.cpp
new file mode 100644
index 0000000..ea39935
--- /dev/null
+++ b/src/lib/crypto/signatures.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2018-2022, [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 HOLDERS 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 <string.h>
+#include "crypto/signatures.h"
+#include "librepgp/stream-packet.h"
+#include "librepgp/stream-sig.h"
+#include "utils.h"
+#include "sec_profile.hpp"
+
+/**
+ * @brief Add signature fields to the hash context and finish it.
+ * @param hash initialized hash context fed with signed data (document, key, etc).
+ * It is finalized in this function.
+ * @param sig populated or loaded signature
+ * @param hbuf buffer to store the resulting hash. Must be large enough for hash output.
+ * @param hlen on success will be filled with the hash size, otherwise zeroed
+ * @return RNP_SUCCESS on success or some error otherwise
+ */
+static void
+signature_hash_finish(const pgp_signature_t &sig, rnp::Hash &hash, uint8_t *hbuf, size_t &hlen)
+{
+ hash.add(sig.hashed_data, sig.hashed_len);
+ if (sig.version > PGP_V3) {
+ uint8_t trailer[6] = {0x04, 0xff, 0x00, 0x00, 0x00, 0x00};
+ STORE32BE(&trailer[2], sig.hashed_len);
+ hash.add(trailer, 6);
+ }
+ hlen = hash.finish(hbuf);
+}
+
+std::unique_ptr<rnp::Hash>
+signature_init(const pgp_key_material_t &key, pgp_hash_alg_t hash_alg)
+{
+ auto hash = rnp::Hash::create(hash_alg);
+ if (key.alg == PGP_PKA_SM2) {
+#if defined(ENABLE_SM2)
+ rnp_result_t r = sm2_compute_za(key.ec, *hash);
+ if (r != RNP_SUCCESS) {
+ RNP_LOG("failed to compute SM2 ZA field");
+ throw rnp::rnp_exception(r);
+ }
+#else
+ RNP_LOG("SM2 ZA computation not available");
+ throw rnp::rnp_exception(RNP_ERROR_NOT_IMPLEMENTED);
+#endif
+ }
+ return hash;
+}
+
+void
+signature_calculate(pgp_signature_t & sig,
+ pgp_key_material_t & seckey,
+ rnp::Hash & hash,
+ rnp::SecurityContext &ctx)
+{
+ uint8_t hval[PGP_MAX_HASH_SIZE];
+ size_t hlen = 0;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ const pgp_hash_alg_t hash_alg = hash.alg();
+
+ /* Finalize hash first, since function is required to do this */
+ try {
+ signature_hash_finish(sig, hash, hval, hlen);
+ } catch (const std::exception &e) {
+ RNP_LOG("Failed to finalize hash: %s", e.what());
+ throw;
+ }
+
+ if (!seckey.secret) {
+ RNP_LOG("Secret key is required.");
+ throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+ }
+ if (sig.palg != seckey.alg) {
+ RNP_LOG("Signature and secret key do not agree on algorithm type.");
+ throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+ }
+ /* Validate key material if didn't before */
+ seckey.validate(ctx, false);
+ if (!seckey.valid()) {
+ RNP_LOG("Attempt to sign with invalid key material.");
+ throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+ }
+
+ /* copy left 16 bits to signature */
+ memcpy(sig.lbits, hval, 2);
+
+ /* sign */
+ pgp_signature_material_t material = {};
+ switch (sig.palg) {
+ case PGP_PKA_RSA:
+ case PGP_PKA_RSA_ENCRYPT_ONLY:
+ case PGP_PKA_RSA_SIGN_ONLY:
+ ret = rsa_sign_pkcs1(&ctx.rng, &material.rsa, sig.halg, hval, hlen, &seckey.rsa);
+ if (ret) {
+ RNP_LOG("rsa signing failed");
+ }
+ break;
+ case PGP_PKA_EDDSA:
+ ret = eddsa_sign(&ctx.rng, &material.ecc, hval, hlen, &seckey.ec);
+ if (ret) {
+ RNP_LOG("eddsa signing failed");
+ }
+ break;
+ case PGP_PKA_DSA:
+ ret = dsa_sign(&ctx.rng, &material.dsa, hval, hlen, &seckey.dsa);
+ if (ret != RNP_SUCCESS) {
+ RNP_LOG("DSA signing failed");
+ }
+ break;
+ /*
+ * ECDH is signed with ECDSA. This must be changed when ECDH will support
+ * X25519, but I need to check how it should be done exactly.
+ */
+ case PGP_PKA_ECDH:
+ case PGP_PKA_ECDSA:
+ case PGP_PKA_SM2: {
+ const ec_curve_desc_t *curve = get_curve_desc(seckey.ec.curve);
+ if (!curve) {
+ RNP_LOG("Unknown curve");
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ break;
+ }
+ if (!curve_supported(seckey.ec.curve)) {
+ RNP_LOG("EC sign: curve %s is not supported.", curve->pgp_name);
+ ret = RNP_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ /* "-2" because ECDSA on P-521 must work with SHA-512 digest */
+ if (BITS_TO_BYTES(curve->bitlen) - 2 > hlen) {
+ RNP_LOG("Message hash too small");
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ break;
+ }
+
+ if (sig.palg == PGP_PKA_SM2) {
+#if defined(ENABLE_SM2)
+ ret = sm2_sign(&ctx.rng, &material.ecc, hash_alg, hval, hlen, &seckey.ec);
+ if (ret) {
+ RNP_LOG("SM2 signing failed");
+ }
+#else
+ RNP_LOG("SM2 signing is not available.");
+ ret = RNP_ERROR_NOT_IMPLEMENTED;
+#endif
+ break;
+ }
+
+ ret = ecdsa_sign(&ctx.rng, &material.ecc, hash_alg, hval, hlen, &seckey.ec);
+ if (ret) {
+ RNP_LOG("ECDSA signing failed");
+ }
+ break;
+ }
+ default:
+ RNP_LOG("Unsupported algorithm %d", sig.palg);
+ break;
+ }
+ if (ret) {
+ throw rnp::rnp_exception(ret);
+ }
+ try {
+ sig.write_material(material);
+ } catch (const std::exception &e) {
+ RNP_LOG("%s", e.what());
+ throw;
+ }
+}
+
+rnp_result_t
+signature_validate(const pgp_signature_t & sig,
+ const pgp_key_material_t & key,
+ rnp::Hash & hash,
+ const rnp::SecurityContext &ctx)
+{
+ if (sig.palg != key.alg) {
+ RNP_LOG("Signature and key do not agree on algorithm type: %d vs %d",
+ (int) sig.palg,
+ (int) key.alg);
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ /* Check signature security */
+ auto action =
+ sig.is_document() ? rnp::SecurityAction::VerifyData : rnp::SecurityAction::VerifyKey;
+ if (ctx.profile.hash_level(sig.halg, sig.creation(), action) <
+ rnp::SecurityLevel::Default) {
+ RNP_LOG("Insecure hash algorithm %d, marking signature as invalid.", sig.halg);
+ return RNP_ERROR_SIGNATURE_INVALID;
+ }
+
+ /* Finalize hash */
+ uint8_t hval[PGP_MAX_HASH_SIZE];
+ size_t hlen = 0;
+ try {
+ signature_hash_finish(sig, hash, hval, hlen);
+ } catch (const std::exception &e) {
+ RNP_LOG("Failed to finalize signature hash.");
+ return RNP_ERROR_GENERIC;
+ }
+
+ /* compare lbits */
+ if (memcmp(hval, sig.lbits, 2)) {
+ RNP_LOG("wrong lbits");
+ return RNP_ERROR_SIGNATURE_INVALID;
+ }
+
+ /* validate signature */
+ pgp_signature_material_t material = {};
+ try {
+ sig.parse_material(material);
+ } catch (const std::exception &e) {
+ RNP_LOG("%s", e.what());
+ return RNP_ERROR_OUT_OF_MEMORY;
+ }
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ switch (sig.palg) {
+ case PGP_PKA_DSA:
+ ret = dsa_verify(&material.dsa, hval, hlen, &key.dsa);
+ break;
+ case PGP_PKA_EDDSA:
+ ret = eddsa_verify(&material.ecc, hval, hlen, &key.ec);
+ break;
+ case PGP_PKA_SM2:
+#if defined(ENABLE_SM2)
+ ret = sm2_verify(&material.ecc, hash.alg(), hval, hlen, &key.ec);
+#else
+ RNP_LOG("SM2 verification is not available.");
+ ret = RNP_ERROR_NOT_IMPLEMENTED;
+#endif
+ break;
+ case PGP_PKA_RSA:
+ case PGP_PKA_RSA_SIGN_ONLY:
+ ret = rsa_verify_pkcs1(&material.rsa, sig.halg, hval, hlen, &key.rsa);
+ break;
+ case PGP_PKA_RSA_ENCRYPT_ONLY:
+ RNP_LOG("RSA encrypt-only signature considered as invalid.");
+ ret = RNP_ERROR_SIGNATURE_INVALID;
+ break;
+ case PGP_PKA_ECDSA:
+ if (!curve_supported(key.ec.curve)) {
+ RNP_LOG("ECDSA verify: curve %d is not supported.", (int) key.ec.curve);
+ ret = RNP_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ ret = ecdsa_verify(&material.ecc, hash.alg(), hval, hlen, &key.ec);
+ break;
+ case PGP_PKA_ELGAMAL:
+ case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
+ RNP_LOG("ElGamal are considered as invalid.");
+ ret = RNP_ERROR_SIGNATURE_INVALID;
+ break;
+ default:
+ RNP_LOG("Unknown algorithm");
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ }
+ return ret;
+}
diff --git a/src/lib/crypto/signatures.h b/src/lib/crypto/signatures.h
new file mode 100644
index 0000000..6ba64ce
--- /dev/null
+++ b/src/lib/crypto/signatures.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018-2022, [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 HOLDERS 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.
+ */
+
+#ifndef RNP_SIGNATURES_H_
+#define RNP_SIGNATURES_H_
+
+#include "crypto/hash.hpp"
+
+/**
+ * @brief Initialize a signature computation.
+ * @param key the key that will be used to sign or verify
+ * @param hash_alg the digest algo to be used
+ * @param hash digest object that will be initialized
+ */
+std::unique_ptr<rnp::Hash> signature_init(const pgp_key_material_t &key,
+ pgp_hash_alg_t hash_alg);
+
+/**
+ * @brief Calculate signature with pre-populated hash
+ * @param sig signature to calculate
+ * @param seckey signing secret key material
+ * @param hash pre-populated with signed data hash context. It is finalized and destroyed
+ * during the execution. Signature fields and trailer are hashed in this function.
+ * @param rng random number generator
+ */
+void signature_calculate(pgp_signature_t & sig,
+ pgp_key_material_t & seckey,
+ rnp::Hash & hash,
+ rnp::SecurityContext &ctx);
+
+/**
+ * @brief Validate a signature with pre-populated hash. This method just checks correspondence
+ * between the hash and signature material. Expiration time and other fields are not
+ * checked for validity.
+ * @param sig signature to validate
+ * @param key public key material of the verifying key
+ * @param hash pre-populated with signed data hash context. It is finalized
+ * during the execution. Signature fields and trailer are hashed in this function.
+ * @return RNP_SUCCESS if signature was successfully validated or error code otherwise.
+ */
+rnp_result_t signature_validate(const pgp_signature_t & sig,
+ const pgp_key_material_t & key,
+ rnp::Hash & hash,
+ const rnp::SecurityContext &ctx);
+
+#endif
diff --git a/src/lib/crypto/sm2.cpp b/src/lib/crypto/sm2.cpp
new file mode 100644
index 0000000..2af537d
--- /dev/null
+++ b/src/lib/crypto/sm2.cpp
@@ -0,0 +1,383 @@
+/*-
+ * Copyright (c) 2017-2022 Ribose Inc.
+ * 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 HOLDERS 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 <string.h>
+#include <botan/ffi.h>
+#include "hash_botan.hpp"
+#include "sm2.h"
+#include "utils.h"
+#include "bn.h"
+
+static bool
+sm2_load_public_key(botan_pubkey_t *pubkey, const pgp_ec_key_t *keydata)
+{
+ const ec_curve_desc_t *curve = NULL;
+ botan_mp_t px = NULL;
+ botan_mp_t py = NULL;
+ size_t sz;
+ bool res = false;
+
+ if (!(curve = get_curve_desc(keydata->curve))) {
+ return false;
+ }
+
+ const size_t sign_half_len = BITS_TO_BYTES(curve->bitlen);
+ sz = mpi_bytes(&keydata->p);
+ if (!sz || (sz != (2 * sign_half_len + 1)) || (keydata->p.mpi[0] != 0x04)) {
+ goto end;
+ }
+
+ if (botan_mp_init(&px) || botan_mp_init(&py) ||
+ botan_mp_from_bin(px, &keydata->p.mpi[1], sign_half_len) ||
+ botan_mp_from_bin(py, &keydata->p.mpi[1 + sign_half_len], sign_half_len)) {
+ goto end;
+ }
+ res = !botan_pubkey_load_sm2(pubkey, px, py, curve->botan_name);
+end:
+ botan_mp_destroy(px);
+ botan_mp_destroy(py);
+ return res;
+}
+
+static bool
+sm2_load_secret_key(botan_privkey_t *seckey, const pgp_ec_key_t *keydata)
+{
+ const ec_curve_desc_t *curve = NULL;
+ bignum_t * x = NULL;
+ bool res = false;
+
+ if (!(curve = get_curve_desc(keydata->curve))) {
+ return false;
+ }
+ if (!(x = mpi2bn(&keydata->x))) {
+ return false;
+ }
+ res = !botan_privkey_load_sm2(seckey, BN_HANDLE_PTR(x), curve->botan_name);
+ bn_free(x);
+ return res;
+}
+
+rnp_result_t
+sm2_compute_za(const pgp_ec_key_t &key, rnp::Hash &hash, const char *ident_field)
+{
+ rnp_result_t result = RNP_ERROR_GENERIC;
+ botan_pubkey_t sm2_key = NULL;
+ int rc;
+
+ const char *hash_algo = rnp::Hash_Botan::name_backend(hash.alg());
+ size_t digest_len = hash.size();
+
+ uint8_t *digest_buf = (uint8_t *) malloc(digest_len);
+ if (!digest_buf) {
+ return RNP_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!sm2_load_public_key(&sm2_key, &key)) {
+ RNP_LOG("Failed to load SM2 key");
+ goto done;
+ }
+
+ if (ident_field == NULL)
+ ident_field = "1234567812345678";
+
+ rc = botan_pubkey_sm2_compute_za(digest_buf, &digest_len, ident_field, hash_algo, sm2_key);
+
+ if (rc != 0) {
+ RNP_LOG("compute_za failed %d", rc);
+ goto done;
+ }
+
+ try {
+ hash.add(digest_buf, digest_len);
+ } catch (const std::exception &e) {
+ RNP_LOG("Failed to update hash: %s", e.what());
+ goto done;
+ }
+
+ result = RNP_SUCCESS;
+done:
+ free(digest_buf);
+ botan_pubkey_destroy(sm2_key);
+ return result;
+}
+
+rnp_result_t
+sm2_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret)
+{
+ botan_pubkey_t bpkey = NULL;
+ botan_privkey_t bskey = NULL;
+ rnp_result_t ret = RNP_ERROR_BAD_PARAMETERS;
+
+ if (!sm2_load_public_key(&bpkey, key) || botan_pubkey_check_key(bpkey, rng->handle(), 0)) {
+ goto done;
+ }
+
+ if (!secret) {
+ ret = RNP_SUCCESS;
+ goto done;
+ }
+
+ if (!sm2_load_secret_key(&bskey, key) ||
+ botan_privkey_check_key(bskey, rng->handle(), 0)) {
+ goto done;
+ }
+ ret = RNP_SUCCESS;
+done:
+ botan_privkey_destroy(bskey);
+ botan_pubkey_destroy(bpkey);
+ return ret;
+}
+
+rnp_result_t
+sm2_sign(rnp::RNG * rng,
+ pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t *key)
+{
+ const ec_curve_desc_t *curve = NULL;
+ botan_pk_op_sign_t signer = NULL;
+ botan_privkey_t b_key = NULL;
+ uint8_t out_buf[2 * MAX_CURVE_BYTELEN] = {0};
+ size_t sign_half_len = 0;
+ size_t sig_len = 0;
+ rnp_result_t ret = RNP_ERROR_SIGNING_FAILED;
+
+ if (botan_ffi_supports_api(20180713) != 0) {
+ RNP_LOG("SM2 signatures requires Botan 2.8 or higher");
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+
+ if (hash_len != rnp::Hash::size(hash_alg)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ if (!(curve = get_curve_desc(key->curve))) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ sign_half_len = BITS_TO_BYTES(curve->bitlen);
+ sig_len = 2 * sign_half_len;
+
+ if (!sm2_load_secret_key(&b_key, key)) {
+ RNP_LOG("Can't load private key");
+ ret = RNP_ERROR_BAD_PARAMETERS;
+ goto end;
+ }
+
+ if (botan_pk_op_sign_create(&signer, b_key, ",Raw", 0)) {
+ goto end;
+ }
+
+ if (botan_pk_op_sign_update(signer, hash, hash_len)) {
+ goto end;
+ }
+
+ if (botan_pk_op_sign_finish(signer, rng->handle(), out_buf, &sig_len)) {
+ RNP_LOG("Signing failed");
+ goto end;
+ }
+
+ // Allocate memory and copy results
+ if (mem2mpi(&sig->r, out_buf, sign_half_len) &&
+ mem2mpi(&sig->s, out_buf + sign_half_len, sign_half_len)) {
+ // All good now
+ ret = RNP_SUCCESS;
+ }
+end:
+ botan_privkey_destroy(b_key);
+ botan_pk_op_sign_destroy(signer);
+ return ret;
+}
+
+rnp_result_t
+sm2_verify(const pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t * key)
+{
+ const ec_curve_desc_t *curve = NULL;
+ botan_pubkey_t pub = NULL;
+ botan_pk_op_verify_t verifier = NULL;
+ rnp_result_t ret = RNP_ERROR_SIGNATURE_INVALID;
+ uint8_t sign_buf[2 * MAX_CURVE_BYTELEN] = {0};
+ size_t r_blen, s_blen, sign_half_len;
+
+ if (botan_ffi_supports_api(20180713) != 0) {
+ RNP_LOG("SM2 signatures requires Botan 2.8 or higher");
+ return RNP_ERROR_NOT_SUPPORTED;
+ }
+
+ if (hash_len != rnp::Hash::size(hash_alg)) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+
+ curve = get_curve_desc(key->curve);
+ if (curve == NULL) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ sign_half_len = BITS_TO_BYTES(curve->bitlen);
+
+ if (!sm2_load_public_key(&pub, key)) {
+ RNP_LOG("Failed to load public key");
+ goto end;
+ }
+
+ if (botan_pk_op_verify_create(&verifier, pub, ",Raw", 0)) {
+ goto end;
+ }
+
+ if (botan_pk_op_verify_update(verifier, hash, hash_len)) {
+ goto end;
+ }
+
+ r_blen = sig->r.len;
+ s_blen = sig->s.len;
+ if (!r_blen || (r_blen > sign_half_len) || !s_blen || (s_blen > sign_half_len) ||
+ (sign_half_len > MAX_CURVE_BYTELEN)) {
+ goto end;
+ }
+
+ mpi2mem(&sig->r, sign_buf + sign_half_len - r_blen);
+ mpi2mem(&sig->s, sign_buf + 2 * sign_half_len - s_blen);
+
+ if (!botan_pk_op_verify_finish(verifier, sign_buf, sign_half_len * 2)) {
+ ret = RNP_SUCCESS;
+ }
+end:
+ botan_pubkey_destroy(pub);
+ botan_pk_op_verify_destroy(verifier);
+ return ret;
+}
+
+rnp_result_t
+sm2_encrypt(rnp::RNG * rng,
+ pgp_sm2_encrypted_t *out,
+ const uint8_t * in,
+ size_t in_len,
+ pgp_hash_alg_t hash_algo,
+ const pgp_ec_key_t * key)
+{
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ const ec_curve_desc_t *curve = NULL;
+ botan_pubkey_t sm2_key = NULL;
+ botan_pk_op_encrypt_t enc_op = NULL;
+ size_t point_len;
+ size_t hash_alg_len;
+ size_t ctext_len;
+
+ curve = get_curve_desc(key->curve);
+ if (curve == NULL) {
+ return RNP_ERROR_GENERIC;
+ }
+ point_len = BITS_TO_BYTES(curve->bitlen);
+ hash_alg_len = rnp::Hash::size(hash_algo);
+ if (!hash_alg_len) {
+ RNP_LOG("Unknown hash algorithm for SM2 encryption");
+ goto done;
+ }
+
+ /*
+ * Format of SM2 ciphertext is a point (2*point_len+1) plus
+ * the masked ciphertext (out_len) plus a hash.
+ */
+ ctext_len = (2 * point_len + 1) + in_len + hash_alg_len;
+ if (ctext_len > PGP_MPINT_SIZE) {
+ RNP_LOG("too large output for SM2 encryption");
+ goto done;
+ }
+
+ if (!sm2_load_public_key(&sm2_key, key)) {
+ RNP_LOG("Failed to load public key");
+ goto done;
+ }
+
+ /*
+ SM2 encryption doesn't have any kind of format specifier because
+ it's an all in one scheme, only the hash (used for the integrity
+ check) is specified.
+ */
+ if (botan_pk_op_encrypt_create(
+ &enc_op, sm2_key, rnp::Hash_Botan::name_backend(hash_algo), 0)) {
+ goto done;
+ }
+
+ out->m.len = sizeof(out->m.mpi);
+ if (botan_pk_op_encrypt(enc_op, rng->handle(), out->m.mpi, &out->m.len, in, in_len) == 0) {
+ out->m.mpi[out->m.len++] = hash_algo;
+ ret = RNP_SUCCESS;
+ }
+done:
+ botan_pk_op_encrypt_destroy(enc_op);
+ botan_pubkey_destroy(sm2_key);
+ return ret;
+}
+
+rnp_result_t
+sm2_decrypt(uint8_t * out,
+ size_t * out_len,
+ const pgp_sm2_encrypted_t *in,
+ const pgp_ec_key_t * key)
+{
+ const ec_curve_desc_t *curve;
+ botan_pk_op_decrypt_t decrypt_op = NULL;
+ botan_privkey_t b_key = NULL;
+ size_t in_len;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ uint8_t hash_id;
+ const char * hash_name = NULL;
+
+ curve = get_curve_desc(key->curve);
+ in_len = mpi_bytes(&in->m);
+ if (curve == NULL || in_len < 64) {
+ goto done;
+ }
+
+ if (!sm2_load_secret_key(&b_key, key)) {
+ RNP_LOG("Can't load private key");
+ goto done;
+ }
+
+ hash_id = in->m.mpi[in_len - 1];
+ hash_name = rnp::Hash_Botan::name_backend((pgp_hash_alg_t) hash_id);
+ if (!hash_name) {
+ RNP_LOG("Unknown hash used in SM2 ciphertext");
+ goto done;
+ }
+
+ if (botan_pk_op_decrypt_create(&decrypt_op, b_key, hash_name, 0) != 0) {
+ goto done;
+ }
+
+ if (botan_pk_op_decrypt(decrypt_op, out, out_len, in->m.mpi, in_len - 1) == 0) {
+ ret = RNP_SUCCESS;
+ }
+done:
+ botan_privkey_destroy(b_key);
+ botan_pk_op_decrypt_destroy(decrypt_op);
+ return ret;
+}
diff --git a/src/lib/crypto/sm2.h b/src/lib/crypto/sm2.h
new file mode 100644
index 0000000..a16f7fa
--- /dev/null
+++ b/src/lib/crypto/sm2.h
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 2017-2022 Ribose Inc.
+ * 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 HOLDERS 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.
+ */
+
+#ifndef RNP_SM2_H_
+#define RNP_SM2_H_
+
+#include "config.h"
+#include "ec.h"
+
+typedef struct pgp_sm2_encrypted_t {
+ pgp_mpi_t m;
+} pgp_sm2_encrypted_t;
+
+namespace rnp {
+class Hash;
+} // namespace rnp
+
+#if defined(ENABLE_SM2)
+rnp_result_t sm2_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret);
+
+/**
+ * Compute the SM2 "ZA" field, and add it to the hash object
+ *
+ * If ident_field is null, uses the default value
+ */
+rnp_result_t sm2_compute_za(const pgp_ec_key_t &key,
+ rnp::Hash & hash,
+ const char * ident_field = NULL);
+
+rnp_result_t sm2_sign(rnp::RNG * rng,
+ pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t *key);
+
+rnp_result_t sm2_verify(const pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t * key);
+
+rnp_result_t sm2_encrypt(rnp::RNG * rng,
+ pgp_sm2_encrypted_t *out,
+ const uint8_t * in,
+ size_t in_len,
+ pgp_hash_alg_t hash_algo,
+ const pgp_ec_key_t * key);
+
+rnp_result_t sm2_decrypt(uint8_t * out,
+ size_t * out_len,
+ const pgp_sm2_encrypted_t *in,
+ const pgp_ec_key_t * key);
+#endif // defined(ENABLE_SM2)
+
+#endif // SM2_H_
diff --git a/src/lib/crypto/sm2_ossl.cpp b/src/lib/crypto/sm2_ossl.cpp
new file mode 100644
index 0000000..4c1eaf1
--- /dev/null
+++ b/src/lib/crypto/sm2_ossl.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 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 <string.h>
+#include "sm2.h"
+#include "utils.h"
+
+rnp_result_t
+sm2_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret)
+{
+ return RNP_ERROR_NOT_IMPLEMENTED;
+}
+
+rnp_result_t
+sm2_sign(rnp::RNG * rng,
+ pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t *key)
+{
+ return RNP_ERROR_NOT_IMPLEMENTED;
+}
+
+rnp_result_t
+sm2_verify(const pgp_ec_signature_t *sig,
+ pgp_hash_alg_t hash_alg,
+ const uint8_t * hash,
+ size_t hash_len,
+ const pgp_ec_key_t * key)
+{
+ return RNP_ERROR_NOT_IMPLEMENTED;
+}
+
+rnp_result_t
+sm2_encrypt(rnp::RNG * rng,
+ pgp_sm2_encrypted_t *out,
+ const uint8_t * in,
+ size_t in_len,
+ pgp_hash_alg_t hash_algo,
+ const pgp_ec_key_t * key)
+{
+ return RNP_ERROR_NOT_IMPLEMENTED;
+}
+
+rnp_result_t
+sm2_decrypt(uint8_t * out,
+ size_t * out_len,
+ const pgp_sm2_encrypted_t *in,
+ const pgp_ec_key_t * key)
+{
+ return RNP_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/src/lib/crypto/symmetric.cpp b/src/lib/crypto/symmetric.cpp
new file mode 100644
index 0000000..aeed784
--- /dev/null
+++ b/src/lib/crypto/symmetric.cpp
@@ -0,0 +1,648 @@
+/*
+ * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is originally derived from software contributed to
+ * The NetBSD Foundation by Alistair Crooks (agc@netbsd.org), and
+ * carried further by Ribose Inc (https://www.ribose.com).
+ *
+ * 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 HOLDERS 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.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+#include "crypto.h"
+#include "config.h"
+#include "defaults.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <botan/ffi.h>
+#include "utils.h"
+
+static const char *
+pgp_sa_to_botan_string(int alg, bool silent = false)
+{
+ switch (alg) {
+#if defined(BOTAN_HAS_IDEA) && defined(ENABLE_IDEA)
+ case PGP_SA_IDEA:
+ return "IDEA";
+#endif
+
+#if defined(BOTAN_HAS_DES)
+ case PGP_SA_TRIPLEDES:
+ return "TripleDES";
+#endif
+
+#if defined(BOTAN_HAS_CAST) && defined(ENABLE_CAST5)
+ case PGP_SA_CAST5:
+ return "CAST-128";
+#endif
+
+#if defined(BOTAN_HAS_BLOWFISH) && defined(ENABLE_BLOWFISH)
+ case PGP_SA_BLOWFISH:
+ return "Blowfish";
+#endif
+
+#if defined(BOTAN_HAS_AES)
+ case PGP_SA_AES_128:
+ return "AES-128";
+ case PGP_SA_AES_192:
+ return "AES-192";
+ case PGP_SA_AES_256:
+ return "AES-256";
+#endif
+
+#if defined(BOTAN_HAS_SM4) && defined(ENABLE_SM2)
+ case PGP_SA_SM4:
+ return "SM4";
+#endif
+
+#if defined(BOTAN_HAS_TWOFISH) && defined(ENABLE_TWOFISH)
+ case PGP_SA_TWOFISH:
+ return "Twofish";
+#endif
+
+#if defined(BOTAN_HAS_CAMELLIA)
+ case PGP_SA_CAMELLIA_128:
+ return "Camellia-128";
+ case PGP_SA_CAMELLIA_192:
+ return "Camellia-192";
+ case PGP_SA_CAMELLIA_256:
+ return "Camellia-256";
+#endif
+
+ default:
+ if (!silent) {
+ RNP_LOG("Unsupported symmetric algorithm %d", alg);
+ }
+ return NULL;
+ }
+}
+
+#if defined(ENABLE_AEAD)
+static bool
+pgp_aead_to_botan_string(pgp_symm_alg_t ealg, pgp_aead_alg_t aalg, char *buf, size_t len)
+{
+ const char *ealg_name = pgp_sa_to_botan_string(ealg);
+ size_t ealg_len;
+
+ if (!ealg_name) {
+ return false;
+ }
+
+ ealg_len = strlen(ealg_name);
+
+ if (len < ealg_len + 5) {
+ RNP_LOG("buffer too small");
+ return false;
+ }
+
+ switch (aalg) {
+ case PGP_AEAD_EAX:
+ memcpy(buf, ealg_name, ealg_len);
+ strncpy(buf + ealg_len, "/EAX", len - ealg_len);
+ break;
+ case PGP_AEAD_OCB:
+ memcpy(buf, ealg_name, ealg_len);
+ strncpy(buf + ealg_len, "/OCB", len - ealg_len);
+ break;
+ default:
+ RNP_LOG("unsupported AEAD alg %d", (int) aalg);
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+bool
+pgp_cipher_cfb_start(pgp_crypt_t * crypt,
+ pgp_symm_alg_t alg,
+ const uint8_t *key,
+ const uint8_t *iv)
+{
+ memset(crypt, 0x0, sizeof(*crypt));
+
+ const char *cipher_name = pgp_sa_to_botan_string(alg);
+ if (!cipher_name) {
+ return false;
+ }
+
+ crypt->alg = alg;
+ crypt->blocksize = pgp_block_size(alg);
+
+ // This shouldn't happen if pgp_sa_to_botan_string returned a ptr
+ if (botan_block_cipher_init(&(crypt->cfb.obj), cipher_name) != 0) {
+ RNP_LOG("Block cipher '%s' not available", cipher_name);
+ return false;
+ }
+
+ const size_t keysize = pgp_key_size(alg);
+
+ if (botan_block_cipher_set_key(crypt->cfb.obj, key, keysize) != 0) {
+ RNP_LOG("Failure setting key on block cipher object");
+ return false;
+ }
+
+ if (iv != NULL) {
+ // Otherwise left as all zeros via memset at start of function
+ memcpy(crypt->cfb.iv, iv, crypt->blocksize);
+ }
+
+ crypt->cfb.remaining = 0;
+
+ return true;
+}
+
+void
+pgp_cipher_cfb_resync(pgp_crypt_t *crypt, const uint8_t *buf)
+{
+ /* iv will be encrypted in the upcoming call to encrypt/decrypt */
+ memcpy(crypt->cfb.iv, buf, crypt->blocksize);
+ crypt->cfb.remaining = 0;
+}
+
+int
+pgp_cipher_cfb_finish(pgp_crypt_t *crypt)
+{
+ if (!crypt) {
+ return 0;
+ }
+ if (crypt->cfb.obj) {
+ botan_block_cipher_destroy(crypt->cfb.obj);
+ crypt->cfb.obj = NULL;
+ }
+ botan_scrub_mem((uint8_t *) crypt, sizeof(*crypt));
+ return 0;
+}
+
+/* we rely on fact that in and out could be the same */
+int
+pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t bytes)
+{
+ uint64_t *in64;
+ uint64_t buf64[512]; // 4KB - page size
+ uint64_t iv64[2];
+ size_t blocks, blockb;
+ unsigned blsize = crypt->blocksize;
+
+ /* encrypting till the block boundary */
+ while (bytes && crypt->cfb.remaining) {
+ *out = *in++ ^ crypt->cfb.iv[blsize - crypt->cfb.remaining];
+ crypt->cfb.iv[blsize - crypt->cfb.remaining] = *out++;
+ crypt->cfb.remaining--;
+ bytes--;
+ }
+
+ if (!bytes) {
+ return 0;
+ }
+
+ /* encrypting full blocks */
+ if (bytes > blsize) {
+ memcpy(iv64, crypt->cfb.iv, blsize);
+ while ((blocks = bytes & ~(blsize - 1)) > 0) {
+ if (blocks > sizeof(buf64)) {
+ blocks = sizeof(buf64);
+ }
+ bytes -= blocks;
+ blockb = blocks;
+ memcpy(buf64, in, blockb);
+ in64 = buf64;
+
+ if (blsize == 16) {
+ blocks >>= 4;
+ while (blocks--) {
+ botan_block_cipher_encrypt_blocks(
+ crypt->cfb.obj, (uint8_t *) iv64, (uint8_t *) iv64, 1);
+ *in64 ^= iv64[0];
+ iv64[0] = *in64++;
+ *in64 ^= iv64[1];
+ iv64[1] = *in64++;
+ }
+ } else {
+ blocks >>= 3;
+ while (blocks--) {
+ botan_block_cipher_encrypt_blocks(
+ crypt->cfb.obj, (uint8_t *) iv64, (uint8_t *) iv64, 1);
+ *in64 ^= iv64[0];
+ iv64[0] = *in64++;
+ }
+ }
+
+ memcpy(out, buf64, blockb);
+ out += blockb;
+ in += blockb;
+ }
+
+ memcpy(crypt->cfb.iv, iv64, blsize);
+ }
+
+ if (!bytes) {
+ return 0;
+ }
+
+ botan_block_cipher_encrypt_blocks(crypt->cfb.obj, crypt->cfb.iv, crypt->cfb.iv, 1);
+ crypt->cfb.remaining = blsize;
+
+ /* encrypting tail */
+ while (bytes) {
+ *out = *in++ ^ crypt->cfb.iv[blsize - crypt->cfb.remaining];
+ crypt->cfb.iv[blsize - crypt->cfb.remaining] = *out++;
+ crypt->cfb.remaining--;
+ bytes--;
+ }
+
+ return 0;
+}
+
+/* we rely on fact that in and out could be the same */
+int
+pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t bytes)
+{
+ /* for better code readability */
+ uint64_t *out64, *in64;
+ uint64_t inbuf64[512]; // 4KB - page size
+ uint64_t outbuf64[512];
+ uint64_t iv64[2];
+ size_t blocks, blockb;
+ unsigned blsize = crypt->blocksize;
+
+ /* decrypting till the block boundary */
+ while (bytes && crypt->cfb.remaining) {
+ uint8_t c = *in++;
+ *out++ = c ^ crypt->cfb.iv[blsize - crypt->cfb.remaining];
+ crypt->cfb.iv[blsize - crypt->cfb.remaining] = c;
+ crypt->cfb.remaining--;
+ bytes--;
+ }
+
+ if (!bytes) {
+ return 0;
+ }
+
+ /* decrypting full blocks */
+ if (bytes > blsize) {
+ memcpy(iv64, crypt->cfb.iv, blsize);
+
+ while ((blocks = bytes & ~(blsize - 1)) > 0) {
+ if (blocks > sizeof(inbuf64)) {
+ blocks = sizeof(inbuf64);
+ }
+ bytes -= blocks;
+ blockb = blocks;
+ memcpy(inbuf64, in, blockb);
+ out64 = outbuf64;
+ in64 = inbuf64;
+
+ if (blsize == 16) {
+ blocks >>= 4;
+ while (blocks--) {
+ botan_block_cipher_encrypt_blocks(
+ crypt->cfb.obj, (uint8_t *) iv64, (uint8_t *) iv64, 1);
+ *out64++ = *in64 ^ iv64[0];
+ iv64[0] = *in64++;
+ *out64++ = *in64 ^ iv64[1];
+ iv64[1] = *in64++;
+ }
+ } else {
+ blocks >>= 3;
+ while (blocks--) {
+ botan_block_cipher_encrypt_blocks(
+ crypt->cfb.obj, (uint8_t *) iv64, (uint8_t *) iv64, 1);
+ *out64++ = *in64 ^ iv64[0];
+ iv64[0] = *in64++;
+ }
+ }
+
+ memcpy(out, outbuf64, blockb);
+ out += blockb;
+ in += blockb;
+ }
+
+ memcpy(crypt->cfb.iv, iv64, blsize);
+ }
+
+ if (!bytes) {
+ return 0;
+ }
+
+ botan_block_cipher_encrypt_blocks(crypt->cfb.obj, crypt->cfb.iv, crypt->cfb.iv, 1);
+ crypt->cfb.remaining = blsize;
+
+ /* decrypting tail */
+ while (bytes) {
+ uint8_t c = *in++;
+ *out++ = c ^ crypt->cfb.iv[blsize - crypt->cfb.remaining];
+ crypt->cfb.iv[blsize - crypt->cfb.remaining] = c;
+ crypt->cfb.remaining--;
+ bytes--;
+ }
+
+ return 0;
+}
+
+size_t
+pgp_cipher_block_size(pgp_crypt_t *crypt)
+{
+ return crypt->blocksize;
+}
+
+unsigned
+pgp_block_size(pgp_symm_alg_t alg)
+{
+ switch (alg) {
+ case PGP_SA_IDEA:
+ case PGP_SA_TRIPLEDES:
+ case PGP_SA_CAST5:
+ case PGP_SA_BLOWFISH:
+ return 8;
+ case PGP_SA_AES_128:
+ case PGP_SA_AES_192:
+ case PGP_SA_AES_256:
+ case PGP_SA_TWOFISH:
+ case PGP_SA_CAMELLIA_128:
+ case PGP_SA_CAMELLIA_192:
+ case PGP_SA_CAMELLIA_256:
+ case PGP_SA_SM4:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+unsigned
+pgp_key_size(pgp_symm_alg_t alg)
+{
+ /* Update MAX_SYMM_KEY_SIZE after adding algorithm
+ * with bigger key size.
+ */
+ static_assert(32 == MAX_SYMM_KEY_SIZE, "MAX_SYMM_KEY_SIZE must be updated");
+
+ switch (alg) {
+ case PGP_SA_IDEA:
+ case PGP_SA_CAST5:
+ case PGP_SA_BLOWFISH:
+ case PGP_SA_AES_128:
+ case PGP_SA_CAMELLIA_128:
+ case PGP_SA_SM4:
+ return 16;
+
+ case PGP_SA_TRIPLEDES:
+ case PGP_SA_AES_192:
+ case PGP_SA_CAMELLIA_192:
+ return 24;
+
+ case PGP_SA_TWOFISH:
+ case PGP_SA_AES_256:
+ case PGP_SA_CAMELLIA_256:
+ return 32;
+
+ default:
+ return 0;
+ }
+}
+
+bool
+pgp_is_sa_supported(int alg, bool silent)
+{
+ return pgp_sa_to_botan_string(alg, silent);
+}
+
+#if defined(ENABLE_AEAD)
+bool
+pgp_cipher_aead_init(pgp_crypt_t * crypt,
+ pgp_symm_alg_t ealg,
+ pgp_aead_alg_t aalg,
+ const uint8_t *key,
+ bool decrypt)
+{
+ char cipher_name[32];
+ uint32_t flags;
+
+ memset(crypt, 0x0, sizeof(*crypt));
+
+ if (!pgp_aead_to_botan_string(ealg, aalg, cipher_name, sizeof(cipher_name))) {
+ return false;
+ }
+
+ crypt->alg = ealg;
+ crypt->blocksize = pgp_block_size(ealg);
+ crypt->aead.alg = aalg;
+ crypt->aead.decrypt = decrypt;
+ crypt->aead.taglen = PGP_AEAD_EAX_OCB_TAG_LEN; /* it's the same for EAX and OCB */
+
+ flags = decrypt ? BOTAN_CIPHER_INIT_FLAG_DECRYPT : BOTAN_CIPHER_INIT_FLAG_ENCRYPT;
+
+ if (botan_cipher_init(&(crypt->aead.obj), cipher_name, flags)) {
+ RNP_LOG("cipher %s is not available", cipher_name);
+ return false;
+ }
+
+ if (botan_cipher_set_key(crypt->aead.obj, key, (size_t) pgp_key_size(ealg))) {
+ RNP_LOG("failed to set key");
+ return false;
+ }
+
+ if (botan_cipher_get_update_granularity(crypt->aead.obj, &crypt->aead.granularity)) {
+ RNP_LOG("failed to get update granularity");
+ return false;
+ }
+
+ return true;
+}
+
+size_t
+pgp_cipher_aead_granularity(pgp_crypt_t *crypt)
+{
+ return crypt->aead.granularity;
+}
+#endif
+
+size_t
+pgp_cipher_aead_nonce_len(pgp_aead_alg_t aalg)
+{
+ switch (aalg) {
+ case PGP_AEAD_EAX:
+ return PGP_AEAD_EAX_NONCE_LEN;
+ case PGP_AEAD_OCB:
+ return PGP_AEAD_OCB_NONCE_LEN;
+ default:
+ return 0;
+ }
+}
+
+size_t
+pgp_cipher_aead_tag_len(pgp_aead_alg_t aalg)
+{
+ switch (aalg) {
+ case PGP_AEAD_EAX:
+ case PGP_AEAD_OCB:
+ return PGP_AEAD_EAX_OCB_TAG_LEN;
+ default:
+ return 0;
+ }
+}
+
+#if defined(ENABLE_AEAD)
+bool
+pgp_cipher_aead_set_ad(pgp_crypt_t *crypt, const uint8_t *ad, size_t len)
+{
+ return botan_cipher_set_associated_data(crypt->aead.obj, ad, len) == 0;
+}
+
+bool
+pgp_cipher_aead_start(pgp_crypt_t *crypt, const uint8_t *nonce, size_t len)
+{
+ return botan_cipher_start(crypt->aead.obj, nonce, len) == 0;
+}
+
+bool
+pgp_cipher_aead_update(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t len)
+{
+ size_t outwr = 0;
+ size_t inread = 0;
+
+ if (len % crypt->aead.granularity) {
+ RNP_LOG("aead wrong update len");
+ return false;
+ }
+
+ if (botan_cipher_update(crypt->aead.obj, 0, out, len, &outwr, in, len, &inread) != 0) {
+ RNP_LOG("aead update failed");
+ return false;
+ }
+
+ if ((outwr != len) || (inread != len)) {
+ RNP_LOG("wrong aead usage");
+ return false;
+ }
+
+ return true;
+}
+
+void
+pgp_cipher_aead_reset(pgp_crypt_t *crypt)
+{
+ botan_cipher_reset(crypt->aead.obj);
+}
+
+bool
+pgp_cipher_aead_finish(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t len)
+{
+ uint32_t flags = BOTAN_CIPHER_UPDATE_FLAG_FINAL;
+ size_t inread = 0;
+ size_t outwr = 0;
+ int res;
+
+ if (crypt->aead.decrypt) {
+ size_t datalen = len - crypt->aead.taglen;
+ /* for decryption we should have tag for the final update call */
+ res =
+ botan_cipher_update(crypt->aead.obj, flags, out, datalen, &outwr, in, len, &inread);
+ if (res != 0) {
+ if (res != BOTAN_FFI_ERROR_BAD_MAC) {
+ RNP_LOG("aead finish failed: %d", res);
+ }
+ return false;
+ }
+
+ if ((outwr != datalen) || (inread != len)) {
+ RNP_LOG("wrong decrypt aead finish usage");
+ return false;
+ }
+ } else {
+ /* for encryption tag will be generated */
+ size_t outlen = len + crypt->aead.taglen;
+ if (botan_cipher_update(
+ crypt->aead.obj, flags, out, outlen, &outwr, in, len, &inread) != 0) {
+ RNP_LOG("aead finish failed");
+ return false;
+ }
+
+ if ((outwr != outlen) || (inread != len)) {
+ RNP_LOG("wrong encrypt aead finish usage");
+ return false;
+ }
+ }
+
+ pgp_cipher_aead_reset(crypt);
+ return true;
+}
+
+void
+pgp_cipher_aead_destroy(pgp_crypt_t *crypt)
+{
+ botan_cipher_destroy(crypt->aead.obj);
+}
+
+size_t
+pgp_cipher_aead_nonce(pgp_aead_alg_t aalg, const uint8_t *iv, uint8_t *nonce, size_t index)
+{
+ switch (aalg) {
+ case PGP_AEAD_EAX:
+ /* The nonce for EAX mode is computed by treating the starting
+ initialization vector as a 16-octet, big-endian value and
+ exclusive-oring the low eight octets of it with the chunk index.
+ */
+ memcpy(nonce, iv, PGP_AEAD_EAX_NONCE_LEN);
+ for (int i = 15; (i > 7) && index; i--) {
+ nonce[i] ^= index & 0xff;
+ index = index >> 8;
+ }
+ return PGP_AEAD_EAX_NONCE_LEN;
+ case PGP_AEAD_OCB:
+ /* The nonce for a chunk of chunk index "i" in OCB processing is defined as:
+ OCB-Nonce_{i} = IV[1..120] xor i
+ */
+ memcpy(nonce, iv, PGP_AEAD_OCB_NONCE_LEN);
+ for (int i = 14; (i >= 0) && index; i--) {
+ nonce[i] ^= index & 0xff;
+ index = index >> 8;
+ }
+ return PGP_AEAD_OCB_NONCE_LEN;
+ default:
+ return 0;
+ }
+}
+#endif // ENABLE_AEAD
diff --git a/src/lib/crypto/symmetric.h b/src/lib/crypto/symmetric.h
new file mode 100644
index 0000000..a50fe9a
--- /dev/null
+++ b/src/lib/crypto/symmetric.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2017, 2021 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is originally derived from software contributed to
+ * The NetBSD Foundation by Alistair Crooks (agc@netbsd.org), and
+ * carried further by Ribose Inc (https://www.ribose.com).
+ *
+ * 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 HOLDERS 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.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+#ifndef SYMMETRIC_CRYPTO_H_
+#define SYMMETRIC_CRYPTO_H_
+
+#include <repgp/repgp_def.h>
+#include "crypto/rng.h"
+#include "config.h"
+#ifdef CRYPTO_BACKEND_OPENSSL
+#include <openssl/evp.h>
+#include "mem.h"
+#endif
+
+/* Nonce len for AEAD/EAX */
+#define PGP_AEAD_EAX_NONCE_LEN 16
+
+/* Nonce len for AEAD/OCB */
+#define PGP_AEAD_OCB_NONCE_LEN 15
+
+/* Maximum AEAD nonce length */
+#define PGP_AEAD_MAX_NONCE_LEN 16
+
+/* Authentication tag len for AEAD/EAX and AEAD/OCB */
+#define PGP_AEAD_EAX_OCB_TAG_LEN 16
+
+/* Maximal size of symmetric key */
+#define MAX_SYMM_KEY_SIZE 32
+
+/* Maximum AEAD tag length */
+#define PGP_AEAD_MAX_TAG_LEN 16
+
+/* Maximum authenticated data length for AEAD */
+#define PGP_AEAD_MAX_AD_LEN 32
+
+struct pgp_crypt_cfb_param_t {
+#ifdef CRYPTO_BACKEND_BOTAN
+ struct botan_block_cipher_struct *obj;
+#endif
+#ifdef CRYPTO_BACKEND_OPENSSL
+ EVP_CIPHER_CTX *obj;
+#endif
+ size_t remaining;
+ uint8_t iv[PGP_MAX_BLOCK_SIZE];
+};
+
+struct pgp_crypt_aead_param_t {
+#ifdef CRYPTO_BACKEND_BOTAN
+ struct botan_cipher_struct *obj;
+#endif
+#ifdef CRYPTO_BACKEND_OPENSSL
+ EVP_CIPHER_CTX * obj;
+ const EVP_CIPHER * cipher;
+ rnp::secure_vector<uint8_t> *key;
+ uint8_t ad[PGP_AEAD_MAX_AD_LEN];
+ size_t ad_len;
+ size_t n_len;
+#endif
+ pgp_aead_alg_t alg;
+ bool decrypt;
+ size_t granularity;
+ size_t taglen;
+};
+
+/** pgp_crypt_t */
+typedef struct pgp_crypt_t {
+ union {
+ struct pgp_crypt_cfb_param_t cfb;
+#if defined(ENABLE_AEAD)
+ struct pgp_crypt_aead_param_t aead;
+#endif
+ };
+
+ pgp_symm_alg_t alg;
+ size_t blocksize;
+ rnp::RNG * rng;
+} pgp_crypt_t;
+
+unsigned pgp_block_size(pgp_symm_alg_t);
+unsigned pgp_key_size(pgp_symm_alg_t);
+bool pgp_is_sa_supported(int alg, bool silent = false);
+size_t pgp_cipher_block_size(pgp_crypt_t *crypt);
+
+/**
+ * Initialize a cipher object.
+ * @param iv if null an all-zero IV is assumed
+ */
+bool pgp_cipher_cfb_start(pgp_crypt_t * crypt,
+ pgp_symm_alg_t alg,
+ const uint8_t *key,
+ const uint8_t *iv);
+
+// Deallocate all storage
+int pgp_cipher_cfb_finish(pgp_crypt_t *crypt);
+// CFB encryption/decryption
+int pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t len);
+int pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t len);
+
+void pgp_cipher_cfb_resync(pgp_crypt_t *crypt, const uint8_t *buf);
+
+#if defined(ENABLE_AEAD)
+/** @brief Initialize AEAD cipher instance
+ * @param crypt pgp crypto object
+ * @param ealg symmetric encryption algorithm to use together with AEAD cipher mode
+ * @param aalg AEAD cipher mode. Only EAX is supported now
+ * @param key key buffer. Number of key bytes is determined by ealg.
+ * @param decrypt true for decryption, or false for encryption
+ * @return true on success or false otherwise.
+ */
+bool pgp_cipher_aead_init(pgp_crypt_t * crypt,
+ pgp_symm_alg_t ealg,
+ pgp_aead_alg_t aalg,
+ const uint8_t *key,
+ bool decrypt);
+
+/** @brief Return the AEAD cipher update granularity. Botan FFI will consume chunks which are
+ * multiple of this value. See the description of pgp_cipher_aead_update()
+ * @param crypt initialized AEAD crypto
+ * @return Update granularity value in bytes
+ */
+size_t pgp_cipher_aead_granularity(pgp_crypt_t *crypt);
+#endif
+
+/** @brief Return the AEAD cipher tag length
+ * @param aalg OpenPGP AEAD algorithm
+ * @return length of authentication tag in bytes, or 0 for unknown algorithm
+ */
+size_t pgp_cipher_aead_tag_len(pgp_aead_alg_t aalg);
+
+/** @brief Return the AEAD cipher nonce and IV length
+ * @param aalg OpenPGP AEAD algorithm
+ * @return length of nonce in bytes, or 0 for unknown algorithm
+ */
+size_t pgp_cipher_aead_nonce_len(pgp_aead_alg_t aalg);
+
+#if defined(ENABLE_AEAD)
+/** @brief Set associated data
+ * @param crypt initialized AEAD crypto
+ * @param ad buffer with data. Cannot be NULL.
+ * @param len number of bytes in ad
+ * @return true on success or false otherwise.
+ */
+bool pgp_cipher_aead_set_ad(pgp_crypt_t *crypt, const uint8_t *ad, size_t len);
+
+/** @brief Start the cipher operation, using the given nonce
+ * @param crypt initialized AEAD crypto
+ * @param nonce buffer with nonce, cannot be NULL.
+ * @param len number of bytes in nonce. Must conform to the cipher properties.
+ * @return true on success or false otherwise.
+ */
+bool pgp_cipher_aead_start(pgp_crypt_t *crypt, const uint8_t *nonce, size_t len);
+
+/** @brief Update the cipher. This should be called for non-final data, respecting the
+ * update granularity of underlying botan cipher. Now it is 256 bytes.
+ * @param crypt initialized AEAD crypto
+ * @param out buffer to put processed data. Cannot be NULL, and should be large enough to put
+ * len bytes
+ * @param in buffer with input, cannot be NULL
+ * @param len number of bytes to process. Should be multiple of update granularity.
+ * @return true on success or false otherwise. On success exactly len processed bytes will be
+ * stored in out buffer
+ */
+bool pgp_cipher_aead_update(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t len);
+
+/** @brief Do final update on the cipher. For decryption final chunk should contain at least
+ * authentication tag, for encryption input could be zero-size.
+ * @param crypt initialized AEAD crypto
+ * @param out buffer to put processed data. For decryption it should be large enough to put
+ * len bytes minus authentication tag, for encryption it should be large enough to
+ * put len byts plus a tag.
+ * @param in buffer with input, if any. May be NULL for encryption, then len should be zero.
+ * For decryption it should contain at least authentication tag.
+ * @param len number of input bytes bytes
+ * @return true on success or false otherwise. On success for decryption len minus tag size
+ * bytes will be stored in out, for encryption out will contain len bytes plus
+ * tag size.
+ */
+bool pgp_cipher_aead_finish(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t len);
+
+/** @brief Reset the AEAD cipher's state, calling the finish() and ignoring the result
+ * @param crypt initialized AEAD crypto
+ */
+void pgp_cipher_aead_reset(pgp_crypt_t *crypt);
+
+/** @brief Destroy the cipher object, deallocating all the memory.
+ * @param crypt initialized AEAD crypto
+ */
+void pgp_cipher_aead_destroy(pgp_crypt_t *crypt);
+
+/** @brief Helper function to set AEAD nonce for the chunk by its index.
+ * iv and nonce should be large enough to hold max nonce bytes
+ * @param aalg AEAD algorithm used
+ * @param iv Initial vector for the message, must have 16 bytes of data
+ * @param nonce Nonce to fill up, should have space for 16 bytes of data
+ * @param index Chunk's index
+ * @return Length of the nonce, or 0 if algorithm is unknown
+ */
+size_t pgp_cipher_aead_nonce(pgp_aead_alg_t aalg,
+ const uint8_t *iv,
+ uint8_t * nonce,
+ size_t index);
+#endif // ENABLE_AEAD
+
+#endif
diff --git a/src/lib/crypto/symmetric_ossl.cpp b/src/lib/crypto/symmetric_ossl.cpp
new file mode 100644
index 0000000..98e90ed
--- /dev/null
+++ b/src/lib/crypto/symmetric_ossl.cpp
@@ -0,0 +1,644 @@
+/*-
+ * Copyright (c) 2021 Ribose Inc.
+ * 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 HOLDERS 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 "crypto.h"
+#include "config.h"
+#include "defaults.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include "mem.h"
+#include "utils.h"
+
+static const char *
+pgp_sa_to_openssl_string(int alg, bool silent = false)
+{
+ switch (alg) {
+#if defined(ENABLE_IDEA)
+ case PGP_SA_IDEA:
+ return "idea-ecb";
+#endif
+ case PGP_SA_TRIPLEDES:
+ return "des-ede3";
+#if defined(ENABLE_CAST5)
+ case PGP_SA_CAST5:
+ return "cast5-ecb";
+#endif
+#if defined(ENABLE_BLOWFISH)
+ case PGP_SA_BLOWFISH:
+ return "bf-ecb";
+#endif
+ case PGP_SA_AES_128:
+ return "aes-128-ecb";
+ case PGP_SA_AES_192:
+ return "aes-192-ecb";
+ case PGP_SA_AES_256:
+ return "aes-256-ecb";
+#if defined(ENABLE_SM2)
+ case PGP_SA_SM4:
+ return "sm4-ecb";
+#endif
+ case PGP_SA_CAMELLIA_128:
+ return "camellia-128-ecb";
+ case PGP_SA_CAMELLIA_192:
+ return "camellia-192-ecb";
+ case PGP_SA_CAMELLIA_256:
+ return "camellia-256-ecb";
+ default:
+ if (!silent) {
+ RNP_LOG("Unsupported symmetric algorithm %d", alg);
+ }
+ return NULL;
+ }
+}
+
+bool
+pgp_cipher_cfb_start(pgp_crypt_t * crypt,
+ pgp_symm_alg_t alg,
+ const uint8_t *key,
+ const uint8_t *iv)
+{
+ memset(crypt, 0x0, sizeof(*crypt));
+
+ const char *cipher_name = pgp_sa_to_openssl_string(alg);
+ if (!cipher_name) {
+ RNP_LOG("Unsupported algorithm: %d", alg);
+ return false;
+ }
+
+ const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_name);
+ if (!cipher) {
+ RNP_LOG("Cipher %s is not supported by OpenSSL.", cipher_name);
+ return false;
+ }
+
+ crypt->alg = alg;
+ crypt->blocksize = pgp_block_size(alg);
+
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ int res = EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv);
+ if (res != 1) {
+ RNP_LOG("Failed to initialize cipher.");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+ crypt->cfb.obj = ctx;
+
+ if (iv) {
+ // Otherwise left as all zeros via memset at start of function
+ memcpy(crypt->cfb.iv, iv, crypt->blocksize);
+ }
+
+ crypt->cfb.remaining = 0;
+ return true;
+}
+
+void
+pgp_cipher_cfb_resync(pgp_crypt_t *crypt, const uint8_t *buf)
+{
+ /* iv will be encrypted in the upcoming call to encrypt/decrypt */
+ memcpy(crypt->cfb.iv, buf, crypt->blocksize);
+ crypt->cfb.remaining = 0;
+}
+
+int
+pgp_cipher_cfb_finish(pgp_crypt_t *crypt)
+{
+ if (!crypt) {
+ return 0;
+ }
+ if (crypt->cfb.obj) {
+ EVP_CIPHER_CTX_free(crypt->cfb.obj);
+ crypt->cfb.obj = NULL;
+ }
+ OPENSSL_cleanse((uint8_t *) crypt, sizeof(*crypt));
+ return 0;
+}
+
+/* we rely on fact that in and out could be the same */
+int
+pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t bytes)
+{
+ uint64_t *in64;
+ uint64_t buf64[512]; // 4KB - page size
+ uint64_t iv64[2];
+ size_t blocks, blockb;
+ unsigned blsize = crypt->blocksize;
+
+ /* encrypting till the block boundary */
+ while (bytes && crypt->cfb.remaining) {
+ *out = *in++ ^ crypt->cfb.iv[blsize - crypt->cfb.remaining];
+ crypt->cfb.iv[blsize - crypt->cfb.remaining] = *out++;
+ crypt->cfb.remaining--;
+ bytes--;
+ }
+
+ if (!bytes) {
+ return 0;
+ }
+
+ /* encrypting full blocks */
+ if (bytes > blsize) {
+ memcpy(iv64, crypt->cfb.iv, blsize);
+ while ((blocks = bytes & ~(blsize - 1)) > 0) {
+ if (blocks > sizeof(buf64)) {
+ blocks = sizeof(buf64);
+ }
+ bytes -= blocks;
+ blockb = blocks;
+ memcpy(buf64, in, blockb);
+ in64 = buf64;
+
+ if (blsize == 16) {
+ blocks >>= 4;
+ while (blocks--) {
+ int outlen = 16;
+ EVP_EncryptUpdate(
+ crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 16);
+ if (outlen != 16) {
+ RNP_LOG("Bad outlen: must be 16");
+ }
+ *in64 ^= iv64[0];
+ iv64[0] = *in64++;
+ *in64 ^= iv64[1];
+ iv64[1] = *in64++;
+ }
+ } else {
+ blocks >>= 3;
+ while (blocks--) {
+ int outlen = 8;
+ EVP_EncryptUpdate(
+ crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 8);
+ if (outlen != 8) {
+ RNP_LOG("Bad outlen: must be 8");
+ }
+ *in64 ^= iv64[0];
+ iv64[0] = *in64++;
+ }
+ }
+
+ memcpy(out, buf64, blockb);
+ out += blockb;
+ in += blockb;
+ }
+
+ memcpy(crypt->cfb.iv, iv64, blsize);
+ }
+
+ if (!bytes) {
+ return 0;
+ }
+
+ int outlen = blsize;
+ EVP_EncryptUpdate(crypt->cfb.obj, crypt->cfb.iv, &outlen, crypt->cfb.iv, (int) blsize);
+ if (outlen != (int) blsize) {
+ RNP_LOG("Bad outlen: must be %u", blsize);
+ }
+ crypt->cfb.remaining = blsize;
+
+ /* encrypting tail */
+ while (bytes) {
+ *out = *in++ ^ crypt->cfb.iv[blsize - crypt->cfb.remaining];
+ crypt->cfb.iv[blsize - crypt->cfb.remaining] = *out++;
+ crypt->cfb.remaining--;
+ bytes--;
+ }
+
+ return 0;
+}
+
+/* we rely on fact that in and out could be the same */
+int
+pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t bytes)
+{
+ /* for better code readability */
+ uint64_t *out64, *in64;
+ uint64_t inbuf64[512]; // 4KB - page size
+ uint64_t outbuf64[512];
+ uint64_t iv64[2];
+ size_t blocks, blockb;
+ unsigned blsize = crypt->blocksize;
+
+ /* decrypting till the block boundary */
+ while (bytes && crypt->cfb.remaining) {
+ uint8_t c = *in++;
+ *out++ = c ^ crypt->cfb.iv[blsize - crypt->cfb.remaining];
+ crypt->cfb.iv[blsize - crypt->cfb.remaining] = c;
+ crypt->cfb.remaining--;
+ bytes--;
+ }
+
+ if (!bytes) {
+ return 0;
+ }
+
+ /* decrypting full blocks */
+ if (bytes > blsize) {
+ memcpy(iv64, crypt->cfb.iv, blsize);
+
+ while ((blocks = bytes & ~(blsize - 1)) > 0) {
+ if (blocks > sizeof(inbuf64)) {
+ blocks = sizeof(inbuf64);
+ }
+ bytes -= blocks;
+ blockb = blocks;
+ memcpy(inbuf64, in, blockb);
+ out64 = outbuf64;
+ in64 = inbuf64;
+
+ if (blsize == 16) {
+ blocks >>= 4;
+ while (blocks--) {
+ int outlen = 16;
+ EVP_EncryptUpdate(
+ crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 16);
+ if (outlen != 16) {
+ RNP_LOG("Bad outlen: must be 16");
+ }
+ *out64++ = *in64 ^ iv64[0];
+ iv64[0] = *in64++;
+ *out64++ = *in64 ^ iv64[1];
+ iv64[1] = *in64++;
+ }
+ } else {
+ blocks >>= 3;
+ while (blocks--) {
+ int outlen = 8;
+ EVP_EncryptUpdate(
+ crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 8);
+ if (outlen != 8) {
+ RNP_LOG("Bad outlen: must be 8");
+ }
+ *out64++ = *in64 ^ iv64[0];
+ iv64[0] = *in64++;
+ }
+ }
+
+ memcpy(out, outbuf64, blockb);
+ out += blockb;
+ in += blockb;
+ }
+
+ memcpy(crypt->cfb.iv, iv64, blsize);
+ }
+
+ if (!bytes) {
+ return 0;
+ }
+
+ int outlen = blsize;
+ EVP_EncryptUpdate(crypt->cfb.obj, crypt->cfb.iv, &outlen, crypt->cfb.iv, (int) blsize);
+ if (outlen != (int) blsize) {
+ RNP_LOG("Bad outlen: must be %u", blsize);
+ }
+ crypt->cfb.remaining = blsize;
+
+ /* decrypting tail */
+ while (bytes) {
+ uint8_t c = *in++;
+ *out++ = c ^ crypt->cfb.iv[blsize - crypt->cfb.remaining];
+ crypt->cfb.iv[blsize - crypt->cfb.remaining] = c;
+ crypt->cfb.remaining--;
+ bytes--;
+ }
+
+ return 0;
+}
+
+size_t
+pgp_cipher_block_size(pgp_crypt_t *crypt)
+{
+ return crypt->blocksize;
+}
+
+unsigned
+pgp_block_size(pgp_symm_alg_t alg)
+{
+ switch (alg) {
+ case PGP_SA_IDEA:
+ case PGP_SA_TRIPLEDES:
+ case PGP_SA_CAST5:
+ case PGP_SA_BLOWFISH:
+ return 8;
+ case PGP_SA_AES_128:
+ case PGP_SA_AES_192:
+ case PGP_SA_AES_256:
+ case PGP_SA_TWOFISH:
+ case PGP_SA_CAMELLIA_128:
+ case PGP_SA_CAMELLIA_192:
+ case PGP_SA_CAMELLIA_256:
+ case PGP_SA_SM4:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+unsigned
+pgp_key_size(pgp_symm_alg_t alg)
+{
+ /* Update MAX_SYMM_KEY_SIZE after adding algorithm
+ * with bigger key size.
+ */
+ static_assert(32 == MAX_SYMM_KEY_SIZE, "MAX_SYMM_KEY_SIZE must be updated");
+
+ switch (alg) {
+ case PGP_SA_IDEA:
+ case PGP_SA_CAST5:
+ case PGP_SA_BLOWFISH:
+ case PGP_SA_AES_128:
+ case PGP_SA_CAMELLIA_128:
+ case PGP_SA_SM4:
+ return 16;
+ case PGP_SA_TRIPLEDES:
+ case PGP_SA_AES_192:
+ case PGP_SA_CAMELLIA_192:
+ return 24;
+ case PGP_SA_TWOFISH:
+ case PGP_SA_AES_256:
+ case PGP_SA_CAMELLIA_256:
+ return 32;
+ default:
+ return 0;
+ }
+}
+
+bool
+pgp_is_sa_supported(int alg, bool silent)
+{
+ return pgp_sa_to_openssl_string(alg, silent);
+}
+
+#if defined(ENABLE_AEAD)
+
+static const char *
+openssl_aead_name(pgp_symm_alg_t ealg, pgp_aead_alg_t aalg)
+{
+ switch (aalg) {
+ case PGP_AEAD_OCB:
+ break;
+ default:
+ RNP_LOG("Only OCB mode is supported by the OpenSSL backend.");
+ return NULL;
+ }
+ switch (ealg) {
+ case PGP_SA_AES_128:
+ return "AES-128-OCB";
+ case PGP_SA_AES_192:
+ return "AES-192-OCB";
+ case PGP_SA_AES_256:
+ return "AES-256-OCB";
+ default:
+ RNP_LOG("Only AES-OCB is supported by the OpenSSL backend.");
+ return NULL;
+ }
+}
+
+bool
+pgp_cipher_aead_init(pgp_crypt_t * crypt,
+ pgp_symm_alg_t ealg,
+ pgp_aead_alg_t aalg,
+ const uint8_t *key,
+ bool decrypt)
+{
+ memset(crypt, 0x0, sizeof(*crypt));
+ /* OpenSSL backend currently supports only AES-OCB */
+ const char *algname = openssl_aead_name(ealg, aalg);
+ if (!algname) {
+ return false;
+ }
+ auto cipher = EVP_get_cipherbyname(algname);
+ if (!cipher) {
+ RNP_LOG("Cipher %s is not supported.", algname);
+ return false;
+ }
+ /* Create and setup context */
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ if (!ctx) {
+ RNP_LOG("Failed to create cipher context: %lu", ERR_peek_last_error());
+ return false;
+ }
+
+ crypt->aead.key = new rnp::secure_vector<uint8_t>(key, key + pgp_key_size(ealg));
+ crypt->alg = ealg;
+ crypt->blocksize = pgp_block_size(ealg);
+ crypt->aead.cipher = cipher;
+ crypt->aead.obj = ctx;
+ crypt->aead.alg = aalg;
+ crypt->aead.decrypt = decrypt;
+ crypt->aead.granularity = crypt->blocksize;
+ crypt->aead.taglen = PGP_AEAD_EAX_OCB_TAG_LEN;
+ crypt->aead.ad_len = 0;
+ crypt->aead.n_len = pgp_cipher_aead_nonce_len(aalg);
+ return true;
+}
+
+size_t
+pgp_cipher_aead_granularity(pgp_crypt_t *crypt)
+{
+ return crypt->aead.granularity;
+}
+#endif
+
+size_t
+pgp_cipher_aead_nonce_len(pgp_aead_alg_t aalg)
+{
+ switch (aalg) {
+ case PGP_AEAD_EAX:
+ return PGP_AEAD_EAX_NONCE_LEN;
+ case PGP_AEAD_OCB:
+ return PGP_AEAD_OCB_NONCE_LEN;
+ default:
+ return 0;
+ }
+}
+
+size_t
+pgp_cipher_aead_tag_len(pgp_aead_alg_t aalg)
+{
+ switch (aalg) {
+ case PGP_AEAD_EAX:
+ case PGP_AEAD_OCB:
+ return PGP_AEAD_EAX_OCB_TAG_LEN;
+ default:
+ return 0;
+ }
+}
+
+#if defined(ENABLE_AEAD)
+bool
+pgp_cipher_aead_set_ad(pgp_crypt_t *crypt, const uint8_t *ad, size_t len)
+{
+ assert(len <= sizeof(crypt->aead.ad));
+ memcpy(crypt->aead.ad, ad, len);
+ crypt->aead.ad_len = len;
+ return true;
+}
+
+bool
+pgp_cipher_aead_start(pgp_crypt_t *crypt, const uint8_t *nonce, size_t len)
+{
+ auto &aead = crypt->aead;
+ auto ctx = aead.obj;
+ int enc = aead.decrypt ? 0 : 1;
+ assert(len == aead.n_len);
+ EVP_CIPHER_CTX_reset(ctx);
+ if (EVP_CipherInit_ex(ctx, aead.cipher, NULL, NULL, NULL, enc) != 1) {
+ RNP_LOG("Failed to initialize cipher: %lu", ERR_peek_last_error());
+ return false;
+ }
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, aead.n_len, NULL) != 1) {
+ RNP_LOG("Failed to set nonce length: %lu", ERR_peek_last_error());
+ return false;
+ }
+ if (EVP_CipherInit_ex(ctx, NULL, NULL, aead.key->data(), nonce, enc) != 1) {
+ RNP_LOG("Failed to start cipher: %lu", ERR_peek_last_error());
+ return false;
+ }
+ int adlen = 0;
+ if (EVP_CipherUpdate(ctx, NULL, &adlen, aead.ad, aead.ad_len) != 1) {
+ RNP_LOG("Failed to set AD: %lu", ERR_peek_last_error());
+ return false;
+ }
+ return true;
+}
+
+bool
+pgp_cipher_aead_update(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t len)
+{
+ if (!len) {
+ return true;
+ }
+ int out_len = 0;
+ bool res = EVP_CipherUpdate(crypt->aead.obj, out, &out_len, in, len) == 1;
+ if (!res) {
+ RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error());
+ }
+ assert(out_len == (int) len);
+ return res;
+}
+
+void
+pgp_cipher_aead_reset(pgp_crypt_t *crypt)
+{
+ /* Do nothing as subsequent pgp_cipher_aead_start() call will reset context */
+}
+
+bool
+pgp_cipher_aead_finish(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size_t len)
+{
+ auto &aead = crypt->aead;
+ auto ctx = aead.obj;
+ if (aead.decrypt) {
+ assert(len >= aead.taglen);
+ if (len < aead.taglen) {
+ RNP_LOG("Invalid state: too few input bytes.");
+ return false;
+ }
+ size_t data_len = len - aead.taglen;
+ int out_len = 0;
+ if (EVP_CipherUpdate(ctx, out, &out_len, in, data_len) != 1) {
+ RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error());
+ return false;
+ }
+ uint8_t tag[PGP_AEAD_MAX_TAG_LEN] = {0};
+ memcpy(tag, in + data_len, aead.taglen);
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, aead.taglen, tag) != 1) {
+ RNP_LOG("Failed to set tag: %lu", ERR_peek_last_error());
+ return false;
+ }
+ int out_len2 = 0;
+ if (EVP_CipherFinal_ex(ctx, out + out_len, &out_len2) != 1) {
+ /* Zero value if auth tag is incorrect */
+ if (ERR_peek_last_error()) {
+ RNP_LOG("Failed to finish AEAD decryption: %lu", ERR_peek_last_error());
+ }
+ return false;
+ }
+ assert(out_len + out_len2 == (int) (len - aead.taglen));
+ } else {
+ int out_len = 0;
+ if (EVP_CipherUpdate(ctx, out, &out_len, in, len) != 1) {
+ RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error());
+ return false;
+ }
+ int out_len2 = 0;
+ if (EVP_CipherFinal_ex(ctx, out + out_len, &out_len2) != 1) {
+ RNP_LOG("Failed to finish AEAD encryption: %lu", ERR_peek_last_error());
+ return false;
+ }
+ assert(out_len + out_len2 == (int) len);
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, aead.taglen, out + len) != 1) {
+ RNP_LOG("Failed to get tag: %lu", ERR_peek_last_error());
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+pgp_cipher_aead_destroy(pgp_crypt_t *crypt)
+{
+ if (crypt->aead.obj) {
+ EVP_CIPHER_CTX_free(crypt->aead.obj);
+ }
+ delete crypt->aead.key;
+ memset(crypt, 0x0, sizeof(*crypt));
+}
+
+size_t
+pgp_cipher_aead_nonce(pgp_aead_alg_t aalg, const uint8_t *iv, uint8_t *nonce, size_t index)
+{
+ switch (aalg) {
+ case PGP_AEAD_EAX:
+ /* The nonce for EAX mode is computed by treating the starting
+ initialization vector as a 16-octet, big-endian value and
+ exclusive-oring the low eight octets of it with the chunk index.
+ */
+ memcpy(nonce, iv, PGP_AEAD_EAX_NONCE_LEN);
+ for (int i = 15; (i > 7) && index; i--) {
+ nonce[i] ^= index & 0xff;
+ index = index >> 8;
+ }
+ return PGP_AEAD_EAX_NONCE_LEN;
+ case PGP_AEAD_OCB:
+ /* The nonce for a chunk of chunk index "i" in OCB processing is defined as:
+ OCB-Nonce_{i} = IV[1..120] xor i
+ */
+ memcpy(nonce, iv, PGP_AEAD_OCB_NONCE_LEN);
+ for (int i = 14; (i >= 0) && index; i--) {
+ nonce[i] ^= index & 0xff;
+ index = index >> 8;
+ }
+ return PGP_AEAD_OCB_NONCE_LEN;
+ default:
+ return 0;
+ }
+}
+#endif