Skip to content

Commit

Permalink
Merge pull request #430 from LedgerHQ/xch/crypto_helper_sig_der_to_rs
Browse files Browse the repository at this point in the history
CX: Add helper to retrieve ECDSA signature R and S values
  • Loading branch information
xchapron-ledger authored Oct 2, 2023
2 parents fe88759 + 1acd091 commit a7f3dd5
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 0 deletions.
51 changes: 51 additions & 0 deletions lib_cxng/include/lcx_ecdsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
31 changes: 31 additions & 0 deletions lib_standard_app/crypto_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
107 changes: 107 additions & 0 deletions lib_standard_app/crypto_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
62 changes: 62 additions & 0 deletions src/cx_wrappers.c
Original file line number Diff line number Diff line change
@@ -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 <stdint.h> // uint*_t
#include <string.h> // memset, explicit_bzero
#include <stdbool.h> // 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;
}

0 comments on commit a7f3dd5

Please sign in to comment.