-
Notifications
You must be signed in to change notification settings - Fork 5k
Initial draft of signed CMS for SLH-DSA #115310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Note regarding the
|
1 similar comment
Note regarding the
|
Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones |
Line 33 in 86949c2
|
I am curious if we need to implement any of this down level, since .NET Framework will have its own implementation. I... think... it probably could make sense to implement all of the new public API surface as
|
public System.Security.Cryptography.AsymmetricAlgorithm? PrivateKey { get { throw null; } set { } } | ||
public System.Security.Cryptography.RSASignaturePadding? SignaturePadding { get { throw null; } set { } } | ||
[System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006")] | ||
public System.Security.Cryptography.SlhDsa? SlhDsaPrivateKey { get { throw null; } set { } } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the AsymmetricAlgorithm version has already bitten us: Don't add a public property here until we have a compelling reason.
// TODO Current spec (as of May 5, 2025) has strength requirements on the hash, but we will | ||
// not enforce them here. If the callers wants to enforce them, they can do so by themselves. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So... what's the TODO? That's just a statement of fact.
using System.Security.Cryptography.X509Certificates; | ||
using Internal.Cryptography; | ||
using static System.Security.Cryptography.Pkcs.CmsSigner; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using static System.Security.Cryptography.Pkcs.CmsSigner; |
set => _privateKey = value; | ||
} | ||
|
||
// TODO not sure why PrivateKey is ifdef'd out... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These types are typeforwarded to .NET Framework for the netfx implementation, which means the .NET Standard or .NET Framework TFMs can't expose any new members.
And that's why for .NET Framework the only scenario is "passed a certificate with an attached private key"... those ctors are the only ones that exist there.
CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2? certificate, SlhDsa? privateKey) | ||
: this(signerIdentifierType, certificate, privateKey, signaturePadding: null) | ||
{ | ||
// TODO this ctor can break users if they had been passing in null for privateKey since it's now ambiguous |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again... what's the TODO? If it's a note for API Review, that doesn't belong in code.
@@ -311,21 +405,63 @@ internal SignerInfoAsn Sign( | |||
ReadOnlyMemory<byte> signatureValue; | |||
ReadOnlyMemory<byte> signatureParameters = default; | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(SlhDsaAlgorithm.SlhDsaSha2_128s, Oids.Sha256), | ||
(SlhDsaAlgorithm.SlhDsaShake128f, Oids.Sha256), | ||
(SlhDsaAlgorithm.SlhDsaSha2_256f, Oids.Sha512), | ||
(SlhDsaAlgorithm.SlhDsaShake256f, Oids.Sha512), // TODO SHAKE256 is not supported but it's the recommended algo here.. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// TODO SHAKE256 is not supported but it's the recommended algo here..
Sounds like you need to put a layer of indirection in front of IncrementalHash, then?
private delegate CmsSigner CreateSignerFunc<TKey>(SubjectIdentifierType sit, X509Certificate2 cert, TKey key); | ||
private static CreateSignerFunc<AsymmetricAlgorithm> CreateAsymmetricAlgorithmSigner = (sit, cert, key) => | ||
{ | ||
return new CmsSigner(sit, cert, key); | ||
}; | ||
|
||
private static CreateSignerFunc<SlhDsa> CreateSlhDsaSigner = (sit, cert, key) => | ||
{ | ||
return new CmsSigner(sit, cert, key); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think these delegates add value (but do add cost and complexity).
VerifyWithExplicitPrivateKey should just take object
and type test to know what to call.
And then I don't have to be upset about the chopping style used where these made the lines too long 😄
CertLoader loader = Certificates.SlhDsaGeneratedCerts.Single(cert => cert.CerData.SequenceEqual(info.Certificate)); | ||
using (X509Certificate2 signerCert = loader.TryGetCertificateWithPrivateKey()) | ||
{ | ||
CmsSigner signer = new CmsSigner(identifierType, signerCert); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing with a CmsSigner using only a cert-attached private key should also be done for the netfx tests. A fair amount of the validation here uses "new" API, so the netfx-capable test will have to be watered down.
Perhaps it should just gain #if around the parts that can't be tested in netfx, and this moved out of the netcoreapp partial.
@@ -868,7 +1133,13 @@ private static void VerifyWithExplicitPrivateKey(X509Certificate2 cert, Asymmetr | |||
} | |||
} | |||
|
|||
private static void VerifyCounterSignatureWithExplicitPrivateKey(X509Certificate2 cert, AsymmetricAlgorithm key, X509Certificate2 counterSignerCert, AsymmetricAlgorithm counterSignerKey) | |||
private static void VerifyCounterSignatureWithExplicitPrivateKey<TKey, TCounterSignerKey>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
De-generic, just take object and type test.
byte[]? attributesToSign = null; | ||
signedAttrsSet.SignedAttributes = PkcsHelpers.NormalizeAttributeSet( | ||
signedAttrs.ToArray(), | ||
encodedSignedAttrs => attributesToSign = encodedSignedAttrs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is the only call to NormalizeAttributeSet now, just change it to out
the value. Capturing via a lambda feels icky.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at current usage, everyone else passes null, which means making a proper overload, one that does the work and has an out, and the other that just sends the out to discard.
CMS for SLH-DSA successfully roundtrips with openssl (message created with SignedCms can be verified with OpenSsl and vice versa). The new implementation forks Sign and Verify into a Pure and Hash mode with the Hash mode being the same as the existing implementation. All existing tests pass.
This is currently a draft mainly so some questions can be answered. These are TODOs in code and briefly enumerated here: