Russ Cox would like Adam Langley to review this change.
[dev.boringcrypto] crypto/rsa: use BoringCrypto
DO NOT SUBMIT
- TODO: DecryptPKCS1v15SessionKey
- TODO: crypto.MD5SHA1
Change-Id: Ibb92f0f8cb487f4d179b069e588e1cb266599384
---
A src/crypto/internal/boring/asn1.go
A src/crypto/internal/boring/asn1_test.go
M src/crypto/internal/boring/hmac.go
M src/crypto/internal/boring/notboring.go
A src/crypto/internal/boring/rsa.go
A src/crypto/rsa/boring.go
M src/crypto/rsa/pkcs1v15.go
M src/crypto/rsa/pkcs1v15_test.go
M src/crypto/rsa/pss.go
M src/crypto/rsa/pss_test.go
M src/crypto/rsa/rsa.go
M src/crypto/rsa/rsa_test.go
12 files changed, 820 insertions(+), 20 deletions(-)
diff --git a/src/crypto/internal/boring/asn1.go b/src/crypto/internal/boring/asn1.go
new file mode 100644
index 0000000..6fc89ab
--- /dev/null
+++ b/src/crypto/internal/boring/asn1.go
@@ -0,0 +1,59 @@
+// Copyright 2017 The Go Authors. All Rights Reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,amd64
+// +build !cmd_go_bootstrap
+
+package boring
+
+import "math/big"
+
+// ASN1Marshal is asn1.Marshal for a sequence of big.Ints
+// (such as used for RSA and ECDSA keys and ECDSA signatures).
+// Duplicating this code (instead of calling asn1.Marshal)
+// avoids introducing a new dependency from crypto/rsa
+// on encoding/asn1.
+func ASN1Marshal(nums ...*big.Int) []byte {
+ enc := make([]byte, 10) // 10 is plenty of space for header
+ for _, x := range nums {
+ enc = append(enc, 0x02) // INTEGER
+ raw := x.Bytes()
+ // Need leading zero for empty bytes (if 0 produces empty)
+ // or to keep a large positive number beginning with 0x80
+ // from appearing to be negative.
+ leadingZero := 0
+ if len(raw) == 0 || raw[0] >= 0x80 {
+ leadingZero = 1
+ }
+ enc = appendASN1Length(enc, leadingZero+len(raw))
+ if leadingZero == 1 {
+ enc = append(enc, 0)
+ }
+ enc = append(enc, raw...)
+ }
+
+ // Construct and insert header: 0x30 followed by length so far.
+ hdr := appendASN1Length([]byte{0x30}, len(enc[10:]))
+ copy(enc[10-len(hdr):], hdr)
+ return enc[10-len(hdr):]
+}
+
+// appendASN1Length appends x to enc as an ASN.1 length.
+// The encoding is a raw byte for x < 0x80, or else
+// 0x80 | number-of-256-bit-digits followed by that many digits,
+// in big-endian order.
+func appendASN1Length(enc []byte, x int) []byte {
+ if x < 0x80 {
+ return append(enc, byte(x))
+ }
+ nbyte := 1
+ for x>>uint(8*nbyte) != 0 {
+ nbyte++
+ }
+ enc = append(enc, 0x80|byte(nbyte))
+ for i := nbyte - 1; i >= 0; i-- {
+ enc = append(enc, byte(x>>uint(8*i)))
+ }
+ return enc
+}
diff --git a/src/crypto/internal/boring/asn1_test.go b/src/crypto/internal/boring/asn1_test.go
new file mode 100644
index 0000000..5a55931
--- /dev/null
+++ b/src/crypto/internal/boring/asn1_test.go
@@ -0,0 +1,42 @@
+// Copyright 2017 The Go Authors. All Rights Reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,amd64
+// +build !cmd_go_bootstrap
+
+package boring
+
+import (
+ "bytes"
+ "encoding/asn1"
+ "math/big"
+ "testing"
+)
+
+var asn1Tests = [][]*big.Int{
+ {},
+ {big.NewInt(0)},
+ {big.NewInt(1)},
+ {big.NewInt(2)},
+ {big.NewInt(0x80)},
+ {big.NewInt(1), big.NewInt(2)},
+ // int lengths fit in 7 bits, but total seq length does not
+ {new(big.Int).Lsh(big.NewInt(1), 800), new(big.Int).Lsh(big.NewInt(0x80), 800)},
+ // int length doesn't fit either
+ {new(big.Int).Lsh(big.NewInt(1), 1600), big.NewInt(3)},
+}
+
+func TestASN1Marshal(t *testing.T) {
+ for i, tt := range asn1Tests {
+ a1 := ASN1Marshal(tt...)
+ a2, err := asn1.Marshal(tt)
+ if err != nil {
+ t.Errorf("#%d: asn1.Marshal: %v", i, err)
+ continue
+ }
+ if !bytes.Equal(a1, a2) {
+ t.Errorf("#%d:\nhave %x\nwant %x", i, a1, a2)
+ }
+ }
+}
diff --git a/src/crypto/internal/boring/hmac.go b/src/crypto/internal/boring/hmac.go
index d98bdbf..832009f 100644
--- a/src/crypto/internal/boring/hmac.go
+++ b/src/crypto/internal/boring/hmac.go
@@ -10,6 +10,7 @@
// #include "goboringcrypto.h"
import "C"
import (
+ "crypto"
"hash"
"runtime"
"unsafe"
@@ -33,6 +34,26 @@
return nil
}
+// cryptoHashToMD converts a crypto.Hash
+// to a BoringCrypto *C.GO_EVP_MD.
+func cryptoHashToMD(ch crypto.Hash) *C.GO_EVP_MD {
+ switch ch {
+ case crypto.MD5:
+ return C._goboringcrypto_EVP_md5()
+ case crypto.SHA1:
+ return C._goboringcrypto_EVP_sha1()
+ case crypto.SHA224:
+ return C._goboringcrypto_EVP_sha224()
+ case crypto.SHA256:
+ return C._goboringcrypto_EVP_sha256()
+ case crypto.SHA384:
+ return C._goboringcrypto_EVP_sha384()
+ case crypto.SHA512:
+ return C._goboringcrypto_EVP_sha512()
+ }
+ return nil
+}
+
// NewHMAC returns a new HMAC using BoringCrypto.
// The function h must return a hash implemented by
// BoringCrypto (for example, h could be boring.NewSHA256).
diff --git a/src/crypto/internal/boring/notboring.go b/src/crypto/internal/boring/notboring.go
index 424f678..adcf96b 100644
--- a/src/crypto/internal/boring/notboring.go
+++ b/src/crypto/internal/boring/notboring.go
@@ -7,6 +7,7 @@
package boring
import (
+ "crypto"
"crypto/cipher"
"hash"
"math/big"
@@ -51,3 +52,40 @@
func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, r, s *big.Int) bool {
panic("boringcrypto: not available")
}
+
+type PublicKeyRSA struct{ _ int }
+type PrivateKeyRSA struct{ _ int }
+
+func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
+ panic("boringcrypto: not available")
+}
+func NewPrivateKeyRSA(enc []byte) (*PrivateKeyRSA, error) { panic("boringcrypto: not available") }
+func NewPublicKeyRSA(enc []byte) (*PublicKeyRSA, error) { panic("boringcrypto: not available") }
+func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error {
+ panic("boringcrypto: not available")
+}
+func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error {
+ panic("boringcrypto: not available")
+}
+
+func ASN1Marshal(...*big.Int) []byte {
+ panic("boringcrypto: not available")
+}
diff --git a/src/crypto/internal/boring/rsa.go b/src/crypto/internal/boring/rsa.go
new file mode 100644
index 0000000..6053ab8
--- /dev/null
+++ b/src/crypto/internal/boring/rsa.go
@@ -0,0 +1,283 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,amd64
+// +build !cmd_go_bootstrap
+
+package boring
+
+// #include "goboringcrypto.h"
+import "C"
+import (
+ "crypto"
+ "crypto/subtle"
+ "errors"
+ "hash"
+ "math/big"
+ "runtime"
+ "strconv"
+ "unsafe"
+)
+
+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
+ bad := func(e error) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
+ return nil, nil, nil, nil, nil, nil, nil, nil, e
+ }
+
+ key := C._goboringcrypto_RSA_new()
+ if key == nil {
+ return bad(fail("RSA_new"))
+ }
+ defer C._goboringcrypto_RSA_free(key)
+
+ if C._goboringcrypto_RSA_generate_key_fips(key, C.int(bits), nil) == 0 {
+ return bad(fail("RSA_generate_key_fips"))
+ }
+
+ var n, e, d, p, q, dp, dq, qinv *C.GO_BIGNUM
+ C._goboringcrypto_RSA_get0_key(key, &n, &e, &d)
+ C._goboringcrypto_RSA_get0_factors(key, &p, &q)
+ C._goboringcrypto_RSA_get0_crt_params(key, &dp, &dq, &qinv)
+ return bnToBig(n), bnToBig(e), bnToBig(d), bnToBig(p), bnToBig(q), bnToBig(dp), bnToBig(dq), bnToBig(qinv), nil
+}
+
+type PublicKeyRSA struct {
+ key *C.GO_RSA
+}
+
+func NewPublicKeyRSA(enc []byte) (*PublicKeyRSA, error) {
+ key := C._goboringcrypto_RSA_public_key_from_bytes((*C.uint8_t)(unsafe.Pointer(&enc[0])), C.size_t(len(enc)))
+ if key == nil {
+ return nil, fail("RSA_public_key_from_bytes")
+ }
+ k := &PublicKeyRSA{key: key}
+ runtime.SetFinalizer(k, (*PublicKeyRSA).finalize)
+ return k, nil
+}
+
+func (k *PublicKeyRSA) finalize() {
+ C._goboringcrypto_RSA_free(k.key)
+}
+
+type PrivateKeyRSA struct {
+ key *C.GO_RSA
+}
+
+func NewPrivateKeyRSA(enc []byte) (*PrivateKeyRSA, error) {
+ key := C._goboringcrypto_RSA_private_key_from_bytes((*C.uint8_t)(unsafe.Pointer(&enc[0])), C.size_t(len(enc)))
+ if key == nil {
+ return nil, fail("RSA_private_key_from_bytes")
+ }
+ k := &PrivateKeyRSA{key: key}
+ runtime.SetFinalizer(k, (*PrivateKeyRSA).finalize)
+ return k, nil
+}
+
+func (k *PrivateKeyRSA) finalize() {
+ C._goboringcrypto_RSA_free(k.key)
+}
+
+func setupRSA(key *C.GO_RSA,
+ padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash,
+ init func(*C.GO_EVP_PKEY_CTX) C.int) (pkey *C.GO_EVP_PKEY, ctx *C.GO_EVP_PKEY_CTX, err error) {
+ defer func() {
+ if err != nil {
+ if pkey != nil {
+ C._goboringcrypto_EVP_PKEY_free(pkey)
+ pkey = nil
+ }
+ if ctx != nil {
+ C._goboringcrypto_EVP_PKEY_CTX_free(ctx)
+ ctx = nil
+ }
+ }
+ }()
+
+ pkey = C._goboringcrypto_EVP_PKEY_new()
+ if pkey == nil {
+ return nil, nil, fail("EVP_PKEY_new")
+ }
+ if C._goboringcrypto_EVP_PKEY_set1_RSA(pkey, key) == 0 {
+ return nil, nil, fail("EVP_PKEY_set1_RSA")
+ }
+ ctx = C._goboringcrypto_EVP_PKEY_CTX_new(pkey, nil)
+ if ctx == nil {
+ return nil, nil, fail("EVP_PKEY_CTX_new")
+ }
+ if init(ctx) == 0 {
+ return nil, nil, fail("EVP_PKEY_operation_init")
+ }
+ if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(ctx, padding) == 0 {
+ return nil, nil, fail("EVP_PKEY_CTX_set_rsa_padding")
+ }
+ if padding == C.GO_RSA_PKCS1_OAEP_PADDING {
+ md := hashToMD(h)
+ if md == nil {
+ return nil, nil, errors.New("crypto/rsa: unsupported hash function")
+ }
+ if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) == 0 {
+ return nil, nil, fail("EVP_PKEY_set_rsa_oaep_md")
+ }
+ // ctx takes ownership of label, so malloc a copy for BoringCrypto to free.
+ clabel := (*C.uint8_t)(C.malloc(C.size_t(len(label))))
+ if clabel == nil {
+ return nil, nil, fail("malloc")
+ }
+ copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label)
+ if C._goboringcrypto_EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, clabel, C.size_t(len(label))) == 0 {
+ return nil, nil, fail("EVP_PKEY_CTX_set0_rsa_oaep_label")
+ }
+ }
+ if padding == C.GO_RSA_PKCS1_PSS_PADDING {
+ if saltLen != 0 {
+ if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, C.int(saltLen)) == 0 {
+ return nil, nil, fail("EVP_PKEY_set_rsa_pss_saltlen")
+ }
+ }
+ md := cryptoHashToMD(ch)
+ if md == nil {
+ return nil, nil, errors.New("crypto/rsa: unsupported hash function")
+ }
+ if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md) == 0 {
+ return nil, nil, fail("EVP_PKEY_set_rsa_mgf1_md")
+ }
+ }
+
+ return pkey, ctx, nil
+}
+
+func cryptRSA(key *C.GO_RSA,
+ padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash,
+ init func(*C.GO_EVP_PKEY_CTX) C.int,
+ crypt func(*C.GO_EVP_PKEY_CTX, *C.uint8_t, *C.size_t, *C.uint8_t, C.size_t) C.int,
+ in []byte) ([]byte, error) {
+
+ pkey, ctx, err := setupRSA(key, padding, h, label, saltLen, ch, init)
+ if err != nil {
+ return nil, err
+ }
+ defer C._goboringcrypto_EVP_PKEY_free(pkey)
+ defer C._goboringcrypto_EVP_PKEY_CTX_free(ctx)
+
+ var outLen C.size_t
+ if crypt(ctx, nil, &outLen, base(in), C.size_t(len(in))) == 0 {
+ return nil, fail("EVP_PKEY_decrypt/encrypt")
+ }
+ out := make([]byte, outLen)
+ if crypt(ctx, base(out), &outLen, base(in), C.size_t(len(in))) == 0 {
+ return nil, fail("EVP_PKEY_decrypt/encrypt")
+ }
+ return out[:outLen], nil
+}
+
+func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
+ return cryptRSA(priv.key, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, decryptInit, decrypt, ciphertext)
+}
+
+func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
+ return cryptRSA(pub.key, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, encryptInit, encrypt, msg)
+}
+
+func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+ return cryptRSA(priv.key, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, decryptInit, decrypt, ciphertext)
+}
+
+func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+ return cryptRSA(pub.key, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, encryptInit, encrypt, msg)
+}
+
+// These dumb wrappers work around the fact that cgo functions cannot be used as values directly.
+
+func decryptInit(ctx *C.GO_EVP_PKEY_CTX) C.int {
+ return C._goboringcrypto_EVP_PKEY_decrypt_init(ctx)
+}
+
+func decrypt(ctx *C.GO_EVP_PKEY_CTX, out *C.uint8_t, outLen *C.size_t, in *C.uint8_t, inLen C.size_t) C.int {
+ return C._goboringcrypto_EVP_PKEY_decrypt(ctx, out, outLen, in, inLen)
+}
+
+func encryptInit(ctx *C.GO_EVP_PKEY_CTX) C.int {
+ return C._goboringcrypto_EVP_PKEY_encrypt_init(ctx)
+}
+
+func encrypt(ctx *C.GO_EVP_PKEY_CTX, out *C.uint8_t, outLen *C.size_t, in *C.uint8_t, inLen C.size_t) C.int {
+ return C._goboringcrypto_EVP_PKEY_encrypt(ctx, out, outLen, in, inLen)
+}
+
+func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) {
+ md := cryptoHashToMD(h)
+ if md == nil {
+ return nil, errors.New("crypto/rsa: unsupported hash function")
+ }
+ if saltLen == 0 {
+ saltLen = -1
+ }
+ out := make([]byte, C._goboringcrypto_RSA_size(priv.key))
+ var outLen C.size_t
+ if C._goboringcrypto_RSA_sign_pss_mgf1(priv.key, &outLen, base(out), C.size_t(len(out)), base(hashed), C.size_t(len(hashed)), md, nil, C.int(saltLen)) == 0 {
+ return nil, fail("RSA_sign_pss_mgf1")
+ }
+
+ return out[:outLen], nil
+}
+
+func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error {
+ md := cryptoHashToMD(h)
+ if md == nil {
+ return errors.New("crypto/rsa: unsupported hash function")
+ }
+ if saltLen == 0 {
+ saltLen = -2 // auto-recover
+ }
+ if C._goboringcrypto_RSA_verify_pss_mgf1(pub.key, base(hashed), C.size_t(len(hashed)), md, nil, C.int(saltLen), base(sig), C.size_t(len(sig))) == 0 {
+ return fail("RSA_verify_pss_mgf1")
+ }
+ return nil
+}
+
+func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) {
+ out := make([]byte, C._goboringcrypto_RSA_size(priv.key))
+ if h == 0 {
+ // No hashing.
+ var outLen C.size_t
+ if C._goboringcrypto_RSA_sign_raw(priv.key, &outLen, base(out), C.size_t(len(out)), base(hashed), C.size_t(len(hashed)), C.GO_RSA_PKCS1_PADDING) == 0 {
+ return nil, fail("RSA_sign_raw")
+ }
+ return out[:outLen], nil
+ }
+
+ md := cryptoHashToMD(h)
+ if md == nil {
+ return nil, errors.New("crypto/rsa: unsupported hash function: " + strconv.Itoa(int(h)))
+ }
+ nid := C._goboringcrypto_EVP_MD_type(md)
+ var outLen C.uint
+ if C._goboringcrypto_RSA_sign(nid, base(hashed), C.uint(len(hashed)), base(out), &outLen, priv.key) == 0 {
+ return nil, fail("RSA_sign")
+ }
+ return out[:outLen], nil
+}
+
+func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error {
+ if h == 0 {
+ var outLen C.size_t
+ out := make([]byte, C._goboringcrypto_RSA_size(pub.key))
+ if C._goboringcrypto_RSA_verify_raw(pub.key, &outLen, base(out), C.size_t(len(out)), base(sig), C.size_t(len(sig)), C.GO_RSA_PKCS1_PADDING) == 0 {
+ return fail("RSA_verify")
+ }
+ if subtle.ConstantTimeCompare(hashed, out[:outLen]) != 1 {
+ return fail("RSA_verify")
+ }
+ return nil
+ }
+ md := cryptoHashToMD(h)
+ if md == nil {
+ return errors.New("crypto/rsa: unsupported hash function")
+ }
+ nid := C._goboringcrypto_EVP_MD_type(md)
+ if C._goboringcrypto_RSA_verify(nid, base(hashed), C.size_t(len(hashed)), base(sig), C.size_t(len(sig)), pub.key) == 0 {
+ return fail("RSA_verify")
+ }
+ return nil
+}
diff --git a/src/crypto/rsa/boring.go b/src/crypto/rsa/boring.go
new file mode 100644
index 0000000..50f27c4
--- /dev/null
+++ b/src/crypto/rsa/boring.go
@@ -0,0 +1,215 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package rsa
+
+import (
+ "crypto/internal/boring"
+ "crypto/rand"
+ "errors"
+ "math/big"
+)
+
+// Cached conversions from Go PublicKey/PrivateKey to BoringCrypto.
+//
+// A new 'boring atomic.Value' field in both PublicKey and PrivateKey
+// serves as a cache for the most recent conversion. The cache is an
+// atomic.Value because code might reasonably set up a key and then
+// (thinking it immutable) use it from multiple goroutines simultaneously.
+// The first operation initializes the cache; if there are multiple simultaneous
+// first operations, they will do redundant work but not step on each other.
+//
+// We could just assume that once used in a sign/verify/encrypt/decrypt operation,
+// a particular key is never again modified, but that has not been a
+// stated assumption before. Just in case there is any existing code that
+// does modify the key between operations, we compute a simple checksum
+// of the Go key and use it to verify that the cached BoringCrypto key is
+// still up to date. The checksum applies a very simple mixing function
+// to successive words of the key and should be no slower than just
+// reading the key from memory. The theory is that any operation on the
+// key is going to need to do that anyway, so the overhead of computing
+// and checking the checksum should not be significant.
+
+type boringPub struct {
+ sum uint64
+ key *boring.PublicKeyRSA
+}
+
+func mix(x, y uint64) uint64 {
+ // Dumb but hopefully not completely awful mixing function.
+ return x ^ y ^ (x*y)>>13
+}
+
+func bigSum(x *big.Int) uint64 {
+ words := x.Bits()
+ sum := uint64(len(words))
+ for _, w := range x.Bits() {
+ sum = mix(sum, uint64(w))
+ }
+ return sum
+}
+
+func boringPublicSum(pub *PublicKey) uint64 {
+ return mix(uint64(pub.E), bigSum(pub.N))
+}
+
+func boringPublicKey(pub *PublicKey) (*boring.PublicKeyRSA, error) {
+ b, _ := pub.boring.Load().(boringPub)
+ sum := boringPublicSum(pub)
+ if b.key != nil && b.sum == sum {
+ return b.key, nil
+ }
+
+ key, err := boring.NewPublicKeyRSA(boring.ASN1Marshal(pub.N, big.NewInt(int64(pub.E))))
+ if err != nil {
+ return nil, err
+ }
+ b.sum = sum
+ b.key = key
+ pub.boring.Store(b)
+ return key, nil
+}
+
+type boringPriv struct {
+ sum uint64
+ key *boring.PrivateKeyRSA
+}
+
+func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyRSA, error) {
+ if len(priv.Primes) > 2 {
+ return nil, errors.New("crypto/rsa: multiprime private key not supported")
+ }
+
+ b, _ := priv.boring.Load().(boringPriv)
+ sum := boringPrivateSum(priv)
+ if b.key != nil && b.sum == sum {
+ return b.key, nil
+ }
+
+ primes := priv.Primes
+ pre := priv.Precomputed
+ if len(primes) == 0 || pre.Dp == nil {
+ priv := *priv
+ if len(primes) == 0 {
+ err := factorKey(&priv)
+ if err != nil {
+ // Assume factoring failed due to multiprime key.
+ return nil, errors.New("crypto/rsa: multiprime private key not supported")
+ }
+ }
+ if pre.Dp == nil {
+ priv.Precompute()
+ }
+ primes = priv.Primes
+ pre = priv.Precomputed
+ }
+
+ enc := boring.ASN1Marshal(
+ big.NewInt(0), // Version
+ priv.N,
+ big.NewInt(int64(priv.E)),
+ priv.D,
+ primes[0],
+ primes[1],
+ pre.Dp,
+ pre.Dq,
+ pre.Qinv,
+ )
+
+ key, err := boring.NewPrivateKeyRSA(enc)
+ if err != nil {
+ return nil, err
+ }
+ b.sum = sum
+ b.key = key
+ priv.boring.Store(b)
+ return key, nil
+}
+
+func boringPrivateSum(priv *PrivateKey) uint64 {
+ return mix(boringPublicSum(&priv.PublicKey), bigSum(priv.D))
+}
+
+// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
+// Copied from crypto/x509/pkcs1.go, which we cannot depend on.
+// (It depends on crypto/rsa, which is us.)
+type pkcs1PrivateKey struct {
+ Version int
+ N *big.Int
+ E int
+ D *big.Int
+ P *big.Int `asn1:"optional"`
+ Q *big.Int `asn1:"optional"`
+ // We ignore these values, if present, because rsa will calculate them.
+ Dp *big.Int `asn1:"optional"`
+ Dq *big.Int `asn1:"optional"`
+ Qinv *big.Int `asn1:"optional"`
+}
+
+var (
+ big1 = big.NewInt(1)
+ big2 = big.NewInt(2)
+ big3 = big.NewInt(3)
+)
+
+var errFactor = errors.New("cannot factor key")
+
+// Factor key.N using key.D and key.E.
+// See Handbook of Applied Cryptography, 8.2.2(i).
+// http://cacr.uwaterloo.ca/hac/about/chap8.pdf.
+// The algorithm likely extends to multi-prime keys,
+// but that's not implemented here.
+func factorKey(key *PrivateKey) error {
+ if len(key.Primes) > 0 {
+ return nil
+ }
+
+ // d*e - 1 is a multiple of phi(n).
+ // Compute d*e - 1 as 2^s * t, a multiple of phi(n).
+ s := 0
+ t := new(big.Int).Mul(key.D, big.NewInt(int64(key.E)))
+ t.Sub(t, big1)
+ for t.Bit(s) == 0 {
+ s++
+ }
+ t.Rsh(t, uint(s))
+
+ nm3 := new(big.Int).Sub(key.N, big3)
+Trials:
+ for {
+ a, err := rand.Int(rand.Reader, nm3)
+ if err != nil {
+ return err
+ }
+ a.Add(a, big.NewInt(2))
+
+ nm1 := new(big.Int).Sub(key.N, big1)
+ y := new(big.Int).Exp(a, t, key.N)
+ if y.Cmp(big1) == 0 || y.Cmp(nm1) == 0 {
+ continue
+ }
+ for j := 0; j < s; j++ {
+ y2 := new(big.Int).Exp(y, big2, key.N)
+ if y2.Cmp(nm1) == 0 {
+ continue Trials
+ }
+ if y2.Cmp(big1) == 0 {
+ // gcd(y-1, n) is a non-trivial factor of n.
+ y.Sub(y, big1)
+ p := new(big.Int).GCD(nil, nil, y, key.N)
+ q := new(big.Int).Div(key.N, p)
+ m := new(big.Int).Mul(p, q)
+ if m.Cmp(key.N) != 0 {
+ return errFactor
+ }
+ if !p.ProbablyPrime(20) || !q.ProbablyPrime(20) {
+ return errFactor
+ }
+ key.Primes = []*big.Int{p, q}
+ key.Precompute()
+ return nil
+ }
+ }
+ }
+}
diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go
index 3517a8c..5be840c 100644
--- a/src/crypto/rsa/pkcs1v15.go
+++ b/src/crypto/rsa/pkcs1v15.go
@@ -6,6 +6,7 @@
import (
"crypto"
+ "crypto/internal/boring"
"crypto/subtle"
"errors"
"io"
@@ -34,7 +35,7 @@
//
// WARNING: use of this function to encrypt plaintexts other than
// session keys is dangerous. Use RSA OAEP in new protocols.
-func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) {
+func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, error) {
if err := checkPub(pub); err != nil {
return nil, err
}
@@ -43,11 +44,19 @@
return nil, ErrMessageTooLong
}
+ if boring.Enabled && random == boring.RandReader {
+ bkey, err := boringPublicKey(pub)
+ if err != nil {
+ return nil, err
+ }
+ return boring.EncryptRSAPKCS1(bkey, msg)
+ }
+
// EM = 0x00 || 0x02 || PS || 0x00 || M
em := make([]byte, k)
em[1] = 2
ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):]
- err := nonZeroRandomBytes(ps, rand)
+ err := nonZeroRandomBytes(ps, random)
if err != nil {
return nil, err
}
@@ -73,6 +82,19 @@
if err := checkPub(&priv.PublicKey); err != nil {
return nil, err
}
+
+ if boring.Enabled {
+ bkey, err := boringPrivateKey(priv)
+ if err != nil {
+ return nil, err
+ }
+ out, err := boring.DecryptRSAPKCS1(bkey, ciphertext)
+ if err != nil {
+ return nil, ErrDecryption
+ }
+ return out, nil
+ }
+
valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext)
if err != nil {
return nil, err
@@ -103,6 +125,7 @@
// and thus whether the padding was correct. This defeats the point of this
// function. Using at least a 16-byte key will protect against this attack.
func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) error {
+ // Note: No BoringCrypto here.
if err := checkPub(&priv.PublicKey); err != nil {
return err
}
@@ -225,7 +248,7 @@
// messages is small, an attacker may be able to build a map from
// messages to signatures and identify the signed messages. As ever,
// signatures provide authenticity, not confidentiality.
-func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
+func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
if err != nil {
return nil, err
@@ -237,6 +260,14 @@
return nil, ErrMessageTooLong
}
+ if boring.Enabled && random == boring.RandReader && hash != crypto.MD5SHA1 {
+ bkey, err := boringPrivateKey(priv)
+ if err != nil {
+ return nil, err
+ }
+ return boring.SignRSAPKCS1v15(bkey, hash, hashed)
+ }
+
// EM = 0x00 || 0x01 || PS || 0x00 || T
em := make([]byte, k)
em[1] = 1
@@ -247,7 +278,7 @@
copy(em[k-hashLen:k], hashed)
m := new(big.Int).SetBytes(em)
- c, err := decryptAndCheck(rand, priv, m)
+ c, err := decryptAndCheck(random, priv, m)
if err != nil {
return nil, err
}
@@ -262,6 +293,17 @@
// returning a nil error. If hash is zero then hashed is used directly. This
// isn't advisable except for interoperability.
func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error {
+ if boring.Enabled && hash != crypto.MD5SHA1 {
+ bkey, err := boringPublicKey(pub)
+ if err != nil {
+ return err
+ }
+ if err := boring.VerifyRSAPKCS1v15(bkey, hash, hashed, sig); err != nil {
+ return ErrVerification
+ }
+ return nil
+ }
+
hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
if err != nil {
return err
diff --git a/src/crypto/rsa/pkcs1v15_test.go b/src/crypto/rsa/pkcs1v15_test.go
index 47444f3..7f380b6 100644
--- a/src/crypto/rsa/pkcs1v15_test.go
+++ b/src/crypto/rsa/pkcs1v15_test.go
@@ -64,7 +64,7 @@
for i, test := range decryptPKCS1v15Tests {
out, err := decryptFunc(decodeBase64(test.in))
if err != nil {
- t.Errorf("#%d error decrypting", i)
+ t.Errorf("#%d error decrypting: %v", i, err)
}
want := []byte(test.out)
if !bytes.Equal(out, want) {
diff --git a/src/crypto/rsa/pss.go b/src/crypto/rsa/pss.go
index 1ba194a..393ef16 100644
--- a/src/crypto/rsa/pss.go
+++ b/src/crypto/rsa/pss.go
@@ -11,6 +11,7 @@
import (
"bytes"
"crypto"
+ "crypto/internal/boring"
"errors"
"hash"
"io"
@@ -259,6 +260,14 @@
hash = opts.Hash
}
+ if boring.Enabled {
+ bkey, err := boringPrivateKey(priv)
+ if err != nil {
+ return nil, err
+ }
+ return boring.SignRSAPSS(bkey, hash, hashed, saltLength)
+ }
+
salt := make([]byte, saltLength)
if _, err := io.ReadFull(rand, salt); err != nil {
return nil, err
@@ -277,6 +286,16 @@
// verifyPSS verifies a PSS signature with the given salt length.
func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error {
+ if boring.Enabled {
+ bkey, err := boringPublicKey(pub)
+ if err != nil {
+ return err
+ }
+ if err := boring.VerifyRSAPSS(bkey, hash, hashed, sig, saltLen); err != nil {
+ return ErrVerification
+ }
+ return nil
+ }
nBits := pub.N.BitLen()
if len(sig) != (nBits+7)/8 {
return ErrVerification
diff --git a/src/crypto/rsa/pss_test.go b/src/crypto/rsa/pss_test.go
index cae24e5..4ce5ad8 100644
--- a/src/crypto/rsa/pss_test.go
+++ b/src/crypto/rsa/pss_test.go
@@ -9,7 +9,6 @@
"bytes"
"compress/bzip2"
"crypto"
- _ "crypto/md5"
"crypto/rand"
"crypto/sha1"
_ "crypto/sha256"
@@ -211,7 +210,7 @@
{8, 8, true},
}
- hash := crypto.MD5
+ hash := crypto.SHA1
h := hash.New()
h.Write([]byte("testing"))
hashed := h.Sum(nil)
diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go
index 1de4fcb..2cd2e51 100644
--- a/src/crypto/rsa/rsa.go
+++ b/src/crypto/rsa/rsa.go
@@ -24,6 +24,7 @@
import (
"crypto"
+ "crypto/internal/boring"
"crypto/rand"
"crypto/subtle"
"errors"
@@ -31,6 +32,7 @@
"io"
"math"
"math/big"
+ "sync/atomic"
)
var bigZero = big.NewInt(0)
@@ -40,6 +42,8 @@
type PublicKey struct {
N *big.Int // modulus
E int // public exponent
+
+ boring atomic.Value
}
// OAEPOptions is an interface for passing options to OAEP decryption using the
@@ -85,6 +89,8 @@
// Precomputed contains precomputed values that speed up private
// operations, if available.
Precomputed PrecomputedValues
+
+ boring atomic.Value
}
// Public returns the public key corresponding to priv.
@@ -195,6 +201,31 @@
// GenerateKey generates an RSA keypair of the given bit size using the
// random source random (for example, crypto/rand.Reader).
func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) {
+ if boring.Enabled && (bits == 2048 || bits == 3072) {
+ N, E, D, P, Q, Dp, Dq, Qinv, err := boring.GenerateKeyRSA(bits)
+ if err != nil {
+ return nil, err
+ }
+ e64 := E.Int64()
+ if !E.IsInt64() || int64(int(e64)) != e64 {
+ return nil, errors.New("crypto/rsa: generated key exponent too large")
+ }
+ key := &PrivateKey{
+ PublicKey: PublicKey{
+ N: N,
+ E: int(e64),
+ },
+ D: D,
+ Primes: []*big.Int{P, Q},
+ Precomputed: PrecomputedValues{
+ Dp: Dp,
+ Dq: Dq,
+ Qinv: Qinv,
+ },
+ }
+ return key, nil
+ }
+
return GenerateMultiPrimeKey(random, 2, bits)
}
@@ -376,6 +407,14 @@
return nil, ErrMessageTooLong
}
+ if boring.Enabled && random == boring.RandReader {
+ bkey, err := boringPublicKey(pub)
+ if err != nil {
+ return nil, err
+ }
+ return boring.EncryptRSAOAEP(hash, bkey, msg, label)
+ }
+
hash.Write(label)
lHash := hash.Sum(nil)
hash.Reset()
@@ -592,6 +631,17 @@
return nil, ErrDecryption
}
+ if boring.Enabled {
+ bkey, err := boringPrivateKey(priv)
+ if err != nil {
+ return nil, err
+ }
+ out, err := boring.DecryptRSAOAEP(hash, bkey, ciphertext, label)
+ if err != nil {
+ return nil, ErrDecryption
+ }
+ return out, nil
+ }
c := new(big.Int).SetBytes(ciphertext)
m, err := decrypt(random, priv, c)
diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go
index 84b1674..4742678 100644
--- a/src/crypto/rsa/rsa_test.go
+++ b/src/crypto/rsa/rsa_test.go
@@ -10,23 +10,25 @@
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
+ "fmt"
"math/big"
"testing"
)
func TestKeyGeneration(t *testing.T) {
- size := 1024
- if testing.Short() {
- size = 128
+ for _, size := range []int{128, 1024, 2048, 3072} {
+ priv, err := GenerateKey(rand.Reader, size)
+ if err != nil {
+ t.Errorf("GenerateKey(%d): %v", size, err)
+ }
+ if bits := priv.N.BitLen(); bits != size {
+ t.Errorf("key too short (%d vs %d)", bits, size)
+ }
+ testKeyBasics(t, priv)
+ if testing.Short() {
+ break
+ }
}
- priv, err := GenerateKey(rand.Reader, size)
- if err != nil {
- t.Errorf("failed to generate key")
- }
- if bits := priv.N.BitLen(); bits != size {
- t.Errorf("key too short (%d vs %d)", bits, size)
- }
- testKeyBasics(t, priv)
}
func Test3PrimeKeyGeneration(t *testing.T) {
@@ -222,7 +224,7 @@
n := new(big.Int)
for i, test := range testEncryptOAEPData {
n.SetString(test.modulus, 16)
- public := PublicKey{n, test.e}
+ public := PublicKey{N: n, E: test.e}
for j, message := range test.msgs {
randomSource := bytes.NewReader(message.seed)
@@ -247,7 +249,7 @@
n.SetString(test.modulus, 16)
d.SetString(test.d, 16)
private := new(PrivateKey)
- private.PublicKey = PublicKey{n, test.e}
+ private.PublicKey = PublicKey{N: n, E: test.e}
private.D = d
for j, message := range test.msgs {
@@ -272,6 +274,36 @@
}
}
+func TestEncryptDecryptOAEP(t *testing.T) {
+ sha256 := sha256.New()
+ n := new(big.Int)
+ d := new(big.Int)
+ for i, test := range testEncryptOAEPData {
+ n.SetString(test.modulus, 16)
+ d.SetString(test.d, 16)
+ priv := new(PrivateKey)
+ priv.PublicKey = PublicKey{N: n, E: test.e}
+ priv.D = d
+
+ for j, message := range test.msgs {
+ label := []byte(fmt.Sprintf("hi#%d", j))
+ enc, err := EncryptOAEP(sha256, rand.Reader, &priv.PublicKey, message.in, label)
+ if err != nil {
+ t.Errorf("#%d,%d: EncryptOAEP: %v", i, j, err)
+ continue
+ }
+ dec, err := DecryptOAEP(sha256, rand.Reader, priv, enc, label)
+ if err != nil {
+ t.Errorf("#%d,%d: DecryptOAEP: %v", i, j, err)
+ continue
+ }
+ if !bytes.Equal(dec, message.in) {
+ t.Errorf("#%d,%d: round trip %q -> %q", i, j, message.in, dec)
+ }
+ }
+ }
+}
+
// testEncryptOAEPData contains a subset of the vectors from RSA's "Test vectors for RSA-OAEP".
var testEncryptOAEPData = []testEncryptOAEPStruct{
// Key 1
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
TryBots beginning. Status page: https://farmer.golang.org/try?commit=f9ae9f46
Build is still in progress...
This change failed on misc-vet-vetall:
See https://storage.googleapis.com/go-build-log/f9ae9f46/misc-vet-vetall_633a63b4.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
2 of 21 TryBots failed:
Failed on misc-vet-vetall: https://storage.googleapis.com/go-build-log/f9ae9f46/misc-vet-vetall_633a63b4.log
Failed on linux-amd64-race: https://storage.googleapis.com/go-build-log/f9ae9f46/linux-amd64-race_548b07e0.log
Consult https://build.golang.org/ to see whether they are new failures.
Patch set 1:TryBot-Result -1
Russ Cox uploaded patch set #2 to this change.
[dev.boringcrypto] crypto/rsa: use BoringCrypto
Change-Id: Ibb92f0f8cb487f4d179b069e588e1cb266599384
---
A src/crypto/internal/boring/asn1.go
A src/crypto/internal/boring/asn1_test.go
M src/crypto/internal/boring/hmac.go
M src/crypto/internal/boring/notboring.go
A src/crypto/internal/boring/rsa.go
A src/crypto/rsa/boring.go
A src/crypto/rsa/boring_export_test.go
A src/crypto/rsa/boring_test.go
M src/crypto/rsa/pkcs1v15.go
M src/crypto/rsa/pkcs1v15_test.go
M src/crypto/rsa/pss.go
M src/crypto/rsa/pss_test.go
M src/crypto/rsa/rsa.go
M src/crypto/rsa/rsa_test.go
14 files changed, 985 insertions(+), 25 deletions(-)
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
Build is still in progress...
This change failed on darwin-amd64-10_11:
See https://storage.googleapis.com/go-build-log/1d1934d5/darwin-amd64-10_11_7c22dcfe.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
Adam, PTAL.
The whole stack has been updated to address your comments here. I also added the Bleichenbacher oracle and crypto.MD5SHA1 support as you outlined.
I also added a new assertion boringcrypto.Unreachable that panics if called when BoringCrypto should be in use, and I added calls to it in a few key places throughout the stack. It found a few places I'd missed hooking in, like sha256.Sum (the helper). I also added boringcrypto.UnreachableExceptTests for code that should only be reached in tests (like the fallback code for using fixed not-really-random random sources).
There's a race failure in the sha CL that I need to track down, and then this CL introduces a vet failure that I need to fix as well. But otherwise I think many more should be +2able.
11 of 21 TryBots failed:
Failed on darwin-amd64-10_11: https://storage.googleapis.com/go-build-log/1d1934d5/darwin-amd64-10_11_7c22dcfe.log
Failed on freebsd-amd64-110: https://storage.googleapis.com/go-build-log/1d1934d5/freebsd-amd64-110_495a2c26.log
Failed on windows-386-2008: https://storage.googleapis.com/go-build-log/1d1934d5/windows-386-2008_fe91c533.log
Failed on openbsd-amd64-60: https://storage.googleapis.com/go-build-log/1d1934d5/openbsd-amd64-60_0772aea0.log
Failed on windows-amd64-2016: https://storage.googleapis.com/go-build-log/1d1934d5/windows-amd64-2016_24549a89.log
Failed on nacl-386: https://storage.googleapis.com/go-build-log/1d1934d5/nacl-386_c6f5d0eb.log
Failed on linux-amd64-race: https://storage.googleapis.com/go-build-log/1d1934d5/linux-amd64-race_7c3df541.log
Failed on nacl-amd64p32: https://storage.googleapis.com/go-build-log/1d1934d5/nacl-amd64p32_ccbe9c9b.log
Failed on linux-arm: https://storage.googleapis.com/go-build-log/1d1934d5/linux-arm_ad0379d9.log
Failed on misc-vet-vetall: https://storage.googleapis.com/go-build-log/1d1934d5/misc-vet-vetall_9d164921.log
Failed on linux-386: https://storage.googleapis.com/go-build-log/1d1934d5/linux-386_3a2f1ab5.log
Consult https://build.golang.org/ to see whether they are new failures.
Patch set 2:TryBot-Result -1
5 comments:
File src/crypto/internal/boring/asn1.go:
(nit: extra space)
File src/crypto/internal/boring/rsa.go:
Patch Set #2, Line 273: if subtle.ConstantTimeCompare(hashed, out[:outLen]) != 1 {
Doesn't hurt, but verify operations only operate on non-secret data thus constant-time is not required.
File src/crypto/rsa/boring.go:
Patch Set #2, Line 140: err := factorKey(&priv)
You can avoid this: BoringSSL can cope with RSA keys without the CRT parameters if you set the fields on an RSA* directly.
Patch Set #2, Line 158: // but maybe some day it will (if so, change boringKeySupported above).
Multiprime keys are against FIPS so we actually removed support from BoringSSL during the FIPS process.
Patch Set #2, Line 217: // The expected number of trials is 2, so the key is probably invalid.
It depends the difference between the Euler and Carmichael totient for the key. I agree that 100 tries is very likely enough, but I'm curious why TestAlertFlushing triggers this.
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
Russ Cox uploaded patch set #3 to this change.
[dev.boringcrypto] crypto/rsa: use BoringCrypto
Change-Id: Ibb92f0f8cb487f4d179b069e588e1cb266599384
---
A src/crypto/internal/boring/asn1.go
A src/crypto/internal/boring/asn1_test.go
M src/crypto/internal/boring/hmac.go
M src/crypto/internal/boring/notboring.go
A src/crypto/internal/boring/rsa.go
A src/crypto/rsa/boring.go
A src/crypto/rsa/boring_export_test.go
A src/crypto/rsa/boring_test.go
M src/crypto/rsa/pkcs1v15.go
M src/crypto/rsa/pkcs1v15_test.go
M src/crypto/rsa/pss.go
M src/crypto/rsa/pss_test.go
M src/crypto/rsa/rsa.go
M src/crypto/rsa/rsa_test.go
14 files changed, 1,011 insertions(+), 25 deletions(-)
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
Build is still in progress...
This change failed on darwin-amd64-10_11:
See https://storage.googleapis.com/go-build-log/07a67cdc/darwin-amd64-10_11_fefa66ac.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
11 of 21 TryBots failed:
Failed on darwin-amd64-10_11: https://storage.googleapis.com/go-build-log/07a67cdc/darwin-amd64-10_11_fefa66ac.log
Failed on freebsd-amd64-110: https://storage.googleapis.com/go-build-log/07a67cdc/freebsd-amd64-110_40c1a8cb.log
Failed on misc-vet-vetall: https://storage.googleapis.com/go-build-log/07a67cdc/misc-vet-vetall_e015ace4.log
Failed on openbsd-amd64-60: https://storage.googleapis.com/go-build-log/07a67cdc/openbsd-amd64-60_dcd1a99f.log
Failed on windows-386-2008: https://storage.googleapis.com/go-build-log/07a67cdc/windows-386-2008_801a5c55.log
Failed on windows-amd64-2016: https://storage.googleapis.com/go-build-log/07a67cdc/windows-amd64-2016_eaa7b62b.log
Failed on nacl-amd64p32: https://storage.googleapis.com/go-build-log/07a67cdc/nacl-amd64p32_02b6699c.log
Failed on nacl-386: https://storage.googleapis.com/go-build-log/07a67cdc/nacl-386_80306457.log
Failed on linux-amd64-race: https://storage.googleapis.com/go-build-log/07a67cdc/linux-amd64-race_92a6443e.log
Failed on linux-386: https://storage.googleapis.com/go-build-log/07a67cdc/linux-386_a62a9286.log
Failed on linux-arm: https://storage.googleapis.com/go-build-log/07a67cdc/linux-arm_e3e5e9bd.log
Consult https://build.golang.org/ to see whether they are new failures.
Patch set 3:TryBot-Result -1
Build is still in progress...
This change failed on misc-vet-vetall:
See https://storage.googleapis.com/go-build-log/14d592d3/misc-vet-vetall_8396ddc1.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
11 of 21 TryBots failed:
Failed on misc-vet-vetall: https://storage.googleapis.com/go-build-log/14d592d3/misc-vet-vetall_8396ddc1.log
Failed on darwin-amd64-10_11: https://storage.googleapis.com/go-build-log/14d592d3/darwin-amd64-10_11_2ea7ccfd.log
Failed on nacl-amd64p32: https://storage.googleapis.com/go-build-log/14d592d3/nacl-amd64p32_f99edf70.log
Failed on freebsd-amd64-110: https://storage.googleapis.com/go-build-log/14d592d3/freebsd-amd64-110_0a7675b7.log
Failed on nacl-386: https://storage.googleapis.com/go-build-log/14d592d3/nacl-386_b4af7009.log
Failed on windows-386-2008: https://storage.googleapis.com/go-build-log/14d592d3/windows-386-2008_b2c33aec.log
Failed on openbsd-amd64-60: https://storage.googleapis.com/go-build-log/14d592d3/openbsd-amd64-60_7528dd91.log
Failed on windows-amd64-2016: https://storage.googleapis.com/go-build-log/14d592d3/windows-amd64-2016_7725cc89.log
Failed on linux-386: https://storage.googleapis.com/go-build-log/14d592d3/linux-386_b6b8e6e9.log
Failed on linux-amd64-race: https://storage.googleapis.com/go-build-log/14d592d3/linux-amd64-race_6e4c83f4.log
Failed on linux-arm: https://storage.googleapis.com/go-build-log/14d592d3/linux-arm_c2ceee29.log
Consult https://build.golang.org/ to see whether they are new failures.
Patch set 4:TryBot-Result -1
Russ Cox uploaded patch set #5 to this change.
[dev.boringcrypto] crypto/rsa: use BoringCrypto
Change-Id: Ibb92f0f8cb487f4d179b069e588e1cb266599384
---
A src/crypto/internal/boring/asn1.go
A src/crypto/internal/boring/asn1_test.go
M src/crypto/internal/boring/hmac.go
M src/crypto/internal/boring/notboring.go
A src/crypto/internal/boring/rsa.go
A src/crypto/rsa/boring.go
A src/crypto/rsa/boring_export_test.go
A src/crypto/rsa/boring_test.go
M src/crypto/rsa/pkcs1v15.go
M src/crypto/rsa/pkcs1v15_test.go
M src/crypto/rsa/pss.go
M src/crypto/rsa/pss_test.go
M src/crypto/rsa/rsa.go
M src/crypto/rsa/rsa_test.go
14 files changed, 1,015 insertions(+), 25 deletions(-)
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
Build is still in progress...
This change failed on misc-vet-vetall:
See https://storage.googleapis.com/go-build-log/69b2e45b/misc-vet-vetall_18c9bd29.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
I'll look into the simpler conversion.
1 comment:
File src/crypto/rsa/boring.go:
Patch Set #2, Line 217: // Factor key.N using key.D and key.E.
It depends the difference between the Euler and Carmichael totient for the key. […]
TestAlertFlushing says:
// Cause a signature-time error
brokenKey := rsa.PrivateKey{PublicKey: testRSAPrivateKey.PublicKey}
brokenKey.D = big.NewInt(42)
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
2 of 21 TryBots failed:
Failed on misc-vet-vetall: https://storage.googleapis.com/go-build-log/69b2e45b/misc-vet-vetall_18c9bd29.log
Failed on linux-amd64-race: https://storage.googleapis.com/go-build-log/69b2e45b/linux-amd64-race_58e02c4b.log
Consult https://build.golang.org/ to see whether they are new failures.
Patch set 5:TryBot-Result -1
Build is still in progress...
This change failed on misc-vet-vetall:
See https://storage.googleapis.com/go-build-log/69b2e45b/misc-vet-vetall_e4cc10f7.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
2 of 21 TryBots failed:
Failed on misc-vet-vetall: https://storage.googleapis.com/go-build-log/69b2e45b/misc-vet-vetall_e4cc10f7.log
Failed on linux-amd64-race: https://storage.googleapis.com/go-build-log/69b2e45b/linux-amd64-race_c4eb3d0c.log
Consult https://build.golang.org/ to see whether they are new failures.
Russ Cox uploaded patch set #6 to this change.
[dev.boringcrypto] crypto/rsa: use BoringCrypto
Change-Id: Ibb92f0f8cb487f4d179b069e588e1cb266599384
---
M src/crypto/internal/boring/boring.go
M src/crypto/internal/boring/goboringcrypto.h
M src/crypto/internal/boring/hmac.go
M src/crypto/internal/boring/notboring.go
A src/crypto/internal/boring/rsa.go
A src/crypto/rsa/boring.go
M src/crypto/rsa/pkcs1v15.go
M src/crypto/rsa/pkcs1v15_test.go
M src/crypto/rsa/pss.go
M src/crypto/rsa/pss_test.go
M src/crypto/rsa/rsa.go
M src/crypto/rsa/rsa_test.go
12 files changed, 747 insertions(+), 31 deletions(-)
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
PTAL. I was able to delete all the ASN.1 and factoring code, and I made minor adjustments elsewhere so that func encrypt can be marked boring.Unreachable (not just boring.UnreachableExceptTests).
4 comments:
(nit: extra space)
File deleted. :-)
File src/crypto/internal/boring/rsa.go:
Patch Set #2, Line 273: if md == nil {
Doesn't hurt, but verify operations only operate on non-secret data thus constant-time is not requir […]
I think I was mimicking the real rsa.VerifyPKCS1v15 fwiw. Will leave it.
File src/crypto/rsa/boring.go:
You can avoid this: BoringSSL can cope with RSA keys without the CRT parameters if you set the field […]
Thank you. I feel a tiny bit bad about setting the fields directly, but I deleted hundreds of lines of code as a result, so I'll get over it. :-)
TestAlertFlushing says: […]
Code is gone anyway.
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
TryBots beginning. Status page: https://farmer.golang.org/try?commit=ae2ad20a
Build is still in progress...
This change failed on linux-amd64-race:
See https://storage.googleapis.com/go-build-log/ae2ad20a/linux-amd64-race_4971bbac.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
1 of 21 TryBots failed:
Failed on linux-amd64-race: https://storage.googleapis.com/go-build-log/ae2ad20a/linux-amd64-race_4971bbac.log
Consult https://build.golang.org/ to see whether they are new failures.
Patch set 6:TryBot-Result -1
Russ Cox uploaded patch set #7 to this change.
[dev.boringcrypto] crypto/rsa: use BoringCrypto
Change-Id: Ibb92f0f8cb487f4d179b069e588e1cb266599384
---
M src/crypto/internal/boring/boring.go
M src/crypto/internal/boring/goboringcrypto.h
M src/crypto/internal/boring/hmac.go
M src/crypto/internal/boring/notboring.go
A src/crypto/internal/boring/rsa.go
A src/crypto/rsa/boring.go
M src/crypto/rsa/pkcs1v15.go
M src/crypto/rsa/pkcs1v15_test.go
M src/crypto/rsa/pss.go
M src/crypto/rsa/pss_test.go
M src/crypto/rsa/rsa.go
M src/crypto/rsa/rsa_test.go
12 files changed, 755 insertions(+), 31 deletions(-)
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
TryBots beginning. Status page: https://farmer.golang.org/try?commit=fb542ec9
TryBots are happy.
Patch set 7:TryBot-Result +1
Patch set 7:Code-Review +2
Russ Cox merged this change.
[dev.boringcrypto] crypto/rsa: use BoringCrypto
Change-Id: Ibb92f0f8cb487f4d179b069e588e1cb266599384
Reviewed-on: https://go-review.googlesource.com/55479
Run-TryBot: Russ Cox <r...@golang.org>
TryBot-Result: Gobot Gobot <go...@golang.org>
Reviewed-by: Adam Langley <a...@golang.org>
---
M src/crypto/internal/boring/boring.go
M src/crypto/internal/boring/goboringcrypto.h
M src/crypto/internal/boring/hmac.go
M src/crypto/internal/boring/notboring.go
A src/crypto/internal/boring/rsa.go
A src/crypto/rsa/boring.go
M src/crypto/rsa/pkcs1v15.go
M src/crypto/rsa/pkcs1v15_test.go
M src/crypto/rsa/pss.go
M src/crypto/rsa/pss_test.go
M src/crypto/rsa/rsa.go
M src/crypto/rsa/rsa_test.go
12 files changed, 755 insertions(+), 31 deletions(-)
diff --git a/src/crypto/internal/boring/boring.go b/src/crypto/internal/boring/boring.go
index bf15091..b2d47c0 100644
--- a/src/crypto/internal/boring/boring.go
+++ b/src/crypto/internal/boring/boring.go
@@ -53,3 +53,20 @@
n := C._goboringcrypto_BN_bn2bin(bn, base(raw))
return new(big.Int).SetBytes(raw[:n])
}
+
+func bigToBn(bnp **C.GO_BIGNUM, b *big.Int) bool {
+ if *bnp != nil {
+ C._goboringcrypto_BN_free(*bnp)
+ *bnp = nil
+ }
+ if b == nil {
+ return true
+ }
+ raw := b.Bytes()
+ bn := C._goboringcrypto_BN_bin2bn(base(raw), C.size_t(len(raw)), nil)
+ if bn == nil {
+ return false
+ }
+ *bnp = bn
+ return true
+}
diff --git a/src/crypto/internal/boring/goboringcrypto.h b/src/crypto/internal/boring/goboringcrypto.h
index 1a25da9..2cc327f 100644
--- a/src/crypto/internal/boring/goboringcrypto.h
+++ b/src/crypto/internal/boring/goboringcrypto.h
@@ -177,7 +177,9 @@
int _goboringcrypto_ECDSA_verify(int, const uint8_t*, size_t, const uint8_t*, size_t, const GO_EC_KEY*);
// #include <openssl/rsa.h>
-/*unchecked (opaque)*/ typedef struct GO_RSA { char data[1]; } GO_RSA;
+
+// Note: order of struct fields here is unchecked.
+typedef struct GO_RSA { void *meth; GO_BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; char data[120]; } GO_RSA;
/*unchecked (opaque)*/ typedef struct GO_BN_GENCB { char data[1]; } GO_BN_GENCB;
GO_RSA* _goboringcrypto_RSA_new(void);
void _goboringcrypto_RSA_free(GO_RSA*);
diff --git a/src/crypto/internal/boring/hmac.go b/src/crypto/internal/boring/hmac.go
index a70bc5e..f4497e9 100644
--- a/src/crypto/internal/boring/hmac.go
+++ b/src/crypto/internal/boring/hmac.go
@@ -10,6 +10,7 @@
// #include "goboringcrypto.h"
import "C"
import (
+ "crypto"
"hash"
"runtime"
"unsafe"
@@ -33,6 +34,28 @@
return nil
}
+// cryptoHashToMD converts a crypto.Hash
+// to a BoringCrypto *C.GO_EVP_MD.
+func cryptoHashToMD(ch crypto.Hash) *C.GO_EVP_MD {
+ switch ch {
+ case crypto.MD5:
+ return C._goboringcrypto_EVP_md5()
+ case crypto.MD5SHA1:
+ return C._goboringcrypto_EVP_md5_sha1()
+ case crypto.SHA1:
+ return C._goboringcrypto_EVP_sha1()
+ case crypto.SHA224:
+ return C._goboringcrypto_EVP_sha224()
+ case crypto.SHA256:
+ return C._goboringcrypto_EVP_sha256()
+ case crypto.SHA384:
+ return C._goboringcrypto_EVP_sha384()
+ case crypto.SHA512:
+ return C._goboringcrypto_EVP_sha512()
+ }
+ return nil
+}
+
// NewHMAC returns a new HMAC using BoringCrypto.
// The function h must return a hash implemented by
// BoringCrypto (for example, h could be boring.NewSHA256).
diff --git a/src/crypto/internal/boring/notboring.go b/src/crypto/internal/boring/notboring.go
index 5ef2f84..c8bcf66 100644
--- a/src/crypto/internal/boring/notboring.go
+++ b/src/crypto/internal/boring/notboring.go
@@ -7,6 +7,7 @@
package boring
import (
+ "crypto"
"crypto/cipher"
"hash"
"math/big"
@@ -59,3 +60,44 @@
func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, r, s *big.Int) bool {
panic("boringcrypto: not available")
}
+
+type PublicKeyRSA struct{ _ int }
+type PrivateKeyRSA struct{ _ int }
+
+func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
+ panic("boringcrypto: not available")
+}
+func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv *big.Int) (*PrivateKeyRSA, error) {
+ panic("boringcrypto: not available")
+}
+func NewPublicKeyRSA(N, E *big.Int) (*PublicKeyRSA, error) { panic("boringcrypto: not available") }
+func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) {
+ panic("boringcrypto: not available")
+}
+func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error {
+ panic("boringcrypto: not available")
+}
+func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error {
+ panic("boringcrypto: not available")
+}
diff --git a/src/crypto/internal/boring/rsa.go b/src/crypto/internal/boring/rsa.go
new file mode 100644
index 0000000..2415609
--- /dev/null
+++ b/src/crypto/internal/boring/rsa.go
@@ -0,0 +1,305 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,amd64
+// +build !cmd_go_bootstrap
+
+package boring
+
+// #include "goboringcrypto.h"
+import "C"
+import (
+ "crypto"
+ "crypto/subtle"
+ "errors"
+ "hash"
+ "math/big"
+ "runtime"
+ "strconv"
+ "unsafe"
+)
+
+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
+ bad := func(e error) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
+ return nil, nil, nil, nil, nil, nil, nil, nil, e
+ }
+
+ key := C._goboringcrypto_RSA_new()
+ if key == nil {
+ return bad(fail("RSA_new"))
+ }
+ defer C._goboringcrypto_RSA_free(key)
+
+ if C._goboringcrypto_RSA_generate_key_fips(key, C.int(bits), nil) == 0 {
+ return bad(fail("RSA_generate_key_fips"))
+ }
+
+ var n, e, d, p, q, dp, dq, qinv *C.GO_BIGNUM
+ C._goboringcrypto_RSA_get0_key(key, &n, &e, &d)
+ C._goboringcrypto_RSA_get0_factors(key, &p, &q)
+ C._goboringcrypto_RSA_get0_crt_params(key, &dp, &dq, &qinv)
+ return bnToBig(n), bnToBig(e), bnToBig(d), bnToBig(p), bnToBig(q), bnToBig(dp), bnToBig(dq), bnToBig(qinv), nil
+}
+
+type PublicKeyRSA struct {
+ key *C.GO_RSA
+}
+
+func NewPublicKeyRSA(N, E *big.Int) (*PublicKeyRSA, error) {
+ key := C._goboringcrypto_RSA_new()
+ if key == nil {
+ return nil, fail("RSA_new")
+ }
+ if !bigToBn(&key.n, N) ||
+ !bigToBn(&key.e, E) {
+ return nil, fail("BN_bin2bn")
+ }
+ k := &PublicKeyRSA{key: key}
+ runtime.SetFinalizer(k, (*PublicKeyRSA).finalize)
+ return k, nil
+}
+
+func (k *PublicKeyRSA) finalize() {
+ C._goboringcrypto_RSA_free(k.key)
+}
+
+type PrivateKeyRSA struct {
+ key *C.GO_RSA
+}
+
+func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv *big.Int) (*PrivateKeyRSA, error) {
+ key := C._goboringcrypto_RSA_new()
+ if key == nil {
+ return nil, fail("RSA_new")
+ }
+ if !bigToBn(&key.n, N) ||
+ !bigToBn(&key.e, E) ||
+ !bigToBn(&key.d, D) ||
+ !bigToBn(&key.p, P) ||
+ !bigToBn(&key.q, Q) ||
+ !bigToBn(&key.dmp1, Dp) ||
+ !bigToBn(&key.dmq1, Dq) ||
+ !bigToBn(&key.iqmp, Qinv) {
+ return nil, fail("BN_bin2bn")
+ }
+ k := &PrivateKeyRSA{key: key}
+ runtime.SetFinalizer(k, (*PrivateKeyRSA).finalize)
+ return k, nil
+}
+
+func (k *PrivateKeyRSA) finalize() {
+ C._goboringcrypto_RSA_free(k.key)
+}
+
+func setupRSA(key *C.GO_RSA,
+ padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash,
+ init func(*C.GO_EVP_PKEY_CTX) C.int) (pkey *C.GO_EVP_PKEY, ctx *C.GO_EVP_PKEY_CTX, err error) {
+ defer func() {
+ if err != nil {
+ if pkey != nil {
+ C._goboringcrypto_EVP_PKEY_free(pkey)
+ pkey = nil
+ }
+ if ctx != nil {
+ C._goboringcrypto_EVP_PKEY_CTX_free(ctx)
+ ctx = nil
+ }
+ }
+ }()
+
+ pkey = C._goboringcrypto_EVP_PKEY_new()
+ if pkey == nil {
+ return nil, nil, fail("EVP_PKEY_new")
+ }
+ if C._goboringcrypto_EVP_PKEY_set1_RSA(pkey, key) == 0 {
+ return nil, nil, fail("EVP_PKEY_set1_RSA")
+ }
+ ctx = C._goboringcrypto_EVP_PKEY_CTX_new(pkey, nil)
+ if ctx == nil {
+ return nil, nil, fail("EVP_PKEY_CTX_new")
+ }
+ if init(ctx) == 0 {
+ return nil, nil, fail("EVP_PKEY_operation_init")
+ }
+ if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(ctx, padding) == 0 {
+ return nil, nil, fail("EVP_PKEY_CTX_set_rsa_padding")
+ }
+ if padding == C.GO_RSA_PKCS1_OAEP_PADDING {
+ md := hashToMD(h)
+ if md == nil {
+ return nil, nil, errors.New("crypto/rsa: unsupported hash function")
+ }
+ if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) == 0 {
+ return nil, nil, fail("EVP_PKEY_set_rsa_oaep_md")
+ }
+ // ctx takes ownership of label, so malloc a copy for BoringCrypto to free.
+ clabel := (*C.uint8_t)(C.malloc(C.size_t(len(label))))
+ if clabel == nil {
+ return nil, nil, fail("malloc")
+ }
+ copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label)
+ if C._goboringcrypto_EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, clabel, C.size_t(len(label))) == 0 {
+ return nil, nil, fail("EVP_PKEY_CTX_set0_rsa_oaep_label")
+ }
+ }
+ if padding == C.GO_RSA_PKCS1_PSS_PADDING {
+ if saltLen != 0 {
+ if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, C.int(saltLen)) == 0 {
+ return nil, nil, fail("EVP_PKEY_set_rsa_pss_saltlen")
+ }
+ }
+ md := cryptoHashToMD(ch)
+ if md == nil {
+ return nil, nil, errors.New("crypto/rsa: unsupported hash function")
+ }
+ if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md) == 0 {
+ return nil, nil, fail("EVP_PKEY_set_rsa_mgf1_md")
+ }
+ }
+
+ return pkey, ctx, nil
+}
+
+func cryptRSA(key *C.GO_RSA,
+ padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash,
+ init func(*C.GO_EVP_PKEY_CTX) C.int,
+ crypt func(*C.GO_EVP_PKEY_CTX, *C.uint8_t, *C.size_t, *C.uint8_t, C.size_t) C.int,
+ in []byte) ([]byte, error) {
+
+ pkey, ctx, err := setupRSA(key, padding, h, label, saltLen, ch, init)
+ if err != nil {
+ return nil, err
+ }
+ defer C._goboringcrypto_EVP_PKEY_free(pkey)
+ defer C._goboringcrypto_EVP_PKEY_CTX_free(ctx)
+
+ var outLen C.size_t
+ if crypt(ctx, nil, &outLen, base(in), C.size_t(len(in))) == 0 {
+ return nil, fail("EVP_PKEY_decrypt/encrypt")
+ }
+ out := make([]byte, outLen)
+ if crypt(ctx, base(out), &outLen, base(in), C.size_t(len(in))) == 0 {
+ return nil, fail("EVP_PKEY_decrypt/encrypt")
+ }
+ return out[:outLen], nil
+}
+
+func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
+ return cryptRSA(priv.key, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, decryptInit, decrypt, ciphertext)
+}
+
+func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
+ return cryptRSA(pub.key, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, encryptInit, encrypt, msg)
+}
+
+func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+ return cryptRSA(priv.key, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, decryptInit, decrypt, ciphertext)
+}
+
+func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+ return cryptRSA(pub.key, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, encryptInit, encrypt, msg)
+}
+
+func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+ return cryptRSA(priv.key, C.GO_RSA_NO_PADDING, nil, nil, 0, 0, decryptInit, decrypt, ciphertext)
+}
+
+func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+ return cryptRSA(pub.key, C.GO_RSA_NO_PADDING, nil, nil, 0, 0, encryptInit, encrypt, msg)
+}
+
+// These dumb wrappers work around the fact that cgo functions cannot be used as values directly.
+
+func decryptInit(ctx *C.GO_EVP_PKEY_CTX) C.int {
+ return C._goboringcrypto_EVP_PKEY_decrypt_init(ctx)
+}
+
+func decrypt(ctx *C.GO_EVP_PKEY_CTX, out *C.uint8_t, outLen *C.size_t, in *C.uint8_t, inLen C.size_t) C.int {
+ return C._goboringcrypto_EVP_PKEY_decrypt(ctx, out, outLen, in, inLen)
+}
+
+func encryptInit(ctx *C.GO_EVP_PKEY_CTX) C.int {
+ return C._goboringcrypto_EVP_PKEY_encrypt_init(ctx)
+}
+
+func encrypt(ctx *C.GO_EVP_PKEY_CTX, out *C.uint8_t, outLen *C.size_t, in *C.uint8_t, inLen C.size_t) C.int {
+ return C._goboringcrypto_EVP_PKEY_encrypt(ctx, out, outLen, in, inLen)
+}
+
+func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) {
+ md := cryptoHashToMD(h)
+ if md == nil {
+ return nil, errors.New("crypto/rsa: unsupported hash function")
+ }
+ if saltLen == 0 {
+ saltLen = -1
+ }
+ out := make([]byte, C._goboringcrypto_RSA_size(priv.key))
+ var outLen C.size_t
+ if C._goboringcrypto_RSA_sign_pss_mgf1(priv.key, &outLen, base(out), C.size_t(len(out)), base(hashed), C.size_t(len(hashed)), md, nil, C.int(saltLen)) == 0 {
+ return nil, fail("RSA_sign_pss_mgf1")
+ }
+
+ return out[:outLen], nil
+}
+
+func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error {
+ md := cryptoHashToMD(h)
+ if md == nil {
+ return errors.New("crypto/rsa: unsupported hash function")
+ }
+ if saltLen == 0 {
+ saltLen = -2 // auto-recover
+ }
+ if C._goboringcrypto_RSA_verify_pss_mgf1(pub.key, base(hashed), C.size_t(len(hashed)), md, nil, C.int(saltLen), base(sig), C.size_t(len(sig))) == 0 {
+ return fail("RSA_verify_pss_mgf1")
+ }
+ return nil
+}
+
+func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) {
+ out := make([]byte, C._goboringcrypto_RSA_size(priv.key))
+ if h == 0 {
+ // No hashing.
+ var outLen C.size_t
+ if C._goboringcrypto_RSA_sign_raw(priv.key, &outLen, base(out), C.size_t(len(out)), base(hashed), C.size_t(len(hashed)), C.GO_RSA_PKCS1_PADDING) == 0 {
+ return nil, fail("RSA_sign_raw")
+ }
+ return out[:outLen], nil
+ }
+
+ md := cryptoHashToMD(h)
+ if md == nil {
+ return nil, errors.New("crypto/rsa: unsupported hash function: " + strconv.Itoa(int(h)))
+ }
+ nid := C._goboringcrypto_EVP_MD_type(md)
+ var outLen C.uint
+ if C._goboringcrypto_RSA_sign(nid, base(hashed), C.uint(len(hashed)), base(out), &outLen, priv.key) == 0 {
+ return nil, fail("RSA_sign")
+ }
+ return out[:outLen], nil
+}
+
+func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error {
+ if h == 0 {
+ var outLen C.size_t
+ out := make([]byte, C._goboringcrypto_RSA_size(pub.key))
+ if C._goboringcrypto_RSA_verify_raw(pub.key, &outLen, base(out), C.size_t(len(out)), base(sig), C.size_t(len(sig)), C.GO_RSA_PKCS1_PADDING) == 0 {
+ return fail("RSA_verify")
+ }
+ if subtle.ConstantTimeCompare(hashed, out[:outLen]) != 1 {
+ return fail("RSA_verify")
+ }
+ return nil
+ }
+ md := cryptoHashToMD(h)
+ if md == nil {
+ return errors.New("crypto/rsa: unsupported hash function")
+ }
+ nid := C._goboringcrypto_EVP_MD_type(md)
+ if C._goboringcrypto_RSA_verify(nid, base(hashed), C.size_t(len(hashed)), base(sig), C.size_t(len(sig)), pub.key) == 0 {
+ return fail("RSA_verify")
+ }
+ return nil
+}
diff --git a/src/crypto/rsa/boring.go b/src/crypto/rsa/boring.go
new file mode 100644
index 0000000..0f362a2
--- /dev/null
+++ b/src/crypto/rsa/boring.go
@@ -0,0 +1,124 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package rsa
+
+import (
+ "crypto/internal/boring"
+ "math/big"
+ "sync/atomic"
+ "unsafe"
+)
+
+// Cached conversions from Go PublicKey/PrivateKey to BoringCrypto.
+//
+// A new 'boring atomic.Value' field in both PublicKey and PrivateKey
+// serves as a cache for the most recent conversion. The cache is an
+// atomic.Value because code might reasonably set up a key and then
+// (thinking it immutable) use it from multiple goroutines simultaneously.
+// The first operation initializes the cache; if there are multiple simultaneous
+// first operations, they will do redundant work but not step on each other.
+//
+// We could just assume that once used in a sign/verify/encrypt/decrypt operation,
+// a particular key is never again modified, but that has not been a
+// stated assumption before. Just in case there is any existing code that
+// does modify the key between operations, we save the original values
+// alongside the cached BoringCrypto key and check that the real key
+// still matches before using the cached key. The theory is that the real
+// operations are significantly more expensive than the comparison.
+
+type boringPub struct {
+ key *boring.PublicKeyRSA
+ orig PublicKey
+}
+
+func boringPublicKey(pub *PublicKey) (*boring.PublicKeyRSA, error) {
+ b := (*boringPub)(atomic.LoadPointer(&pub.boring))
+ if b != nil && publicKeyEqual(&b.orig, pub) {
+ return b.key, nil
+ }
+
+ b = new(boringPub)
+ b.orig = copyPublicKey(pub)
+ key, err := boring.NewPublicKeyRSA(b.orig.N, big.NewInt(int64(b.orig.E)))
+ if err != nil {
+ return nil, err
+ }
+ b.key = key
+ atomic.StorePointer(&pub.boring, unsafe.Pointer(b))
+ return key, nil
+}
+
+type boringPriv struct {
+ key *boring.PrivateKeyRSA
+ orig PrivateKey
+}
+
+func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyRSA, error) {
+ b := (*boringPriv)(atomic.LoadPointer(&priv.boring))
+ if b != nil && privateKeyEqual(&b.orig, priv) {
+ return b.key, nil
+ }
+
+ b = new(boringPriv)
+ b.orig = copyPrivateKey(priv)
+
+ var N, E, D, P, Q, Dp, Dq, Qinv *big.Int
+ N = b.orig.N
+ E = big.NewInt(int64(b.orig.E))
+ D = b.orig.D
+ if len(b.orig.Primes) == 2 {
+ P = b.orig.Primes[0]
+ Q = b.orig.Primes[1]
+ Dp = b.orig.Precomputed.Dp
+ Dq = b.orig.Precomputed.Dq
+ Qinv = b.orig.Precomputed.Qinv
+ }
+ key, err := boring.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv)
+ if err != nil {
+ return nil, err
+ }
+ b.key = key
+ atomic.StorePointer(&priv.boring, unsafe.Pointer(b))
+ return key, nil
+}
+
+func publicKeyEqual(k1, k2 *PublicKey) bool {
+ return k1.N != nil &&
+ k1.N.Cmp(k2.N) == 0 &&
+ k1.E == k2.E
+}
+
+func copyPublicKey(k *PublicKey) PublicKey {
+ return PublicKey{
+ N: new(big.Int).Set(k.N),
+ E: k.E,
+ }
+}
+
+func privateKeyEqual(k1, k2 *PrivateKey) bool {
+ return publicKeyEqual(&k1.PublicKey, &k2.PublicKey) &&
+ k1.D.Cmp(k2.D) == 0
+}
+
+func copyPrivateKey(k *PrivateKey) PrivateKey {
+ dst := PrivateKey{
+ PublicKey: copyPublicKey(&k.PublicKey),
+ D: new(big.Int).Set(k.D),
+ }
+ dst.Primes = make([]*big.Int, len(k.Primes))
+ for i, p := range k.Primes {
+ dst.Primes[i] = new(big.Int).Set(p)
+ }
+ if x := k.Precomputed.Dp; x != nil {
+ dst.Precomputed.Dp = new(big.Int).Set(x)
+ }
+ if x := k.Precomputed.Dq; x != nil {
+ dst.Precomputed.Dq = new(big.Int).Set(x)
+ }
+ if x := k.Precomputed.Qinv; x != nil {
+ dst.Precomputed.Qinv = new(big.Int).Set(x)
+ }
+ return dst
+}
diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go
index 3517a8c..22475d7 100644
--- a/src/crypto/rsa/pkcs1v15.go
+++ b/src/crypto/rsa/pkcs1v15.go
@@ -6,6 +6,7 @@
import (
"crypto"
+ "crypto/internal/boring"
"crypto/subtle"
"errors"
"io"
@@ -34,7 +35,7 @@
//
// WARNING: use of this function to encrypt plaintexts other than
// session keys is dangerous. Use RSA OAEP in new protocols.
-func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) {
+func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, error) {
if err := checkPub(pub); err != nil {
return nil, err
}
@@ -43,20 +44,37 @@
return nil, ErrMessageTooLong
}
+ if boring.Enabled && random == boring.RandReader {
+ bkey, err := boringPublicKey(pub)
+ if err != nil {
+ return nil, err
+ }
+ return boring.EncryptRSAPKCS1(bkey, msg)
+ }
+ boring.UnreachableExceptTests()
+
// EM = 0x00 || 0x02 || PS || 0x00 || M
em := make([]byte, k)
em[1] = 2
ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):]
- err := nonZeroRandomBytes(ps, rand)
+ err := nonZeroRandomBytes(ps, random)
if err != nil {
return nil, err
}
em[len(em)-len(msg)-1] = 0
copy(mm, msg)
+ if boring.Enabled {
+ var bkey *boring.PublicKeyRSA
+ bkey, err = boringPublicKey(pub)
+ if err != nil {
+ return nil, err
+ }
+ return boring.EncryptRSANoPadding(bkey, em)
+ }
+
m := new(big.Int).SetBytes(em)
c := encrypt(new(big.Int), pub, m)
-
copyWithLeftPad(em, c.Bytes())
return em, nil
}
@@ -73,6 +91,19 @@
if err := checkPub(&priv.PublicKey); err != nil {
return nil, err
}
+
+ if boring.Enabled {
+ bkey, err := boringPrivateKey(priv)
+ if err != nil {
+ return nil, err
+ }
+ out, err := boring.DecryptRSAPKCS1(bkey, ciphertext)
+ if err != nil {
+ return nil, ErrDecryption
+ }
+ return out, nil
+ }
+
valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext)
if err != nil {
return nil, err
@@ -140,13 +171,26 @@
return
}
- c := new(big.Int).SetBytes(ciphertext)
- m, err := decrypt(rand, priv, c)
- if err != nil {
- return
+ if boring.Enabled {
+ var bkey *boring.PrivateKeyRSA
+ bkey, err = boringPrivateKey(priv)
+ if err != nil {
+ return
+ }
+ em, err = boring.DecryptRSANoPadding(bkey, ciphertext)
+ if err != nil {
+ return
+ }
+ } else {
+ c := new(big.Int).SetBytes(ciphertext)
+ var m *big.Int
+ m, err = decrypt(rand, priv, c)
+ if err != nil {
+ return
+ }
+ em = leftPad(m.Bytes(), k)
}
- em = leftPad(m.Bytes(), k)
firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0)
secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2)
@@ -225,7 +269,7 @@
// messages is small, an attacker may be able to build a map from
// messages to signatures and identify the signed messages. As ever,
// signatures provide authenticity, not confidentiality.
-func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
+func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
if err != nil {
return nil, err
@@ -237,6 +281,15 @@
return nil, ErrMessageTooLong
}
+ if boring.Enabled {
+ bkey, err := boringPrivateKey(priv)
+ if err != nil {
+ println("X0")
+ return nil, err
+ }
+ return boring.SignRSAPKCS1v15(bkey, hash, hashed)
+ }
+
// EM = 0x00 || 0x01 || PS || 0x00 || T
em := make([]byte, k)
em[1] = 1
@@ -247,7 +300,7 @@
copy(em[k-hashLen:k], hashed)
m := new(big.Int).SetBytes(em)
- c, err := decryptAndCheck(rand, priv, m)
+ c, err := decryptAndCheck(random, priv, m)
if err != nil {
return nil, err
}
@@ -262,6 +315,17 @@
// returning a nil error. If hash is zero then hashed is used directly. This
// isn't advisable except for interoperability.
func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error {
+ if boring.Enabled {
+ bkey, err := boringPublicKey(pub)
+ if err != nil {
+ return err
+ }
+ if err := boring.VerifyRSAPKCS1v15(bkey, hash, hashed, sig); err != nil {
+ return ErrVerification
+ }
+ return nil
+ }
+
hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
if err != nil {
return err
diff --git a/src/crypto/rsa/pkcs1v15_test.go b/src/crypto/rsa/pkcs1v15_test.go
index 47444f3..7f380b6 100644
--- a/src/crypto/rsa/pkcs1v15_test.go
+++ b/src/crypto/rsa/pkcs1v15_test.go
@@ -64,7 +64,7 @@
for i, test := range decryptPKCS1v15Tests {
out, err := decryptFunc(decodeBase64(test.in))
if err != nil {
- t.Errorf("#%d error decrypting", i)
+ t.Errorf("#%d error decrypting: %v", i, err)
}
want := []byte(test.out)
if !bytes.Equal(out, want) {
diff --git a/src/crypto/rsa/pss.go b/src/crypto/rsa/pss.go
index 1ba194a..393ef16 100644
--- a/src/crypto/rsa/pss.go
+++ b/src/crypto/rsa/pss.go
@@ -11,6 +11,7 @@
import (
"bytes"
"crypto"
+ "crypto/internal/boring"
"errors"
"hash"
"io"
@@ -259,6 +260,14 @@
hash = opts.Hash
}
+ if boring.Enabled {
+ bkey, err := boringPrivateKey(priv)
+ if err != nil {
+ return nil, err
+ }
+ return boring.SignRSAPSS(bkey, hash, hashed, saltLength)
+ }
+
salt := make([]byte, saltLength)
if _, err := io.ReadFull(rand, salt); err != nil {
return nil, err
@@ -277,6 +286,16 @@
// verifyPSS verifies a PSS signature with the given salt length.
func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error {
+ if boring.Enabled {
+ bkey, err := boringPublicKey(pub)
+ if err != nil {
+ return err
+ }
+ if err := boring.VerifyRSAPSS(bkey, hash, hashed, sig, saltLen); err != nil {
+ return ErrVerification
+ }
+ return nil
+ }
nBits := pub.N.BitLen()
if len(sig) != (nBits+7)/8 {
return ErrVerification
diff --git a/src/crypto/rsa/pss_test.go b/src/crypto/rsa/pss_test.go
index cae24e5..4ce5ad8 100644
--- a/src/crypto/rsa/pss_test.go
+++ b/src/crypto/rsa/pss_test.go
@@ -9,7 +9,6 @@
"bytes"
"compress/bzip2"
"crypto"
- _ "crypto/md5"
"crypto/rand"
"crypto/sha1"
_ "crypto/sha256"
@@ -211,7 +210,7 @@
{8, 8, true},
}
- hash := crypto.MD5
+ hash := crypto.SHA1
h := hash.New()
h.Write([]byte("testing"))
hashed := h.Sum(nil)
diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go
index 1de4fcb..8a074e6 100644
--- a/src/crypto/rsa/rsa.go
+++ b/src/crypto/rsa/rsa.go
@@ -24,6 +24,7 @@
import (
"crypto"
+ "crypto/internal/boring"
"crypto/rand"
"crypto/subtle"
"errors"
@@ -31,6 +32,7 @@
"io"
"math"
"math/big"
+ "unsafe"
)
var bigZero = big.NewInt(0)
@@ -40,6 +42,8 @@
type PublicKey struct {
N *big.Int // modulus
E int // public exponent
+
+ boring unsafe.Pointer
}
// OAEPOptions is an interface for passing options to OAEP decryption using the
@@ -85,6 +89,8 @@
// Precomputed contains precomputed values that speed up private
// operations, if available.
Precomputed PrecomputedValues
+
+ boring unsafe.Pointer
}
// Public returns the public key corresponding to priv.
@@ -195,6 +201,31 @@
// GenerateKey generates an RSA keypair of the given bit size using the
// random source random (for example, crypto/rand.Reader).
func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) {
+ if boring.Enabled && (bits == 2048 || bits == 3072) {
+ N, E, D, P, Q, Dp, Dq, Qinv, err := boring.GenerateKeyRSA(bits)
+ if err != nil {
+ return nil, err
+ }
+ e64 := E.Int64()
+ if !E.IsInt64() || int64(int(e64)) != e64 {
+ return nil, errors.New("crypto/rsa: generated key exponent too large")
+ }
+ key := &PrivateKey{
+ PublicKey: PublicKey{
+ N: N,
+ E: int(e64),
+ },
+ D: D,
+ Primes: []*big.Int{P, Q},
+ Precomputed: PrecomputedValues{
+ Dp: Dp,
+ Dq: Dq,
+ Qinv: Qinv,
+ },
+ }
+ return key, nil
+ }
+
return GenerateMultiPrimeKey(random, 2, bits)
}
@@ -344,6 +375,7 @@
var ErrMessageTooLong = errors.New("crypto/rsa: message too long for RSA public key size")
func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int {
+ boring.Unreachable()
e := big.NewInt(int64(pub.E))
c.Exp(m, e, pub.N)
return c
@@ -376,6 +408,15 @@
return nil, ErrMessageTooLong
}
+ if boring.Enabled && random == boring.RandReader {
+ bkey, err := boringPublicKey(pub)
+ if err != nil {
+ return nil, err
+ }
+ return boring.EncryptRSAOAEP(hash, bkey, msg, label)
+ }
+ boring.UnreachableExceptTests()
+
hash.Write(label)
lHash := hash.Sum(nil)
hash.Reset()
@@ -396,10 +437,24 @@
mgf1XOR(db, hash, seed)
mgf1XOR(seed, hash, db)
- m := new(big.Int)
- m.SetBytes(em)
- c := encrypt(new(big.Int), pub, m)
- out := c.Bytes()
+ var out []byte
+ if boring.Enabled {
+ var bkey *boring.PublicKeyRSA
+ bkey, err = boringPublicKey(pub)
+ if err != nil {
+ return nil, err
+ }
+ c, err := boring.EncryptRSANoPadding(bkey, em)
+ if err != nil {
+ return nil, err
+ }
+ out = c
+ } else {
+ m := new(big.Int)
+ m.SetBytes(em)
+ c := encrypt(new(big.Int), pub, m)
+ out = c.Bytes()
+ }
if len(out) < k {
// If the output is too small, we need to left-pad with zeros.
@@ -477,6 +532,9 @@
// decrypt performs an RSA decryption, resulting in a plaintext integer. If a
// random source is given, RSA blinding is used.
func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) {
+ if len(priv.Primes) <= 2 {
+ boring.Unreachable()
+ }
// TODO(agl): can we get away with reusing blinds?
if c.Cmp(priv.N) > 0 {
err = ErrDecryption
@@ -592,6 +650,17 @@
return nil, ErrDecryption
}
+ if boring.Enabled {
+ bkey, err := boringPrivateKey(priv)
+ if err != nil {
+ return nil, err
+ }
+ out, err := boring.DecryptRSAOAEP(hash, bkey, ciphertext, label)
+ if err != nil {
+ return nil, ErrDecryption
+ }
+ return out, nil
+ }
c := new(big.Int).SetBytes(ciphertext)
m, err := decrypt(random, priv, c)
diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go
index 84b1674..3abe88a 100644
--- a/src/crypto/rsa/rsa_test.go
+++ b/src/crypto/rsa/rsa_test.go
@@ -7,26 +7,29 @@
import (
"bytes"
"crypto"
+ "crypto/internal/boring"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
+ "fmt"
"math/big"
"testing"
)
func TestKeyGeneration(t *testing.T) {
- size := 1024
- if testing.Short() {
- size = 128
+ for _, size := range []int{128, 1024, 2048, 3072} {
+ priv, err := GenerateKey(rand.Reader, size)
+ if err != nil {
+ t.Errorf("GenerateKey(%d): %v", size, err)
+ }
+ if bits := priv.N.BitLen(); bits != size {
+ t.Errorf("key too short (%d vs %d)", bits, size)
+ }
+ testKeyBasics(t, priv)
+ if testing.Short() {
+ break
+ }
}
- priv, err := GenerateKey(rand.Reader, size)
- if err != nil {
- t.Errorf("failed to generate key")
- }
- if bits := priv.N.BitLen(); bits != size {
- t.Errorf("key too short (%d vs %d)", bits, size)
- }
- testKeyBasics(t, priv)
}
func Test3PrimeKeyGeneration(t *testing.T) {
@@ -110,6 +113,25 @@
t.Errorf("private exponent too large")
}
+ if boring.Enabled {
+ // Cannot call encrypt/decrypt directly. Test via PKCS1v15.
+ msg := []byte("hi!")
+ enc, err := EncryptPKCS1v15(rand.Reader, &priv.PublicKey, msg)
+ if err != nil {
+ t.Errorf("EncryptPKCS1v15: %v", err)
+ return
+ }
+ dec, err := DecryptPKCS1v15(rand.Reader, priv, enc)
+ if err != nil {
+ t.Errorf("DecryptPKCS1v15: %v", err)
+ return
+ }
+ if !bytes.Equal(dec, msg) {
+ t.Errorf("got:%x want:%x (%+v)", dec, msg, priv)
+ }
+ return
+ }
+
pub := &priv.PublicKey
m := big.NewInt(42)
c := encrypt(new(big.Int), pub, m)
@@ -158,6 +180,10 @@
}
func BenchmarkRSA2048Decrypt(b *testing.B) {
+ if boring.Enabled {
+ b.Skip("no raw decrypt in BoringCrypto")
+ }
+
b.StopTimer()
c := fromBase10("8472002792838218989464636159316973636630013835787202418124758118372358261975764365740026024610403138425986214991379012696600761514742817632790916315594342398720903716529235119816755589383377471752116975374952783629225022962092351886861518911824745188989071172097120352727368980275252089141512321893536744324822590480751098257559766328893767334861211872318961900897793874075248286439689249972315699410830094164386544311554704755110361048571142336148077772023880664786019636334369759624917224888206329520528064315309519262325023881707530002540634660750469137117568199824615333883758410040459705787022909848740188613313")
@@ -180,6 +206,10 @@
}
func Benchmark3PrimeRSA2048Decrypt(b *testing.B) {
+ if boring.Enabled {
+ b.Skip("no raw decrypt in BoringCrypto")
+ }
+
b.StopTimer()
priv := &PrivateKey{
PublicKey: PublicKey{
@@ -222,7 +252,7 @@
n := new(big.Int)
for i, test := range testEncryptOAEPData {
n.SetString(test.modulus, 16)
- public := PublicKey{n, test.e}
+ public := PublicKey{N: n, E: test.e}
for j, message := range test.msgs {
randomSource := bytes.NewReader(message.seed)
@@ -247,7 +277,7 @@
n.SetString(test.modulus, 16)
d.SetString(test.d, 16)
private := new(PrivateKey)
- private.PublicKey = PublicKey{n, test.e}
+ private.PublicKey = PublicKey{N: n, E: test.e}
private.D = d
for j, message := range test.msgs {
@@ -272,6 +302,36 @@
}
}
+func TestEncryptDecryptOAEP(t *testing.T) {
+ sha256 := sha256.New()
+ n := new(big.Int)
+ d := new(big.Int)
+ for i, test := range testEncryptOAEPData {
+ n.SetString(test.modulus, 16)
+ d.SetString(test.d, 16)
+ priv := new(PrivateKey)
+ priv.PublicKey = PublicKey{N: n, E: test.e}
+ priv.D = d
+
+ for j, message := range test.msgs {
+ label := []byte(fmt.Sprintf("hi#%d", j))
+ enc, err := EncryptOAEP(sha256, rand.Reader, &priv.PublicKey, message.in, label)
+ if err != nil {
+ t.Errorf("#%d,%d: EncryptOAEP: %v", i, j, err)
+ continue
+ }
+ dec, err := DecryptOAEP(sha256, rand.Reader, priv, enc, label)
+ if err != nil {
+ t.Errorf("#%d,%d: DecryptOAEP: %v", i, j, err)
+ continue
+ }
+ if !bytes.Equal(dec, message.in) {
+ t.Errorf("#%d,%d: round trip %q -> %q", i, j, message.in, dec)
+ }
+ }
+ }
+}
+
// testEncryptOAEPData contains a subset of the vectors from RSA's "Test vectors for RSA-OAEP".
var testEncryptOAEPData = []testEncryptOAEPStruct{
// Key 1
To view, visit change 55479. To unsubscribe, or for help writing mail filters, visit settings.
Russ Cox would like Adam Langley to review this change.
[dev.boringcrypto.go1.8] crypto/rsa: use BoringCrypto
index 1de4fcb..bac745e 100644+ if E.Cmp(big.NewInt(e64)) != 0 || int64(int(e64)) != e64 {To view, visit change 57942. To unsubscribe, or for help writing mail filters, visit settings.
1 comment:
Patch Set #1, Line 210: if E.Cmp(big.NewInt(e64)) != 0 || int64(int(e64)) != e64 {
This line changed. The first half of the condition used to be E.IsInt64() but that is not available in Go 1.8.
To view, visit change 57942. To unsubscribe, or for help writing mail filters, visit settings.
This CL stack is Go+BoringCrypto on top of Go 1.8, so that I can make a drop-in replacement for Go 1.8.3. I did a plain git cherry-pick of everything from the Go 1.9-based dev.boringcrypto, and it all applied cleanly. It built except for one line noted in my previous comment (and a minor non-crypto adjustment in the cmd/link CL) and then all.bash passed with no other changes. So there shouldn't be much to review here.
TryBots beginning. Status page: https://farmer.golang.org/try?commit=4252404a
Build is still in progress...
This change failed on windows-amd64-2016:
See https://storage.googleapis.com/go-build-log/4252404a/windows-amd64-2016_1c040479.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
To view, visit change 57942. To unsubscribe, or for help writing mail filters, visit settings.
2 of 21 TryBots failed:
Failed on windows-amd64-2016: https://storage.googleapis.com/go-build-log/4252404a/windows-amd64-2016_1c040479.log
Failed on windows-386-2008: https://storage.googleapis.com/go-build-log/4252404a/windows-386-2008_4f4e5d83.log
Consult https://build.golang.org/ to see whether they are new failures.
Patch set 1:TryBot-Result -1
Build is still in progress...
This change failed on windows-386-2008:
See https://storage.googleapis.com/go-build-log/3a140940/windows-386-2008_5ec7786b.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
To view, visit change 57942. To unsubscribe, or for help writing mail filters, visit settings.
3 of 21 TryBots failed:
Failed on windows-386-2008: https://storage.googleapis.com/go-build-log/3a140940/windows-386-2008_5ec7786b.log
Failed on darwin-amd64-10_11: https://storage.googleapis.com/go-build-log/3a140940/darwin-amd64-10_11_5263e2e3.log
Failed on windows-amd64-2016: https://storage.googleapis.com/go-build-log/3a140940/windows-amd64-2016_7bb698e0.log
Consult https://build.golang.org/ to see whether they are new failures.
Patch set 2:TryBot-Result -1
Patch set 2:Code-Review +2
1 comment:
Patch Set #1, Line 210: if E.Cmp(big.NewInt(e64)) != 0 || int64(int(e64)) != e64 {
This line changed. The first half of the condition used to be E. […]
Ack
To view, visit change 57942. To unsubscribe, or for help writing mail filters, visit settings.
Russ Cox merged this change.
[dev.boringcrypto.go1.8] crypto/rsa: use BoringCrypto
Change-Id: Ibb92f0f8cb487f4d179b069e588e1cb266599384
Reviewed-on: https://go-review.googlesource.com/55479
Run-TryBot: Russ Cox <r...@golang.org>
TryBot-Result: Gobot Gobot <go...@golang.org>
Reviewed-by: Adam Langley <a...@golang.org>
Reviewed-on: https://go-review.googlesource.com/57942