@@ -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+ */
492495PRAGMA_DIAG_PUSH
493496PRAGMA ("GCC diagnostic ignored \"-Wpointer-arith\"" );
497+ PRAGMA ("GCC diagnostic ignored \"-Wnested-externs\"" );
498+
499+ #include <linux/list.h>
494500
495501struct km_Sha3TfmCtx {
496502 wolfSSL_Mutex desc_list_lock ;
@@ -577,6 +583,190 @@ WC_MAYBE_UNUSED static int sha3_test_once(void) {
577583
578584PRAGMA_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+ \
9311156static 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 \
9531181static 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 \
9651196struct wc_swallow_the_semicolon
0 commit comments