| /* Copyright 2015 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "dcrypto.h" |
| #include "debug_printf.h" |
| #include "registers.h" |
| #include "setup.h" |
| #include "trng.h" |
| |
| #define LOADERKEYEXP 3 |
| #define RSA_NUM_WORDS 96 |
| #define RSA_NUM_BYTES (RSA_NUM_WORDS * 4) |
| |
| #define RANDOM_STEP 5 |
| |
| inline uint32_t bswap(uint32_t a) |
| { |
| uint32_t result; |
| |
| __asm__ volatile("rev %0, %1;" : "=r"(result) : "r"(a)); |
| |
| return result; |
| } |
| |
| /* Montgomery c[] += a * b[] / R % key. */ |
| static void montMulAdd(const uint32_t *key, |
| uint32_t *c, const uint32_t a, |
| const uint32_t *b) |
| { |
| register uint64_t tmp; |
| uint32_t i, A, B, d0; |
| |
| { |
| |
| tmp = c[0] + (uint64_t)a * b[0]; |
| A = tmp >> 32; |
| d0 = (uint32_t)tmp * *key++; |
| tmp = (uint32_t)tmp + (uint64_t)d0 * *key++; |
| B = tmp >> 32; |
| } |
| |
| for (i = 0; i < RSA_NUM_WORDS - 1; ++i) { |
| tmp = A + (uint64_t)a * b[i + 1] + c[i + 1]; |
| A = tmp >> 32; |
| tmp = B + (uint64_t)d0 * *key++ + (uint32_t)tmp; |
| c[i] = (uint32_t)tmp; |
| B = tmp >> 32; |
| } |
| |
| c[RSA_NUM_WORDS - 1] = A + B; |
| } |
| |
| /* Montgomery c[] = a[] * b[] / R % key. */ |
| static void montMul(const uint32_t *key, |
| uint32_t *c, const uint32_t *a, |
| const uint32_t *b) |
| { |
| int i; |
| |
| for (i = 0; i < RSA_NUM_WORDS; ++i) |
| c[i] = 0; |
| |
| for (i = 0; i < RSA_NUM_WORDS; ++i) |
| montMulAdd(key, c, a[i], b); |
| } |
| |
| /* Montgomery c[] = a[] * 1 / R % key. */ |
| static void montMul1(const uint32_t *key, |
| uint32_t *c, const uint32_t *a) |
| { |
| int i; |
| |
| for (i = 0; i < RSA_NUM_WORDS; ++i) |
| c[i] = 0; |
| |
| montMulAdd(key, c, 1, a); |
| for (i = 1; i < RSA_NUM_WORDS; ++i) |
| montMulAdd(key, c, 0, a); |
| } |
| |
| /* In-place exponentiation to power 3 % key. */ |
| static void modpow3(const uint32_t *key, |
| const uint32_t *signature, uint32_t *out) |
| { |
| static uint32_t aaR[RSA_NUM_WORDS]; |
| static uint32_t aaaR[RSA_NUM_WORDS]; |
| |
| montMul(key, aaR, signature, signature); |
| montMul(key, aaaR, aaR, signature); |
| montMul1(key, out, aaaR); |
| } |
| |
| void LOADERKEY_verify(const uint32_t *key, const uint32_t *signature, |
| const uint32_t *sha256) |
| { |
| static uint32_t buf[RSA_NUM_WORDS] |
| __attribute__((section(".guarded_data"))); |
| static uint32_t hash[SHA256_DIGEST_WORDS] |
| __attribute__((section(".guarded_data"))); |
| uint32_t step, offset; |
| int i; |
| |
| modpow3(key, signature, buf); |
| VERBOSE("sig %ph\n", HEX_BUF(buf, 384)); |
| |
| /* |
| * If key was not 3Kb, assume 2Kb and expand for subsequent |
| * padding + hash verification mangling. |
| */ |
| if (key[96] == 0) { |
| buf[95] ^= buf[63]; |
| buf[63] ^= 0x1ffff; |
| for (i = 63; i < 95; ++i) |
| buf[i] ^= -1; |
| } |
| |
| /* |
| * XOR in offsets across buf. Mostly to get rid of all those -1 words |
| * in there. |
| */ |
| offset = rand() % RSA_NUM_WORDS; |
| step = (RANDOM_STEP % RSA_NUM_WORDS) || 1; |
| |
| for (i = 0; i < RSA_NUM_WORDS; ++i) { |
| buf[offset] ^= (0x1000u + offset); |
| offset = (offset + step) % RSA_NUM_WORDS; |
| } |
| |
| /* |
| * Xor digest location, so all words becomes 0 only iff equal. |
| * |
| * Also XOR in offset and non-zero const. This to avoid repeat |
| * glitches to zero be able to produce the right result. |
| */ |
| offset = rand() % SHA256_DIGEST_WORDS; |
| step = (RANDOM_STEP % SHA256_DIGEST_WORDS) || 1; |
| for (i = 0; i < SHA256_DIGEST_WORDS; ++i) { |
| buf[offset] ^= bswap(sha256[SHA256_DIGEST_WORDS - 1 - offset]) |
| ^ (offset + 0x10u); |
| offset = (offset + step) % SHA256_DIGEST_WORDS; |
| } |
| |
| VERBOSE("\nsig^ %ph\n\n", HEX_BUF(buf, 384)); |
| |
| /* Hash resulting buffer. */ |
| DCRYPTO_SHA256_hash((uint8_t *) buf, RSA_NUM_BYTES, (uint8_t *) hash); |
| |
| VERBOSE("hash %ph\n", HEX_BUF(hash, 32)); |
| |
| /* |
| * Write computed hash to unlock register to unlock execution, iff |
| * right. Idea is that this flow cannot be glitched to have correct |
| * values with any probability. |
| */ |
| for (i = 0; i < SHA256_DIGEST_WORDS; ++i) |
| GREG32_ADDR(GLOBALSEC, SB_BL_SIG0)[i] = hash[i]; |
| |
| /* |
| * Make an unlock attempt. Value written is irrelevant, as long as |
| * something is written. |
| */ |
| GREG32(GLOBALSEC, SIG_UNLOCK) = 1; |
| } |