@@ -85,7 +85,19 @@ public byte[] generateSignature(byte[] message)
8585 @ Override
8686 public boolean verifySignature (byte [] message , byte [] signature )
8787 {
88- return false ;
88+ byte [] hash = new byte [digest .getDigestSize ()];
89+ digest .update (message , 0 , message .length );
90+ digest .doFinal (hash , 0 );
91+ SnovaKeyElements keyElements = new SnovaKeyElements (params , engine );
92+ byte [] pk = pubKey .getEncoded ();
93+ System .arraycopy (pk , 0 , keyElements .publicKey .publicKeySeed , 0 , SnovaKeyPairGenerator .publicSeedLength );
94+ System .arraycopy (pk , SnovaKeyPairGenerator .publicSeedLength , keyElements .publicKey .P22 , 0 , keyElements .publicKey .P22 .length );
95+ engine .genABQP (keyElements .map1 , keyElements .publicKey .publicKeySeed , keyElements .fixedAbq );
96+ byte [] p22_gf16s = new byte [keyElements .publicKey .P22 .length << 1 ];
97+ GF16Utils .decode (keyElements .publicKey .P22 , p22_gf16s , p22_gf16s .length );
98+ byte [][][][] p22 = new byte [params .getM ()][params .getO ()][params .getO ()][params .getLsq ()];
99+ MapGroup1 .fillP (p22_gf16s , 0 , p22 , p22_gf16s .length );
100+ return verifySignatureCore (hash , signature , keyElements .publicKey , keyElements .map1 , p22 );
89101 }
90102
91103 public static void createSignedHash (
@@ -365,6 +377,172 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt,
365377 Arrays .fill (gf16mSecretTemp0 , (byte )0 );
366378 }
367379
380+ public boolean verifySignatureCore (byte [] digest , byte [] signature , PublicKey pkx , MapGroup1 map1 , byte [][][][] p22 )
381+ {
382+ final int bytesHash = (params .getO () * params .getLsq () + 1 ) >>> 1 ;
383+ final int bytesSalt = params .getSaltLength ();
384+ final int l = params .getL ();
385+ final int lsq = params .getLsq ();
386+ final int m = params .getM ();
387+ final int n = params .getN ();
388+ final int v = params .getV ();
389+ final int o = params .getO ();
390+ int bytesSignature = ((n * lsq ) + 1 ) >>> 1 ;
391+
392+ // Extract salt from signature
393+ byte [] ptSalt = Arrays .copyOfRange (signature , bytesSignature , bytesSignature + bytesSalt );
394+ //byte[] signatureBody = Arrays.copyOf(signature, signature.length - bytesSalt);
395+
396+ // Step 1: Regenerate signed hash using public key seed, digest and salt
397+ byte [] signedHash = new byte [bytesHash ];
398+ SHAKEDigest shake = new SHAKEDigest (256 );
399+ shake .update (pkx .publicKeySeed , 0 , pkx .publicKeySeed .length );
400+ shake .update (digest , 0 , digest .length );
401+ shake .update (ptSalt , 0 , ptSalt .length );
402+ shake .doFinal (signedHash , 0 , bytesHash );
403+
404+ // Handle odd-length adjustment (if needed)
405+ if ((o * lsq ) % 2 != 0 )
406+ {
407+ signedHash [bytesHash - 1 ] &= 0x0F ;
408+ }
409+
410+ // Step 2: Convert signature to GF16 matrices
411+ byte [][][] signatureGF16Matrix = new byte [n ][l ][l ];
412+ byte [] decodedSig = new byte [n * lsq ];
413+ GF16Utils .decode (signature , 0 , decodedSig , 0 , decodedSig .length );
414+
415+ for (int i = 0 ; i < n ; i ++)
416+ {
417+ for (int row = 0 ; row < l ; row ++)
418+ {
419+ System .arraycopy (decodedSig , i * lsq + row * l ,
420+ signatureGF16Matrix [i ][row ], 0 , l );
421+ }
422+ }
423+
424+ // Step 3: Evaluate signature using public key
425+ byte [][][] computedHashMatrix = new byte [m ][l ][l ];
426+ evaluation (computedHashMatrix , map1 , p22 , signatureGF16Matrix );
427+
428+ // Convert computed hash matrix to bytes
429+ byte [] computedHashBytes = new byte [m * lsq ];
430+ for (int i = 0 ; i < m ; i ++)
431+ {
432+ for (int row = 0 ; row < l ; row ++)
433+ {
434+ System .arraycopy (computedHashMatrix [i ][row ], 0 ,
435+ computedHashBytes , i * lsq + row * l , l );
436+ }
437+ }
438+ byte [] encodedHash = new byte [bytesHash ];
439+ GF16Utils .encode (computedHashBytes , encodedHash , 0 , computedHashBytes .length );
440+
441+ // Step 4: Compare hashes
442+ return Arrays .areEqual (signedHash , encodedHash );
443+ }
444+
445+ private void evaluation (byte [][][] hashMatrix , MapGroup1 map1 , byte [][][][] p22 , byte [][][] signature )
446+ {
447+ final int m = params .getM ();
448+ final int alpha = params .getAlpha ();
449+ final int n = params .getN ();
450+ final int v = params .getV ();
451+ final int l = params .getL ();
452+
453+ byte [][][][][] Left = new byte [m ][alpha ][n ][l ][l ];
454+ byte [][][][][] Right = new byte [m ][alpha ][n ][l ][l ];
455+ byte [][] temp = new byte [l ][l ];
456+ byte [][] transposedSig = new byte [l ][l ];
457+
458+ // Evaluate Left and Right matrices
459+ for (int mi = 0 ; mi < m ; mi ++)
460+ {
461+ for (int si = 0 ; si < n ; si ++)
462+ {
463+ transposeGF16Matrix (signature [si ], transposedSig );
464+ for (int a = 0 ; a < alpha ; a ++)
465+ {
466+ // Left[mi][a][si] = Aalpha * (sig^T * Qalpha1)
467+ multiplyGF16Matrices (transposedSig , map1 .qAlpha1 [mi ][a ], temp );
468+ multiplyGF16Matrices (map1 .aAlpha [mi ][a ], temp , Left [mi ][a ][si ]);
469+
470+ // Right[mi][a][si] = (Qalpha2 * sig) * Balpha
471+ multiplyGF16Matrices (map1 .qAlpha2 [mi ][a ], signature [si ], temp );
472+ multiplyGF16Matrices (temp , map1 .bAlpha [mi ][a ], Right [mi ][a ][si ]);
473+ }
474+ }
475+ }
476+
477+ // Initialize hash matrix to zero
478+ for (int mi = 0 ; mi < m ; mi ++)
479+ {
480+ for (int i = 0 ; i < l ; i ++)
481+ {
482+ Arrays .fill (hashMatrix [mi ][i ], (byte )0 );
483+ }
484+ }
485+
486+ // Process P matrices and accumulate results
487+ byte [][] sumTemp = new byte [l ][l ];
488+ byte [][] pTemp = new byte [l ][l ];
489+ for (int mi = 0 ; mi < m ; mi ++)
490+ {
491+ for (int a = 0 ; a < alpha ; a ++)
492+ {
493+ int miPrime = iPrime (mi , a );
494+
495+ for (int ni = 0 ; ni < n ; ni ++)
496+ {
497+ // sum_t0 = sum(P[miPrime][ni][nj] * Right[mi][a][nj])
498+ for (int i = 0 ; i < l ; i ++)
499+ {
500+ Arrays .fill (sumTemp [i ], (byte )0 );
501+ }
502+
503+ for (int nj = 0 ; nj < n ; nj ++)
504+ {
505+ byte [] p = getPMatrix (map1 , p22 , miPrime , ni , nj );
506+ multiplyGF16Matrices (p , Right [mi ][a ][nj ], pTemp );
507+ addGF16Matrices (sumTemp , pTemp , sumTemp );
508+ }
509+
510+ // hashMatrix += Left[mi][a][ni] * sumTemp
511+ multiplyGF16Matrices (Left [mi ][a ][ni ], sumTemp , temp );
512+ addGF16Matrices (hashMatrix [mi ], temp , hashMatrix [mi ]);
513+ }
514+ }
515+ }
516+ }
517+
518+ // Helper method to get appropriate P matrix based on indices
519+ private byte [] getPMatrix (MapGroup1 map1 , byte [][][][] p22 , int mi , int ni , int nj )
520+ {
521+ final int v = params .getV ();
522+ if (ni < v )
523+ {
524+ if (nj < v )
525+ {
526+ return map1 .p11 [mi ][ni ][nj ];
527+ }
528+ else
529+ {
530+ return map1 .p12 [mi ][ni ][nj - v ];
531+ }
532+ }
533+ else
534+ {
535+ if (nj < v )
536+ {
537+ return map1 .p21 [mi ][ni - v ][nj ];
538+ }
539+ else
540+ {
541+ return p22 [mi ][ni - v ][nj - v ];
542+ }
543+ }
544+ }
545+
368546 private void transposeGF16Matrix (byte [][] src , byte [][] dest )
369547 {
370548 for (int i = 0 ; i < params .getL (); i ++)
@@ -529,6 +707,17 @@ private void addGF16Matrices(byte[] a, byte[][] b, byte[] result)
529707 }
530708 }
531709
710+ private void addGF16Matrices (byte [][] a , byte [][] b , byte [][] result )
711+ {
712+ for (int i = 0 ; i < b .length ; i ++)
713+ {
714+ for (int j = 0 ; j < b [i ].length ; ++j )
715+ {
716+ result [i ][j ] = GF16Utils .add (a [i ][j ], b [i ][j ]);
717+ }
718+ }
719+ }
720+
532721 private int iPrime (int mi , int alpha )
533722 {
534723 // Implement index calculation based on SNOVA specification
0 commit comments