Skip to content

Commit d733f20

Browse files
authored
Merge pull request #10663 from rizlik/pubkey_ecc_operation_cb
Introduce ECC Make PUB and ECC Check Pub crypto callbacks
2 parents fb54c0a + 66c530f commit d733f20

7 files changed

Lines changed: 824 additions & 54 deletions

File tree

doc/dox_comments/header_files/cryptocb.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,63 @@ void wc_CryptoCb_InfoString(wc_CryptoInfo* info);
251251
\sa wc_AesInit
252252
*/
253253
int wc_CryptoCb_AesSetKey(Aes* aes, const byte* key, word32 keySz);
254+
255+
/*!
256+
\ingroup CryptoCb
257+
258+
\brief Offload deriving an ECC public key Q = d*G from its private key to a
259+
CryptoCB device.
260+
261+
Used by wc_ecc_make_pub / wc_ecc_make_pub_ex. The callback boundary is
262+
math-free: the resulting public point crosses as X9.63 uncompressed bytes
263+
(0x04 || X || Y, each ordinate zero-padded to the curve size) in
264+
wc_CryptoInfo.pk.ecc_make_pub (\c pubOut / \c pubOutSz). This wrapper performs
265+
all bignum (de)serialization, so a device handler only deals with byte arrays
266+
and never with wolfCrypt's internal mp_int representation. The private scalar
267+
is taken from the ecc_key (resident in a secure element, or key->k); curve
268+
identity comes from key->dp.
269+
270+
\param key ECC key providing the device id, curve identity, heap hint and
271+
the private scalar
272+
\param pubOut [out] resulting affine public point Q = d*G
273+
274+
\return 0 on success
275+
\return CRYPTOCB_UNAVAILABLE if no device handles the operation, or key or
276+
pubOut is NULL (wolfCrypt falls back to software, which reports
277+
the argument error)
278+
279+
\sa wc_CryptoCb_RegisterDevice
280+
\sa wc_ecc_make_pub
281+
*/
282+
int wc_CryptoCb_EccMakePub(ecc_key* key, ecc_point* pubOut);
283+
284+
/*!
285+
\ingroup CryptoCb
286+
287+
\brief Offload validating an ECC key to a CryptoCB device.
288+
289+
Used by wc_ecc_check_key and the key generation / import validation paths.
290+
The public point crosses the (math-free) callback boundary as X9.63
291+
uncompressed bytes in wc_CryptoInfo.pk.ecc_check_pub (\c pubKey /
292+
\c pubKeySz); this wrapper serializes key->pubkey so a device handler only
293+
deals with byte arrays. When the key state is \c ECC_PRIVATEKEY_ONLY,
294+
\c pubKey is NULL and \c pubKeySz is 0 so the handler can distinguish "no
295+
public point" from an invalid zero-coordinate public point that must be
296+
validated and rejected. The caller's intent crosses in \c checkOrder and
297+
\c checkPriv.
298+
299+
\param key ECC key to validate (curve identity from key->dp)
300+
\param checkOrder when 1 the caller requested validation that the point
301+
has the curve order (point * order == infinity)
302+
\param checkPriv when 1 the caller also requested validation of the
303+
private part (scalar range, consistency with the public
304+
point)
305+
306+
\return 0 if the key is valid
307+
\return CRYPTOCB_UNAVAILABLE if no device handles the operation (wolfCrypt
308+
falls back to software)
309+
310+
\sa wc_CryptoCb_RegisterDevice
311+
\sa wc_ecc_check_key
312+
*/
313+
int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder, int checkPriv);

tests/swdev/swdev.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,89 @@ static int swdev_ecc_get_sig_size(wc_CryptoInfo* info)
153153
*info->pk.ecc_get_sig_size.sigSize = sz;
154154
return 0;
155155
}
156+
157+
static int swdev_ecc_make_pub(wc_CryptoInfo* info)
158+
{
159+
int ret;
160+
ecc_key* key = info->pk.ecc_make_pub.key;
161+
ecc_point* pub = wc_ecc_new_point_h(key->heap);
162+
163+
if (pub == NULL)
164+
return MEMORY_E;
165+
166+
/* derive Q = d*G in software, then emit X9.63 uncompressed bytes. The
167+
* point is serialized with the curve size from key->dp so custom-curve
168+
* keys (idx == ECC_CUSTOM_IDX) work too; wc_ecc_export_point_der rejects
169+
* negative curve indices. */
170+
ret = wc_ecc_make_pub(key, pub);
171+
if (ret == 0) {
172+
byte* out = info->pk.ecc_make_pub.pubOut;
173+
word32 curveSz = (word32)key->dp->size;
174+
word32 ptSz = 1 + 2 * curveSz;
175+
word32 xSz = curveSz;
176+
word32 ySz = curveSz;
177+
178+
if (*info->pk.ecc_make_pub.pubOutSz < ptSz) {
179+
ret = BUFFER_E;
180+
}
181+
else {
182+
out[0] = ECC_POINT_UNCOMP;
183+
ret = wc_export_int(pub->x, out + 1, &xSz, curveSz,
184+
WC_TYPE_UNSIGNED_BIN);
185+
if (ret == MP_OKAY)
186+
ret = wc_export_int(pub->y, out + 1 + curveSz, &ySz, curveSz,
187+
WC_TYPE_UNSIGNED_BIN);
188+
if (ret == MP_OKAY)
189+
*info->pk.ecc_make_pub.pubOutSz = ptSz;
190+
}
191+
}
192+
193+
wc_ecc_del_point_h(pub, key->heap);
194+
return ret;
195+
}
196+
197+
#ifdef HAVE_ECC_CHECK_KEY
198+
static int swdev_ecc_check_pub(wc_CryptoInfo* info)
199+
{
200+
ecc_key* key = info->pk.ecc_check_pub.key;
201+
int ret = 0;
202+
int validatedFromWire = 0;
203+
204+
if (info->pk.ecc_check_pub.pubKeySz == 0) {
205+
return ECC_INF_E;
206+
}
207+
#ifdef HAVE_ECC_KEY_IMPORT
208+
if (key->idx >= 0) {
209+
WC_DECLARE_VAR(pubOnly, ecc_key, 1, key->heap);
210+
WC_ALLOC_VAR(pubOnly, ecc_key, 1, key->heap);
211+
if (!WC_VAR_OK(pubOnly))
212+
ret = MEMORY_E;
213+
else
214+
ret = wc_ecc_init_ex(pubOnly, key->heap, INVALID_DEVID);
215+
if (ret == 0) {
216+
ret = wc_ecc_import_x963_ex(info->pk.ecc_check_pub.pubKey,
217+
info->pk.ecc_check_pub.pubKeySz, pubOnly, key->dp->id);
218+
if (ret == 0 &&
219+
wc_ecc_cmp_point(&pubOnly->pubkey, &key->pubkey) != MP_EQ) {
220+
/* wire bytes disagree with key->pubkey */
221+
ret = BAD_STATE_E;
222+
}
223+
if (ret == 0)
224+
ret = wc_ecc_check_key(pubOnly);
225+
wc_ecc_free(pubOnly);
226+
}
227+
WC_FREE_VAR(pubOnly, key->heap);
228+
validatedFromWire = 1;
229+
}
230+
#endif
231+
if (ret == 0 && (!validatedFromWire ||
232+
(info->pk.ecc_check_pub.checkPriv &&
233+
key->type == ECC_PRIVATEKEY))) {
234+
ret = wc_ecc_check_key(key);
235+
}
236+
return ret;
237+
}
238+
#endif /* HAVE_ECC_CHECK_KEY */
156239
#endif /* HAVE_ECC */
157240

158241
#ifndef NO_SHA256
@@ -712,6 +795,12 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info,
712795
return swdev_ecc_get_size(info);
713796
case WC_PK_TYPE_EC_GET_SIG_SIZE:
714797
return swdev_ecc_get_sig_size(info);
798+
case WC_PK_TYPE_EC_MAKE_PUB:
799+
return swdev_ecc_make_pub(info);
800+
#ifdef HAVE_ECC_CHECK_KEY
801+
case WC_PK_TYPE_EC_CHECK_PUB_KEY:
802+
return swdev_ecc_check_pub(info);
803+
#endif
715804
#endif /* HAVE_ECC */
716805
default:
717806
return CRYPTOCB_UNAVAILABLE;

wolfcrypt/src/cryptocb.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ static const char* GetPkTypeStr(int pk)
157157
case WC_PK_TYPE_EC_KEYGEN: return "ECC KeyGen";
158158
case WC_PK_TYPE_EC_GET_SIZE: return "ECC GetSize";
159159
case WC_PK_TYPE_EC_GET_SIG_SIZE: return "ECC GetSigSize";
160+
case WC_PK_TYPE_EC_MAKE_PUB: return "ECC MakePub";
161+
case WC_PK_TYPE_EC_CHECK_PUB_KEY: return "ECC CheckPubKey";
160162
}
161163
return NULL;
162164
}
@@ -882,6 +884,127 @@ int wc_CryptoCb_EccGetSigSize(const ecc_key* key, int* sigSize)
882884

883885
return wc_CryptoCb_TranslateErrorCode(ret);
884886
}
887+
888+
int wc_CryptoCb_EccMakePub(ecc_key* key, ecc_point* pubOut)
889+
{
890+
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
891+
CryptoCb* dev;
892+
893+
if (key == NULL || pubOut == NULL || key->dp == NULL)
894+
return ret;
895+
896+
if (key->dp->size > MAX_ECC_BYTES)
897+
return ret;
898+
899+
dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK);
900+
if (dev && dev->cb) {
901+
wc_CryptoInfo cryptoInfo;
902+
word32 curveSz = (word32)key->dp->size;
903+
word32 ptSz = 1 + 2 * curveSz; /* X9.63 uncompressed length */
904+
word32 outSz = 1 + 2 * MAX_ECC_BYTES; /* buffer size on input */
905+
WC_DECLARE_VAR(buf, byte, (1 + 2 * MAX_ECC_BYTES), key->heap);
906+
WC_ALLOC_VAR_EX(buf, byte, (1 + 2 * MAX_ECC_BYTES), key->heap,
907+
DYNAMIC_TYPE_ECC_BUFFER, return MEMORY_E);
908+
909+
/* zero the result buffer so a handler that returns success without
910+
* writing output is rejected deterministically by the tag check */
911+
XMEMSET(buf, 0, ptSz);
912+
913+
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
914+
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
915+
cryptoInfo.pk.type = WC_PK_TYPE_EC_MAKE_PUB;
916+
cryptoInfo.pk.ecc_make_pub.key = key;
917+
cryptoInfo.pk.ecc_make_pub.pubOut = buf;
918+
cryptoInfo.pk.ecc_make_pub.pubOutSz = &outSz;
919+
920+
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
921+
922+
/* deserialize X9.63 uncompressed result into pubOut; reject a result
923+
* whose size is not exactly the curve's X9.63 length */
924+
if (ret == 0) {
925+
if (outSz != ptSz || buf[0] != ECC_POINT_UNCOMP) {
926+
ret = BUFFER_E;
927+
}
928+
else {
929+
int err = mp_read_unsigned_bin(pubOut->x, buf + 1, curveSz);
930+
if (err == MP_OKAY)
931+
err = mp_read_unsigned_bin(pubOut->y, buf + 1 + curveSz,
932+
curveSz);
933+
if (err == MP_OKAY)
934+
err = mp_set(pubOut->z, 1);
935+
if (err != MP_OKAY)
936+
ret = err;
937+
}
938+
}
939+
940+
WC_FREE_VAR_EX(buf, key->heap, DYNAMIC_TYPE_ECC_BUFFER);
941+
}
942+
943+
return wc_CryptoCb_TranslateErrorCode(ret);
944+
}
945+
946+
#ifdef HAVE_ECC_CHECK_KEY
947+
int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder, int checkPriv)
948+
{
949+
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
950+
CryptoCb* dev;
951+
952+
if (key == NULL || key->dp == NULL)
953+
return ret;
954+
955+
if (key->dp->size > MAX_ECC_BYTES)
956+
return ret;
957+
958+
/* locate registered callback */
959+
dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK);
960+
if (dev && dev->cb) {
961+
wc_CryptoInfo cryptoInfo;
962+
word32 curveSz = (word32)key->dp->size;
963+
word32 ptSz = 1 + 2 * curveSz;
964+
word32 xSz = curveSz;
965+
word32 ySz = curveSz;
966+
/* a key with no host-side public point is represented by
967+
* ECC_PRIVATEKEY_ONLY and crosses as pubKey = NULL / pubKeySz = 0.
968+
* If the key state says a public point exists, serialize it even when
969+
* the coordinates are invalid (e.g. 0,0) so the device can reject the
970+
* actual input instead of seeing "no public point". */
971+
int havePub = (key->type != ECC_PRIVATEKEY_ONLY);
972+
WC_DECLARE_VAR(buf, byte, (1 + 2 * MAX_ECC_BYTES), key->heap);
973+
974+
ret = MP_OKAY;
975+
if (havePub) {
976+
WC_ALLOC_VAR_EX(buf, byte, (1 + 2 * MAX_ECC_BYTES), key->heap,
977+
DYNAMIC_TYPE_ECC_BUFFER, return MEMORY_E);
978+
/* serialize key->pubkey to X9.63 uncompressed (0x04 || X || Y) */
979+
buf[0] = ECC_POINT_UNCOMP;
980+
ret = wc_export_int(key->pubkey.x, buf + 1, &xSz, curveSz,
981+
WC_TYPE_UNSIGNED_BIN);
982+
if (ret == MP_OKAY)
983+
ret = wc_export_int(key->pubkey.y, buf + 1 + curveSz, &ySz,
984+
curveSz, WC_TYPE_UNSIGNED_BIN);
985+
}
986+
987+
if (ret == MP_OKAY) {
988+
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
989+
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
990+
cryptoInfo.pk.type = WC_PK_TYPE_EC_CHECK_PUB_KEY;
991+
cryptoInfo.pk.ecc_check_pub.key = key;
992+
cryptoInfo.pk.ecc_check_pub.pubKey = havePub ? buf : NULL;
993+
cryptoInfo.pk.ecc_check_pub.pubKeySz = havePub ? ptSz : 0;
994+
cryptoInfo.pk.ecc_check_pub.checkOrder = checkOrder;
995+
cryptoInfo.pk.ecc_check_pub.checkPriv = checkPriv;
996+
997+
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
998+
}
999+
1000+
if (havePub) {
1001+
WC_FREE_VAR_EX(buf, key->heap, DYNAMIC_TYPE_ECC_BUFFER);
1002+
}
1003+
}
1004+
1005+
return wc_CryptoCb_TranslateErrorCode(ret);
1006+
}
1007+
#endif /* HAVE_ECC_CHECK_KEY */
8851008
#endif /* HAVE_ECC */
8861009

8871010
#ifdef HAVE_CURVE25519

0 commit comments

Comments
 (0)