@@ -278,9 +278,7 @@ public static MinisignKeyPair GenerateKeyPair(string password, bool writeOutputF
278278 public static bool ValidateSignature ( string filePath , MinisignSignature signature , MinisignPublicKey publicKey )
279279 {
280280 if ( filePath != null && ! File . Exists ( filePath ) )
281- {
282281 throw new FileNotFoundException ( "could not find filePath" ) ;
283- }
284282
285283 if ( signature == null )
286284 throw new ArgumentException ( "missing signature input" , nameof ( signature ) ) ;
@@ -289,19 +287,30 @@ public static bool ValidateSignature(string filePath, MinisignSignature signatur
289287 throw new ArgumentException ( "missing publicKey input" , nameof ( publicKey ) ) ;
290288
291289 if ( ! ArrayHelpers . ConstantTimeEquals ( signature . KeyId , publicKey . KeyId ) ) return false ;
292- // load the file into memory
293- var file = LoadMessageFile ( filePath ) ;
294- // verify the signature
295- if ( PublicKeyAuth . VerifyDetached ( signature . Signature , file , publicKey . PublicKey ) )
290+
291+
292+ if ( ! signature . IsHashed )
296293 {
297- // verify the trusted comment
298- return PublicKeyAuth . VerifyDetached ( signature . GlobalSignature ,
299- ArrayHelpers . ConcatArrays ( signature . Signature , signature . TrustedComment ) , publicKey . PublicKey ) ;
294+ var file = LoadMessageFile ( filePath ) ;
295+
296+ // Legacy: Ed25519(message)
297+ if ( ! PublicKeyAuth . VerifyDetached ( signature . Signature , file , publicKey . PublicKey ) ) return false ;
298+ }
299+ else
300+ {
301+ // Hashed: Ed25519(Blake2b-512(message))
302+ var blake = ComputeBlake2bFileHash ( filePath ) ;
303+ if ( ! PublicKeyAuth . VerifyDetached ( signature . Signature , blake , publicKey . PublicKey ) ) return false ;
300304 }
301305
302- return false ;
306+ // Global signature is the same for both formats
307+ return PublicKeyAuth . VerifyDetached (
308+ signature . GlobalSignature ,
309+ ArrayHelpers . ConcatArrays ( signature . Signature , signature . TrustedComment ) ,
310+ publicKey . PublicKey ) ;
303311 }
304312
313+
305314 /// <summary>
306315 /// Validate a file with a MinisignSignature and a MinisignPublicKey object.
307316 /// </summary>
@@ -325,17 +334,27 @@ public static bool ValidateSignature(byte[] message, MinisignSignature signature
325334 throw new ArgumentException ( "missing publicKey input" , nameof ( publicKey ) ) ;
326335
327336 if ( ! ArrayHelpers . ConstantTimeEquals ( signature . KeyId , publicKey . KeyId ) ) return false ;
328- // verify the signature
329- if ( PublicKeyAuth . VerifyDetached ( signature . Signature , message , publicKey . PublicKey ) )
337+
338+ if ( ! signature . IsHashed )
339+ {
340+ // Legacy: Ed25519(message)
341+ if ( ! PublicKeyAuth . VerifyDetached ( signature . Signature , message , publicKey . PublicKey ) ) return false ;
342+ }
343+ else
330344 {
331- // verify the trusted comment
332- return PublicKeyAuth . VerifyDetached ( signature . GlobalSignature ,
333- ArrayHelpers . ConcatArrays ( signature . Signature , signature . TrustedComment ) , publicKey . PublicKey ) ;
345+ // Hashed: Ed25519(Blake2b-512(message))
346+ var blake = GenericHash . Hash ( message , null , 64 ) ;
347+ if ( ! PublicKeyAuth . VerifyDetached ( signature . Signature , blake , publicKey . PublicKey ) ) return false ;
334348 }
335349
336- return false ;
350+ return PublicKeyAuth . VerifyDetached (
351+ signature . GlobalSignature ,
352+ ArrayHelpers . ConcatArrays ( signature . Signature , signature . TrustedComment ) ,
353+ publicKey . PublicKey ) ;
337354 }
338355
356+
357+
339358 #endregion
340359
341360 #region Signature Handling
@@ -430,15 +449,29 @@ public static MinisignSignature LoadSignature(byte[] signature, byte[] trustedCo
430449 if ( globalSignature == null )
431450 throw new ArgumentException ( "missing globalSignature input" , nameof ( globalSignature ) ) ;
432451
433- var minisignSignature = new MinisignSignature
452+ var result = new MinisignSignature ( )
434453 {
435454 SignatureAlgorithm = ArrayHelpers . SubArray ( signature , 0 , 2 ) ,
436455 KeyId = ArrayHelpers . SubArray ( signature , 2 , 8 ) ,
437- Signature = ArrayHelpers . SubArray ( signature , 10 ) ,
438456 TrustedComment = trustedComment ,
439457 GlobalSignature = globalSignature
440458 } ;
441- return minisignSignature ;
459+
460+ var alg = Encoding . UTF8 . GetString ( result . SignatureAlgorithm ) ;
461+ result . IsHashed = alg == "ED" ;
462+
463+ if ( ! result . IsHashed )
464+ {
465+ // Legacy minisign: Ed + keyid(8) + raw signature
466+ result . Signature = ArrayHelpers . SubArray ( signature , 10 ) ;
467+ }
468+ else
469+ {
470+ // Hashed minisign: ED + keyid(8) + signature(64)
471+ result . Signature = ArrayHelpers . SubArray ( signature , 10 , 64 ) ;
472+ }
473+
474+ return result ;
442475 }
443476
444477 #endregion
@@ -710,6 +743,33 @@ private static byte[] LoadMessageFile(string messageFile)
710743 return File . ReadAllBytes ( messageFile ) ;
711744 }
712745
746+ /// <summary>
747+ /// Computes a BLAKE2b-512 hash of a file without loading it fully into memory.
748+ /// Used for hashed minisign signatures.
749+ /// </summary>
750+ /// <param name="messageFile">Path to the file.</param>
751+ /// <returns>64-byte BLAKE2b hash of the file contents.</returns>
752+ /// <exception cref="ArgumentException"></exception>
753+ /// <exception cref="FileNotFoundException"></exception>
754+ /// <exception cref="IOException"></exception>
755+ /// <exception cref="SecurityException"></exception>
756+ /// <exception cref="UnauthorizedAccessException"></exception>
757+ /// <exception cref="DirectoryNotFoundException"></exception>
758+ private static byte [ ] ComputeBlake2bFileHash ( string messageFile )
759+ {
760+ if ( messageFile == null )
761+ throw new ArgumentException ( "missing messageFile input" , nameof ( messageFile ) ) ;
762+
763+ if ( ! File . Exists ( messageFile ) )
764+ throw new FileNotFoundException ( "could not find messageFile" ) ;
765+
766+ using ( var stream = File . OpenRead ( messageFile ) )
767+ using ( var hashStream = new GenericHash . GenericHashAlgorithm ( ( byte [ ] ) null , 64 ) )
768+ {
769+ return hashStream . ComputeHash ( stream ) ;
770+ }
771+ }
772+
713773 /// <summary>
714774 /// Get the current Unix Timestamp.
715775 /// </summary>
0 commit comments