Infamously, big.Int does not provide constant-time arithmetic, making its use in cryptographic code quite tricky. RSA uses big.Int pervasively, in its public API, for key generation, precomputation, and for encryption and decryption. This is a known problem. One mitigation, blinding, is already in place during decryption. This helps mitigate the very leaky exponentiation operation. Because big.Int is fundamentally not constant-time, it's unfortunately difficult to guarantee that mitigations like these are completely effective. This patch removes the use of big.Int for encryption and decryption, replacing it with an internal nat type instead. Signing and verification are also affected, because they depend on encryption and decryption. Overall, this patch degrades performance by 55% for private key operations, and 4-5x for (much faster) public key operations. (Signatures do both, so the slowdown is worse than decryption.) name old time/op new time/op delta DecryptPKCS1v15/2048-8 1.50ms ± 0% 2.34ms ± 0% +56.44% (p=0.000 n=8+10) DecryptPKCS1v15/3072-8 4.40ms ± 0% 6.79ms ± 0% +54.33% (p=0.000 n=10+9) DecryptPKCS1v15/4096-8 9.31ms ± 0% 15.14ms ± 0% +62.60% (p=0.000 n=10+10) EncryptPKCS1v15/2048-8 8.16µs ± 0% 355.58µs ± 0% +4258.90% (p=0.000 n=10+9) DecryptOAEP/2048-8 1.50ms ± 0% 2.34ms ± 0% +55.68% (p=0.000 n=10+9) EncryptOAEP/2048-8 8.51µs ± 0% 355.95µs ± 0% +4082.75% (p=0.000 n=10+9) SignPKCS1v15/2048-8 1.51ms ± 0% 2.69ms ± 0% +77.94% (p=0.000 n=10+10) VerifyPKCS1v15/2048-8 7.25µs ± 0% 354.34µs ± 0% +4789.52% (p=0.000 n=9+9) SignPSS/2048-8 1.51ms ± 0% 2.70ms ± 0% +78.80% (p=0.000 n=9+10) VerifyPSS/2048-8 8.27µs ± 1% 355.65µs ± 0% +4199.39% (p=0.000 n=10+10) Keep in mind that this is without any assembly at all, and that further improvements are likely possible. I think having a review of the logic and the cryptography would be a good idea at this stage, before we complicate the code too much through optimization. The bulk of the work is in nat.go. This introduces two new types: nat, representing natural numbers, and modulus, representing moduli used in modular arithmetic. A nat has an "announced size", which may be larger than its "true size", the number of bits needed to represent this number. Operations on a nat will only ever leak its announced size, never its true size, or other information about its value. The size of a nat is always clear based on how its value is set. For example, x.mod(y, m) will make the announced size of x match that of m, since x is reduced modulo m. Operations assume that the announced size of the operands match what's expected (with a few exceptions). For example, x.modAdd(y, m) assumes that x and y have the same announced size as m, and that they're reduced modulo m. Nats are represented over unsatured bits.UintSize - 1 bit limbs. This means that we can't reuse the assembly routines for big.Int, which use saturated bits.UintSize limbs. The advantage of unsaturated limbs is that it makes Montgomery multiplication faster, by needing fewer registers in a hot loop. This makes exponentiation faster, which consists of many Montgomery multiplications. Moduli use nat internally. Unlike nat, the true size of a modulus always matches its announced size. When creating a modulus, any zero padding is removed. Moduli will also precompute constants when created, which is another reason why having a separate type is desirable. Updates #20654 Co-authored-by: Filippo Valsorda <filippo@golang.org> Change-Id: I73b61f87d58ab912e80a9644e255d552cbadcced Reviewed-on: https://go-review.googlesource.com/c/go/+/326012 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org> Reviewed-by: Joedian Reid <joedian@golang.org> |
||
|---|---|---|
| .github | ||
| api | ||
| doc | ||
| lib/time | ||
| misc | ||
| src | ||
| test | ||
| .gitattributes | ||
| .gitignore | ||
| CONTRIBUTING.md | ||
| LICENSE | ||
| PATENTS | ||
| README.md | ||
| SECURITY.md | ||
| codereview.cfg | ||
README.md
The Go Programming Language
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.
Gopher image by Renee French, licensed under Creative Commons 4.0 Attributions license.
Our canonical Git repository is located at https://go.googlesource.com/go. There is a mirror of the repository at https://github.com/golang/go.
Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the LICENSE file.
Download and Install
Binary Distributions
Official binary distributions are available at https://go.dev/dl/.
After downloading a binary release, visit https://go.dev/doc/install for installation instructions.
Install From Source
If a binary distribution is not available for your combination of operating system and architecture, visit https://go.dev/doc/install/source for source installation instructions.
Contributing
Go is the work of thousands of contributors. We appreciate your help!
To contribute, please read the contribution guidelines at https://go.dev/doc/contribute.
Note that the Go project uses the issue tracker for bug reports and proposals only. See https://go.dev/wiki/Questions for a list of places to ask questions about the Go language.