Skip to content

feat(csharp): capture cryptographic parameters and key material - #559

Merged
fynnth merged 7 commits into
mainfrom
csharp-parameter-capture
Oct 23, 2026
Merged

fynnth merged 7 commits into
mainfrom
csharp-parameter-capture

Conversation

@fynnth

@fynnth fynnth commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

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-2048 findings
account exactly for 5 × RSA.Create(2048), 5 × new RSACryptoServiceProvider(2048) and
4 × .KeySize = 2048.

The 9 call sites that were missed all come down to three C# expression forms the parser does not
yet unwrap (an as cast on the result, a ??= assignment, and a creation inside a switch
expression 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

fynnth added 2 commits October 8, 2026 21:43
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>
@fynnth
fynnth requested a review from a team as a code owner October 9, 2026 09:00
@fynnth

fynnth commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor Author

@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 :)

@san-zrl san-zrl left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@fynnth

fynnth commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor Author

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>
@fynnth

fynnth commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor Author

@san-zrl

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.
It should now also capture the OEAP-part in your Repo ;), but how those findings are connected in the cbom is exactly what i wanted to talk about in the call.

Best regards
Fynn

@san-zrl

san-zrl commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

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
https://github.com/nagilum/cryptocsharp/blob/9ea719e/Cryptography.cs#L207
2.) The detection location is where the RSA key is created. The RSA_OAEP function is performed two lines below (where the SHA-1 are detected).

@fynnth

fynnth commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

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
Fynn

Signed-off-by: Fynn Thierling <fynnth@outlook.de>
@san-zrl

san-zrl commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Hi @fynnth - Do you want to merge or are there more changes to come?

@fynnth

fynnth commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

I think we can merge this as it is for now, and other fixes and features will be done as a separate pr :)

@fynnth
fynnth merged commit bee9964 into main Oct 23, 2026
2 checks passed
@fynnth
fynnth deleted the csharp-parameter-capture branch October 23, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants