summaryrefslogtreecommitdiffstats
path: root/src/lib/crypto/ecdh_utils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/crypto/ecdh_utils.cpp')
-rw-r--r--src/lib/crypto/ecdh_utils.cpp166
1 files changed, 166 insertions, 0 deletions
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);
+}