mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-09 09:01:35 +00:00
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:
committed by
chrome-bot
parent
4a8b509020
commit
282765fdd4
@@ -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
123
common/rma_auth.c
Normal 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;
|
||||
}
|
||||
@@ -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
80
include/rma_auth.h
Normal 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
|
||||
@@ -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
199
test/rma_auth.c
Normal 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
17
test/rma_auth.tasklist
Normal 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 */
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user