/* Copyright 2021 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.
 */

#include <fuzzer/FuzzedDataProvider.h>

#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#define HIDE_EC_STDLIB
extern "C" {
#include "physical_presence.h"
#include "u2f_cmds.h"
#include "u2f_impl.h"
#include "internal.h"
}

extern "C" {
/******************************************************************************/
/* Mock implementations of cr50 board.
 */
int system_get_chip_unique_id(uint8_t **id)
{
	return P256_NBYTES;
}

/******************************************************************************/
/* Mock implementations of Dcrypto functionality.
 */
int DCRYPTO_x509_gen_u2f_cert_name(const p256_int *d, const p256_int *pk_x,
				   const p256_int *pk_y, const p256_int *serial,
				   const char *name, uint8_t *cert, const int n)
{
	memset(cert, 1, n);
	return n;
}

enum dcrypto_result DCRYPTO_p256_key_from_bytes(
	p256_int *x, p256_int *y, p256_int *d,
	const uint8_t key_bytes[P256_NBYTES])
{
	p256_int key;

	p256_from_bin(key_bytes, &key);

	// The actual condition for this function to fail happens rarely,
	// and not able to to control. So we assume it fails for some inputs
	// for fuzz purpose.
	if (P256_DIGIT(&key, 0) % 10 == 0) {
		return DCRYPTO_RETRY;
	}

	if (p256_lt_blinded(&key, &SECP256r1_nMin2) >= 0)
		return DCRYPTO_RETRY;
	p256_add_d(&key, 1, d);
	if (x == NULL || y == NULL)
		return DCRYPTO_OK;
	memset(x, 0, P256_NBYTES);
	memset(y, 0, P256_NBYTES);
	return DCRYPTO_OK;
}

enum dcrypto_result dcrypto_p256_ecdsa_sign(struct drbg_ctx *drbg,
					    const p256_int *key,
					    const p256_int *message,
					    p256_int *r, p256_int *s)
{
	memset(r, 0, sizeof(p256_int));
	memset(s, 0, sizeof(p256_int));
	return DCRYPTO_OK;
}

/******************************************************************************/
/* Mock implementations of U2F functionality.
 */
static struct u2f_state ustate;
struct u2f_state *u2f_get_state(void)
{
	return &ustate;
}

static enum touch_state tstate;
enum touch_state pop_check_presence(int consume)
{
	return tstate;
}

uint8_t buffer[512];

int test_fuzz_one_input(const uint8_t *data, unsigned int size)
{
	FuzzedDataProvider data_provider(data, size);

	if (data_provider.ConsumeBool()) {
		ustate.drbg_entropy_size = 64;
	} else {
		ustate.drbg_entropy_size = 32;
	}

	if (data_provider.ConsumeBool()) {
		tstate = POP_TOUCH_YES;
	} else {
		tstate = POP_TOUCH_NO;
	}

	while (data_provider.remaining_bytes() > 0) {
		std::vector<uint8_t> bytes;
		int command_num = data_provider.ConsumeIntegralInRange(0, 2);

		size_t request_size, response_size;
		struct u2f_generate_req *generate_req;
		struct u2f_generate_resp *generate_resp_v0;
		struct u2f_generate_versioned_resp *generate_resp_v1;
		struct u2f_generate_versioned_resp_v2 *generate_resp_v2;
		struct u2f_sign_req *sign_req_v0;
		struct u2f_sign_versioned_req *sign_req_v1;
		struct u2f_sign_versioned_req_v2 *sign_req_v2;
		struct u2f_attest_req *attest_req;
		struct g2f_register_msg_v0 *g2f_msg_v0;
		uint8_t appId[U2F_APPID_SIZE];
		uint8_t userSecret[U2F_USER_SECRET_SIZE];
		uint8_t authTimeSecretHash[U2F_AUTH_TIME_SECRET_SIZE];
		uint8_t flags;
		struct u2f_key_handle kh_0;
		struct u2f_versioned_key_handle kh_1;
		struct u2f_key_handle_v2 kh_2;
		struct u2f_ec_point public_key;
		int kh_version;
		vendor_cmd_rc rc;

		switch (command_num) {
		case 0:
			bytes = data_provider.ConsumeBytes<uint8_t>(
				sizeof(struct u2f_generate_req));
			memcpy(buffer, bytes.data(), bytes.size());
			generate_req = (u2f_generate_req *)buffer;
			memcpy(appId, generate_req->appId, U2F_APPID_SIZE);
			memcpy(userSecret, generate_req->userSecret,
			       U2F_USER_SECRET_SIZE);
			memcpy(authTimeSecretHash,
			       generate_req->authTimeSecretHash,
			       U2F_AUTH_TIME_SECRET_SIZE);
			flags = generate_req->flags;
			response_size = 512;
			rc = u2f_generate_cmd(VENDOR_CC_U2F_GENERATE, buffer,
					      sizeof(struct u2f_generate_req),
					      &response_size);
			if (rc != VENDOR_RC_SUCCESS) {
				break;
			}
			if (response_size == sizeof(struct u2f_generate_resp)) {
				kh_version = 0;
			} else if (response_size ==
				   sizeof(struct u2f_generate_versioned_resp)) {
				kh_version = 1;
			} else if (response_size ==
				   sizeof(struct u2f_generate_versioned_resp_v2)) {
				kh_version = 2;
			} else {
				exit(1);
			}
			if (kh_version == 0) {
				generate_resp_v0 = (u2f_generate_resp *)buffer;
				kh_0 = generate_resp_v0->keyHandle;
				public_key = generate_resp_v0->pubKey;
				sign_req_v0 = (u2f_sign_req *)buffer;
				memcpy(sign_req_v0->appId, appId,
				       U2F_APPID_SIZE);
				memcpy(sign_req_v0->userSecret, userSecret,
				       U2F_USER_SECRET_SIZE);
				sign_req_v0->flags = flags;
				sign_req_v0->keyHandle = kh_0;
				bytes = data_provider.ConsumeBytes<uint8_t>(
					U2F_P256_SIZE);
				memcpy(sign_req_v0->hash, bytes.data(),
				       bytes.size());
				request_size = sizeof(struct u2f_sign_req);
			} else if (kh_version == 1) {
				generate_resp_v1 =
					(u2f_generate_versioned_resp *)buffer;
				kh_1 = generate_resp_v1->keyHandle;
				sign_req_v1 = (u2f_sign_versioned_req *)buffer;
				memcpy(sign_req_v1->appId, appId,
				       U2F_APPID_SIZE);
				memcpy(sign_req_v1->userSecret, userSecret,
				       U2F_USER_SECRET_SIZE);
				sign_req_v1->flags = flags;
				sign_req_v1->keyHandle = kh_1;
				bytes = data_provider.ConsumeBytes<uint8_t>(
					U2F_P256_SIZE);
				memcpy(sign_req_v1->hash, bytes.data(),
				       bytes.size());
				request_size =
					sizeof(struct u2f_sign_versioned_req);
			} else {
				generate_resp_v2 =
					(u2f_generate_versioned_resp_v2 *)buffer;
				kh_2 = generate_resp_v2->keyHandle;
				sign_req_v2 =
					(u2f_sign_versioned_req_v2 *)buffer;
				memcpy(sign_req_v2->appId, appId,
				       U2F_APPID_SIZE);
				memcpy(sign_req_v2->userSecret, userSecret,
				       U2F_USER_SECRET_SIZE);
				memcpy(sign_req_v2->authTimeSecret,
				       authTimeSecretHash,
				       U2F_AUTH_TIME_SECRET_SIZE);
				sign_req_v2->flags = flags;
				sign_req_v2->keyHandle = kh_2;
				bytes = data_provider.ConsumeBytes<uint8_t>(
					U2F_P256_SIZE);
				memcpy(sign_req_v2->hash, bytes.data(),
				       bytes.size());
				request_size = sizeof(
					struct u2f_sign_versioned_req_v2);
			}
			response_size = 512;
			u2f_sign_cmd(VENDOR_CC_U2F_SIGN, buffer, request_size,
				     &response_size);
			if (kh_version == 0) {
				attest_req = (u2f_attest_req *)buffer;
				attest_req->format = U2F_ATTEST_FORMAT_REG_RESP;
				attest_req->dataLen =
					sizeof(struct g2f_register_msg_v0);
				memcpy(attest_req->userSecret, userSecret,
				       U2F_USER_SECRET_SIZE);
				g2f_msg_v0 =
					(g2f_register_msg_v0 *)attest_req->data;
				g2f_msg_v0->reserved = 0;
				memcpy(g2f_msg_v0->app_id, appId,
				       U2F_APPID_SIZE);
				memcpy(g2f_msg_v0->key_handle.hmac, kh_0.hmac,
				       sizeof(kh_0.hmac));
				memcpy(g2f_msg_v0->key_handle.origin_seed,
				       kh_0.origin_seed,
				       sizeof(kh_0.origin_seed));
				g2f_msg_v0->public_key = public_key;
				bytes = data_provider.ConsumeBytes<uint8_t>(
					U2F_CHAL_SIZE);
				memcpy(g2f_msg_v0->challenge, bytes.data(),
				       bytes.size());
				response_size = 512;
				u2f_attest_cmd(VENDOR_CC_U2F_ATTEST, buffer,
					       sizeof(struct u2f_attest_req),
					       &response_size);
			}
			break;
		case 1: {
			int version =
				data_provider.ConsumeIntegralInRange(0, 2);
			request_size =
				(version == 0) ?
					      sizeof(struct u2f_sign_req) :
				(version == 1) ?
					      sizeof(struct u2f_sign_versioned_req) :
					      sizeof(struct u2f_sign_versioned_req_v2);
			bytes = data_provider.ConsumeBytes<uint8_t>(
				request_size);
			memcpy(buffer, bytes.data(), bytes.size());
			response_size = 512;
			u2f_sign_cmd(VENDOR_CC_U2F_SIGN, buffer, request_size,
				     &response_size);
			break;
		}
		case 2:
			auto str = data_provider.ConsumeRandomLengthString(256);
			memcpy(buffer, str.data(), str.size());
			attest_req = (u2f_attest_req *)buffer;
			attest_req->dataLen =
				sizeof(struct g2f_register_msg_v0);
			response_size = 512;
			u2f_attest_cmd(VENDOR_CC_U2F_ATTEST, buffer, str.size(),
				       &response_size);
			break;
		}
		break;
	}
	return 0;
}
}