mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-10 01:21:49 +00:00
Different devices could have different sized unique device IDs. Let's
just use the IDs as is if they are no larger than the
rma_challenge:device_id field, or the first 8 bytes of the HMAC_sha256
value of the unique device ID, where the unique device ID is used both
as the key and the payload.
The server expects the board ID field in big endian format, let's swap
it before calculating the RMA auth challenge.
The test's server side implementation needs to be also adjusted.
BRANCH=cr50
BUG=b:37952913
TEST=make buildall -j passes. With the rest of the patches applied RMA
authentication process generates sensible values.
Change-Id: Ia1fbf9161e01de30a2da8214258008f6e5f7d915
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/690991
Reviewed-by: Michael Tang <ntang@chromium.org>
204 lines
5.7 KiB
C
204 lines
5.7 KiB
C
/* 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 <endian.h>
|
|
#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;
|
|
uint32_t inverted_board_id;
|
|
|
|
/* 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.
|
|
*/
|
|
memcpy(&inverted_board_id, dummy_board_id, sizeof(inverted_board_id));
|
|
inverted_board_id = be32toh(inverted_board_id);
|
|
if (memcmp(c.board_id, &inverted_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();
|
|
}
|