Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

ECDSA signature reproducibility

Our device is based on nRF52840 with SDK 15.0.0

The device communicates over BLE with PC application (aka client) via "uart" service.

PC (client) messages are signed using ECDSA with secp265r1 curve and the device verifies the signature of received messages before processing them.

PC side  ECDSA implementation is based on Org.BouncyCastle.Asn1.Sec package for C#.

Device side ECDSA implementation is almost identical to ecdsa example (Bob's verification).

Verification alway failed on execution backend implementation with error code 34070  - invalid signature despite I used private and public keys from ecdsa example.

Any ideas?

Moreover I ran ecdsa example from SDK on Nordic's DK PCA10056 and just put alice sign and bob verification in a loop.

Each cycle (see below output) I got different signature but bob's verification pass.

Is that makes sense?

<info> app: ECDSA example started.

<info> app: Alice's signature generation
<info> app: Alice's message hash:
42BA8354DB263A6A5A9F74D6B7CEB4C962A3D8FD58A41969E521EB0222455415
<info> app: Alice's signature:
000F05AD9269E25050B05BB4754C480C66A515650C63863C12A4D6634F04580490DCE86B7CED05C526202A412A7F4D7CD586F768B5FBA0592EAF56A63E7C0320
<info> app: Bob's message verification
<info> app: Signature is valid. Message is authentic.
<info> app: Alice's signature generation
<info> app: Alice's message hash:
42BA8354DB263A6A5A9F74D6B7CEB4C962A3D8FD58A41969E521EB0222455415
<info> app: Alice's signature:
EECEF1BF8D3CFA84EB748DE4990B14B832BD383ADB4C9B2D5C51AEA7DF0E91DD13B39228BE460A5D6163514B8C7F63A8FF476D9E062BD0E7A50F87DE00AA5BAB
<info> app: Bob's message verification
<info> app: Signature is valid. Message is authentic.
<info> app: Alice's signature generation
<info> app: Alice's message hash:
42BA8354DB263A6A5A9F74D6B7CEB4C962A3D8FD58A41969E521EB0222455415
<info> app: Alice's signature:
D423EDD1F6ABA0B6213380DA51794A6B724D7F3DFC351AD881DFDD4B86B8348EEAFEC7E0CFCFCA3C64C774E03470A96573E4358FDC873B7F7CDE086241DF89D6
<info> app: Bob's message verification
<info> app: Signature is valid. Message is authentic.
<info> app: Alice's signature generation
<info> app: Alice's message hash:
42BA8354DB263A6A5A9F74D6B7CEB4C962A3D8FD58A41969E521EB0222455415
<info> app: Alice's signature:
8AF1749C0B605995C7F18E30A68D78D43AD41431174683AF8CDDD89C2105441EB5D00A215FDDF5ACB6AC0D8C465271716819D594F78B5976B3B74759463AED93
<info> app: Bob's message verification
<info> app: Signature is valid. Message is authentic.
<info> app: Alice's signature generation
<info> app: Alice's message hash:
42BA8354DB263A6A5A9F74D6B7CEB4C962A3D8FD58A41969E521EB0222455415
<info> app: Alice's signature:
03A29A6A08F02E8282AD210CA44253B1D15C66E8194FA27F4EB45D3F6171C65CBB8A7311D1A9CC436F13EBFA7AC3BED3C1C3C365682AEBF2138A28065761A42B
<info> app: Bob's message verification
<info> app: Signature is valid. Message is authentic.
<info> app: ECDSA example executed successfully.

  • The reason for different signatures is the random number which is generated for each cycle as part of the Alice's signing process

  • Hi,

    Have you double checked that you use the correct curve in both ends, and that you have not introduced any errors when copying the keys e.g. due to endianness or how they are otherwise encoded?

    Regards,
    Terje

  • Regarding curve both sides is configured to secp256r1 curve

    PC side:

    private static ECDsa LoadPrivateKey(byte[] key)
    {
    var privKeyInt = new Org.BouncyCastle.Math.BigInteger(+1, key);
    var parameters = SecNamedCurves.GetByName("secp256r1");
    var ecPoint = parameters.G.Multiply(privKeyInt);
    var privKeyX = ecPoint.Normalize().XCoord.ToBigInteger().ToByteArrayUnsigned();
    var privKeyY = ecPoint.Normalize().YCoord.ToBigInteger().ToByteArrayUnsigned();

    var d = privKeyInt.ToByteArrayUnsigned();
    return ECDsa.Create(new ECParameters
    {
    Curve = ECCurve.NamedCurves.nistP256,
    D = privKeyInt.ToByteArrayUnsigned(),
    Q = new ECPoint
    {
    X = privKeyX,
    Y = privKeyY
    }
    });
    }

    for device side:

    /*static*/ bool
    bIsValidSignature(const uint8_t* a_ipHashMessage, const uint8_t* a_ipSignature, uint8_t* a_ipEcdsaPublicKey)
    // Brief: verify signature
    // Get: a_ipHashMessage - message
    // a_ipSignature - signature
    // a_ipEcdsaPublicKey - public key
    // Return: true if successful, false otherwise
    // Pre: -
    // Post: signature verified
    // Method: Trivial
    {
    // swap endian
    // ~~~~~~~~~~~
    uint8_t l_iaPkCopy[d_iAuthPubKeySize];
    nrf_crypto_internal_double_swap_endian(l_iaPkCopy, a_ipEcdsaPublicKey, d_iAuthPubKeySize / 2);

    // ecc public key from raw
    // ~~~~~~~~~~~~~~~~~~~~~~~
    nrf_crypto_ecc_public_key_t l_uPublicKey;
    if (NRF_SUCCESS != nrf_crypto_ecc_public_key_from_raw(&g_nrf_crypto_ecc_secp256r1_curve_info, &l_uPublicKey, l_iaPkCopy, d_iAuthPubKeySize))
    {
    LOG_WARNING(LOG_PRINT_CRYPTO, "Crypto - verify signature - failed - ecc public key from raw");
    return false;
    }

    // verify the message using ECDSA and SHA-256
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    if (NRF_SUCCESS != nrf_crypto_ecdsa_verify(NULL, &l_uPublicKey, a_ipHashMessage, d_iAuthHashSize, a_ipSignature, d_iAuthSigLen))
    {
    LOG_WARNING(LOG_PRINT_CRYPTO, "Crypto - verify signature - failed - ECDSA verify");
    return false;
    }

    return true;
    }

    Regarding endianness:

    Device code swaps it before verifying

    PC run on Win 10 which is little endian

Related