-
Notifications
You must be signed in to change notification settings - Fork 18k
crypto/rsa: some valid P1v1.5 signatures rejected by VerifyPKCS1v15 #16683
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
Comments
cc @agl |
Specifying a complex structure for this data was a terrible mistake and has caused a lot of problems. We're doing pretty well expunging that mistake. Chrome (entirely), Android (in many places) and Google services implement RSA checking in the same fashion as Go. I don't wish to add complexity to this sensitive area of code I'm afraid. |
I had already written a patch when you closed the issue, and it doesn't really increase the complexity of this function much - it adds a second |
The spec for PKCS#1 v1.5 specifies the behaviour that Go implements. Steps 3 and 4 say to encode the expected message and compare. Every TLS server and all but one (now two) certificates that I've seen manages to put the NULL in there. Sometimes the reality of the situation is such that concessions have to be made, but that's not the case here and there's good reason why we're strict about these things whenever possible in the security area. The reality of implementations is that they're aimed in the general direction of a spec and then debugged until they roughly interoperate with one (or, if you're lucky, two) major implementations. If some piece of code is inadvertently depending on some liberty of a major implementation then it might not find the problem until very late, when it fails in practice against a minor implementation. If implementations had been stricter then the problem would have been found sooner. The sooner a bug is found the cheaper the fix. (Ask the Estonian ID cards folks.) Thus strict implementations help grow a healthy ecosystem. None the less, as shown here, the pressures are towards replicating every laxness in every implementation. That only works in the short-term however. We really want implementations that follow the spec for this as trying to parse the ASN.1 has been a disaster in practice and that means trying to defend the simple answer. Additionally, implementations are reality, but specs are what gets analysed by researchers and proven. If there's a gap between the two then we can end up believing things that aren't actually true in reality. So the real issue here is that OpenSSL parses signatures rather than comparing them and that has allowed Yubikey to end up inadvertently depending on it. |
The citation in RFC 4055, by the way, refers to OIDs like id-sha256WithRSAEncryption. That's for the The |
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (
go version
)?I'm using
go version go1.6.2 darwin/amd64
What operating system and processor architecture are you using (
go env
)?What did you do?
https://play.golang.org/p/Dh7S2wq-4c
This program tries to verify that one certificate is properly signed by another. The given signing certificate is the attestation certificate of a yubikey, as described on https://developers.yubico.com/PIV/Introduction/PIV_attestation.html
What did you expect to see?
I expected the program to finish successfully. The certificate is in fact properly signed - placing the certificates in the relevant files and running
openssl verify -partial_chain -CAfile /path/to/attestingCert /path/to/attestation
(with openssl 1.0.2 for thepartial_chain
flag)passes.
What did you see instead?
I got an error from crypto/rsa/pkcs1v15.go:293 - In particular, the check on line 285 fails, citing an incorrect "prefix".
The way go's crypto library implements this check is interesting. From
crypto/rsa/pkcs1v15.go
:When the function which checks signatures checks that the signature is valid, rather than decoding the ASN.1 DER structure mentioned above, it checks that the structure is equal to the one it expects. However, there are in fact 2 legal ways of representing each of the above algorithms. RFC 4055 defines a set of oids for representing a
digestAlgorithm
:The go implementation contains the ASN.1 structure corresponding to the object identifier with a NULL parameter. However, the certificates that yubikeys generate instead contain the ASN.1 structure without a NULL parameter. These examples give a digest of all zeroes, but that bitstring would normally contain the hashed value.
With parameter: https://lapo.it/asn1js/#3031300D0609608648016503040201050004200000000000000000000000000000000000000000000000000000000000000000
Without: https://lapo.it/asn1js/#302F300B060960864801650304020104200000000000000000000000000000000000000000000000000000000000000000
Thus, despite the certificate being valid, the go library finds the "prefix" to be incorrect and errors when validating it.
I see a few possible fixes:
I'd tend toward implementing the second of the above fixes, because the first seems more likely to introduce timing attacks toward breaking the relevant crypto; however, I'm not an expert and could be convinced to implement either.
The text was updated successfully, but these errors were encountered: