Open bazuker opened 1 year ago
cc @golang/security
go.1.20.5@
This one is easy to root-cause (https://github.com/golang/go/commit/5aa6313e587d7fa7caba9a76d23dd4d246cb6d1f). The code has had bit of a refactor since the change that introduced the issue, I will refer to what is currently in tree.
This used to call check := encrypt(&priv.PublicKey, m)
. While the new operation is the equivalent RSA encryption, N
is now pulled from the PrivateKey if precomputed values are present.
The fix to restore previous behavior is to unconditionally de-serialize priv.N
in the check branch, for example:
enn, err := bigmod.NewModulusFromBig(priv.N)
if err != nil {
return nil, ErrDecryption
}
c1 := bigmod.NewNat().ExpShort(m, uint(priv.E), enn)
Edit: One could conceivably also splatter checks all over the place where the precomputed values are used to try to detect "user did something unwise and there is now a mismatch", but I'm not sure how much that is worth it.
The check is there to ensure that if we do the CRT wrong due to a bug or a fault, we don't leak the private key. It's actually safer and more correct to do it against what we used to produce the signature. The consistency check was a side effect, and I'm not sure it was an intentional one. (For sure there was no test or dedicated error message.)
NewModulusFromBig is a slow operation, adding it to every signature would be significant.
We can probably find a quicker way to check the PublicKey is not mismatched but 1) how can that happen? and 2) how bad is it? PrivateKeys can be corrupted in a number of ways we don't catch, the assumption is always that the private key is trusted (for better or worse).
The check is there to ensure that if we do the CRT wrong due to a bug or a fault, we don't leak the private key. It's actually safer and more correct to do it against what we used to produce the signature. The consistency check was a side effect, and I'm not sure it was an intentional one. (For sure there was no test or dedicated error message.)
Makes sense.
NewModulusFromBig is a slow operation, adding it to every signature would be significant.
We can probably find a quicker way to check the PublicKey is not mismatched but 1) how can that happen? and 2) how bad is it? PrivateKeys can be corrupted in a number of ways we don't catch, the assumption is always that the private key is trusted (for better or worse).
This sort of thing is why I tend to be a proponent of "make key types as opaque as I can get away with", but due to backward compatibility concerns that ship has long sailed here. FWIW, I personally also would not be overly concerned with this case, because this feels like something that lands firmly in "So don't do that then" territory.
PrivateKey.Validate
correctly flags this inconsistency, so "surely if the user messes with the private key, they will call the dedicated validation routine"...
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
rsa.SignPKCS1v15
does not return an error ingo1.20.5
when public key and private key are mismatched.The test below passes in
go.1.19.7
because the error is returned as expected. Ingo1.20.5
no error is returned and hence the test fails.What did you expect to see?
I expect this test to pass in
go.1.20.5
.What did you see instead?
The test fails in
go1.20.5
.