Repository navigation
feat(csharp): capture cryptographic parameters and key material - #559
Conversation
Rules declare their parameters by .NET name and declared type instead of by position, so one rule per method covers every overload and a value lands on the right algorithm (201 named declarations; withAnyParameters 159 -> 88). Values resolve only where the syntax is certain, and the guards drop a value rather than guess one. Adds the call sites that were invisible before: #if regions, creations that are not statements of their own, the static HashData/TryHashData helpers, and certificate key accessors in both spellings. A certificate-derived key is now typed as private or public key material, so an absent key length reads as a fact rather than a gap. Dispatch moves from one detection per method body to one per call site, matching Java and Python. Several calls of the same type in one method used to share a detection store, so all but the first lost its parameter. Signed-off-by: Fynn Thierling <fynnth@outlook.de>
Signed-off-by: Fynn Thierling <fynnth@outlook.de>
|
@cbomkit/cbomkit-maintainers It would be really great if you could have a short integration test on your side as well against a repository with a sonarqube docker and give some feedback how it performs for you :) |
There was a problem hiding this comment.
Hi @fynnth,
I tested the new parameter detection with sonarqube on docker against the https://github.com/nagilum/cryptocsharp repo. Before parameter detection I got 6 findings. Now I get 8. The 2 new findings are
{
"type" : "cryptographic-asset",
"bom-ref" : "9d9c4afc-6ed0-4616-8b13-2dac7d3b41a1",
"name" : "SHA-1",
"evidence" : {
"occurrences" : [
{
"location" : "Cryptography.cs",
"line" : 173,
"offset" : 24,
"additionalContext" : "Decrypt"
},
{
"location" : "Cryptography.cs",
"line" : 209,
"offset" : 24,
"additionalContext" : "Encrypt"
}
]
},
"cryptoProperties" : {
"assetType" : "algorithm",
"algorithmProperties" : {
"primitive" : "hash",
"parameterSetIdentifier" : "160",
"cryptoFunctions" : [
"digest"
]
},
"oid" : "1.3.14.3.2.26"
}
}The RSA key creation findings depend on these SHA-1 findings. Is this the correct behavior? I would have expected RSA-OAEP detections with the RSA key creation and the SHA-1 digest as dependencies.
|
Thanks Andreas, No this is not supposed to be like this (at least the oaep part is missing right now). I checked the detection store for this repo and i believe there is everything correct. So it it probably is just an presentation error in the cbom and the reorganizer rules for csharp. i will have a look :) |
Signed-off-by: Fynn Thierling <fynnth@outlook.de>
Signed-off-by: Fynn Thierling <fynnth@outlook.de>
Signed-off-by: Fynn Thierling <fynnth@outlook.de>
Signed-off-by: Fynn Thierling <fynnth@outlook.de>
|
I think this should be ready for a merge now. This state is not yet perfect but i believe every other gap should be a separate pr. I also would like to discuss some things in regards to the CBOM component structure for findings in the next TSC. Best regards |
|
Hi @fynnth, Now I get RSA-OAEP findings that depend on SHA-1. Looking closer I came across two inaccuracies: {
"type" : "cryptographic-asset",
"bom-ref" : "3e873d63-43a7-4583-9821-331fd9dac3d4",
"name" : "RSA-OAEP",
"evidence" : {
"occurrences" : [
{
"location" : "Cryptography.cs",
"line" : 171,
"offset" : 28,
"additionalContext" : "Create"
},
{
"location" : "Cryptography.cs",
"line" : 207,
"offset" : 28,
"additionalContext" : "Create"
}
]
},
"cryptoProperties" : {
"assetType" : "algorithm",
"algorithmProperties" : {
"primitive" : "pke",
"padding" : "oaep",
"cryptoFunctions" : [
"decrypt"
]
},
"oid" : "1.2.840.113549.1.1.7"
}
},1.) Both RSA-OAEP findings are reported as decrypt. This is not true, the second one is an encryption |
|
Hi @san-zrl, That is exactly what i would like to talk about during TSC :) If it is ok with you i would leave it like this for the moment and wait for our discussion on it. Best |
Signed-off-by: Fynn Thierling <fynnth@outlook.de>
|
Hi @fynnth - Do you want to merge or are there more changes to come? |
|
I think we can merge this as it is for now, and other fixes and features will be done as a separate pr :) |
What this does
Parameters now land on the algorithm they belong to. Previously most C# findings were just
an algorithm name. The rules now capture the values that decide whether a component is sound:
key sizes, elliptic curves, iteration counts, salt and derived-key lengths, signature digests,
RSA paddings, AEAD nonce and tag lengths, and post-quantum parameter sets. Parameters are
matched by their real .NET names rather than by position, so one rule per method covers every
overload and a value still lands correctly when an overload reorders it.
Variable tracking got substantially more robust. A value reaches the algorithm whether it
is written inline, held in a local, a constant or a field, passed along through an alias, or set
through a property afterwards. Control flow inside a method no longer interrupts tracking, and
creations that are not statements of their own (field and property initializers, a creation
handed straight to another constructor, object- and collection-initializer values) are now
call sites too.
First coverage of the certificate namespace. A key taken from an X.509 certificate is
recorded as private or public key material. This matters for how the result reads: such a key
has no length in the source, because the certificate holds it. Recording the key kind is what
lets a reader tell "there is nothing to resolve here" from "we failed to resolve it".
Throughout, the rule is that a value is only reported when it can be established with
certainty. Where it cannot, nothing is reported rather than a guess — several tests assert the
absence of a value for exactly that reason.
How I validated this
I scanned five real C# projects (Microsoft IdentityModel, MimeKit, NuGet.Client, Bitwarden
server, ASP.NET Core Data Protection) and checked the results against the source by hand rather
than trusting the totals.
For IdentityModel I counted every cryptographic call site in the repository (171) and compared
them one by one with what the scan reported. 162 were found, and everything that was found was
correct: no fabricated values, nothing attributed to the wrong algorithm, and no family
reported more often than it occurs in the code. As a spot check, the 14
RSA-2048findingsaccount exactly for 5 ×
RSA.Create(2048), 5 ×new RSACryptoServiceProvider(2048)and4 ×
.KeySize = 2048.The 9 call sites that were missed all come down to three C# expression forms the parser does not
yet unwrap (an
ascast on the result, a??=assignment, and a creation inside aswitchexpression arm). They are listed under the known limitations in the README rather than left
implicit.
Scan result for IdentityModel:
24x (MessageDigest) SHA-256
18x (PublicKeyEncryption) RSA
16x (Signature) ECDSA
14x (PublicKeyEncryption) RSA-2048
14x (PublicKey) RSA
13x (Signature) ML-DSA-44
11x (Signature) ML-DSA
9x (PseudorandomNumberGenerator) NATIVEPRNG
6x (Signature) ECDSA-nistP256
5x (BlockCipher) AES
5x (PrivateKey) RSA
4x (MessageDigest) SHA-384
4x (MessageDigest) SHA-512
3x (Signature) ECDSA-nistP384
2x (Mac) HMAC-SHA-256
2x (Mac) HMAC-SHA-384
2x (Mac) HMAC-SHA-512
2x (BlockCipher) AES-CBC-PKCS7
2x (KeyAgreement) ECDH
2x (Signature) ECDSA-nistP521
1x (BlockCipher) AES-ECB-None
1x (PrivateKey) ECDSA
1x (PublicKey) ECDSA
1x (Signature) ML-DSA-65