summaryrefslogtreecommitdiff
path: root/board/cr50/dcrypto/p256_ec.c
blob: 2f458080ce4c17e0f8f1310e7d5a8f59951504fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* Copyright 2015 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 "internal.h"

enum dcrypto_result DCRYPTO_p256_ecdsa_verify(const p256_int *key_x,
					      const p256_int *key_y,
					      const p256_int *message,
					      const p256_int *r,
					      const p256_int *s)
{
	if (!fips_crypto_allowed())
		return DCRYPTO_FAIL;

	return dcrypto_p256_ecdsa_verify(key_x, key_y, message, r, s);
}

/* return codes match dcrypto_p256_ecdsa_sign */
enum dcrypto_result DCRYPTO_p256_ecdsa_sign(const p256_int *key,
					    const p256_int *message,
					    p256_int *r, p256_int *s)
{
	if (!fips_drbg_init()) /* Also check for fips_crypto_allowed(). */
		return DCRYPTO_FAIL;

	return dcrypto_p256_fips_sign_internal(&fips_drbg, key, message, r, s);
}

/* p256_base_point_mul sets {out_x,out_y} = nG, where n is < the
 * order of the group. */
enum dcrypto_result DCRYPTO_p256_base_point_mul(p256_int *out_x,
		p256_int *out_y, const p256_int *n)
{
	if (!fips_crypto_allowed() || p256_is_zero(n)) {
		p256_clear(out_x);
		p256_clear(out_y);
		return DCRYPTO_FAIL;
	}

	return dcrypto_p256_base_point_mul(n, out_x, out_y);
}

enum dcrypto_result DCRYPTO_p256_is_valid_point(const p256_int *x,
						const p256_int *y)
{
	if (!fips_crypto_allowed())
		return DCRYPTO_FAIL;
	return dcrypto_p256_is_valid_point(x, y);
}

/* DCRYPTO_p256_point_mul sets {out_x,out_y} = n*{in_x,in_y}, where n is <
 * the order of the group. */
enum dcrypto_result DCRYPTO_p256_point_mul(p256_int *out_x, p256_int *out_y,
		const p256_int *n, const p256_int *in_x, const p256_int *in_y)
{
	if (!fips_crypto_allowed() || p256_is_zero(n) ||
	    (dcrypto_p256_is_valid_point(in_x, in_y) != DCRYPTO_OK)) {
		p256_clear(out_x);
		p256_clear(out_y);
		return DCRYPTO_FAIL;
	}
	return dcrypto_p256_point_mul(n, in_x, in_y, out_x, out_y);
}

enum dcrypto_result dcrypto_p256_fips_sign_internal(struct drbg_ctx *drbg,
						    const p256_int *key,
						    const p256_int *message,
						    p256_int *r, p256_int *s)
{
	enum dcrypto_result result;
	p256_int k;

	/* Pick uniform 0 < k < R */
	result = fips_p256_hmac_drbg_generate(drbg, &k);

	result |= dcrypto_p256_ecdsa_sign_raw(&k, key, message, r, s);

	/* Wipe temp k */
	p256_clear(&k);

	return dcrypto_ok_if_zero(result - DCRYPTO_OK);
}

enum dcrypto_result dcrypto_p256_key_pwct(struct drbg_ctx *drbg,
					  const p256_int *d, const p256_int *x,
					  const p256_int *y)
{
	p256_int message, r, s;
	enum dcrypto_result result;

	if (p256_is_zero(d))
		return DCRYPTO_FAIL;

	/* set some pseudo-random message. */
	p256_fast_random(&message);

	result = dcrypto_p256_fips_sign_internal(drbg, d, &message, &r, &s);
	if (result != DCRYPTO_OK)
		return result;

#ifdef CRYPTO_TEST_SETUP
	if (fips_break_cmd == FIPS_BREAK_ECDSA_PWCT)
		message.a[0] = ~message.a[0];
#endif
	return dcrypto_p256_ecdsa_verify(x, y, &message, &r, &s);
}

/**
 * Key selection based on FIPS-186-4, section B.4.2 (Key Pair
 * Generation by Testing Candidates).
 */
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_int tx, ty;

	if (!fips_crypto_allowed())
		return DCRYPTO_FAIL;

	p256_from_bin(key_bytes, &key);

	/**
	 * We need key to be in the range  0 < key < SECP256r1 - 1.
	 * To achieve that, first check key < SECP256r1 - 2, and
	 * then add 1 to key. Since key is unsigned number this will
	 * bring key in proper range.
	 */
	if (p256_lt_blinded(&key, &SECP256r1_nMin2) >= 0)
		return DCRYPTO_RETRY;
	p256_add_d(&key, 1, d);
	always_memset(&key, 0, sizeof(key));

	/* We need public key anyway for pair-wise consistency test. */
	if (!x)
		x = &tx;
	if (!y)
		y = &ty;

	/* Compute public key (x,y) = d * G */
	if (dcrypto_p256_base_point_mul(d, x, y) != DCRYPTO_OK)
		return DCRYPTO_FAIL;

	return dcrypto_p256_key_pwct(&fips_drbg, d, x, y);
}