@@ -120,14 +120,7 @@ public void Dispose()
120120 /// </exception>
121121 public void SignData ( ReadOnlySpan < byte > data , Span < byte > destination , ReadOnlySpan < byte > context = default )
122122 {
123- int signatureSizeInBytes = Algorithm . SignatureSizeInBytes ;
124-
125- if ( destination . Length != signatureSizeInBytes )
126- {
127- throw new ArgumentException (
128- SR . Format ( SR . Argument_DestinationImprecise , signatureSizeInBytes ) ,
129- nameof ( destination ) ) ;
130- }
123+ Helpers . ThrowIfDestinationWrongLength ( destination , Algorithm . SignatureSizeInBytes ) ;
131124
132125 if ( context . Length > MaxContextLength )
133126 {
@@ -309,13 +302,7 @@ public bool VerifyData(byte[] data, byte[] signature, byte[]? context = default)
309302 public void SignPreHash ( ReadOnlySpan < byte > hash , Span < byte > destination , string hashAlgorithmOid , ReadOnlySpan < byte > context = default )
310303 {
311304 ArgumentNullException . ThrowIfNull ( hashAlgorithmOid ) ;
312-
313- if ( destination . Length != Algorithm . SignatureSizeInBytes )
314- {
315- throw new ArgumentException (
316- SR . Format ( SR . Argument_DestinationImprecise , Algorithm . SignatureSizeInBytes ) ,
317- nameof ( destination ) ) ;
318- }
305+ Helpers . ThrowIfDestinationWrongLength ( destination , Algorithm . SignatureSizeInBytes ) ;
319306
320307 if ( context . Length > MaxContextLength )
321308 {
@@ -507,6 +494,155 @@ public bool VerifyPreHash(byte[] hash, byte[] signature, string hashAlgorithmOid
507494 new ReadOnlySpan < byte > ( context ) ) ;
508495 }
509496
497+ /// <inheritdoc cref="SignMu(ReadOnlySpan{byte})"/>
498+ /// <exception cref="ArgumentNullException"><paramref name="externalMu"/> is <see langword="null"/>.</exception>
499+ public byte [ ] SignMu ( byte [ ] externalMu )
500+ {
501+ ArgumentNullException . ThrowIfNull ( externalMu ) ;
502+
503+ return SignMu ( new ReadOnlySpan < byte > ( externalMu ) ) ;
504+ }
505+
506+ /// <summary>
507+ /// Signs the specified externally computed signature mu (μ) value.
508+ /// </summary>
509+ /// <param name="externalMu">
510+ /// The signature mu value to sign.
511+ /// </param>
512+ /// <returns>
513+ /// ML-DSA signature for the specified mu value.
514+ /// </returns>
515+ /// <exception cref="ArgumentException">
516+ /// The buffer in <paramref name="externalMu"/> is the incorrect length for the signature mu value.
517+ /// </exception>
518+ /// <exception cref="ObjectDisposedException">
519+ /// This instance has been disposed.
520+ /// </exception>
521+ /// <exception cref="CryptographicException">
522+ /// <para>The instance represents only a public key.</para>
523+ /// <para>-or-</para>
524+ /// <para>An error occurred while signing the hash.</para>
525+ /// </exception>
526+ /// <exception cref="PlatformNotSupportedException">
527+ /// The current platform does not support signing with an externally computed mu value.
528+ /// </exception>
529+ /// <seealso cref="VerifyMu(byte[], byte[])"/>
530+ public byte [ ] SignMu ( ReadOnlySpan < byte > externalMu )
531+ {
532+ byte [ ] destination = new byte [ Algorithm . SignatureSizeInBytes ] ;
533+ SignMu ( externalMu , destination . AsSpan ( ) ) ;
534+ return destination ;
535+ }
536+
537+ /// <summary>
538+ /// Signs the specified externally computed signature mu (μ) value,
539+ /// writing the signature into the provided buffer.
540+ /// </summary>
541+ /// <param name="externalMu">
542+ /// The signature mu value to sign.
543+ /// </param>
544+ /// <param name="destination">
545+ /// The buffer to receive the signature. Its length must be exactly
546+ /// <see cref="MLDsaAlgorithm.SignatureSizeInBytes"/>.
547+ /// </param>
548+ /// <exception cref="ArgumentException">
549+ /// <para>
550+ /// The buffer in <paramref name="externalMu"/> is the incorrect length for the signature mu value.
551+ /// </para>
552+ /// <para>-or-</para>
553+ /// <para>
554+ /// The buffer in <paramref name="destination"/> is the incorrect length to receive the signature.
555+ /// </para>
556+ /// </exception>
557+ /// <exception cref="ObjectDisposedException">
558+ /// This instance has been disposed.
559+ /// </exception>
560+ /// <exception cref="CryptographicException">
561+ /// <para>The instance represents only a public key.</para>
562+ /// <para>-or-</para>
563+ /// <para>An error occurred while signing the hash.</para>
564+ /// </exception>
565+ /// <exception cref="PlatformNotSupportedException">
566+ /// The current platform does not support signing with an externally computed mu value.
567+ /// </exception>
568+ /// <seealso cref="VerifyMu(ReadOnlySpan{byte}, ReadOnlySpan{byte})"/>
569+ public void SignMu ( ReadOnlySpan < byte > externalMu , Span < byte > destination )
570+ {
571+ if ( externalMu . Length != Algorithm . MuSizeInBytes )
572+ throw new ArgumentException ( SR . Argument_MLDsaMuInvalidLength , nameof ( externalMu ) ) ;
573+
574+ Helpers . ThrowIfDestinationWrongLength ( destination , Algorithm . SignatureSizeInBytes ) ;
575+ ThrowIfDisposed ( ) ;
576+
577+ SignMuCore ( externalMu , destination ) ;
578+ }
579+
580+ /// <summary>
581+ /// When overridden in a derived class, computes the remainder of the signature from the
582+ /// precomputed mu (μ) value, writing it into the provided buffer.
583+ /// </summary>
584+ /// <param name="externalMu">
585+ /// The signature mu value to sign.
586+ /// </param>
587+ /// <param name="destination">
588+ /// The buffer to receive the signature, which will always be the exactly correct size for the algorithm.
589+ /// </param>
590+ /// <exception cref="CryptographicException">
591+ /// An error occurred while computing the signature.
592+ /// </exception>
593+ protected abstract void SignMuCore ( ReadOnlySpan < byte > externalMu , Span < byte > destination ) ;
594+
595+ /// <inheritdoc cref="VerifyMu(ReadOnlySpan{byte}, ReadOnlySpan{byte})"/>
596+ /// <exception cref="ArgumentNullException">
597+ /// <paramref name="externalMu"/> or <paramref name="signature"/> is <see langword="null"/>.
598+ /// </exception>
599+ public bool VerifyMu ( byte [ ] externalMu , byte [ ] signature )
600+ {
601+ ArgumentNullException . ThrowIfNull ( externalMu ) ;
602+ ArgumentNullException . ThrowIfNull ( signature ) ;
603+
604+ return VerifyMu ( new ReadOnlySpan < byte > ( externalMu ) , new ReadOnlySpan < byte > ( signature ) ) ;
605+ }
606+
607+ /// <summary>
608+ /// Verifies that a digital signature is valid for the provided externally computed signature mu (μ) value.
609+ /// </summary>
610+ /// <param name="externalMu">The signature mu value.</param>
611+ /// <param name="signature">The signature to verify.</param>
612+ /// <returns>
613+ /// <see langword="true"/> if the digital signature is valid for the provided mu value;
614+ /// otherwise, <see langword="false"/>.
615+ /// </returns>
616+ /// <exception cref="ObjectDisposedException">
617+ /// This instance has been disposed.
618+ /// </exception>
619+ /// <exception cref="CryptographicException">An error occurred while verifying the mu value.</exception>
620+ /// <exception cref="PlatformNotSupportedException">
621+ /// The current platform does not support verification with an externally computed mu value.
622+ /// </exception>
623+ public bool VerifyMu ( ReadOnlySpan < byte > externalMu , ReadOnlySpan < byte > signature )
624+ {
625+ if ( externalMu . Length != Algorithm . MuSizeInBytes || signature . Length != Algorithm . SignatureSizeInBytes )
626+ {
627+ return false ;
628+ }
629+
630+ ThrowIfDisposed ( ) ;
631+
632+ return VerifyMuCore ( externalMu , signature ) ;
633+ }
634+
635+ /// <summary>
636+ /// When overridden in a derived class,
637+ /// verifies that a digital signature is valid for the provided externally computed signature mu (μ) value.
638+ /// </summary>
639+ /// <param name="externalMu">The signature mu value.</param>
640+ /// <param name="signature">The signature to verify.</param>
641+ /// <returns>
642+ /// <see langword="true"/> if the mu value is valid; otherwise, <see langword="false"/>.
643+ /// </returns>
644+ protected abstract bool VerifyMuCore ( ReadOnlySpan < byte > externalMu , ReadOnlySpan < byte > signature ) ;
645+
510646 /// <summary>
511647 /// Exports the public-key portion of the current key in the X.509 SubjectPublicKeyInfo format.
512648 /// </summary>
@@ -1063,15 +1199,7 @@ public byte[] ExportMLDsaPublicKey()
10631199 /// </remarks>
10641200 public void ExportMLDsaPublicKey ( Span < byte > destination )
10651201 {
1066- int publicKeySizeInBytes = Algorithm . PublicKeySizeInBytes ;
1067-
1068- if ( destination . Length != publicKeySizeInBytes )
1069- {
1070- throw new ArgumentException (
1071- SR . Format ( SR . Argument_DestinationImprecise , publicKeySizeInBytes ) ,
1072- nameof ( destination ) ) ;
1073- }
1074-
1202+ Helpers . ThrowIfDestinationWrongLength ( destination , Algorithm . PublicKeySizeInBytes ) ;
10751203 ThrowIfDisposed ( ) ;
10761204
10771205 ExportMLDsaPublicKeyCore ( destination ) ;
@@ -1113,15 +1241,7 @@ public byte[] ExportMLDsaSecretKey()
11131241 /// </exception>
11141242 public void ExportMLDsaSecretKey ( Span < byte > destination )
11151243 {
1116- int secretKeySizeInBytes = Algorithm . SecretKeySizeInBytes ;
1117-
1118- if ( destination . Length != secretKeySizeInBytes )
1119- {
1120- throw new ArgumentException (
1121- SR . Format ( SR . Argument_DestinationImprecise , secretKeySizeInBytes ) ,
1122- nameof ( destination ) ) ;
1123- }
1124-
1244+ Helpers . ThrowIfDestinationWrongLength ( destination , Algorithm . SecretKeySizeInBytes ) ;
11251245 ThrowIfDisposed ( ) ;
11261246
11271247 ExportMLDsaSecretKeyCore ( destination ) ;
@@ -1161,14 +1281,7 @@ public byte[] ExportMLDsaPrivateSeed()
11611281 /// </exception>
11621282 public void ExportMLDsaPrivateSeed ( Span < byte > destination )
11631283 {
1164- int privateSeedSizeInBytes = Algorithm . PrivateSeedSizeInBytes ;
1165- if ( destination . Length != privateSeedSizeInBytes )
1166- {
1167- throw new ArgumentException (
1168- SR . Format ( SR . Argument_DestinationImprecise , privateSeedSizeInBytes ) ,
1169- nameof ( destination ) ) ;
1170- }
1171-
1284+ Helpers . ThrowIfDestinationWrongLength ( destination , Algorithm . PrivateSeedSizeInBytes ) ;
11721285 ThrowIfDisposed ( ) ;
11731286
11741287 ExportMLDsaPrivateSeedCore ( destination ) ;
0 commit comments