RavenDB Security ReportCollision in Certificate Serial Numbers
This issue in the RavenDB Security Report is pretty simple, when we generate a certificate, we need to generate a certificate serial number. We were using a random number that is 64 bits in length, but that is too small. The problem is the birthday attack. For a 64 bits number, you only need about 5 billion attempts to generate a collision. In modern cryptography, that is actually a very low security threshold.
So we fixed it and used a random value that is 20 bytes in length. Or so we thought. This single issue is worth the trouble of publicly discussing the security report. As it turned out, I didn’t read the API docs properly and used this construction:
new BigInteger(20, random);
Where the random is a cryptographically secured random number generator. The problem here is that this BigInteger constructor uses bits length, not bytes length. And that resulted in a security “fix” that actually much worse than the previous situation (you only need a bit over a thousand tries to generate a collision). This has already been fixed, obviously, but I’m very happy that it was caught.
More posts in "RavenDB Security Report" series:
- (06 Apr 2018) Collision in Certificate Serial Numbers
- (05 Apr 2018) Man in the middle for customer domains
- (04 Apr 2018) Non-high Strength RSA Keys
- (30 Mar 2018) Inconsistent Use of KDF and Master Key
- (29 Mar 2018) Redundant or Missing Authentication
Comments
Can you explain what kind of attack is performed based on "collision" of serial number of certificate?
Just in case anyone is confused as I was, the type in question is
Org.BouncyCastle.Math.BigInteger
, not the more commonSystem.Numerics.BigInteger
.Also, one approach that can help with avoiding such bugs is to use named arguments:
new BigInteger(sizeInBits: 20, random);
(this specific syntax requires C# 7.2).Andrey, See discussion here on the difference between serial and thumbprint. https://security.stackexchange.com/questions/35691/what-is-the-difference-between-serial-number-and-thumbprint
In general, a signer should always generate unique certificate ids, and not doing so can lead to issues, see: https://bugs.chromium.org/p/chromium/issues/detail?id=81960
In particular, if you are using the serial number as the key, that can be an issue. We use the certificate's thumbprint, instead, so we aren't vulnerable in this regard.
This seems to suggest that the serial is used in revocation lists, instead of the thumbprint, but this is a low priority issue:
https://www.itweb.co.za/content/Pero3qZg36bvQb6m
@svick I agree. I always used named arguments for values like 0, 1, 20, etc, and true, false. Pretty much named arguments everywhere. R# makes it easy and it allows me more freedom when refactoring code. Reading code of the sort MyFunc(20, 4, true, false) is horrible compared to MyFunc(start: 20, length: 4, inclusive: true, destroyTheWord: false)
Of course, an options object would be even better, but you can't always control the API you're having to call but you can at least control your code. Also helps if you have mean third parties that add optional parameters and pull other shenanigans that could result in subtle changes to which method overload you're actually calling.
Comment preview