mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-01 04:43:50 +00:00
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: 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>
215 lines
5.2 KiB
C
215 lines
5.2 KiB
C
/* 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. */
|
|
}
|