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

/* APDU dispatcher and U2F command handlers. */

#include "console.h"

#include "dcrypto.h"
#include "extension.h"
#include "nvmem_vars.h"
#include "physical_presence.h"
#include "system.h"
#include "tpm_nvmem_ops.h"
#include "tpm_vendor_cmds.h"
#include "u2f.h"
#include "u2f_cmds.h"
#include "u2f_impl.h"
#include "util.h"

#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ##args)

size_t g2f_attestation_cert(uint8_t *buf)
{
	uint8_t *serial;

	const struct u2f_state *state = u2f_get_state();

	if (!state)
		return 0;

	if (system_get_chip_unique_id(&serial) != P256_NBYTES)
		return 0;

#ifdef CHIP_G
	/**
	 * chip/g implementation of system_get_chip_unique_id() always
	 * returns 32-bit aligned pointer, but host mock-ups do no guarantee
	 * that, so copy data to aligned location.
	 */
	return g2f_attestation_cert_serial(state, (p256_int *)serial, buf);
#else
	{
		p256_int p256_serial;

		memcpy(&p256_serial, serial, sizeof(p256_serial));
		return g2f_attestation_cert_serial(state, &p256_serial, buf);
	}
#endif
}

/* U2F GENERATE command  */
enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf,
				    size_t input_size, size_t *response_size)
{
	struct u2f_generate_req *req = buf;
	union u2f_generate_response *resp = buf;
	struct u2f_ec_point *pubKey = NULL;

	const struct u2f_state *state = u2f_get_state();
	uint8_t kh_version = 0; /* Fall back version of key handle. */

	static const size_t kh_version_response_size[] = {
		sizeof(struct u2f_generate_resp),
		sizeof(struct u2f_generate_versioned_resp),
		sizeof(struct u2f_generate_versioned_resp_v2)
	};

	/**
	 * Buffer for generating key handle as part of response. Note, it
	 * overlaps with authTimeSecret in response since request and response
	 * shares same buffer.
	 */
	union u2f_key_handle_variant *kh_buf = NULL;

	uint8_t authTimeSecretHash[U2F_AUTH_TIME_SECRET_SIZE];

	size_t response_buf_size = *response_size;
	enum ec_error_list result;

	*response_size = 0;

	if (input_size != sizeof(struct u2f_generate_req))
		return VENDOR_RC_BOGUS_ARGS;

	if (state == NULL)
		return VENDOR_RC_INTERNAL_ERROR;

	if ((req->flags & U2F_V2_KH_MASK) == U2F_V2_KH_MASK)
		kh_version = U2F_KH_VERSION_2;
	else if (req->flags & U2F_UV_ENABLED_KH)
		kh_version = U2F_KH_VERSION_1;

	/* Check there is enough room for response in response buffer. */
	if (response_buf_size < kh_version_response_size[kh_version])
		return VENDOR_RC_BOGUS_ARGS;

	/* Copy to avoid overwriting data before use. */
	memcpy(authTimeSecretHash, req->authTimeSecretHash,
	       sizeof(authTimeSecretHash));

	switch (kh_version) {
	case U2F_KH_VERSION_2:
		pubKey = &resp->v2.pubKey;
		kh_buf = (union u2f_key_handle_variant *)&resp->v2.keyHandle;
		break;
	case U2F_KH_VERSION_1:
		pubKey = &resp->v1.pubKey;
		kh_buf = (union u2f_key_handle_variant *)&resp->v1.keyHandle;
		break;
	default: /* Version 0 */
		pubKey = &resp->v0.pubKey;
		kh_buf = (union u2f_key_handle_variant *)&resp->v0.keyHandle;
		break;
	}

	/* Maybe enforce user presence, w/ optional consume */
	if (pop_check_presence(req->flags & G2F_CONSUME) != POP_TOUCH_YES &&
	    (req->flags & U2F_AUTH_FLAG_TUP) != 0)
		return VENDOR_RC_NOT_ALLOWED;

	/**
	 * req->userSecret and req->appId are consumed  by u2f_generate() before
	 * being overwritten.
	 */
	result = u2f_generate(state, req->userSecret, req->appId,
			      authTimeSecretHash, kh_buf, kh_version, pubKey);

	always_memset(authTimeSecretHash, 0, sizeof(authTimeSecretHash));

	if (result != EC_SUCCESS)
		return VENDOR_RC_INTERNAL_ERROR;

	/*
	 * From this point: the request 'req' content is invalid as it is
	 * overridden by the response we are building in the same buffer.
	 */
	*response_size = kh_version_response_size[kh_version];
	return VENDOR_RC_SUCCESS;
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_GENERATE, u2f_generate_cmd);

/* Below, we depend on the response not being larger than than the request. */
BUILD_ASSERT(sizeof(struct u2f_sign_resp) <= sizeof(struct u2f_sign_req));


/* U2F SIGN command */
enum vendor_cmd_rc u2f_sign_cmd(enum vendor_cmd_cc code, void *buf,
				size_t input_size, size_t *response_size)
{
	const union u2f_sign_request *req = buf;
	union u2f_key_handle_variant *kh;

	const struct u2f_state *state = u2f_get_state();

	const uint8_t *hash, *user, *origin;
	uint8_t *authTimeSecret = NULL;
	uint8_t flags;
	struct u2f_sign_resp *resp;

	/* Version of KH; 0 if KH is not versioned. */
	uint8_t kh_version;

	enum ec_error_list result;

	/* Response is smaller than request, so no need to check this. */
	*response_size = 0;

	if (!state)
		return VENDOR_RC_INTERNAL_ERROR;

	/**
	 * Request can be in old (non-versioned) and new (versioned) formats,
	 * which differs in size. Use request size to distinguish it.
	 */
	if (input_size == sizeof(struct u2f_sign_req)) {
		kh_version = 0;
		kh = (union u2f_key_handle_variant *)&req->v0.keyHandle;
		hash = req->v0.hash;
		flags = req->v0.flags;
		user = req->v0.userSecret;
		origin = req->v0.appId;
	} else if (input_size == sizeof(struct u2f_sign_versioned_req)) {
		kh = (union u2f_key_handle_variant *)&req->v1.keyHandle;
		kh_version = U2F_KH_VERSION_1;
		hash = req->v1.hash;
		flags = req->v1.flags;
		user = req->v1.userSecret;
		origin = req->v1.appId;
		/**
		 * TODO(b/184393647): Enforce user verification if no user
		 * presence check is requested.
		 */
		if ((flags & U2F_AUTH_FLAG_TUP) == 0)
			authTimeSecret = (uint8_t *)req->v1.authTimeSecret;
	} else if (input_size == sizeof(struct u2f_sign_versioned_req_v2)) {
		kh = (union u2f_key_handle_variant *)&req->v2.keyHandle;
		kh_version = U2F_KH_VERSION_2;
		hash = req->v2.hash;
		flags = req->v2.flags;
		user = req->v2.userSecret;
		origin = req->v2.appId;
		authTimeSecret = (uint8_t *)req->v2.authTimeSecret;
	} else
		return VENDOR_RC_BOGUS_ARGS;

	if (authTimeSecret) {
		/**
		 * U2F_Sign receives a pre-hashed version of credential which
		 * was used by U2F_Generate, so perform hash before use.
		 */
		if (!DCRYPTO_SHA256_hash(authTimeSecret,
					 U2F_AUTH_TIME_SECRET_SIZE,
					 authTimeSecret))
			return VENDOR_RC_INTERNAL_ERROR;
	}
	result = u2f_authorize_keyhandle(state, kh, kh_version, user, origin,
					 authTimeSecret);
	if (result == EC_ERROR_ACCESS_DENIED)
		return VENDOR_RC_PASSWORD_REQUIRED;
	if (result != EC_SUCCESS)
		return VENDOR_RC_INTERNAL_ERROR;

	/* We might not actually need to sign anything. */
	if ((flags & U2F_AUTH_CHECK_ONLY) == U2F_AUTH_CHECK_ONLY)
		return VENDOR_RC_SUCCESS;

	/*
	 * Enforce user presence for version 0 KHs, with optional consume.
	 * TODO(b/184393647): update logic for version 2 if needed.
	 */
	if (pop_check_presence(flags & G2F_CONSUME) != POP_TOUCH_YES) {
		if (kh_version != U2F_KH_VERSION_1)
			return VENDOR_RC_NOT_ALLOWED;
		if ((flags & U2F_AUTH_FLAG_TUP) != 0)
			return VENDOR_RC_NOT_ALLOWED;
	}

	/*
	 * u2f_sign first consume all data from request 'req', and compute
	 * result in temporary storage. Once accomplished, it stores it in
	 * provided buffer. This allows overlap between input and output
	 * parameters.
	 * The response is smaller than the request, so we have the space.
	 */
	resp = buf;

	result = u2f_sign(state, kh, kh_version, user, origin, authTimeSecret,
			  hash, (struct u2f_signature *)resp);

	if (result == EC_ERROR_ACCESS_DENIED)
		return VENDOR_RC_PASSWORD_REQUIRED;
	if (result != EC_SUCCESS)
		return VENDOR_RC_INTERNAL_ERROR;

	*response_size = sizeof(*resp);

	return VENDOR_RC_SUCCESS;
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_SIGN, u2f_sign_cmd);

static inline size_t u2f_attest_format_size(uint8_t format)
{
	switch (format) {
	case U2F_ATTEST_FORMAT_REG_RESP:
		return sizeof(struct g2f_register_msg_v0);
	default:
		return 0;
	}
}

/* U2F ATTEST command */
enum vendor_cmd_rc u2f_attest_cmd(enum vendor_cmd_cc code, void *buf,
				  size_t input_size, size_t *response_size)
{
	const struct u2f_attest_req *req = buf;
	struct u2f_attest_resp *resp;
	struct g2f_register_msg_v0 *msg = (void *)req->data;
	enum ec_error_list result;

	size_t response_buf_size = *response_size;

	const struct u2f_state *state = u2f_get_state();

	*response_size = 0;

	if (!state)
		return VENDOR_RC_INTERNAL_ERROR;

	if (input_size < offsetof(struct u2f_attest_req, data) ||
	    input_size <
		    (offsetof(struct u2f_attest_req, data) + req->dataLen) ||
	    input_size > sizeof(struct u2f_attest_req) ||
	    response_buf_size < sizeof(*resp))
		return VENDOR_RC_BOGUS_ARGS;

	/* Only one format is supported, key handle version is 0. */
	if (req->format != U2F_ATTEST_FORMAT_REG_RESP)
		return VENDOR_RC_NOT_ALLOWED;

	if (req->dataLen != sizeof(struct g2f_register_msg_v0))
		return VENDOR_RC_NOT_ALLOWED;

	if (msg->reserved != 0)
		return VENDOR_RC_NOT_ALLOWED;

	/*
	 * u2f_attest first consume all data from request 'req', and compute
	 * result in temporary storage. Once accomplished, it stores it in
	 * provided buffer. This allows overlap between input and output
	 * parameters.
	 * The response is smaller than the request, so we have the space.
	 */
	resp = buf;

	/* TODO: If WebAuthn support is needed, pass AuthTimeSecret. */
	result = u2f_attest(state,
			    (union u2f_key_handle_variant *)&msg->key_handle, 0,
			    req->userSecret, msg->app_id, NULL,
			    &msg->public_key, req->data,
			    u2f_attest_format_size(req->format),
			    (struct u2f_signature *)resp);

	if (result == EC_ERROR_ACCESS_DENIED)
		return VENDOR_RC_NOT_ALLOWED;

	if (result != EC_SUCCESS) {
		CPRINTF("G2F Attestation failed");
		return VENDOR_RC_INTERNAL_ERROR;
	}

	*response_size = sizeof(*resp);
	return VENDOR_RC_SUCCESS;
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_ATTEST, u2f_attest_cmd);
