public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
From: Alex G. <mr.nuke.me@gmail.com>
To: u-boot@lists.denx.de
Subject: [PATCH RFC v2 3/5] lib: Add support for ECDSA image signing
Date: Thu, 7 Jan 2021 10:27:50 -0600	[thread overview]
Message-ID: <826b4d42-c9bb-70cf-9deb-c2b29145e10f@gmail.com> (raw)
In-Reply-To: <CAPnjgZ1rJgbAvEA3A4NeC-VdNvKmMmiQx73qJJ+UXrPoRuPs_w@mail.gmail.com>

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
> 

  reply	other threads:[~2021-01-07 16:27 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-30 21:00 [PATCH RFC v2 0/5] Add support for ECDSA image signing (with test) Alexandru Gagniuc
2020-12-30 21:00 ` [PATCH RFC v2 1/5] lib: Rename rsa-checksum.c to hash-checksum.c Alexandru Gagniuc
2021-01-07 12:35   ` Simon Glass
2020-12-30 21:00 ` [PATCH RFC v2 2/5] lib/rsa: Make fdt_add_bignum() available outside of RSA code Alexandru Gagniuc
2021-01-07 12:35   ` Simon Glass
2020-12-30 21:00 ` [PATCH RFC v2 3/5] lib: Add support for ECDSA image signing Alexandru Gagniuc
2021-01-07 12:35   ` Simon Glass
2021-01-07 16:27     ` Alex G. [this message]
2021-01-07 17:25       ` Tom Rini
2021-01-07 22:24         ` Alex G.
2021-01-07 17:29       ` Simon Glass
2021-01-07 19:56         ` Alex G.
2020-12-30 21:00 ` [PATCH RFC v2 4/5] doc: signature.txt: Document devicetree format for ECDSA keys Alexandru Gagniuc
2021-01-07 12:35   ` Simon Glass
2020-12-30 21:00 ` [PATCH RFC v2 5/5] test/py: ecdsa: Add test for mkimage ECDSA signing Alexandru Gagniuc
2021-01-07 12:35   ` Simon Glass
2021-01-07 16:44     ` Alex G.
2021-01-07 17:31       ` Simon Glass
2021-01-07 18:44         ` Alex G.

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=826b4d42-c9bb-70cf-9deb-c2b29145e10f@gmail.com \
    --to=mr.nuke.me@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox