mirror of https://github.com/golang/go.git
314 lines
9.5 KiB
Go
314 lines
9.5 KiB
Go
// 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 !android
|
|
// +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 {
|
|
size := int(C._goboringcrypto_RSA_size(pub.key))
|
|
if len(sig) < size {
|
|
// BoringCrypto requires sig to be same size as RSA key, so pad with leading zeros.
|
|
zsig := make([]byte, size)
|
|
copy(zsig[len(zsig)-len(sig):], sig)
|
|
sig = zsig
|
|
}
|
|
if h == 0 {
|
|
var outLen C.size_t
|
|
out := make([]byte, size)
|
|
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
|
|
}
|