From 727bc55a7d050b13ee4dfb8d638f9dc4c501f251 Mon Sep 17 00:00:00 2001 From: Xavier Chapron Date: Mon, 18 Sep 2023 17:54:01 +0200 Subject: [PATCH 1/2] cx_wrappers: Implement cx_ecdsa_sign_rs_no_throw --- lib_cxng/include/lcx_ecdsa.h | 51 +++++++++++++++++++++++++++++ src/cx_wrappers.c | 62 ++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/cx_wrappers.c diff --git a/lib_cxng/include/lcx_ecdsa.h b/lib_cxng/include/lcx_ecdsa.h index 35b2382ab..ac0e6b36a 100644 --- a/lib_cxng/include/lcx_ecdsa.h +++ b/lib_cxng/include/lcx_ecdsa.h @@ -111,6 +111,57 @@ DEPRECATED static inline size_t cx_ecdsa_sign(const cx_ecfp_private_key_t *pvkey return sig_len_; } +/** + * @brief Sign a message digest according to ECDSA specification + * + * @param[in] pvkey Private key. + * Shall be initialized with #cx_ecfp_init_private_key_no_throw. + * + * @param[in] mode Crypto mode flags. + * Supported flags: + * - CX_RND_TRNG + * - CX_RND_RFC6979 + * + * @param[in] hashID Message digest algorithm identifier. + * This parameter is mandatory with the flag CX_RND_RFC6979. + * + * @param[in] hash Digest of the message to be signed. + * The length of *hash* must be shorter than the curve domain size. + * + * @param[in] hash_len Length of the digest in octets. + * + * @param[in] rs_len Length of r and s buffer. 32 for usual curves. + * + * @param[out] sig_r Buffer where to store the signature r value. + * + * @param[out] sig_s Buffer where to store the signature s value. + * + * @param[out] info Set with CX_ECCINFO_PARITY_ODD if the y-coordinate is odd when computing + * **[k].G**. + * + * @return Error code: + * - CX_OK on success + * - CX_EC_INVALID_CURVE + * - CX_INVALID_PARAMETER + * - CX_INTERNAL_ERROR + * - CX_NOT_UNLOCKED + * - CX_INVALID_PARAMETER_SIZE + * - CX_MEMORY_FULL + * - CX_NOT_LOCKED + * - CX_EC_INVALID_POINT + * - CX_EC_INFINITE_POINT + * - CX_INVALID_PARAMETER_VALUE + */ +cx_err_t cx_ecdsa_sign_rs_no_throw(const cx_ecfp_private_key_t *key, + uint32_t mode, + cx_md_t hashID, + const uint8_t *hash, + size_t hash_len, + size_t rs_len, + uint8_t *sig_r, + uint8_t *sig_s, + uint32_t *info); + /** * @brief Verifies an ECDSA signature according to ECDSA specification. * diff --git a/src/cx_wrappers.c b/src/cx_wrappers.c new file mode 100644 index 000000000..f1db2af9a --- /dev/null +++ b/src/cx_wrappers.c @@ -0,0 +1,62 @@ + +/******************************************************************************* + * (c) 2023 Ledger SAS. + * + * 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 // uint*_t +#include // memset, explicit_bzero +#include // bool + +#include "cx.h" +#include "lib_cxng/src/cx_ecfp.h" + +// At some point this could/should be migrated to a cx func included in the cx_lib +cx_err_t cx_ecdsa_sign_rs_no_throw(const cx_ecfp_private_key_t *key, + uint32_t mode, + cx_md_t hashID, + const uint8_t *hash, + size_t hash_len, + size_t rs_size, + uint8_t *sig_r, + uint8_t *sig_s, + uint32_t *info) +{ + cx_err_t error = CX_OK; + uint8_t sig_der[100]; + size_t sig_der_len = sizeof(sig_der); + const uint8_t *r; + const uint8_t *s; + size_t r_len, s_len; + + explicit_bzero(sig_r, rs_size); + explicit_bzero(sig_s, rs_size); + + CX_CHECK( + cx_ecdsa_sign_no_throw(key, mode, hashID, hash, hash_len, sig_der, &sig_der_len, info)); + + if (cx_ecfp_decode_sig_der(sig_der, sig_der_len, rs_size, &r, &r_len, &s, &s_len) != 1) { + error = CX_INVALID_PARAMETER; + goto end; + } + + // Copy R and S. No need to pad as the full buffer has been memset + memcpy(sig_r + rs_size - r_len, r, r_len); + memcpy(sig_s + rs_size - s_len, s, s_len); + +end: + if (error != CX_OK) { + *info = 0; + } + return error; +} From 1acd09110539188d7f83f7f764eebdea0d752232 Mon Sep 17 00:00:00 2001 From: Xavier Chapron Date: Thu, 21 Sep 2023 16:33:25 +0200 Subject: [PATCH 2/2] lib_standard_app: crypto_helpers: Implement bip32_derive_ecdsa_sign_rs_hash_256 --- lib_standard_app/crypto_helpers.c | 31 +++++++++ lib_standard_app/crypto_helpers.h | 107 ++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/lib_standard_app/crypto_helpers.c b/lib_standard_app/crypto_helpers.c index fb6054951..75405f299 100644 --- a/lib_standard_app/crypto_helpers.c +++ b/lib_standard_app/crypto_helpers.c @@ -136,6 +136,37 @@ WARN_UNUSED_RESULT cx_err_t bip32_derive_with_seed_ecdsa_sign_hash_256(unsigned return error; } +WARN_UNUSED_RESULT cx_err_t +bip32_derive_with_seed_ecdsa_sign_rs_hash_256(unsigned int derivation_mode, + cx_curve_t curve, + const uint32_t *path, + size_t path_len, + uint32_t sign_mode, + cx_md_t hashID, + const uint8_t *hash, + size_t hash_len, + uint8_t sig_r[static 32], + uint8_t sig_s[static 32], + uint32_t *info, + unsigned char *seed, + size_t seed_len) +{ + cx_err_t error = CX_OK; + cx_ecfp_256_private_key_t privkey; + + // Derive private key according to BIP32 path + CX_CHECK(bip32_derive_with_seed_init_privkey_256( + derivation_mode, curve, path, path_len, &privkey, NULL, seed, seed_len)); + + CX_CHECK(cx_ecdsa_sign_rs_no_throw( + &privkey, sign_mode, hashID, hash, hash_len, 32, sig_r, sig_s, info)); + +end: + explicit_bzero(&privkey, sizeof(privkey)); + + return error; +} + WARN_UNUSED_RESULT cx_err_t bip32_derive_with_seed_eddsa_sign_hash_256(unsigned int derivation_mode, cx_curve_t curve, const uint32_t *path, diff --git a/lib_standard_app/crypto_helpers.h b/lib_standard_app/crypto_helpers.h index beeafc940..cec943336 100644 --- a/lib_standard_app/crypto_helpers.h +++ b/lib_standard_app/crypto_helpers.h @@ -246,6 +246,113 @@ WARN_UNUSED_RESULT static inline cx_err_t bip32_derive_ecdsa_sign_hash_256(cx_cu 0); } +/** + * @brief Sign a hash with ecdsa using the device seed derived from the specified bip32 path and + * seed key. + * + * @param[in] derivation_mode Derivation mode, one of HDW_NORMAL / HDW_ED25519_SLIP10 / HDW_SLIP21. + * + * @param[in] curve Curve identifier. + * + * @param[in] path Bip32 path to use for derivation. + * + * @param[in] path_len Bip32 path length. + * + * @param[in] hashID Message digest algorithm identifier. + * + * @param[in] hash Digest of the message to be signed. + * The length of *hash* must be shorter than the group order size. + * Otherwise it is truncated. + * + * @param[in] hash_len Length of the digest in octets. + * + * @param[out] sig_r Buffer where to store the signature r value. + * + * @param[out] sig_s Buffer where to store the signature s value. + * + * @param[out] info Set with CX_ECCINFO_PARITY_ODD if the y-coordinate is odd when + * computing **[k].G**. + * + * @param[in] seed Seed key to use for derivation. + * + * @param[in] seed_len Seed key length. + * + * @return Error code: + * - CX_OK on success + * - CX_EC_INVALID_CURVE + * - CX_INTERNAL_ERROR + */ +WARN_UNUSED_RESULT cx_err_t +bip32_derive_with_seed_ecdsa_sign_rs_hash_256(unsigned int derivation_mode, + cx_curve_t curve, + const uint32_t *path, + size_t path_len, + uint32_t sign_mode, + cx_md_t hashID, + const uint8_t *hash, + size_t hash_len, + uint8_t sig_r[static 32], + uint8_t sig_s[static 32], + uint32_t *info, + unsigned char *seed, + size_t seed_len); + +/** + * @brief Sign a hash with ecdsa using the device seed derived from the specified bip32 path. + * + * @param[in] curve Curve identifier. + * + * @param[in] path Bip32 path to use for derivation. + * + * @param[in] path_len Bip32 path length. + * + * @param[in] hashID Message digest algorithm identifier. + * + * @param[in] hash Digest of the message to be signed. + * The length of *hash* must be shorter than the group order size. + * Otherwise it is truncated. + * + * @param[in] hash_len Length of the digest in octets. + * + * @param[out] sig_r Buffer where to store the signature r value. + * + * @param[out] sig_s Buffer where to store the signature s value. + * + * @param[out] info Set with CX_ECCINFO_PARITY_ODD if the y-coordinate is odd when + * computing **[k].G**. + * + * @return Error code: + * - CX_OK on success + * - CX_EC_INVALID_CURVE + * - CX_INTERNAL_ERROR + */ +WARN_UNUSED_RESULT static inline cx_err_t bip32_derive_ecdsa_sign_rs_hash_256( + cx_curve_t curve, + const uint32_t *path, + size_t path_len, + uint32_t sign_mode, + cx_md_t hashID, + const uint8_t *hash, + size_t hash_len, + uint8_t sig_r[static 32], + uint8_t sig_s[static 32], + uint32_t *info) +{ + return bip32_derive_with_seed_ecdsa_sign_rs_hash_256(HDW_NORMAL, + curve, + path, + path_len, + sign_mode, + hashID, + hash, + hash_len, + sig_r, + sig_s, + info, + NULL, + 0); +} + /** * @brief Sign a hash with eddsa using the device seed derived from the specified bip32 path and * seed key.