From: Stephen Warren <swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
To: Jimmy Zhang <jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Cc: amartin-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org,
swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org,
linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: Re: [cbootimage PATCH v1 4/8] Add new configuration keyword "PkcKey"
Date: Mon, 21 Sep 2015 15:41:20 -0600 [thread overview]
Message-ID: <56007980.6060203@wwwdotorg.org> (raw)
In-Reply-To: <1441228760-26042-5-git-send-email-jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
On 09/02/2015 03:19 PM, Jimmy Zhang wrote:
> Use "PkcKey" to specify rsa key filename and load in
> rsa private keys from file.
>
> When keyword "PkcKey" is present, rsa pss signatures are generated
> for both bootloader and bct. rsa pubkey will also be filled into bct.
Oh, so the config file can either specify a literal value for all the
hashes, or specify a key and have cbootimage generate them? It would be
good to add some form of high-level documentation of the use-cases into
README.txt
> PkcKey syntax:
>
> PkcKey = <rsa_key_filename> [, --save];
>
> Examples:
>
> PkcKey = rsa_priv.txt;
> Load in keys from file rsa_priv.txt
>
> PkcKey = rsa_priv.pem, --save;
> Load in keys from file rsa_priv.pem and save pubkey and pubkey hash
> value to file pubkey.mod and pubkey.sha respectively.
Can't the output filenames be either specified in the config file, or
derived from the filename passed to PkcKey (so rsa_priv.pem ->
rsa_priv.mod, rsa_priv.sha, rather than presumably hard-coding
"pubkey.mod", "pubkey.sha")?
> Two key formats are supported.
> 1. Polar SSL format
> P = ...
> Q = ...
>
> 2. Open SSL format
> -----BEGIN RSA PRIVATE KEY-----
> ...
> -----END RSA PRIVATE KEY-----
Do we need to support two formats; can't the openssl (or other) cmdline
application convert the data file between the formats to reduce the
complexity in cbootimage?
> diff --git a/src/cbootimage.h b/src/cbootimage.h
> +typedef enum
> +{
> + false = 0,
> + true = 1,
> +} bool;
This is a standard type; the definition should come from a standard
system header file.
> @@ -110,6 +117,7 @@ typedef struct build_image_context_rec
>
> char *bct_filename;
> char *rsa_filename;
> + void *pkckey;
Please expand on what this variable means (comment or better field name).
> diff --git a/src/crypto.c b/src/crypto.c
> - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
> + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved.
Please don't delete old copyright dates, but just add to them. i.e.
"2012, 2015".
> +#include "libm/pkcs1-rsa.h"
> +#include "libm/hash.h"
I would expect libm/include/ to be in the #include path via a -I
directive, so those would be:
#include <mcrypto/pkcs1-rsa.h>
#include <mcrypto/hash.h>
(At least the examples that ship with libmcrypto appear to expect the
library gets installed with the <libmcrypto/> path available, so we
should match that in any code using libmcrypto)
> +static u_int8_t *pkc_get_pubkey(void *key)
> +{
> + NvTegraPkcKey *pPkcKey = (NvTegraPkcKey *)key;
> + return (u_int8_t *)pPkcKey->Modulus.Number;
> +}
pkc_get_pubkey_modulus()? Since presumably *pPkcKey contains more than
just the Modulus field?
> +int
> +pkc_sign_buffer(void *key, u_int8_t *buffer, u_int32_t size, u_int8_t **pSign)
...
> + /* TODO: define constant for ssk.len */
> + ssk.len = (UINT)pPkcKey->Modulus.Digits; /* length in digits of modulus in term of 32 bits */
What is the implication of this TODO? Why isn't this dynamic assignment
acceptable?
> @@ -283,17 +321,116 @@ sign_bct(build_image_context *context,
> - e = sign_data_block(bct + Offset, length, hash_buffer);
> - if (e != 0)
> + if ((e = sign_data_block(bct + Offset, length, hash_buffer)) != 0)
> goto fail;
The original code was cleaner.
> - e = g_soc_config->set_data(token_crypto_hash,
> + if ((e = g_soc_config->set_data(token_crypto_hash,
Same here. It's much better to assign the return code to a variable then
test that variable rather than smash everything together into one
hard-to-read expression. The same comment applies elsewhere too.
> + /* pkc signing? */
> + if (context->pkckey) {
...
> + g_soc_config->set_value(token_pub_key_modulus,
> + pkc_get_pubkey(context->pkckey),
> + bct);
> + }
> +
> + fail:
> + free(hash_buffer);
> + return e;
> +}
> +
> goto fail;
>
> fail:
> free(hash_buffer);
> return e;
> }
> +void
> +SwapEndianness(
Something is wrong with the indentation at the end of that function, and
there should be a blank line between the functinos.
> +int
> +pkc_savefile(const char *pFilename,
> + u_int8_t *buffer, size_t bytes, const char* ext)
> +{
> + int ret = 0;
> + char *fn = malloc(sizeof(pFilename) + sizeof(ext) + 1);
sizeof yields the size of the pointer. I suspect you mean strlen.
> + FILE *output_file;
> +
> + sprintf(fn, "%s%s", pFilename, ext);
> +
> + printf("Saving file %s\n", fn);
> + output_file = fopen(fn, "w+");
I assume this is a binary file given the use of fwrite() below. I don't
see any mixture of reading and writing. So that should be "wb".
> + if (output_file == NULL) {
> + printf("Error opening raw file %s.\n", fn);
> + ret = -1;
> + goto fail;
> + }
Indentation is wrong there; I suggest scanning all the patches for
TABs-vs-spaces.
> +int
> +pkc_save_pubkey(
> + void *pKey,
> + const char *pFileName)
I think this is saving the modulus and hash, not the key itself? So,
pkc_save_pubkey_modulus_and_hash()?
> diff --git a/src/crypto.h b/src/crypto.h
> +#define PUBKEY_FILENAME "pubkey."
that shouldn't be hard-coded.
> +#define PUBKEY_MODULUS "mod"
> +#define PUBKEY_MODULUS_HASH "sha"
Those names don't imply they're filename extensions.
PUBKEY_FILENAME_EXT_{MODULUS,HASH} would be better.
> +#define BCT_FILENAME "bct."
> +#define BL_FILENAME "bl."
FILENAME_PREFIX/SUFFIX?
> +#define EXT_SIGNATURE "sig"
FILENAME_EXT_SIGNATURE?
> +#define NV_BIGINT_MAX_DW 64
A comment re: why that's the right max size would be useful.
> +}NvTegraPkcsVersion;
> +}NvTegraPkcKey;
Missing space after }.
> diff --git a/src/data_layout.c b/src/data_layout.c
> @@ -620,6 +620,24 @@ write_image(build_image_context *context, file_type image_type)
> token_bl_crypto_hash,
> (u_int32_t*)hash_buffer,
> + /* save bl sig to file bl.sig */
> + pkc_savefile(BL_FILENAME, (u_int8_t *)sign,
> + RSA_KEY_BYTE_SIZE, EXT_SIGNATURE);
This filename also shouldn't be hard-coded.
> diff --git a/src/parse.c b/src/parse.c
> +static int
> +parse_pkckey_file(build_image_context *context, parse_token token, char *rest)
> + /* check save option */
> + if (*rest == ',') {
> + ++rest;
> +
> + /* Parse option. */
> + if (strstr(rest, OPTION_SAVE) != NULL)
> + save_key = 1;
This should error out if there's any other unexpected garbage.
> + }
else error?
> diff --git a/src/rsa_key_parse.c b/src/rsa_key_parse.c
> +static void pkc_string_to_prime(u_int8_t *pBuffer, u_int8_t *pPrimeNum)
> +{
> + u_int32_t i = 0;
> +
> + while (i < (RSA_KEY_BYTE_SIZE * 2)) {
> + *pPrimeNum = HEX_TO_DEC(pBuffer[i]) * 16 +
> + HEX_TO_DEC(pBuffer[i + 1]);
> + i += 2;
> + pPrimeNum++;
> + }
What if the string is too short or has an odd length; are those problems
possible?
> +static bool
> +pkc_extract_polar_ssl_primes(
> + /* Parse POLARSSL format */
> + pTokenP = (u_int8_t *)strstr((char *)pBuffer, PRIME_PATTERN_P);
> + pTokenQ = (u_int8_t *)strstr((char *)pBuffer, PRIME_PATTERN_Q);
> +
> + if (pTokenP != NULL && pTokenQ != NULL) {
There would be fewer indentation levels if this was:
if (!pTokenP || !pTokenQ)
return false;
> + printf("PKC key in PolarSSL format\n");
Shouldn't that be debug output only? The tool shouldn't be noisy all the
time.
> +static int
> +NvTegraPkcAsnParser(
> + NvU8 *pBuf,
> + NvU32 BufSize,
> + NvU8 *pP,
> + NvU8 *pQ)
> +{
> + NvS32 i = 0;
> + NvS32 LocalSize = 0;
> + NvU32 Index = 0;
> + NvU32 SequenceNum = 0;
> + NvU32 IntegerNum = 0;
> + NvU32 Expo = 0;
> + NvU8 Type = 0;
All these initializations just hide used-before-initialized bugs. Can
you please try removing them? The same comment applies elsewhere.
> + case INTEGER: /* INTEGER */
> + switch(IntegerNum)
> + {
> + case 0: /* PrivateKeyInfo version */
> + case 1: /* PrivateKey version */
> + case 3: /* Modulus */
...
> + case 2: /* Public Exponent */
All those instances of multiple spaces should be collapsed to just 1.
The same comment may apply elsewhere.
> + case 4: /* P */
...
> + for (i = 0, Index++; i < LocalSize; i++)
> + {
> + *(pP++) = pBuf[Index++];
> + }
That looks like memcpy. {} aren't needed.
> + case 5: /* Q */
Cases 4 and 5 are identical, save for whether writing to pP or pQ. Can
they be combined?
> + default:
> + /* Ideally control should not come here for a .pk8 file */
> + return NvError_BadParameter;
"Ideally" implies "should not happen, but we accept it if it happens".
This seems to error out if that happens, which is a strong assertion.
> +static bool
> +pkc_extract_open_ssl_primes(
> + if ((memcmp(pBuffer, DerFormat, sizeof(DerFormat)) != 0) &&
> + (memcmp(pBuffer, PEMFORMAT_START, sizeof(PEMFORMAT_START) - 1) != 0))
> + {
> + return false;
> + }
Is it guaranteed that in PEM format, there are no blank lines or other
data at the start/end of the file? I don't expect so since that's the
entire point of the large strings that are used as PEM format markers,
but perhaps.
> + DerKeySize = BufSize - sizeof(PEMFORMAT_END);
> + pPemStart = pBuffer + sizeof(PEMFORMAT_START);
> +
> + if (memcmp(pBuffer + DerKeySize, PEMFORMAT_END,
> + sizeof(PEMFORMAT_END) - 1) != 0)
> + {
> + return false;
> + }
What if the file is shorter than DerKeySize + strlen(PEMFORMAT_START) +
strlen(PEMFORMAT_END)?
> + printf("PKC key in Open SSL format\n");
> +
> + if (memcmp(pBuffer, DerFormat, sizeof(DerFormat)) != 0)
> + {
Please be consistent about wrapping/not-wrapping braces. I think this
project uses wrapped braces.
> + DerKeySize = DerKeySize - sizeof(PEMFORMAT_START);
> +
> + e = NvBase64Decode(pPemStart, DerKeySize, NULL, &Base64Size);
> + if (e != NvSuccess) {
> + printf("Base 64 decoding size failed\n");
> + goto fail;
> + }
> +
> +
> + pBase64Buf = malloc(Base64Size);
Two blank lines there, and indentation issues throughout the function.
> +fail:
> +
> + if (pBase64Buf != NULL)
> + free(pBase64Buf);
free() handles NULL.
> + if (e != NvSuccess)
> + return false;
> +
> + return true;
that's: return e == NvSuccess;
> +static bool
> +pkc_extract_private_keys(
> + u_int8_t *pBuffer,
> + u_int32_t BufSize,
> + u_int8_t *pP,
> + u_int8_t *pQ)
> +{
> + return
> + ((pkc_extract_open_ssl_primes(pBuffer, BufSize, pP, pQ) == true) ||
> + (pkc_extract_polar_ssl_primes(pBuffer, BufSize, pP, pQ) == true));
Wonky indentation. No need for "== true";
> +static NvU32 NvInverseDigit(NvU32 x)
> +{
> + NvU32 i = 1;
> + NvU32 j = 2;
> + do
> + {
> + i ^= (j ^ (j & (x * i)));
> + j += j;
> + }
> + while (j);
"while" should be wrapped with }
What does this function do and how/why? A comment is needed.
> +static NvU32
> +NvBigIntIsZero(
> + const NvU32 *x,
> + const NvU32 Digits)
> +{
> + NvU32 i;
> + for (i = 0; i < Digits; i++)
> + {
> + if (x[i] != 0)
> + {
> + return NV_FALSE;
> + }
No need for {}.
> +static NvU32
> +NvBigIntSubtract(
> + NvU32 *z,
> + const NvU32 *x,
> + const NvU32 *y,
> + const NvU32 Digits)
> +{
> + NvU32 i, j, k;
> + for (i = k = 0; i < Digits; i ++)
> + {
> + j = x[i] - k;
> + k = (j > (~k)) ? 1 : 0;
> + j -= y[i];
> + k += (j > (~y[i])) ? 1 : 0;
> + z[i] = j;
> + }
> + return k;
">" should evaluate to 1 or 0, so you can drop the ?:.
It would help to use sane variable names, e.g k -> borrow, j -> tmp or
something like that.
I would also have expected simpler checks for borrow and only a single
check to be necessary. i.e. a final "if (j > x[i])". I assume that an
NvBigInt is unsigned? NvBigIntcompare's implementation seems to imply so.
I wonder why all these math functions are re-implemented rather than
making use of the "big digits" code in libmcrypto? Where did all this
math code come from?
> +static NvU32 NvBigIntGetBit(const NvU32 *x, NvU32 i)
> +{
> + NvU32 b = 0;
> +
> + return (((x[i >> 5] >> (i & 0x1f)) ^ b) & 1);
Why "^ b" when b is hard-coded to 0?
I haven't reviewed the rest of the math functions in any way.
> +NvBase64Decode(
> + if (pOutBuf == NULL)
> + {
> + /* Valid if the caller is requesting the size of the decoded
> + * binary buffer so they know how much to alloc.
> + */
> + *pOutBufSize = 0;
> + }
> + else
> + {
> + /* Validate the size of the passed input data buffer.
> + * In theory the input buffer size should be 3/4 the size of
> + * the decoded buffer. But if there were some white
> + * space chars in input buffer then it is possible that the
> + * input buffer is smaller.
> + * First validate against 2/3rds the size of the input buffer
> + * here. That allows for some slop.
> + * Below code makes sure output buffer size is big enough.
> + */
> + if (*pOutBufSize < (InBufSize * 2)/3)
If there's a known maximal minimum size for the buffer, why not just
return that in the !pOutBuf case rather than executing the big/slow loop
below?
> + /* This loop is less efficient than it could be because
> + * it's designed to tolerate bad characters (like whitespace)
> + * in the input buffer.
> + */
> + while (i < InBufSize)
> + {
> + NvU8 CurrVal;
> + NvU32 ValidLen = 0;
> + NvU8 ValidBuf[4];
> +
> + // gather 4 good chars from the pInBufput stream
Inconsistent comment style.
> + if (pOutBuf == NULL)
> + {
> + // just measurpInBufg the size of the destpInBufation buffer
Search/replace typos there.
> + switch (ValidLen)
> + {
> + case 4:
> + // 4 pInBufput chars equals 3 pOutBufput chars
> + pOutBuf[2] = (unsigned char ) (((ValidBuf[2] << 6) & 0xc0) | ValidBuf[3]);
> + // fall through
> + case 3:
> + // 3 pInBufput chars equals 2 pOutBufput chars
> + pOutBuf[1] = (unsigned char ) (ValidBuf[1] << 4 | ValidBuf[2] >> 2);
> + // fall through
> + case 2:
> + // 2 pInBufput chars equals 1 pOutBufput char
> + pOutBuf[0] = (unsigned char ) (ValidBuf[0] << 2 | ValidBuf[1] >> 4);
> + pOutBuf += ValidLen - 1;
> + break;
> + case 1:
> + // Unexpected
> + break;
> + case 0:
> + // conceivable if white space follows the end of valid data
> + break;
> + default:
> + // Unexpected
> + break;
> + }
Shouldn't at least cases 1, return an error?
There are lots of formatting errors in this patch that I didn't
explicitly call out. Lots of cleanup is required.
I didn't review the math code. It'd be best if it was replaced with
something pre-existing rather than re-inventing the wheel.
Someone familiar with our chip security needs to review the crypto usage
in this code.
> diff --git a/src/rsa_key_parse.h b/src/rsa_key_parse.h
> +#define HEX_TO_DEC(c) \
> + (((c) >= '0' && (c) <= '9') ? (c) - '0' : \
> + ((c) >= 'a' && (c) <= 'f') ? (c) - 'a' + 10 : \
> + ((c) >= 'A' && (c) <= 'F') ? (c) - 'A' + 10 : -1)
I believe that's already in libmcrypto's hex2byte().
> +/* Explicitly sized signed and unsigned ints. */
> +typedef unsigned char NvU8; /**< 0 to 255 */
> +typedef unsigned short NvU16; /**< 0 to 65535 */
> +typedef unsigned int NvU32; /**< 0 to 4294967295 */
> +typedef unsigned long long NvU64; /**< 0 to 18446744073709551615 */
> +typedef signed char NvS8; /**< -128 to 127 */
> +typedef signed short NvS16; /**< -32768 to 32767 */
> +typedef signed int NvS32; /**< -2147483648 to 2147483647 */
> +typedef signed long long NvS64; /**< 2^-63 to 2^63-1 */
Let's not introduce even more types. cbootimage already has types for these.
> +#define NV_TRUE 1
> +#define NV_FALSE 0
Let's just use defines from system header files for these.
> +#define NvSuccess 0
> +#define NvError_BadParameter -10
> +#define NvError_InsufficientMemory -11
Can't we use error codes from <errno.h>?
> +#endif /* #ifndef INCLUDED_RSA_KEY_PARSE_H_N */
Just "#endif"; no need for the comment.
next prev parent reply other threads:[~2015-09-21 21:41 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-02 21:19 [cbootimage PATCH v1 0/8] Add rsa pss signature support Jimmy Zhang
[not found] ` <1441228760-26042-1-git-send-email-jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-09-02 21:19 ` [cbootimage PATCH v1 1/8] Enable --update | -u option support for t210 Jimmy Zhang
[not found] ` <1441228760-26042-2-git-send-email-jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-09-21 19:55 ` Stephen Warren
[not found] ` <5600609F.60002-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-09-21 23:07 ` Jimmy Zhang
2015-09-02 21:19 ` [cbootimage PATCH v1 2/8] Add bct_dump " Jimmy Zhang
[not found] ` <1441228760-26042-3-git-send-email-jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-09-21 20:05 ` Stephen Warren
[not found] ` <56006319.5030009-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-09-21 23:27 ` Jimmy Zhang
2015-09-02 21:19 ` [cbootimage PATCH v1 3/8] Add in libmcrypto Jimmy Zhang
[not found] ` <1441228760-26042-4-git-send-email-jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-09-21 20:11 ` Stephen Warren
[not found] ` <56006457.4040601-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-09-22 0:05 ` Jimmy Zhang
2015-09-02 21:19 ` [cbootimage PATCH v1 4/8] Add new configuration keyword "PkcKey" Jimmy Zhang
[not found] ` <1441228760-26042-5-git-send-email-jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-09-21 21:41 ` Stephen Warren [this message]
[not found] ` <56007980.6060203-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-09-22 18:07 ` Jimmy Zhang
2015-09-02 21:19 ` [cbootimage PATCH v1 5/8] Fix some issues found in libmcrypto Jimmy Zhang
[not found] ` <1441228760-26042-6-git-send-email-jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-09-21 22:08 ` Stephen Warren
[not found] ` <56007FEB.7010408-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-09-22 1:11 ` Jimmy Zhang
2015-09-02 21:19 ` [cbootimage PATCH v1 6/8] Add new configuration keyword "ReSignBl" Jimmy Zhang
[not found] ` <1441228760-26042-7-git-send-email-jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-09-21 22:10 ` Stephen Warren
[not found] ` <5600804F.402-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-09-22 0:45 ` Jimmy Zhang
2015-09-02 21:19 ` [cbootimage PATCH v1 7/8] Add new command line option "--sign | -n" to sign binary image Jimmy Zhang
[not found] ` <1441228760-26042-8-git-send-email-jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-09-21 22:13 ` Stephen Warren
[not found] ` <560080F8.9090008-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-09-22 0:36 ` Jimmy Zhang
2015-09-02 21:19 ` [cbootimage PATCH v1 8/8] Bump to version 1.6 Jimmy Zhang
2015-09-08 17:19 ` [cbootimage PATCH v1 0/8] Add rsa pss signature support Jimmy Zhang
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=56007980.6060203@wwwdotorg.org \
--to=swarren-3lzwwm7+weoh9zmkesr00q@public.gmane.org \
--cc=amartin-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org \
--cc=jimmzhang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org \
--cc=linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org \
/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;
as well as URLs for NNTP newsgroup(s).