diff mbox series

[RFC,v2,3/5] lib: Add support for ECDSA image signing

Message ID 20201230210028.4065824-4-mr.nuke.me@gmail.com
State Superseded
Delegated to: Tom Rini
Headers show
Series Add support for ECDSA image signing (with test) | expand

Commit Message

Alexandru Gagniuc Dec. 30, 2020, 9 p.m. UTC
mkimage supports rsa2048, and rsa4096 signatures. With newer silicon
now supporting hardware-accelerated ECDSA, it makes sense to expand
signing support to elliptic curves.

Implement host-side ECDSA signing and verification with libcrypto.
Device-side implementation of signature verification is beyond the
scope of this patch.

Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
 common/image-sig.c          |  14 +-
 include/u-boot/ecdsa.h      |  27 ++++
 lib/ecdsa/ecdsa-libcrypto.c | 300 ++++++++++++++++++++++++++++++++++++
 tools/Makefile              |   3 +
 4 files changed, 342 insertions(+), 2 deletions(-)
 create mode 100644 include/u-boot/ecdsa.h
 create mode 100644 lib/ecdsa/ecdsa-libcrypto.c

Comments

Simon Glass Jan. 7, 2021, 12:35 p.m. UTC | #1
Hi Alexandru,

On Wed, 30 Dec 2020 at 14:00, Alexandru Gagniuc <mr.nuke.me@gmail.com> wrote:
>
> mkimage supports rsa2048, and rsa4096 signatures. With newer silicon
> now supporting hardware-accelerated ECDSA, it makes sense to expand
> signing support to elliptic curves.
>
> Implement host-side ECDSA signing and verification with libcrypto.
> Device-side implementation of signature verification is beyond the
> scope of this patch.
>
> Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
> ---
>  common/image-sig.c          |  14 +-
>  include/u-boot/ecdsa.h      |  27 ++++
>  lib/ecdsa/ecdsa-libcrypto.c | 300 ++++++++++++++++++++++++++++++++++++
>  tools/Makefile              |   3 +
>  4 files changed, 342 insertions(+), 2 deletions(-)
>  create mode 100644 include/u-boot/ecdsa.h
>  create mode 100644 lib/ecdsa/ecdsa-libcrypto.c
>
> diff --git a/common/image-sig.c b/common/image-sig.c
> index 21dafe6b91..2548b3eb0f 100644
> --- a/common/image-sig.c
> +++ b/common/image-sig.c
> @@ -15,6 +15,7 @@
>  DECLARE_GLOBAL_DATA_PTR;
>  #endif /* !USE_HOSTCC*/
>  #include <image.h>
> +#include <u-boot/ecdsa.h>
>  #include <u-boot/rsa.h>
>  #include <u-boot/hash-checksum.h>
>
> @@ -82,8 +83,17 @@ struct crypto_algo crypto_algos[] = {
>                 .sign = rsa_sign,
>                 .add_verify_data = rsa_add_verify_data,
>                 .verify = rsa_verify,
> -       }
> -
> +       },
> +#if defined(USE_HOSTCC)
> +       /* Currently, only host support exists for ECDSA */
> +       {
> +               .name = "ecdsa256",
> +               .key_len = ECDSA256_BYTES,
> +               .sign = ecdsa_sign,
> +               .add_verify_data = ecdsa_add_verify_data,
> +               .verify = ecdsa_verify,
> +       },
> +#endif
>  };
>
>  struct padding_algo padding_algos[] = {
> diff --git a/include/u-boot/ecdsa.h b/include/u-boot/ecdsa.h
> new file mode 100644
> index 0000000000..a13a7267e1
> --- /dev/null
> +++ b/include/u-boot/ecdsa.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>.
> + */
> +
> +#ifndef _ECDSA_H
> +#define _ECDSA_H
> +
> +#include <errno.h>
> +#include <image.h>
> +
> +/**
> + * crypto_algo API impementation for ECDSA;
> + * @see "struct crypto_algo"
> + * @{
> + */
> +int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
> +              int region_count, uint8_t **sigp, uint *sig_len);
> +int ecdsa_verify(struct image_sign_info *info,
> +                const struct image_region region[], int region_count,
> +                uint8_t *sig, uint sig_len);
> +int ecdsa_add_verify_data(struct image_sign_info *info, void *keydest);

Please always add full comments when you export functions, or have a
non-trivial static function.

> +/** @} */
> +
> +#define ECDSA256_BYTES (256 / 8)
> +
> +#endif
> diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c
> new file mode 100644
> index 0000000000..ff491411d0
> --- /dev/null
> +++ b/lib/ecdsa/ecdsa-libcrypto.c
> @@ -0,0 +1,300 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * ECDSA image signing implementation using libcrypto backend
> + *
> + * The signature is a binary representation of the (R, S) points, padded to the
> + * key size. The signature will be (2 * key_size_bits) / 8 bytes.
> + *
> + * Deviations from behavior of RSA equivalent:
> + *  - Verification uses private key. This is not technically required, but a
> + *    limitation on how clumsy the openssl API is to use.

I'm not quite sure what the implications are on this. If this is
public key crypto, the private key is not available, so how can you
verify with it?

> + *  - Handling of keys and key paths:
> + *    - The '-K' key directory option must contain path to the key file,
> + *      instead of the key directory.

If we make this change we should update RSA to do the same.

> + *    - No assumptions are made about the file extension of the key
> + *    - The 'key-name-hint' property is only used for naming devicetree nodes,
> + *      but is not used for looking up keys on the filesystem.
> + *
> + * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>
> + */
> +
> +#include <u-boot/ecdsa.h>
> +#include <u-boot/fdt-libcrypto.h>
> +#include <openssl/ssl.h>
> +#include <openssl/ec.h>
> +#include <openssl/bn.h>
> +
> +struct signer {
> +       EVP_PKEY *evp_key;
> +       EC_KEY *ecdsa_key;
> +       void *hash;
> +       void *signature;        /* Do not free() !*/

need comments

> +};
> +
> +static int alloc_ctx(struct signer *ctx, const struct image_sign_info *info)
> +{
> +       memset(ctx, 0, sizeof(*ctx));
> +
> +       if (!OPENSSL_init_ssl(0, NULL)) {
> +               fprintf(stderr, "Failure to init SSL library\n");
> +               return -1;
> +       }
> +
> +       ctx->hash = malloc(info->checksum->checksum_len);
> +       ctx->signature = malloc(info->crypto->key_len * 2);
> +
> +       if (!ctx->hash || !ctx->signature)
> +               return -1;
> +
> +       return 0;
> +}
> +
> +static void free_ctx(struct signer *ctx)
> +{
> +       if (ctx->ecdsa_key)
> +               EC_KEY_free(ctx->ecdsa_key);
> +
> +       if (ctx->evp_key)
> +               EVP_PKEY_free(ctx->evp_key);
> +
> +       if (ctx->hash)
> +               free(ctx->hash);
> +}
> +
> +/*
> + * Convert an ECDSA signature to raw format
> + *
> + * openssl DER-encodes 'binary' signatures. We want the signature in a raw
> + * (R, S) point pair. So we have to dance a bit.
> + */
> +static void ecdsa_sig_encode_raw(void *buf, const ECDSA_SIG *sig, size_t order)
> +{
> +       int point_bytes = order;
> +       const BIGNUM *r, *s;
> +       uintptr_t s_buf;
> +
> +       ECDSA_SIG_get0(sig, &r, &s);
> +       s_buf = (uintptr_t)buf + point_bytes;
> +       BN_bn2binpad(r, buf, point_bytes);
> +       BN_bn2binpad(s, (void *)s_buf, point_bytes);
> +}
> +
> +static ECDSA_SIG *ecdsa_sig_from_raw(void *buf, size_t order)
> +{
> +       int point_bytes = order;
> +       uintptr_t s_buf;
> +       ECDSA_SIG *sig;
> +       BIGNUM *r, *s;
> +
> +       sig = ECDSA_SIG_new();
> +       if (!sig)
> +               return NULL;
> +
> +       s_buf = (uintptr_t)buf + point_bytes;
> +       r = BN_bin2bn(buf, point_bytes, NULL);
> +       s = BN_bin2bn((void *)s_buf, point_bytes, NULL);
> +       ECDSA_SIG_set0(sig, r, s);
> +
> +       return sig;
> +}
> +
> +static size_t ecdsa_key_size_bytes(const EC_KEY *key)

I think all of these functions need a comment.

> +{
> +       const EC_GROUP *group;
> +
> +       group = EC_KEY_get0_group(key);
> +       return EC_GROUP_order_bits(group) / 8;
> +}
> +
> +static int read_key(struct signer *ctx, const char *key_name)
> +{
> +       FILE *f = fopen(key_name, "r");
> +
> +       if (!f) {
> +               fprintf(stderr, "Can not get key file '%s'\n", key_name);
> +               return -1;

return -EIO perhaps?

> +       }
> +
> +       ctx->evp_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
> +       fclose(f);
> +       if (!ctx->evp_key) {
> +               fprintf(stderr, "Can not read key from '%s'\n", key_name);
> +               return -1;

These should return useful error number: -1 is -EPERM which doesn't
seem right here.

> +       }
> +
> +       if (EVP_PKEY_id(ctx->evp_key) != EVP_PKEY_EC) {
> +               fprintf(stderr, "'%s' is not an ECDSA key\n", key_name);
> +               return -1;
> +       }
> +
> +       ctx->ecdsa_key = EVP_PKEY_get1_EC_KEY(ctx->evp_key);
> +       if (!ctx->ecdsa_key)
> +               fprintf(stderr, "Can not extract ECDSA key\n");
> +
> +       return (ctx->ecdsa_key) ? 0 : -1;
> +}
> +
> +static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
> +{
> +       const char *kname = info->keydir;
> +       int key_len_bytes;
> +
> +       if (alloc_ctx(ctx, info) < 0)

That function returns 0 on success, so you don't need the < 0. A
comment on the above function would make that clear.

> +               return -1;
> +
> +       if (read_key(ctx, kname) < 0)
> +               return -1;
> +
> +       key_len_bytes = ecdsa_key_size_bytes(ctx->ecdsa_key);
> +       if (key_len_bytes != info->crypto->key_len) {
> +               fprintf(stderr, "Expected a %u-bit key, got %u-bit key\n",
> +                       info->crypto->key_len * 8, key_len_bytes * 8);
> +               return -1;
> +       }
> +
> +       return 0;
> +}
> +

[..]

Regards,
Simon
Alexandru Gagniuc Jan. 7, 2021, 4:27 p.m. UTC | #2
On 1/7/21 6:35 AM, Simon Glass wrote:
> Hi Alexandru,

Hi Simon,

(pun alert!) A lot of your comments have to do with comments. I use 
comments as a tool to add something of value to code. When the code is 
self-documenting, comments don't help much.
See kernel coding style chapter 8.

What comments can do, is increase code size and break code flow -- there 
is a cost to having them. I'm certain you've seen functions that need to 
be scrolled up and down because of a huge comment smack in the middle. 
I'll address individual comment comments below.

> 
> On Wed, 30 Dec 2020 at 14:00, Alexandru Gagniuc <mr.nuke.me@gmail.com> wrote:
>>
>> mkimage supports rsa2048, and rsa4096 signatures. With newer silicon
>> now supporting hardware-accelerated ECDSA, it makes sense to expand
>> signing support to elliptic curves.
>>
>> Implement host-side ECDSA signing and verification with libcrypto.
>> Device-side implementation of signature verification is beyond the
>> scope of this patch.
>>
>> Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
>> ---
>>   common/image-sig.c          |  14 +-
>>   include/u-boot/ecdsa.h      |  27 ++++
>>   lib/ecdsa/ecdsa-libcrypto.c | 300 ++++++++++++++++++++++++++++++++++++
>>   tools/Makefile              |   3 +
>>   4 files changed, 342 insertions(+), 2 deletions(-)
>>   create mode 100644 include/u-boot/ecdsa.h
>>   create mode 100644 lib/ecdsa/ecdsa-libcrypto.c
>>
>> diff --git a/common/image-sig.c b/common/image-sig.c
>> index 21dafe6b91..2548b3eb0f 100644
>> --- a/common/image-sig.c
>> +++ b/common/image-sig.c
>> @@ -15,6 +15,7 @@
>>   DECLARE_GLOBAL_DATA_PTR;
>>   #endif /* !USE_HOSTCC*/
>>   #include <image.h>
>> +#include <u-boot/ecdsa.h>
>>   #include <u-boot/rsa.h>
>>   #include <u-boot/hash-checksum.h>
>>
>> @@ -82,8 +83,17 @@ struct crypto_algo crypto_algos[] = {
>>                  .sign = rsa_sign,
>>                  .add_verify_data = rsa_add_verify_data,
>>                  .verify = rsa_verify,
>> -       }
>> -
>> +       },
>> +#if defined(USE_HOSTCC)
>> +       /* Currently, only host support exists for ECDSA */
>> +       {
>> +               .name = "ecdsa256",
>> +               .key_len = ECDSA256_BYTES,
>> +               .sign = ecdsa_sign,
>> +               .add_verify_data = ecdsa_add_verify_data,
>> +               .verify = ecdsa_verify,
>> +       },
>> +#endif
>>   };
>>
>>   struct padding_algo padding_algos[] = {
>> diff --git a/include/u-boot/ecdsa.h b/include/u-boot/ecdsa.h
>> new file mode 100644
>> index 0000000000..a13a7267e1
>> --- /dev/null
>> +++ b/include/u-boot/ecdsa.h
>> @@ -0,0 +1,27 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>.
>> + */
>> +
>> +#ifndef _ECDSA_H
>> +#define _ECDSA_H
>> +
>> +#include <errno.h>
>> +#include <image.h>
>> +
>> +/**
>> + * crypto_algo API impementation for ECDSA;
>> + * @see "struct crypt_algo"
>> + * @{
>> + */
>> +int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
>> +              int region_count, uint8_t **sigp, uint *sig_len);
>> +int ecdsa_verify(struct image_sign_info *info,
>> +                const struct image_region region[], int region_count,
>> +                uint8_t *sig, uint sig_len);
>> +int ecdsa_add_verify_data(struct image_sign_info *info, void *keydest);
> 
> Please always add full comments when you export functions, or have a
> non-trivial static function.

I disagree. These functions implement and are designed to be used via 
the crypt_algo API. One should understand the crypt_algo API, and any 
deviations in behavior would be a bug. So there is no scenario in which 
comments here would be useful.

>> +/** @} */
>> +
>> +#define ECDSA256_BYTES (256 / 8)
>> +
>> +#endif
>> diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c
>> new file mode 100644
>> index 0000000000..ff491411d0
>> --- /dev/null
>> +++ b/lib/ecdsa/ecdsa-libcrypto.c
>> @@ -0,0 +1,300 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * ECDSA image signing implementation using libcrypto backend
>> + *
>> + * The signature is a binary representation of the (R, S) points, padded to the
>> + * key size. The signature will be (2 * key_size_bits) / 8 bytes.
>> + *
>> + * Deviations from behavior of RSA equivalent:
>> + *  - Verification uses private key. This is not technically required, but a
>> + *    limitation on how clumsy the openssl API is to use.
> 
> I'm not quite sure what the implications are on this. If this is
> public key crypto, the private key is not available, so how can you
> verify with it?

I presume this is fixable, but only as an academic exercise. This file 
is for mkimage, which doesn't do standalone verification. The way you 
verify is in u-boot. That is the subject of another series.

>> + *  - Handling of keys and key paths:
>> + *    - The '-K' key directory option must contain path to the key file,
>> + *      instead of the key directory.
> 
> If we make this change we should update RSA to do the same.

Of course, but is there agreement as to this direction? There seem to be 
some hidden subtleties to key-name-hint that I don't fully understand yet.

>> + *    - No assumptions are made about the file extension of the key
>> + *    - The 'key-name-hint' property is only used for naming devicetree nodes,
>> + *      but is not used for looking up keys on the filesystem.
>> + *
>> + * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>
>> + */
>> +
>> +#include <u-boot/ecdsa.h>
>> +#include <u-boot/fdt-libcrypto.h>
>> +#include <openssl/ssl.h>
>> +#include <openssl/ec.h>
>> +#include <openssl/bn.h>
>> +
>> +struct signer {
>> +       EVP_PKEY *evp_key;
>> +       EC_KEY *ecdsa_key;
>> +       void *hash;
>> +       void *signature;        /* Do not free() !*/
> 
> need comments

Is this for the sake of having comments, or is there something of value 
that I've missed? The only member that could be confusing is evp_key, 
but that becomes obvious in the context of libcrypto.

>> +};
>> +
>> +static int alloc_ctx(struct signer *ctx, const struct image_sign_info *info)
>> +{
>> +       memset(ctx, 0, sizeof(*ctx));
>> +
>> +       if (!OPENSSL_init_ssl(0, NULL)) {
>> +               fprintf(stderr, "Failure to init SSL library\n");
>> +               return -1;
>> +       }
>> +
>> +       ctx->hash = malloc(info->checksum->checksum_len);
>> +       ctx->signature = malloc(info->crypto->key_len * 2);
>> +
>> +       if (!ctx->hash || !ctx->signature)
>> +               return -1;
>> +
>> +       return 0;
>> +}
>> +
>> +static void free_ctx(struct signer *ctx)
>> +{
>> +       if (ctx->ecdsa_key)
>> +               EC_KEY_free(ctx->ecdsa_key);
>> +
>> +       if (ctx->evp_key)
>> +               EVP_PKEY_free(ctx->evp_key);
>> +
>> +       if (ctx->hash)
>> +               free(ctx->hash);
>> +}
>> +
>> +/*
>> + * Convert an ECDSA signature to raw format
>> + *
>> + * openssl DER-encodes 'binary' signatures. We want the signature in a raw
>> + * (R, S) point pair. So we have to dance a bit.
>> + */
>> +static void ecdsa_sig_encode_raw(void *buf, const ECDSA_SIG *sig, size_t order)
>> +{
>> +       int point_bytes = order;
>> +       const BIGNUM *r, *s;
>> +       uintptr_t s_buf;
>> +
>> +       ECDSA_SIG_get0(sig, &r, &s);
>> +       s_buf = (uintptr_t)buf + point_bytes;
>> +       BN_bn2binpad(r, buf, point_bytes);
>> +       BN_bn2binpad(s, (void *)s_buf, point_bytes);
>> +}
>> +
>> +static ECDSA_SIG *ecdsa_sig_from_raw(void *buf, size_t order)
>> +{
>> +       int point_bytes = order;
>> +       uintptr_t s_buf;
>> +       ECDSA_SIG *sig;
>> +       BIGNUM *r, *s;
>> +
>> +       sig = ECDSA_SIG_new();
>> +       if (!sig)
>> +               return NULL;
>> +
>> +       s_buf = (uintptr_t)buf + point_bytes;
>> +       r = BN_bin2bn(buf, point_bytes, NULL);
>> +       s = BN_bin2bn((void *)s_buf, point_bytes, NULL);
>> +       ECDSA_SIG_set0(sig, r, s);
>> +
>> +       return sig;
>> +}
>> +
>> +static size_t ecdsa_key_size_bytes(const EC_KEY *key)
> 
> I think all of these functions need a comment.

The function names are self-explanatory. One point of confusion would be 
the meaning of raw signature -- this is already explained at the top.

> 
>> +{
>> +       const EC_GROUP *group;
>> +
>> +       group = EC_KEY_get0_group(key);
>> +       return EC_GROUP_order_bits(group) / 8;
>> +}
>> +
>> +static int read_key(struct signer *ctx, const char *key_name)
>> +{
>> +       FILE *f = fopen(key_name, "r");
>> +
>> +       if (!f) {
>> +               fprintf(stderr, "Can not get key file '%s'\n", key_name);
>> +               return -1;
> 
> return -EIO perhaps?

Good idea!

>> +       }
>> +
>> +       ctx->evp_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
>> +       fclose(f);
>> +       if (!ctx->evp_key) {
>> +               fprintf(stderr, "Can not read key from '%s'\n", key_name);
>> +               return -1;
> 
> These should return useful error number: -1 is -EPERM which doesn't
> seem right here.

-EIO again?

>> +       }
>> +
>> +       if (EVP_PKEY_id(ctx->evp_key) != EVP_PKEY_EC) {
>> +               fprintf(stderr, "'%s' is not an ECDSA key\n", key_name);
>> +               return -1;
>> +       }
>> +
>> +       ctx->ecdsa_key = EVP_PKEY_get1_EC_KEY(ctx->evp_key);
>> +       if (!ctx->ecdsa_key)
>> +               fprintf(stderr, "Can not extract ECDSA key\n");
>> +
>> +       return (ctx->ecdsa_key) ? 0 : -1;
>> +}
>> +
>> +static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
>> +{
>> +       const char *kname = info->keydir;
>> +       int key_len_bytes;
>> +
>> +       if (alloc_ctx(ctx, info) < 0)
> 
> That function returns 0 on success, so you don't need the < 0. A
> comment on the above function would make that clear.

I think the following would be less clear:

	if (alloc_ctx())
		 error();

Does alloc_ctx() return an int? Does it return memory? It has (m)alloc 
in the name. By having a '< 0' in the predicate, it's now clear that 
this can't return a pointer. So yes, you might scratch your head as to 
why there's a '< 0' that's not needed. Also, you know that this function 
returns an error code without needing to scroll up to its definition. A 
comment above the function wouldn't eliminate the need to scroll up.

>> +               return -1;
>> +
>> +       if (read_key(ctx, kname) < 0)
>> +               return -1;
>> +
>> +       key_len_bytes = ecdsa_key_size_bytes(ctx->ecdsa_key);
>> +       if (key_len_bytes != info->crypto->key_len) {
>> +               fprintf(stderr, "Expected a %u-bit key, got %u-bit key\n",
>> +                       info->crypto->key_len * 8, key_len_bytes * 8);
>> +               return -1;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
> 
> [..]
> 
> Regards,
> Simon
>
Tom Rini Jan. 7, 2021, 5:25 p.m. UTC | #3
On Thu, Jan 07, 2021 at 10:27:50AM -0600, Alex G. wrote:
> On 1/7/21 6:35 AM, Simon Glass wrote:
> > Hi Alexandru,
> 
> Hi Simon,
> 
> (pun alert!) A lot of your comments have to do with comments. I use comments
> as a tool to add something of value to code. When the code is
> self-documenting, comments don't help much.
> See kernel coding style chapter 8.

Comments for comments sake are bad.  Comments so that we can also have
reasonable generated documentation are good.  Function prototypes fall
in to that second category to me, with few exceptions (and that we have
lots of bad examples isn't a good reason).  The function names may well
make it obvious what's going on but the comments allow for generated
documentation to include that when explaining the not so obvious parts.
Thanks!
Simon Glass Jan. 7, 2021, 5:29 p.m. UTC | #4
Hi Alex,

On Thu, 7 Jan 2021 at 09:27, Alex G. <mr.nuke.me@gmail.com> wrote:
>
> On 1/7/21 6:35 AM, Simon Glass wrote:
> > Hi Alexandru,
>
> Hi Simon,
>
> (pun alert!) A lot of your comments have to do with comments. I use
> comments as a tool to add something of value to code. When the code is
> self-documenting, comments don't help much.
> See kernel coding style chapter 8.
>
> What comments can do, is increase code size and break code flow -- there
> is a cost to having them. I'm certain you've seen functions that need to
> be scrolled up and down because of a huge comment smack in the middle.
> I'll address individual comment comments below.
>

Don't get me started on comments in the kernel...there seems to almost
be a ban on them :-)

We used to follow the same approach but now we have comments for
non-trivial code. Comments and tests are closely related.

- if there is no comment, we don't know what the function is supposed
to do so we can't change it (there is no contract), we are not sure if
line 5 is a bug or a real quirk, casual readers can't understand what
is going on, the automated docs don't produce anything, no one wants
to refactor it, etc.
- if there is no test, presumably the code doesn't work now, if it ever did

> >
> > On Wed, 30 Dec 2020 at 14:00, Alexandru Gagniuc <mr.nuke.me@gmail.com> wrote:
> >>
> >> mkimage supports rsa2048, and rsa4096 signatures. With newer silicon
> >> now supporting hardware-accelerated ECDSA, it makes sense to expand
> >> signing support to elliptic curves.
> >>
> >> Implement host-side ECDSA signing and verification with libcrypto.
> >> Device-side implementation of signature verification is beyond the
> >> scope of this patch.
> >>
> >> Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
> >> ---
> >>   common/image-sig.c          |  14 +-
> >>   include/u-boot/ecdsa.h      |  27 ++++
> >>   lib/ecdsa/ecdsa-libcrypto.c | 300 ++++++++++++++++++++++++++++++++++++
> >>   tools/Makefile              |   3 +
> >>   4 files changed, 342 insertions(+), 2 deletions(-)
> >>   create mode 100644 include/u-boot/ecdsa.h
> >>   create mode 100644 lib/ecdsa/ecdsa-libcrypto.c
> >>
> >> diff --git a/common/image-sig.c b/common/image-sig.c
> >> index 21dafe6b91..2548b3eb0f 100644
> >> --- a/common/image-sig.c
> >> +++ b/common/image-sig.c
> >> @@ -15,6 +15,7 @@
> >>   DECLARE_GLOBAL_DATA_PTR;
> >>   #endif /* !USE_HOSTCC*/
> >>   #include <image.h>
> >> +#include <u-boot/ecdsa.h>
> >>   #include <u-boot/rsa.h>
> >>   #include <u-boot/hash-checksum.h>
> >>
> >> @@ -82,8 +83,17 @@ struct crypto_algo crypto_algos[] = {
> >>                  .sign = rsa_sign,
> >>                  .add_verify_data = rsa_add_verify_data,
> >>                  .verify = rsa_verify,
> >> -       }
> >> -
> >> +       },
> >> +#if defined(USE_HOSTCC)
> >> +       /* Currently, only host support exists for ECDSA */
> >> +       {
> >> +               .name = "ecdsa256",
> >> +               .key_len = ECDSA256_BYTES,
> >> +               .sign = ecdsa_sign,
> >> +               .add_verify_data = ecdsa_add_verify_data,
> >> +               .verify = ecdsa_verify,
> >> +       },
> >> +#endif
> >>   };
> >>
> >>   struct padding_algo padding_algos[] = {
> >> diff --git a/include/u-boot/ecdsa.h b/include/u-boot/ecdsa.h
> >> new file mode 100644
> >> index 0000000000..a13a7267e1
> >> --- /dev/null
> >> +++ b/include/u-boot/ecdsa.h
> >> @@ -0,0 +1,27 @@
> >> +/* SPDX-License-Identifier: GPL-2.0+ */
> >> +/*
> >> + * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>.
> >> + */
> >> +
> >> +#ifndef _ECDSA_H
> >> +#define _ECDSA_H
> >> +
> >> +#include <errno.h>
> >> +#include <image.h>
> >> +
> >> +/**
> >> + * crypto_algo API impementation for ECDSA;
> >> + * @see "struct crypt_algo"
> >> + * @{
> >> + */
> >> +int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
> >> +              int region_count, uint8_t **sigp, uint *sig_len);
> >> +int ecdsa_verify(struct image_sign_info *info,
> >> +                const struct image_region region[], int region_count,
> >> +                uint8_t *sig, uint sig_len);
> >> +int ecdsa_add_verify_data(struct image_sign_info *info, void *keydest);
> >
> > Please always add full comments when you export functions, or have a
> > non-trivial static function.
>
> I disagree. These functions implement and are designed to be used via
> the crypt_algo API. One should understand the crypt_algo API, and any
> deviations in behavior would be a bug. So there is no scenario in which
> comments here would be useful.

Please add full comments on exported function, no exceptions. Again,
this is not Linux and people don't have as much time to cogitate on
code. They are just trying to get their device working.

>
> >> +/** @} */
> >> +
> >> +#define ECDSA256_BYTES (256 / 8)
> >> +
> >> +#endif
> >> diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c
> >> new file mode 100644
> >> index 0000000000..ff491411d0
> >> --- /dev/null
> >> +++ b/lib/ecdsa/ecdsa-libcrypto.c
> >> @@ -0,0 +1,300 @@
> >> +// SPDX-License-Identifier: GPL-2.0+
> >> +/*
> >> + * ECDSA image signing implementation using libcrypto backend
> >> + *
> >> + * The signature is a binary representation of the (R, S) points, padded to the
> >> + * key size. The signature will be (2 * key_size_bits) / 8 bytes.
> >> + *
> >> + * Deviations from behavior of RSA equivalent:
> >> + *  - Verification uses private key. This is not technically required, but a
> >> + *    limitation on how clumsy the openssl API is to use.
> >
> > I'm not quite sure what the implications are on this. If this is
> > public key crypto, the private key is not available, so how can you
> > verify with it?
>
> I presume this is fixable, but only as an academic exercise. This file
> is for mkimage, which doesn't do standalone verification. The way you
> verify is in u-boot. That is the subject of another series.

OK I'm just a bit confused as to how this tests anything. But sure it
can come later.

>
> >> + *  - Handling of keys and key paths:
> >> + *    - The '-K' key directory option must contain path to the key file,
> >> + *      instead of the key directory.
> >
> > If we make this change we should update RSA to do the same.
>
> Of course, but is there agreement as to this direction? There seem to be
> some hidden subtleties to key-name-hint that I don't fully understand yet.

It's just that the filename is defined by the code not the parameter.
There is a public key file and a certificate file in the same dir, so
it is convenient to specify the dir rather than the filename. But I
don't see a big problem with changing it.  Everyone will have to
update their scripts I suppose, so there is that...

Another option would be to add a new arg which specifies the exact file.

>
> >> + *    - No assumptions are made about the file extension of the key
> >> + *    - The 'key-name-hint' property is only used for naming devicetree nodes,
> >> + *      but is not used for looking up keys on the filesystem.
> >> + *
> >> + * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>
> >> + */
> >> +
> >> +#include <u-boot/ecdsa.h>
> >> +#include <u-boot/fdt-libcrypto.h>
> >> +#include <openssl/ssl.h>
> >> +#include <openssl/ec.h>
> >> +#include <openssl/bn.h>
> >> +
> >> +struct signer {
> >> +       EVP_PKEY *evp_key;
> >> +       EC_KEY *ecdsa_key;
> >> +       void *hash;
> >> +       void *signature;        /* Do not free() !*/
> >
> > need comments
>
> Is this for the sake of having comments, or is there something of value
> that I've missed? The only member that could be confusing is evp_key,
> but that becomes obvious in the context of libcrypto.

What does hash point to and what is it for? Same with signature. Why
are there two keys and what is the difference? Please, if you are
contributing to U-Boot, add comments.

>[..]

> >> +static size_t ecdsa_key_size_bytes(const EC_KEY *key)
> >
> > I think all of these functions need a comment.
>
> The function names are self-explanatory. One point of confusion would be
> the meaning of raw signature -- this is already explained at the top.

I would still like the comments please. If it's really trivial that's
fine, but this code is not obvious to me. We need to make it easy to
contribute to U-Boot.

>
> >
> >> +{
> >> +       const EC_GROUP *group;
> >> +
> >> +       group = EC_KEY_get0_group(key);
> >> +       return EC_GROUP_order_bits(group) / 8;
> >> +}
> >> +
> >> +static int read_key(struct signer *ctx, const char *key_name)
> >> +{
> >> +       FILE *f = fopen(key_name, "r");
> >> +
> >> +       if (!f) {
> >> +               fprintf(stderr, "Can not get key file '%s'\n", key_name);
> >> +               return -1;
> >
> > return -EIO perhaps?
>
> Good idea!
>
> >> +       }
> >> +
> >> +       ctx->evp_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
> >> +       fclose(f);
> >> +       if (!ctx->evp_key) {
> >> +               fprintf(stderr, "Can not read key from '%s'\n", key_name);
> >> +               return -1;
> >
> > These should return useful error number: -1 is -EPERM which doesn't
> > seem right here.
>
> -EIO again?

Sure, or perhaps a different error if this could mean that the file is corrupt.

[..]

> >> +static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
> >> +{
> >> +       const char *kname = info->keydir;
> >> +       int key_len_bytes;
> >> +
> >> +       if (alloc_ctx(ctx, info) < 0)
> >
> > That function returns 0 on success, so you don't need the < 0. A
> > comment on the above function would make that clear.
>
> I think the following would be less clear:
>
>         if (alloc_ctx())
>                  error();
>
> Does alloc_ctx() return an int? Does it return memory? It has (m)alloc
> in the name. By having a '< 0' in the predicate, it's now clear that
> this can't return a pointer. So yes, you might scratch your head as to
> why there's a '< 0' that's not needed. Also, you know that this function
> returns an error code without needing to scroll up to its definition. A
> comment above the function wouldn't eliminate the need to scroll up.

U-Boot generally returns 0 for success and -ve for error. The +ve
numbers are used when the function needs to return a value, if valid.

>
> >> +               return -1;

-ENOMEM

> >> +
> >> +       if (read_key(ctx, kname) < 0)
> >> +               return -1;

I recommend handling errors like this:

int ret;

ret = read_key(ctx, kname);
if (ret)
   return log_msg_ret("read", ret);

so that people can quickly figure out exactly what is failing either
by looking at the error number (propagated to the top) or turning on
CONFIG_LOG_ERROR_RETURN

> >> +
> >> +       key_len_bytes = ecdsa_key_size_bytes(ctx->ecdsa_key);
> >> +       if (key_len_bytes != info->crypto->key_len) {
> >> +               fprintf(stderr, "Expected a %u-bit key, got %u-bit key\n",
> >> +                       info->crypto->key_len * 8, key_len_bytes * 8);
> >> +               return -1;
> >> +       }
> >> +
> >> +       return 0;
> >> +}
> >> +
> >
> > [..]
> >
> > Regards,
> > Simon
> >

Regards,
Simon
Alexandru Gagniuc Jan. 7, 2021, 7:56 p.m. UTC | #5
On 1/7/21 11:29 AM, Simon Glass wrote:
> Hi Alex,
> 
> On Thu, 7 Jan 2021 at 09:27, Alex G. <mr.nuke.me@gmail.com> wrote:
>>
>> On 1/7/21 6:35 AM, Simon Glass wrote:
>>> Hi Alexandru,
>>
>> Hi Simon,
>>
>> (pun alert!) A lot of your comments have to do with comments. I use
>> comments as a tool to add something of value to code. When the code is
>> self-documenting, comments don't help much.
>> See kernel coding style chapter 8.
>>
>> What comments can do, is increase code size and break code flow -- there
>> is a cost to having them. I'm certain you've seen functions that need to
>> be scrolled up and down because of a huge comment smack in the middle.
>> I'll address individual comment comments below.
>>
> 
> Don't get me started on comments in the kernel...there seems to almost
> be a ban on them :-)
> 
> We used to follow the same approach but now we have comments for
> non-trivial code. Comments and tests are closely related.
> 
> - if there is no comment, we don't know what the function is supposed
> to do so we can't change it (there is no contract), we are not sure if
> line 5 is a bug or a real quirk, casual readers can't understand what
> is going on, the automated docs don't produce anything, no one wants
> to refactor it, etc.
> - if there is no test, presumably the code doesn't work now, if it ever did

I will add each of the comments you are requesting, but not for the 
reasons quoted. The comments that I could write won't add anything of 
value -- they would just make the code larger, and increase the 
cognitive load. You are the maintainer, and starting an argument would 
be pointless. In the end, comments don't get compiled, and the code 
works just the same :)

[snip]
> 
> Again,
> this is not Linux and people don't have as much time to cogitate on
> code. 

I resent that statement. It takes me longer to do a task in u-boot than 
it would take me to do a similar task in linux. In the context of 
comments (note I intentionally did not say 'documentation'), does 
plastering the same information over and over in a way that hides the 
essence really help? I think the current path is misguided, and will 
only aggravate the problem. (I'll add the comments, though)

> They are just trying to get their device working.

That's true for linux too.

[snip]

All other comments will be addressed in v3
Alexandru Gagniuc Jan. 7, 2021, 10:24 p.m. UTC | #6
On 1/7/21 11:25 AM, Tom Rini wrote:
> On Thu, Jan 07, 2021 at 10:27:50AM -0600, Alex G. wrote:
>> On 1/7/21 6:35 AM, Simon Glass wrote:
>>> Hi Alexandru,
>>
>> Hi Simon,
>>
>> (pun alert!) A lot of your comments have to do with comments. I use comments
>> as a tool to add something of value to code. When the code is
>> self-documenting, comments don't help much.
>> See kernel coding style chapter 8.
> 
> Comments for comments sake are bad.  Comments so that we can also have
> reasonable generated documentation are good.  Function prototypes fall
> in to that second category to me, with few exceptions (and that we have
> lots of bad examples isn't a good reason).  The function names may well
> make it obvious what's going on but the comments allow for generated
> documentation to include that when explaining the not so obvious parts.
> Thanks!
> 
The problem with generated documentation is that it's not very useful. 
People add comments for the sake of comments to have something 
generated. Most often you end up with a detailed description of all the 
details, but almost never the big picture. Nowadays, I don't even waste 
my time reading generated documentation.

I am getting ready to send the new series with the goodies requested by 
Simon. I don't find the new comments to be useful, and I find some to 
spread out the code such that it hurts readability. They will be there 
because you and Simon asked nicely, although I think you're wrong.

Alex
diff mbox series

Patch

diff --git a/common/image-sig.c b/common/image-sig.c
index 21dafe6b91..2548b3eb0f 100644
--- a/common/image-sig.c
+++ b/common/image-sig.c
@@ -15,6 +15,7 @@ 
 DECLARE_GLOBAL_DATA_PTR;
 #endif /* !USE_HOSTCC*/
 #include <image.h>
+#include <u-boot/ecdsa.h>
 #include <u-boot/rsa.h>
 #include <u-boot/hash-checksum.h>
 
@@ -82,8 +83,17 @@  struct crypto_algo crypto_algos[] = {
 		.sign = rsa_sign,
 		.add_verify_data = rsa_add_verify_data,
 		.verify = rsa_verify,
-	}
-
+	},
+#if defined(USE_HOSTCC)
+	/* Currently, only host support exists for ECDSA */
+	{
+		.name = "ecdsa256",
+		.key_len = ECDSA256_BYTES,
+		.sign = ecdsa_sign,
+		.add_verify_data = ecdsa_add_verify_data,
+		.verify = ecdsa_verify,
+	},
+#endif
 };
 
 struct padding_algo padding_algos[] = {
diff --git a/include/u-boot/ecdsa.h b/include/u-boot/ecdsa.h
new file mode 100644
index 0000000000..a13a7267e1
--- /dev/null
+++ b/include/u-boot/ecdsa.h
@@ -0,0 +1,27 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>.
+ */
+
+#ifndef _ECDSA_H
+#define _ECDSA_H
+
+#include <errno.h>
+#include <image.h>
+
+/**
+ * crypto_algo API impementation for ECDSA;
+ * @see "struct crypto_algo"
+ * @{
+ */
+int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
+	       int region_count, uint8_t **sigp, uint *sig_len);
+int ecdsa_verify(struct image_sign_info *info,
+		 const struct image_region region[], int region_count,
+		 uint8_t *sig, uint sig_len);
+int ecdsa_add_verify_data(struct image_sign_info *info, void *keydest);
+/** @} */
+
+#define ECDSA256_BYTES	(256 / 8)
+
+#endif
diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c
new file mode 100644
index 0000000000..ff491411d0
--- /dev/null
+++ b/lib/ecdsa/ecdsa-libcrypto.c
@@ -0,0 +1,300 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ECDSA image signing implementation using libcrypto backend
+ *
+ * The signature is a binary representation of the (R, S) points, padded to the
+ * key size. The signature will be (2 * key_size_bits) / 8 bytes.
+ *
+ * Deviations from behavior of RSA equivalent:
+ *  - Verification uses private key. This is not technically required, but a
+ *    limitation on how clumsy the openssl API is to use.
+ *  - Handling of keys and key paths:
+ *    - The '-K' key directory option must contain path to the key file,
+ *      instead of the key directory.
+ *    - No assumptions are made about the file extension of the key
+ *    - The 'key-name-hint' property is only used for naming devicetree nodes,
+ *      but is not used for looking up keys on the filesystem.
+ *
+ * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ */
+
+#include <u-boot/ecdsa.h>
+#include <u-boot/fdt-libcrypto.h>
+#include <openssl/ssl.h>
+#include <openssl/ec.h>
+#include <openssl/bn.h>
+
+struct signer {
+	EVP_PKEY *evp_key;
+	EC_KEY *ecdsa_key;
+	void *hash;
+	void *signature;	/* Do not free() !*/
+};
+
+static int alloc_ctx(struct signer *ctx, const struct image_sign_info *info)
+{
+	memset(ctx, 0, sizeof(*ctx));
+
+	if (!OPENSSL_init_ssl(0, NULL)) {
+		fprintf(stderr, "Failure to init SSL library\n");
+		return -1;
+	}
+
+	ctx->hash = malloc(info->checksum->checksum_len);
+	ctx->signature = malloc(info->crypto->key_len * 2);
+
+	if (!ctx->hash || !ctx->signature)
+		return -1;
+
+	return 0;
+}
+
+static void free_ctx(struct signer *ctx)
+{
+	if (ctx->ecdsa_key)
+		EC_KEY_free(ctx->ecdsa_key);
+
+	if (ctx->evp_key)
+		EVP_PKEY_free(ctx->evp_key);
+
+	if (ctx->hash)
+		free(ctx->hash);
+}
+
+/*
+ * Convert an ECDSA signature to raw format
+ *
+ * openssl DER-encodes 'binary' signatures. We want the signature in a raw
+ * (R, S) point pair. So we have to dance a bit.
+ */
+static void ecdsa_sig_encode_raw(void *buf, const ECDSA_SIG *sig, size_t order)
+{
+	int point_bytes = order;
+	const BIGNUM *r, *s;
+	uintptr_t s_buf;
+
+	ECDSA_SIG_get0(sig, &r, &s);
+	s_buf = (uintptr_t)buf + point_bytes;
+	BN_bn2binpad(r, buf, point_bytes);
+	BN_bn2binpad(s, (void *)s_buf, point_bytes);
+}
+
+static ECDSA_SIG *ecdsa_sig_from_raw(void *buf, size_t order)
+{
+	int point_bytes = order;
+	uintptr_t s_buf;
+	ECDSA_SIG *sig;
+	BIGNUM *r, *s;
+
+	sig = ECDSA_SIG_new();
+	if (!sig)
+		return NULL;
+
+	s_buf = (uintptr_t)buf + point_bytes;
+	r = BN_bin2bn(buf, point_bytes, NULL);
+	s = BN_bin2bn((void *)s_buf, point_bytes, NULL);
+	ECDSA_SIG_set0(sig, r, s);
+
+	return sig;
+}
+
+static size_t ecdsa_key_size_bytes(const EC_KEY *key)
+{
+	const EC_GROUP *group;
+
+	group = EC_KEY_get0_group(key);
+	return EC_GROUP_order_bits(group) / 8;
+}
+
+static int read_key(struct signer *ctx, const char *key_name)
+{
+	FILE *f = fopen(key_name, "r");
+
+	if (!f) {
+		fprintf(stderr, "Can not get key file '%s'\n", key_name);
+		return -1;
+	}
+
+	ctx->evp_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
+	fclose(f);
+	if (!ctx->evp_key) {
+		fprintf(stderr, "Can not read key from '%s'\n", key_name);
+		return -1;
+	}
+
+	if (EVP_PKEY_id(ctx->evp_key) != EVP_PKEY_EC) {
+		fprintf(stderr, "'%s' is not an ECDSA key\n", key_name);
+		return -1;
+	}
+
+	ctx->ecdsa_key = EVP_PKEY_get1_EC_KEY(ctx->evp_key);
+	if (!ctx->ecdsa_key)
+		fprintf(stderr, "Can not extract ECDSA key\n");
+
+	return (ctx->ecdsa_key) ? 0 : -1;
+}
+
+static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
+{
+	const char *kname = info->keydir;
+	int key_len_bytes;
+
+	if (alloc_ctx(ctx, info) < 0)
+		return -1;
+
+	if (read_key(ctx, kname) < 0)
+		return -1;
+
+	key_len_bytes = ecdsa_key_size_bytes(ctx->ecdsa_key);
+	if (key_len_bytes != info->crypto->key_len) {
+		fprintf(stderr, "Expected a %u-bit key, got %u-bit key\n",
+			info->crypto->key_len * 8, key_len_bytes * 8);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int do_sign(struct signer *ctx, struct image_sign_info *info,
+		   const struct image_region region[], int region_count)
+{
+	const struct checksum_algo *algo = info->checksum;
+	ECDSA_SIG *sig;
+
+	algo->calculate(algo->name, region, region_count, ctx->hash);
+	sig = ECDSA_do_sign(ctx->hash, algo->checksum_len, ctx->ecdsa_key);
+
+	ecdsa_sig_encode_raw(ctx->signature, sig, info->crypto->key_len);
+
+	return 0;
+}
+
+static int ecdsa_check_signature(struct signer *ctx, struct image_sign_info *info)
+{
+	ECDSA_SIG *sig;
+	int okay;
+
+	sig = ecdsa_sig_from_raw(ctx->signature, info->crypto->key_len);
+	if (!sig)
+		return -1;
+
+	okay = ECDSA_do_verify(ctx->hash, info->checksum->checksum_len,
+			       sig, ctx->ecdsa_key);
+	if (!okay)
+		fprintf(stderr, "WARNING: Signature is fake news!\n");
+
+	ECDSA_SIG_free(sig);
+	return !okay;
+}
+
+static int do_verify(struct signer *ctx, struct image_sign_info *info,
+		     const struct image_region region[], int region_count,
+		     uint8_t *raw_sig, uint sig_len)
+{
+	const struct checksum_algo *algo = info->checksum;
+
+	if (sig_len != info->crypto->key_len * 2) {
+		fprintf(stderr, "Signature has wrong length\n");
+		return -1;
+	}
+
+	memcpy(ctx->signature, raw_sig, sig_len);
+	algo->calculate(algo->name, region, region_count, ctx->hash);
+
+	return ecdsa_check_signature(ctx, info);
+}
+
+int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
+	       int region_count, uint8_t **sigp, uint *sig_len)
+{
+	struct signer ctx;
+	int ret;
+
+	ret = prepare_ctx(&ctx, info);
+	if (ret >= 0) {
+		do_sign(&ctx, info, region, region_count);
+		*sigp = ctx.signature;
+		*sig_len = info->crypto->key_len * 2;
+
+		ret = ecdsa_check_signature(&ctx, info);
+	}
+
+	free_ctx(&ctx);
+	return ret;
+}
+
+int ecdsa_verify(struct image_sign_info *info,
+		 const struct image_region region[], int region_count,
+		 uint8_t *sig, uint sig_len)
+{
+	struct signer ctx;
+	int ret;
+
+	ret = prepare_ctx(&ctx, info);
+	if (ret >= 0)
+		ret = do_verify(&ctx, info, region, region_count, sig, sig_len);
+
+	free_ctx(&ctx);
+	return ret;
+}
+
+static int do_add(struct signer *ctx, void *fdt, const char *key_node_name)
+{
+	int signature_node, key_node, ret, key_bits;
+	const char *curve_name;
+	const EC_GROUP *group;
+	const EC_POINT *point;
+	BIGNUM *x, *y;
+
+	signature_node = fdt_subnode_offset(fdt, 0, FIT_SIG_NODENAME);
+	if (signature_node < 0) {
+		fprintf(stderr, "Could not find 'signature node: %s\n",
+			fdt_strerror(signature_node));
+		return signature_node;
+	}
+
+	key_node = fdt_add_subnode(fdt, signature_node, key_node_name);
+	if (key_node < 0) {
+		fprintf(stderr, "Could not create '%s' node: %s\n",
+			key_node_name, fdt_strerror(key_node));
+		return key_node;
+	}
+
+	group = EC_KEY_get0_group(ctx->ecdsa_key);
+	key_bits = EC_GROUP_order_bits(group);
+	curve_name = OBJ_nid2sn(EC_GROUP_get_curve_name(group));
+	/* Let 'x' and 'y' memory leak by not BN_free()'ing them. */
+	x = BN_new();
+	y = BN_new();
+	point = EC_KEY_get0_public_key(ctx->ecdsa_key);
+	EC_POINT_get_affine_coordinates(group, point, x, y, NULL);
+
+	ret = fdt_setprop_string(fdt, key_node, "ecdsa,curve", curve_name);
+	if (ret < 0)
+		return ret;
+
+	ret = fdt_add_bignum(fdt, key_node, "ecdsa,x-point", x, key_bits);
+	if (ret < 0)
+		return ret;
+
+	ret = fdt_add_bignum(fdt, key_node, "ecdsa,y-point", y, key_bits);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int ecdsa_add_verify_data(struct image_sign_info *info, void *fdt)
+{
+	const char *fdt_key_name;
+	struct signer ctx;
+	int ret;
+
+	fdt_key_name = info->keyname ? info->keyname : "default-key";
+	ret = prepare_ctx(&ctx, info);
+	if (ret >= 0)
+		do_add(&ctx, fdt, fdt_key_name);
+
+	free_ctx(&ctx);
+	return ret;
+}
diff --git a/tools/Makefile b/tools/Makefile
index af7698fd01..a4f7b7e80c 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -70,6 +70,8 @@  RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \
 					rsa-sign.o rsa-verify.o \
 					rsa-mod-exp.o)
 
+ECDSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/ecdsa/, ecdsa-libcrypto.o)
+
 AES_OBJS-$(CONFIG_FIT_CIPHER) := $(addprefix lib/aes/, \
 					aes-encrypt.o aes-decrypt.o)
 
@@ -119,6 +121,7 @@  dumpimage-mkimage-objs := aisimage.o \
 			gpimage.o \
 			gpimage-common.o \
 			mtk_image.o \
+			$(ECDSA_OBJS-y) \
 			$(RSA_OBJS-y) \
 			$(AES_OBJS-y)