Skip to content

Commit ed3079f

Browse files
committed
linuxkm/lkcapi_sha_glue.c: add struct km_sha3_export_state, and implement km_sha3_export() and per-alg _import() methods, and km_sha3_test_export_import().
1 parent e11e9f7 commit ed3079f

2 files changed

Lines changed: 237 additions & 5 deletions

File tree

.wolfssl_known_macro_extras

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ HASH_DIGEST
292292
HASH_DataType_8b
293293
HASH_IMR_DCIE
294294
HASH_IMR_DINIE
295+
HASH_MAX_STATESIZE
295296
HAVE_AESGCM_DECRYPT
296297
HAVE_BYTEREVERSE64
297298
HAVE_CERTIFICATE_STATUS_V2

linuxkm/lkcapi_sha_glue.c

Lines changed: 236 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,9 +488,15 @@ wc_static_assert(sizeof(struct km_sha_state) <= HASH_MAX_DESCSIZE);
488488
#error LINUXKM_LKCAPI_REGISTER_SHA3 requires spinlock-based mutexes.
489489
#endif
490490

491-
/* The kernel list macros provoke "pointer of type `void *' used in arithmetic". */
491+
/* The kernel list macros provoke "pointer of type `void *' used in arithmetic",
492+
* and on older kernels, "nested extern declaration of
493+
* `__compiletime_assert_foo'".
494+
*/
492495
PRAGMA_DIAG_PUSH
493496
PRAGMA("GCC diagnostic ignored \"-Wpointer-arith\"");
497+
PRAGMA("GCC diagnostic ignored \"-Wnested-externs\"");
498+
499+
#include <linux/list.h>
494500

495501
struct km_Sha3TfmCtx {
496502
wolfSSL_Mutex desc_list_lock;
@@ -577,6 +583,190 @@ WC_MAYBE_UNUSED static int sha3_test_once(void) {
577583

578584
PRAGMA_DIAG_POP
579585

586+
/* Serialized SHA-3 state for .export / .import. This is the canonical
587+
* {core, block, len} form the kernel budgets HASH_MAX_STATESIZE for -- worst
588+
* case sha3-224, 200 + 144 + 1. Deliberately NOT a struct copy of wc_Sha3:
589+
* that carries the full 200-byte t[] plus heap/devId/fn-ptrs, which would both
590+
* blow the statesize budget and ship non-portable, non-state fields across
591+
* descs. s[] and t[] are stored in native byte order -- export/import always
592+
* round-trips within one host, so no canonical encoding is needed. */
593+
struct km_sha3_export_state {
594+
byte s[sizeof(((struct wc_Sha3 *)0)->s)]; /* KECCAK sponge, 200 bytes */
595+
byte t[WC_SHA3_224_BLOCK_SIZE]; /* pending block; 144 = max rate
596+
* of the registered SHA-3
597+
* variants (sha3-224) */
598+
byte i; /* valid bytes in t[]; always
599+
* < rate <= 144, since
600+
* Sha3Final rejects i >= rate */
601+
};
602+
603+
/* HASH_MAX_STATESIZE added by 2b1a29ce33, kernel 6.16. */
604+
#ifdef HASH_MAX_STATESIZE
605+
wc_static_assert(sizeof(struct km_sha3_export_state) <= HASH_MAX_STATESIZE);
606+
#endif
607+
608+
/* Non-destructive: serialize the live sponge into the caller's statesize
609+
* buffer, leaving the desc (and its cleanup-list node) intact for continued
610+
* streaming. Variant-agnostic -- s/t/i live at the same offset in every union
611+
* member, so the generic .sha3_state accessor serves all four. */
612+
WC_MAYBE_UNUSED static int km_sha3_export(struct shash_desc *desc, void *out)
613+
{
614+
struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);
615+
struct km_sha3_export_state *blob = (struct km_sha3_export_state *)out;
616+
const struct wc_Sha3 *sha3;
617+
618+
if (ctx->sha3_state == NULL)
619+
return -EINVAL;
620+
sha3 = &ctx->sha3_state->sha3_state;
621+
622+
/* i < rate <= sizeof(blob->t) always; guard defensively so a corrupted
623+
* live state can't overrun blob->t. */
624+
if (sha3->i > sizeof(blob->t))
625+
return -EINVAL;
626+
627+
XMEMCPY(blob->s, sha3->s, sizeof(blob->s));
628+
XMEMSET(blob->t, 0, sizeof(blob->t));
629+
XMEMCPY(blob->t, sha3->t, sha3->i);
630+
blob->i = (byte)sha3->i;
631+
632+
return 0;
633+
}
634+
635+
/* Kernel-API export/import test coverage. Exercises the cross-desc path that
636+
* distinguishes real state serialization from pointer aliasing: testmgr's
637+
* reimport divisions are same-desc, so a default memcpy of the desc pointer
638+
* would round-trip within one desc yet double-free across two. Here we export
639+
* mid-stream, import into a distinct poisoned desc, finish BOTH independently,
640+
* and require both to match a one-shot reference -- plus statesize and
641+
* malformed-blob rejection probes.
642+
*/
643+
WC_MAYBE_UNUSED static int km_sha3_test_export_import(
644+
const char *cra_name, const char *cra_driver_name, unsigned int block_size)
645+
{
646+
int ret;
647+
struct crypto_shash *tfm = NULL;
648+
struct shash_desc *desc = NULL;
649+
struct shash_desc *desc2 = NULL;
650+
struct km_sha3_export_state *blob = NULL;
651+
size_t desc_size = 0;
652+
unsigned int split, i;
653+
byte msg[300];
654+
byte ref[WC_SHA3_512_DIGEST_SIZE];
655+
byte tag[WC_SHA3_512_DIGEST_SIZE];
656+
657+
for (i = 0; i < (unsigned int)sizeof(msg); i++)
658+
msg[i] = (byte)(i * 7 + 1);
659+
660+
tfm = crypto_alloc_shash(cra_name, 0, 0);
661+
if (IS_ERR(tfm)) {
662+
ret = (int)PTR_ERR(tfm);
663+
pr_err("error: crypto_alloc_shash(%s) failed: %d\n", cra_name, ret);
664+
return ret;
665+
}
666+
667+
if (crypto_shash_statesize(tfm) != sizeof(struct km_sha3_export_state)) {
668+
pr_err("error: %s statesize %u != expected %u\n", cra_driver_name,
669+
crypto_shash_statesize(tfm),
670+
(unsigned int)sizeof(struct km_sha3_export_state));
671+
ret = -EINVAL;
672+
goto out;
673+
}
674+
675+
desc_size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
676+
desc = (struct shash_desc *)malloc(desc_size);
677+
desc2 = (struct shash_desc *)malloc(desc_size);
678+
blob = (struct km_sha3_export_state *)malloc(sizeof(*blob));
679+
if ((desc == NULL) || (desc2 == NULL) || (blob == NULL)) {
680+
ret = -ENOMEM;
681+
goto out;
682+
}
683+
XMEMSET(desc, 0, desc_size);
684+
desc->tfm = tfm;
685+
686+
/* Reference digest over the whole message. */
687+
ret = crypto_shash_init(desc);
688+
if (ret == 0)
689+
ret = crypto_shash_update(desc, msg, sizeof(msg));
690+
if (ret == 0)
691+
ret = crypto_shash_final(desc, ref);
692+
if (ret) {
693+
pr_err("error: %s reference digest failed: %d\n", cra_driver_name, ret);
694+
goto out;
695+
}
696+
697+
/* Split leaves block_size/2 unabsorbed bytes, so the export blob carries a
698+
* non-empty partial block for every variant (rate 72..144).
699+
*/
700+
split = block_size + block_size / 2;
701+
702+
ret = crypto_shash_init(desc);
703+
if (ret == 0)
704+
ret = crypto_shash_update(desc, msg, split);
705+
if (ret == 0)
706+
ret = crypto_shash_export(desc, blob);
707+
if (ret) {
708+
pr_err("error: %s export sequence failed: %d\n", cra_driver_name, ret);
709+
goto out;
710+
}
711+
712+
/* Import into a poisoned second desc: import must not read prior ctx. */
713+
XMEMSET(desc2, 0xa5, desc_size);
714+
desc2->tfm = tfm;
715+
ret = crypto_shash_import(desc2, blob);
716+
if (ret == 0)
717+
ret = crypto_shash_update(desc2, msg + split, sizeof(msg) - split);
718+
if (ret == 0)
719+
ret = crypto_shash_final(desc2, tag);
720+
if (ret) {
721+
pr_err("error: %s import sequence failed: %d\n", cra_driver_name, ret);
722+
goto out;
723+
}
724+
if (XMEMCMP(tag, ref, crypto_shash_digestsize(tfm)) != 0) {
725+
pr_err("error: %s import-continuation digest mismatch\n",
726+
cra_driver_name);
727+
ret = -EBADMSG;
728+
goto out;
729+
}
730+
731+
/* The exporting desc must remain live and independent of desc2. */
732+
ret = crypto_shash_update(desc, msg + split, sizeof(msg) - split);
733+
if (ret == 0)
734+
ret = crypto_shash_final(desc, tag);
735+
if (ret) {
736+
pr_err("error: %s post-export continuation failed: %d\n",
737+
cra_driver_name, ret);
738+
goto out;
739+
}
740+
if (XMEMCMP(tag, ref, crypto_shash_digestsize(tfm)) != 0) {
741+
pr_err("error: %s post-export digest mismatch\n", cra_driver_name);
742+
ret = -EBADMSG;
743+
goto out;
744+
}
745+
746+
/* Malformed state (partial length >= rate) must be rejected before any
747+
* allocation or installation.
748+
*/
749+
blob->i = (byte)block_size;
750+
if (crypto_shash_import(desc2, blob) == 0) {
751+
pr_err("error: %s import accepted out-of-range partial length\n",
752+
cra_driver_name);
753+
ret = -EINVAL;
754+
goto out;
755+
}
756+
757+
ret = 0;
758+
759+
out:
760+
761+
free(blob);
762+
free(desc2);
763+
free(desc);
764+
if (tfm)
765+
crypto_free_shash(tfm);
766+
767+
return ret;
768+
}
769+
580770
#endif
581771

582772
#define WC_LINUXKM_SHA1_IMPLEMENT(name, digest_size, block_size, \
@@ -928,6 +1118,41 @@ static int km_ ## name ## _digest(struct shash_desc *desc, const u8 *data, \
9281118
return ret == 0 ? 0 : -EINVAL; \
9291119
} \
9301120
\
1121+
static int km_ ## name ## _import(struct shash_desc *desc, \
1122+
const void *in) \
1123+
{ \
1124+
struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\
1125+
const struct km_sha3_export_state *blob = \
1126+
(const struct km_sha3_export_state *)in; \
1127+
struct wc_Sha3 *sha3; \
1128+
int ret; \
1129+
\
1130+
if (blob->i >= (block_size)) \
1131+
return -EINVAL; \
1132+
\
1133+
ret = km_sha3_alloc_tstate(desc); \
1134+
if (ret) \
1135+
return ret; \
1136+
\
1137+
sha3 = &ctx->sha3_state-> name ## _state; \
1138+
ret = init_f(sha3, NULL, INVALID_DEVID); \
1139+
if (ret != 0) { \
1140+
km_sha3_free_tstate(desc); \
1141+
return -EINVAL; \
1142+
} \
1143+
\
1144+
XMEMCPY(sha3->s, blob->s, sizeof(sha3->s)); \
1145+
XMEMCPY(sha3->t, blob->t, blob->i); \
1146+
XMEMSET(sha3->t + blob->i, 0, sizeof(sha3->t) - blob->i); \
1147+
sha3->i = blob->i; \
1148+
\
1149+
return 0; \
1150+
} \
1151+
\
1152+
wc_static_assert((block_size) <= \
1153+
sizeof(((struct km_sha3_export_state *)0)->t)); \
1154+
\
1155+
\
9311156
static struct shash_alg name ## _alg = \
9321157
{ \
9331158
.init_tfm = km_sha3_init_tfm, \
@@ -938,6 +1163,9 @@ static struct shash_alg name ## _alg = \
9381163
.finup = km_ ## name ## _finup, \
9391164
.digest = km_ ## name ## _digest, \
9401165
.descsize = sizeof(struct km_sha_state), \
1166+
.export = km_sha3_export, \
1167+
.import = km_ ## name ## _import, \
1168+
.statesize = sizeof(struct km_sha3_export_state), \
9411169
.exit_tfm = km_sha3_exit_tfm, \
9421170
.base = { \
9431171
.cra_name = (this_cra_name), \
@@ -952,14 +1180,17 @@ static int name ## _alg_loaded = 0; \
9521180
\
9531181
static int linuxkm_test_ ## name(void) { \
9541182
wc_test_ret_t ret = test_routine(); \
955-
if (ret >= 0) \
956-
return check_shash_driver_masking(NULL /* tfm */, this_cra_name, \
957-
this_cra_driver_name); \
958-
else { \
1183+
if (ret < 0) { \
9591184
wc_test_render_error_message("linuxkm_test_" #name " failed: ", \
9601185
ret); \
9611186
return WC_TEST_RET_DEC_EC(ret); \
9621187
} \
1188+
ret = check_shash_driver_masking(NULL /* tfm */, this_cra_name, \
1189+
this_cra_driver_name); \
1190+
if (ret) \
1191+
return ret; \
1192+
return km_sha3_test_export_import(this_cra_name, this_cra_driver_name, \
1193+
(block_size)); \
9631194
} \
9641195
\
9651196
struct wc_swallow_the_semicolon

0 commit comments

Comments
 (0)