diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2014-09-26 11:04:55 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-10-10 18:23:05 +0000 |
commit | 6148f440aa8b177a0cbdaf8684d1ef5d2eba343b (patch) | |
tree | 59dde7db9f2241a3c8e9b6bd4037849c2f4640f4 | |
parent | 6e9501d31c2ea8fc0f801034eed22556bdec432c (diff) | |
download | chrome-ec-6148f440aa8b177a0cbdaf8684d1ef5d2eba343b.tar.gz |
CHERRY-PICK: add RSA signature verification code
2048-bit RSA public key cryptography signature verification code
which uses a pre-processed key for computation.
it is based on the code from vboot :
platform/vboot_reference/firmware/2lib/2rsa.c
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
BRANCH=samus
BUG=chrome-os-partner:28336
TEST=using following CL, on Zinger, verify RW firmware signature.
Change-Id: Idb7fa00ae3648273c4e0a71ad442f51a844adc13
Original-Change-Id: I681a29144eb805cd5758aa6efe697ce2f656a298
Reviewed-on: https://chromium-review.googlesource.com/220186
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Alec Berg <alecaberg@chromium.org>
Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
Tested-by: Vincent Palatin <vpalatin@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/222847
Reviewed-by: Mohammed Habibulla <moch@chromium.org>
Commit-Queue: Mohammed Habibulla <moch@chromium.org>
Tested-by: Mohammed Habibulla <moch@chromium.org>
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/rsa.c | 214 | ||||
-rw-r--r-- | include/config.h | 3 | ||||
-rw-r--r-- | include/rsa.h | 26 |
4 files changed, 244 insertions, 0 deletions
diff --git a/common/build.mk b/common/build.mk index cf76e5a0a7..cd77040a3d 100644 --- a/common/build.mk +++ b/common/build.mk @@ -59,6 +59,7 @@ common-$(CONFIG_POWER_BUTTON_X86)+=power_button_x86.o common-$(CONFIG_PSTORE)+=pstore_commands.o common-$(CONFIG_PWM)+=pwm.o common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o +common-$(CONFIG_RSA)+=rsa.o common-$(CONFIG_SHA1)+=sha1.o common-$(CONFIG_SMBUS)+= smbus.o common-$(CONFIG_SOFTWARE_CLZ)+=clz.o diff --git a/common/rsa.c b/common/rsa.c new file mode 100644 index 0000000000..f3ca8601fb --- /dev/null +++ b/common/rsa.c @@ -0,0 +1,214 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* + * Implementation of RSA signature verification which uses a pre-processed key + * for computation. + */ + +#include "rsa.h" +#include "sha256.h" +#include "util.h" + +/** + * a[] -= mod + */ +static void sub_mod(const struct rsa_public_key *key, uint32_t *a) +{ + int64_t A = 0; + uint32_t i; + for (i = 0; i < RSANUMWORDS; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +/** + * Return a[] >= mod + */ +static int ge_mod(const struct rsa_public_key *key, const uint32_t *a) +{ + uint32_t i; + for (i = RSANUMWORDS; i;) { + --i; + if (a[i] < key->n[i]) + return 0; + if (a[i] > key->n[i]) + return 1; + } + return 1; /* equal */ +} + +/** + * Montgomery c[] += a * b[] / R % mod + */ +static void mont_mul_add(const struct rsa_public_key *key, + uint32_t *c, + const uint32_t a, + const uint32_t *b) +{ + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + uint32_t i; + + for (i = 1; i < RSANUMWORDS; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) + sub_mod(key, c); +} + +/** + * Montgomery c[] = a[] * b[] / R % mod + */ +static void mont_mul(const struct rsa_public_key *key, + uint32_t *c, + const uint32_t *a, + const uint32_t *b) +{ + uint32_t i; + for (i = 0; i < RSANUMWORDS; ++i) + c[i] = 0; + + for (i = 0; i < RSANUMWORDS; ++i) + mont_mul_add(key, c, a[i], b); +} + +/** + * In-place public exponentiation. + * + * @param key Key to use in signing + * @param inout Input and output big-endian byte array + * @param workbuf32 Work buffer; caller must verify this is + * 3 x RSANUMWORDS elements long. + */ +static void mod_pow_F4(const struct rsa_public_key *key, uint8_t *inout, + uint32_t *workbuf32) +{ + uint32_t *a = workbuf32; + uint32_t *a_r = a + RSANUMWORDS; + uint32_t *aa_r = a_r + RSANUMWORDS; + uint32_t *aaa = aa_r; /* Re-use location. */ + int i; + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0; i < RSANUMWORDS; ++i) { + uint32_t tmp = + (inout[((RSANUMWORDS - 1 - i) * 4) + 0] << 24) | + (inout[((RSANUMWORDS - 1 - i) * 4) + 1] << 16) | + (inout[((RSANUMWORDS - 1 - i) * 4) + 2] << 8) | + (inout[((RSANUMWORDS - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + mont_mul(key, a_r, a, key->rr); /* a_r = a * RR / R mod M */ + for (i = 0; i < 16; i += 2) { + mont_mul(key, aa_r, a_r, a_r); /* aa_r = a_r * a_r / R mod M */ + mont_mul(key, a_r, aa_r, aa_r);/* a_r = aa_r * aa_r / R mod M */ + } + mont_mul(key, aaa, a_r, a); /* aaa = a_r * a / R mod M */ + + /* Make sure aaa < mod; aaa is at most 1x mod too large. */ + if (ge_mod(key, aaa)) + sub_mod(key, aaa); + + /* Convert to bigendian byte array */ + for (i = RSANUMWORDS - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = (uint8_t)(tmp >> 24); + *inout++ = (uint8_t)(tmp >> 16); + *inout++ = (uint8_t)(tmp >> 8); + *inout++ = (uint8_t)(tmp >> 0); + } +} + +/* + * PKCS#1 padding (from the RSA PKCS#1 v2.1 standard) + * + * The DER-encoded padding is defined as follows : + * 0x00 || 0x01 || PS || 0x00 || T + * + * T: DER Encoded DigestInfo value which depends on the hash function used, + * for SHA-256: + * (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H. + * + * Length(T) = 51 octets for SHA-256 + * + * PS: octet string consisting of {Length(RSA Key) - Length(T) - 3} 0xFF + */ +static const uint8_t sha256_tail[] = { + 0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x20 +}; + +#define PKCS_PAD_SIZE (RSANUMBYTES - SHA256_DIGEST_SIZE) + +/** + * Check PKCS#1 padding bytes + * + * @param sig Signature to verify + * @return 0 if the padding is correct. + */ +static int check_padding(const uint8_t *sig) +{ + uint8_t *ptr = (uint8_t *)sig; + int result = 0; + int i; + + /* First 2 bytes are always 0x00 0x01 */ + result |= *ptr++ ^ 0x00; + result |= *ptr++ ^ 0x01; + + /* Then 0xff bytes until the tail */ + for (i = 0; i < PKCS_PAD_SIZE - sizeof(sha256_tail) - 2; i++) + result |= *ptr++ ^ 0xff; + + /* Check the tail. */ + result |= memcmp(ptr, sha256_tail, sizeof(sha256_tail)); + + return !!result; +} + +/* + * Verify a 2048 bit SHA256WithRSA PKCS#1 v1.5 signature against an expected + * SHA256 hash. + * + * @param key RSA public key + * @param signature 2048-bit RSA signature + * @param sha SHA-256 digest of the content to verify + * @param workbuf32 Work buffer; caller must verify this is + * 3 x RSANUMWORDS elements long. + * @return 0 on failure, 1 on success. + */ +int rsa_verify(const struct rsa_public_key *key, const uint8_t *signature, + const uint8_t *sha, uint32_t *workbuf32) +{ + uint8_t buf[RSANUMBYTES]; + + /* Copy input to local workspace. */ + memcpy(buf, signature, RSANUMBYTES); + + mod_pow_F4(key, buf, workbuf32); /* In-place exponentiation. */ + + /* Check the PKCS#1 padding */ + if (check_padding(buf) != 0) + return 0; + + /* Check the digest. */ + if (memcmp(buf + PKCS_PAD_SIZE, sha, SHA256_DIGEST_SIZE) != 0) + return 0; + + return 1; /* All checked out OK. */ +} diff --git a/include/config.h b/include/config.h index cbc460408a..6da9a55c1e 100644 --- a/include/config.h +++ b/include/config.h @@ -799,6 +799,9 @@ /* Support IR357x Link voltage regulator debugging / reprogramming */ #undef CONFIG_REGULATOR_IR357X +/* Support verifying 2048-bit RSA signature */ +#undef CONFIG_RSA + /* * If defined, the hash module will save its last computed hash when jumping * between EC images. diff --git a/include/rsa.h b/include/rsa.h new file mode 100644 index 0000000000..95e17450c6 --- /dev/null +++ b/include/rsa.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef _INCLUDE_RSA_H +#define _INCLUDE_RSA_H + +#include "common.h" + +#define RSANUMBYTES 256 /* 2048 bit key length */ +#define RSANUMWORDS (RSANUMBYTES / sizeof(uint32_t)) + +/* 2048-bit RSA public key definition */ +struct rsa_public_key { + uint32_t n[RSANUMWORDS]; /* modulus as little endian array */ + uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */ + uint32_t n0inv; /* -1 / n[0] mod 2^32 */ +}; + +int rsa_verify(const struct rsa_public_key *key, + const uint8_t *signature, + const uint8_t *sha, + uint32_t *workbuf32); + +#endif /* _INCLUDE_RSA_H */ |