common: Add RMA reset auth challenge-response crypto

RMA auth uses X25519 to generate a relatively small challenge and
response.

Currently, nothing calls the rma_auth code.  We'll need console and
TPM vendor commands to do so.

BUG=b:37952913
BRANCH=none
TEST=make buildall

Change-Id: Iec7f2d0e3dc8243f79b009ead16bb3ba9f1bef9d
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/544184
This commit is contained in:
Randall Spangler
2017-06-21 13:10:57 -07:00
committed by chrome-bot
parent 4a8b509020
commit 282765fdd4
8 changed files with 438 additions and 0 deletions

View File

@@ -81,6 +81,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_RMA_AUTH)+=rma_auth.o
common-$(CONFIG_RSA)+=rsa.o
common-$(CONFIG_ROLLBACK)+=rollback.o
common-$(CONFIG_RWSIG)+=rwsig.o

123
common/rma_auth.c Normal file
View File

@@ -0,0 +1,123 @@
/* Copyright 2017 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.
*/
/* RMA authorization challenge-response */
#include "common.h"
#include "base32.h"
#include "chip/g/board_id.h"
#include "curve25519.h"
#include "rma_auth.h"
#include "sha256.h"
#include "system.h"
#include "timer.h"
#include "util.h"
/* Minimum time since system boot or last challenge before making a new one */
#define CHALLENGE_INTERVAL (10 * SECOND)
/* Number of tries to properly enter auth code */
#define MAX_AUTHCODE_TRIES 3
/* Server public key and key ID */
static const uint8_t server_pub_key[32] = CONFIG_RMA_AUTH_SERVER_PUBLIC_KEY;
static const uint8_t server_key_id = CONFIG_RMA_AUTH_SERVER_KEY_ID;
static char challenge[RMA_CHALLENGE_BUF_SIZE];
static char authcode[RMA_AUTHCODE_BUF_SIZE];
static int tries_left;
static uint64_t last_challenge_time;
/**
* Create a new RMA challenge/response
*
* @return EC_SUCCESS, EC_ERROR_TIMEOUT if too soon since the last challenge,
* or other non-zero error code.
*/
int rma_create_challenge(void)
{
uint8_t temp[32]; /* Private key or HMAC */
uint8_t secret[32];
struct rma_challenge c;
struct board_id bid;
uint8_t *device_id;
uint8_t *cptr = (uint8_t *)&c;
uint64_t t;
/* Clear the current challenge and authcode, if any */
memset(challenge, 0, sizeof(challenge));
memset(authcode, 0, sizeof(authcode));
/* Rate limit challenges */
t = get_time().val;
if (t - last_challenge_time < CHALLENGE_INTERVAL)
return EC_ERROR_TIMEOUT;
last_challenge_time = t;
memset(&c, 0, sizeof(c));
c.version_key_id = RMA_CHALLENGE_VKID_BYTE(
RMA_CHALLENGE_VERSION, server_key_id);
if (read_board_id(&bid))
return EC_ERROR_UNKNOWN;
memcpy(c.board_id, &bid.type, sizeof(c.board_id));
if (system_get_chip_unique_id(&device_id) != sizeof(c.device_id))
return EC_ERROR_UNKNOWN;
memcpy(c.device_id, device_id, sizeof(c.device_id));
/* Calculate a new ephemeral key pair */
X25519_keypair(c.device_pub_key, temp);
/* Encode the challenge */
if (base32_encode(challenge, sizeof(challenge), cptr, 8 * sizeof(c), 9))
return EC_ERROR_UNKNOWN;
/* Calculate the shared secret */
X25519(secret, temp, server_pub_key);
/*
* Auth code is a truncated HMAC of the ephemeral public key, BoardID,
* and DeviceID. Those are all in the right order in the challenge
* struct, after the version/key id byte.
*/
hmac_SHA256(temp, secret, sizeof(secret), cptr + 1, sizeof(c) - 1);
if (base32_encode(authcode, sizeof(authcode), temp,
RMA_AUTHCODE_CHARS * 5, 0))
return EC_ERROR_UNKNOWN;
tries_left = MAX_AUTHCODE_TRIES;
return EC_SUCCESS;
}
const char *rma_get_challenge(void)
{
return challenge;
}
int rma_try_authcode(const char *code)
{
int rv = EC_ERROR_INVAL;
/* Fail if out of tries */
if (!tries_left)
return EC_ERROR_ACCESS_DENIED;
if (safe_memcmp(authcode, code, RMA_AUTHCODE_CHARS)) {
/* Mismatch */
tries_left--;
} else {
rv = EC_SUCCESS;
tries_left = 0;
}
/* Clear challenge and response if out of tries */
if (!tries_left) {
memset(challenge, 0, sizeof(challenge));
memset(authcode, 0, sizeof(authcode));
}
return rv;
}

View File

@@ -1916,6 +1916,12 @@
/* Support IR357x Link voltage regulator debugging / reprogramming */
#undef CONFIG_REGULATOR_IR357X
/* Support RMA auth challenge-response */
#undef CONFIG_RMA_AUTH
/* If that's defined, the server public key and ID must also be defined */
#undef CONFIG_RMA_AUTH_SERVER_PUBLIC_KEY /* 32 bytes: {0xNN, 0xNN, ... 0xNN} */
#undef CONFIG_RMA_AUTH_SERVER_KEY_ID /* 6-bit key ID, 0xMM */
/* Enable hardware Random Number generator support */
#undef CONFIG_RNG

80
include/rma_auth.h Normal file
View File

@@ -0,0 +1,80 @@
/* Copyright 2017 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.
*/
/* RMA challenge-response */
#ifndef __CROS_EC_RMA_AUTH_H
#define __CROS_EC_RMA_AUTH_H
#include <stdint.h>
/* Test server public and private keys */
#define RMA_TEST_SERVER_PUBLIC_KEY { \
0x03, 0xae, 0x2d, 0x2c, 0x06, 0x23, 0xe0, 0x73, \
0x0d, 0xd3, 0xb7, 0x92, 0xac, 0x54, 0xc5, 0xfd, \
0x7e, 0x9c, 0xf0, 0xa8, 0xeb, 0x7e, 0x2a, 0xb5, \
0xdb, 0xf4, 0x79, 0x5f, 0x8a, 0x0f, 0x28, 0x3f}
#define RMA_TEST_SERVER_PRIVATE_KEY { \
0x47, 0x3b, 0xa5, 0xdb, 0xc4, 0xbb, 0xd6, 0x77, \
0x20, 0xbd, 0xd8, 0xbd, 0xc8, 0x7a, 0xbb, 0x07, \
0x03, 0x79, 0xba, 0x7b, 0x52, 0x8c, 0xec, 0xb3, \
0x4d, 0xaa, 0x69, 0xf5, 0x65, 0xb4, 0x31, 0xad}
#define RMA_TEST_SERVER_KEY_ID 0x10
/* Current challenge protocol version */
#define RMA_CHALLENGE_VERSION 0
/* Getters and setters for version_key_id byte */
#define RMA_CHALLENGE_VKID_BYTE(version, keyid) \
(((version) << 6) | ((keyid) & 0x3f))
#define RMA_CHALLENGE_GET_VERSION(vkidbyte) ((vkidbyte) >> 6)
#define RMA_CHALLENGE_GET_KEY_ID(vkidbyte) ((vkidbyte) & 0x3f)
struct __packed rma_challenge {
/* Top 2 bits are protocol version; bottom 6 are server KeyID */
uint8_t version_key_id;
/* Ephemeral public key from device */
uint8_t device_pub_key[32];
/* Board ID (.type) */
uint8_t board_id[4];
/* Device ID */
uint8_t device_id[8];
};
/* Size of encoded challenge and response, and buffer sizes to hold them */
#define RMA_CHALLENGE_CHARS 80
#define RMA_CHALLENGE_BUF_SIZE (RMA_CHALLENGE_CHARS + 1)
#define RMA_AUTHCODE_CHARS 8
#define RMA_AUTHCODE_BUF_SIZE (RMA_AUTHCODE_CHARS + 1)
/**
* Create a new RMA challenge/response
*
* @return EC_SUCCESS, EC_ERROR_TIMEOUT if too soon since the last challenge,
* or other non-zero error code.
*/
int rma_create_challenge(void);
/**
* Get the current challenge string
*
* @return a pointer to the challenge string. String will be empty if there
* is no active challenge.
*/
const char *rma_get_challenge(void);
/**
* Try a RMA authorization code
*
* @param code Authorization code to try
* @return EC_SUCCESS if the response was correct, or non-zero error code.
*/
int rma_try_authcode(const char *code);
#endif

View File

@@ -66,6 +66,7 @@ test-list-host += nvmem_vars
test-list-host += pingpong
test-list-host += power_button
test-list-host += queue
test-list-host += rma_auth
test-list-host += rsa
test-list-host += rsa3
test-list-host += sbs_charging_v2
@@ -113,6 +114,7 @@ pingpong-y=pingpong.o
power_button-y=power_button.o
powerdemo-y=powerdemo.o
queue-y=queue.o
rma_auth-y=rma_auth.o
rsa-y=rsa.o
rsa3-y=rsa.o
sbs_charging-y=sbs_charging.o

199
test/rma_auth.c Normal file
View File

@@ -0,0 +1,199 @@
/* Copyright 2017 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.
*
* Test RMA auth challenge/response
*/
#include <stdio.h>
#include "common.h"
#include "chip/g/board_id.h"
#include "curve25519.h"
#include "base32.h"
#include "sha256.h"
#include "rma_auth.h"
#include "test_util.h"
#include "timer.h"
#include "util.h"
/* Dummy implementations for testing */
static uint8_t dummy_board_id[4] = {'Z', 'Z', 'C', 'R'};
static uint8_t dummy_device_id[8] = {'T', 'H', 'X', 1, 1, 3, 8, 0xfe};
static int server_protocol_version = RMA_CHALLENGE_VERSION;
static uint8_t server_private_key[32] = RMA_TEST_SERVER_PRIVATE_KEY;
static int server_key_id = RMA_TEST_SERVER_KEY_ID;
void rand_bytes(void *buffer, size_t len)
{
FILE *f = fopen("/dev/urandom", "rb");
assert(f);
fread(buffer, 1, len, f);
fclose(f);
}
int read_board_id(struct board_id *id)
{
memcpy(&id->type, dummy_board_id, sizeof(id->type));
id->type_inv = ~id->type;
id->flags = 0xFF00;
return EC_SUCCESS;
}
int system_get_chip_unique_id(uint8_t **id)
{
*id = dummy_device_id;
return sizeof(dummy_device_id);
}
/**
* Simulate the server side of a RMA challenge-response.
*
* @param out_auth_code Buffer for generated authorization code
* (must be >= CR50_AUTH_CODE_CHARS + 1 chars)
* @param challenge Challenge from device
* @return 0 if success, non-zero if error.
*/
int rma_server_side(char *out_auth_code, const char *challenge)
{
int version, key_id;
uint32_t device_id[2];
uint8_t secret[32];
uint8_t hmac[32];
struct rma_challenge c;
uint8_t *cptr = (uint8_t *)&c;
/* Convert the challenge back into binary */
if (base32_decode(cptr, 8 * sizeof(c), challenge, 9) != 8 * sizeof(c)) {
printf("Error decoding challenge\n");
return -1;
}
version = RMA_CHALLENGE_GET_VERSION(c.version_key_id);
if (version != server_protocol_version) {
printf("Unsupported challenge version %d\n", version);
return -1;
}
key_id = RMA_CHALLENGE_GET_KEY_ID(c.version_key_id);
printf("\nChallenge: %s\n", challenge);
printf(" Version: %d\n", version);
printf(" Server KeyID: %d\n", key_id);
printf(" BoardID: %c%c%c%c\n",
isprint(c.board_id[0]) ? c.board_id[0] : '?',
isprint(c.board_id[1]) ? c.board_id[1] : '?',
isprint(c.board_id[2]) ? c.board_id[2] : '?',
isprint(c.board_id[3]) ? c.board_id[3] : '?');
memcpy(device_id, c.device_id, sizeof(device_id));
printf(" DeviceID: 0x%08x 0x%08x\n", device_id[0], device_id[1]);
if (key_id != server_key_id) {
printf("Unsupported KeyID %d\n", key_id);
return -1;
}
/*
* Make sure the current user is authorized to reset this board.
*
* Since this is just a test, here we'll just make sure the BoardID
* and DeviceID match what we expected.
*/
if (memcmp(c.board_id, dummy_board_id, sizeof(c.board_id))) {
printf("BoardID mismatch\n");
return -1;
}
if (memcmp(c.device_id, dummy_device_id, sizeof(c.device_id))) {
printf("DeviceID mismatch\n");
return -1;
}
/* Calculate the shared secret */
X25519(secret, server_private_key, c.device_pub_key);
/*
* Auth code is a truncated HMAC of the ephemeral public key, BoardID,
* and DeviceID.
*/
hmac_SHA256(hmac, secret, sizeof(secret), cptr + 1, sizeof(c) - 1);
if (base32_encode(out_auth_code, RMA_AUTHCODE_BUF_SIZE,
hmac, RMA_AUTHCODE_CHARS * 5, 0)) {
printf("Error encoding auth code\n");
return -1;
}
printf("Authcode: %s\n", out_auth_code);
return 0;
};
#define FORCE_TIME(t) { ts.val = (t); force_time(ts); }
static int test_rma_auth(void)
{
const char *challenge;
char authcode[RMA_AUTHCODE_BUF_SIZE];
timestamp_t ts;
/* Test rate limiting */
FORCE_TIME(9 * SECOND);
TEST_ASSERT(rma_create_challenge() == EC_ERROR_TIMEOUT);
TEST_ASSERT(rma_try_authcode("Bad") == EC_ERROR_ACCESS_DENIED);
TEST_ASSERT(strlen(rma_get_challenge()) == 0);
FORCE_TIME(10 * SECOND);
TEST_ASSERT(rma_create_challenge() == 0);
TEST_ASSERT(strlen(rma_get_challenge()) == RMA_CHALLENGE_CHARS);
/* Test using up tries */
TEST_ASSERT(rma_try_authcode("Bad") == EC_ERROR_INVAL);
TEST_ASSERT(strlen(rma_get_challenge()) == RMA_CHALLENGE_CHARS);
TEST_ASSERT(rma_try_authcode("BadCodeZ") == EC_ERROR_INVAL);
TEST_ASSERT(strlen(rma_get_challenge()) == RMA_CHALLENGE_CHARS);
TEST_ASSERT(rma_try_authcode("BadLongCode") == EC_ERROR_INVAL);
/* Out of tries now */
TEST_ASSERT(strlen(rma_get_challenge()) == 0);
TEST_ASSERT(rma_try_authcode("Bad") == EC_ERROR_ACCESS_DENIED);
FORCE_TIME(19 * SECOND);
TEST_ASSERT(rma_create_challenge() == EC_ERROR_TIMEOUT);
TEST_ASSERT(strlen(rma_get_challenge()) == 0);
FORCE_TIME(21 * SECOND);
TEST_ASSERT(rma_create_challenge() == 0);
challenge = rma_get_challenge();
TEST_ASSERT(strlen(challenge) == RMA_CHALLENGE_CHARS);
TEST_ASSERT(rma_server_side(authcode, challenge) == 0);
TEST_ASSERT(rma_try_authcode(authcode) == EC_SUCCESS);
/*
* Make sure the server-side checks for fields work. That is, test
* our ability to test those fields...
*/
server_protocol_version++;
TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
server_protocol_version--;
server_key_id++;
TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
server_key_id--;
dummy_board_id[0]++;
TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
dummy_board_id[0]--;
dummy_device_id[0]++;
TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
dummy_device_id[0]--;
return EC_SUCCESS;
}
void run_test(void)
{
test_reset();
RUN_TEST(test_rma_auth);
test_print_result();
}

17
test/rma_auth.tasklist Normal file
View File

@@ -0,0 +1,17 @@
/* Copyright 2017 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.
*/
/**
* List of enabled tasks in the priority order
*
* The first one has the lowest priority.
*
* For each task, use the macro TASK_TEST(n, r, d, s) where :
* 'n' in the name of the task
* 'r' in the main routine of the task
* 'd' in an opaque parameter passed to the routine at startup
* 's' is the stack size in bytes; must be a multiple of 8
*/
#define CONFIG_TEST_TASK_LIST /* No test task */

View File

@@ -58,6 +58,16 @@
#define CONFIG_TABLET_MODE
#endif
#ifdef TEST_RMA_AUTH
#define CONFIG_BASE32
#define CONFIG_CURVE25519
#define CONFIG_RMA_AUTH
#define CONFIG_RMA_AUTH_SERVER_PUBLIC_KEY RMA_TEST_SERVER_PUBLIC_KEY
#define CONFIG_RMA_AUTH_SERVER_KEY_ID RMA_TEST_SERVER_KEY_ID
#define CONFIG_RNG
#define CONFIG_SHA256
#endif
#ifdef TEST_RSA
#define CONFIG_RSA
#define CONFIG_RSA_KEY_SIZE 2048