RE: [cbootimage PATCH v1 4/8] Add new configuration keyword "PkcKey"

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> -----Original Message-----
> From: Stephen Warren [mailto:swarren@xxxxxxxxxxxxx]
> Sent: Monday, September 21, 2015 2:41 PM
> To: Jimmy Zhang
> Cc: Allen Martin; Stephen Warren; linux-tegra@xxxxxxxxxxxxxxx
> Subject: Re: [cbootimage PATCH v1 4/8] Add new configuration keyword
> "PkcKey"
> 
> 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
> 
OK.

> > 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")?
> 
OK.

> > 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?
> 
OK. Then it is Open SSL format.

> > diff --git a/src/cbootimage.h b/src/cbootimage.h
> 
> > +typedef enum
> > +{
> > +	false = 0,
> > +	true = 1,
> > +} bool;
> 

I can try again. Compiler complains for missing type 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).

Will remove "rsa_filename".

> 
> > 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?
> 

This is because type NvTegraPkcKey is not the same as key type defined by libmcrypto. 

> > +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?
> 
Will clean it up. 

> > @@ -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,
> 

OK.

> 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.
> 

Will clean it up.

> > +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.
> 

OK.

> > 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?
> 
> 
Key length should have been verified before calling this function..

> > +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 }
> 

Many functions are directly ported from Tegrasign.

> 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.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux