Skip to content

Commit

Permalink
Merge pull request #26 from microsoft/dev/qmuntal/bbig
Browse files Browse the repository at this point in the history
Remove math/big and encoding/asn1 deps from openssl package
  • Loading branch information
qmuntal authored May 23, 2022
2 parents de376e2 + b69c23e commit 8a421b5
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 89 deletions.
38 changes: 38 additions & 0 deletions openssl/bbig/big.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2022 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.

// This is a mirror of crypto/internal/boring/bbig/big.go.

package bbig

import (
"math/big"
"unsafe"

"github.com/microsoft/go-crypto-openssl/openssl"
)

func Enc(b *big.Int) openssl.BigInt {
if b == nil {
return nil
}
x := b.Bits()
if len(x) == 0 {
return openssl.BigInt{}
}
// TODO: Use unsafe.Slice((*uint)(&x[0]), len(x)) once go1.16 is no longer supported.
return (*(*[]uint)(unsafe.Pointer(&x)))[:len(x)]
}

func Dec(b openssl.BigInt) *big.Int {
if b == nil {
return nil
}
if len(b) == 0 {
return new(big.Int)
}
// TODO: Use unsafe.Slice((*uint)(&b[0]), len(b)) once go1.16 is no longer supported.
x := (*(*[]big.Word)(unsafe.Pointer(&b)))[:len(b)]
return new(big.Int).SetBits(x)
}
83 changes: 83 additions & 0 deletions openssl/bbig/bridge/bridge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// These wrappers only exist for code reuse in places where we need the old pre-go1.19 signature.

package bridge

import (
"encoding/asn1"
"math/big"

"github.com/microsoft/go-crypto-openssl/openssl"
"github.com/microsoft/go-crypto-openssl/openssl/bbig"
)

func GenerateKeyECDSA(curve string) (X, Y, D *big.Int, err error) {
x, y, d, err := openssl.GenerateKeyECDSA(curve)
if err != nil {
return nil, nil, nil, err
}
return bbig.Dec(x), bbig.Dec(y), bbig.Dec(d), nil
}

type ecdsaSignature struct {
R, S *big.Int
}

func SignECDSA(priv *openssl.PrivateKeyECDSA, hash []byte) (r, s *big.Int, err error) {
sig, err := openssl.SignMarshalECDSA(priv, hash)
if err != nil {
return nil, nil, err
}
var esig ecdsaSignature
if _, err := asn1.Unmarshal(sig, &esig); err != nil {
return nil, nil, err
}
return esig.R, esig.S, nil
}

func NewPrivateKeyECDSA(curve string, X, Y, D *big.Int) (*openssl.PrivateKeyECDSA, error) {
return openssl.NewPrivateKeyECDSA(curve, bbig.Enc(X), bbig.Enc(Y), bbig.Enc(D))
}

func NewPublicKeyECDSA(curve string, X, Y *big.Int) (*openssl.PublicKeyECDSA, error) {
return openssl.NewPublicKeyECDSA(curve, bbig.Enc(X), bbig.Enc(Y))
}

func VerifyECDSA(pub *openssl.PublicKeyECDSA, hash []byte, r, s *big.Int) bool {
sig, err := asn1.Marshal(ecdsaSignature{r, s})
if err != nil {
return false
}
return openssl.VerifyECDSA(pub, hash, sig)
}

func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
bN, bE, bD, bP, bQ, bDp, bDq, bQinv, err1 := openssl.GenerateKeyRSA(bits)
if err1 != nil {
err = err1
return
}
N = bbig.Dec(bN)
E = bbig.Dec(bE)
D = bbig.Dec(bD)
P = bbig.Dec(bP)
Q = bbig.Dec(bQ)
Dp = bbig.Dec(bDp)
Dq = bbig.Dec(bDq)
Qinv = bbig.Dec(bQinv)
return
}

func NewPublicKeyRSA(N, E *big.Int) (*openssl.PublicKeyRSA, error) {
return openssl.NewPublicKeyRSA(bbig.Enc(N), bbig.Enc(E))
}

func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv *big.Int) (*openssl.PrivateKeyRSA, error) {
return openssl.NewPrivateKeyRSA(
bbig.Enc(N), bbig.Enc(E), bbig.Enc(D),
bbig.Enc(P), bbig.Enc(Q),
bbig.Enc(Dp), bbig.Enc(Dq), bbig.Enc(Qinv),
)
}
39 changes: 5 additions & 34 deletions openssl/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,11 @@ package openssl
// #include "goopenssl.h"
import "C"
import (
"encoding/asn1"
"errors"
"math/big"
"runtime"
"unsafe"
)

type ecdsaSignature struct {
R, S *big.Int
}

type PrivateKeyECDSA struct {
// _pkey MUST NOT be accessed directly. Instead, use the withKey method.
_pkey C.GO_EVP_PKEY_PTR
Expand Down Expand Up @@ -65,7 +59,7 @@ func curveNID(curve string) (C.int, error) {
return 0, errUnknownCurve
}

func NewPublicKeyECDSA(curve string, X, Y *big.Int) (*PublicKeyECDSA, error) {
func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) {
pkey, err := newECKey(curve, X, Y, nil)
if err != nil {
return nil, err
Expand All @@ -79,7 +73,7 @@ func NewPublicKeyECDSA(curve string, X, Y *big.Int) (*PublicKeyECDSA, error) {
return k, nil
}

func newECKey(curve string, X, Y, D *big.Int) (pkey C.GO_EVP_PKEY_PTR, err error) {
func newECKey(curve string, X, Y, D BigInt) (pkey C.GO_EVP_PKEY_PTR, err error) {
var nid C.int
if nid, err = curveNID(curve); err != nil {
return nil, err
Expand Down Expand Up @@ -135,7 +129,7 @@ func newECKey(curve string, X, Y, D *big.Int) (pkey C.GO_EVP_PKEY_PTR, err error
return pkey, nil
}

func NewPrivateKeyECDSA(curve string, X, Y *big.Int, D *big.Int) (*PrivateKeyECDSA, error) {
func NewPrivateKeyECDSA(curve string, X, Y, D BigInt) (*PrivateKeyECDSA, error) {
pkey, err := newECKey(curve, X, Y, D)
if err != nil {
return nil, err
Expand All @@ -149,38 +143,15 @@ func NewPrivateKeyECDSA(curve string, X, Y *big.Int, D *big.Int) (*PrivateKeyECD
return k, nil
}

func SignECDSA(priv *PrivateKeyECDSA, hash []byte) (r, s *big.Int, err error) {
// We could use ECDSA_do_sign instead but would need to convert
// the resulting BIGNUMs to *big.Int form. If we're going to do a
// conversion, converting the ASN.1 form is more convenient and
// likely not much more expensive.
sig, err := SignMarshalECDSA(priv, hash)
if err != nil {
return nil, nil, err
}
var esig ecdsaSignature
if _, err := asn1.Unmarshal(sig, &esig); err != nil {
return nil, nil, err
}
return esig.R, esig.S, nil
}

func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) {
return evpSign(priv.withKey, 0, 0, 0, hash)
}

func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, r, s *big.Int) bool {
// We could use ECDSA_do_verify instead but would need to convert
// r and s to BIGNUM form. If we're going to do a conversion, marshaling
// to ASN.1 is more convenient and likely not much more expensive.
sig, err := asn1.Marshal(ecdsaSignature{r, s})
if err != nil {
return false
}
func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool {
return evpVerify(pub.withKey, 0, 0, 0, sig, hash) == nil
}

func GenerateKeyECDSA(curve string) (X, Y, D *big.Int, err error) {
func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) {
pkey, err := generateEVPPKey(C.GO_EVP_PKEY_EC, 0, curve)
if err != nil {
return nil, nil, nil, err
Expand Down
16 changes: 9 additions & 7 deletions openssl/ecdsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
//go:build linux && !android
// +build linux,!android

package openssl
package openssl_test

import (
"crypto/ecdsa"
"crypto/elliptic"
"testing"

"github.com/microsoft/go-crypto-openssl/openssl/bbig/bridge"
)

func testAllCurves(t *testing.T, f func(*testing.T, elliptic.Curve)) {
Expand Down Expand Up @@ -55,32 +57,32 @@ func testECDSASignAndVerify(t *testing.T, c elliptic.Curve) {
t.Fatal(err)
}

priv, err := NewPrivateKeyECDSA(key.Params().Name, key.X, key.Y, key.D)
priv, err := bridge.NewPrivateKeyECDSA(key.Params().Name, key.X, key.Y, key.D)
if err != nil {
t.Fatal(err)
}
hashed := []byte("testing")
r, s, err := SignECDSA(priv, hashed)
r, s, err := bridge.SignECDSA(priv, hashed)
if err != nil {
t.Errorf("error signing: %s", err)
return
}

pub, err := NewPublicKeyECDSA(key.Params().Name, key.X, key.Y)
pub, err := bridge.NewPublicKeyECDSA(key.Params().Name, key.X, key.Y)
if err != nil {
t.Fatal(err)
}
if !VerifyECDSA(pub, hashed, r, s) {
if !bridge.VerifyECDSA(pub, hashed, r, s) {
t.Errorf("Verify failed")
}
hashed[0] ^= 0xff
if VerifyECDSA(pub, hashed, r, s) {
if bridge.VerifyECDSA(pub, hashed, r, s) {
t.Errorf("Verify succeeded despite intentionally invalid hash!")
}
}

func generateKeycurve(c elliptic.Curve) (*ecdsa.PrivateKey, error) {
x, y, d, err := GenerateKeyECDSA(c.Params().Name)
x, y, d, err := bridge.GenerateKeyECDSA(c.Params().Name)
if err != nil {
return nil, err
}
Expand Down
23 changes: 13 additions & 10 deletions openssl/openssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ package openssl
import "C"
import (
"errors"
"math/big"
"math/bits"
"strconv"
"strings"
Expand All @@ -22,6 +21,11 @@ import (
"unsafe"
)

// A BigInt is the raw words from a BigInt.
// This definition allows us to avoid importing math/big.
// Conversion between BigInt and *big.Int is in openssl/bbig.
type BigInt []uint

var (
providerNameFips = C.CString("fips")
providerNameDefault = C.CString("default")
Expand Down Expand Up @@ -245,30 +249,29 @@ func (e fail) Error() string { return "openssl: " + string(e) + " failed" }

const wordBytes = bits.UintSize / 8

func wbase(b []big.Word) *C.uchar {
func wbase(b BigInt) *C.uchar {
if len(b) == 0 {
return nil
}
return (*C.uchar)(unsafe.Pointer(&b[0]))
}

func bigToBN(x *big.Int) C.GO_BIGNUM_PTR {
if x == nil {
func bigToBN(x BigInt) C.GO_BIGNUM_PTR {
if len(x) == 0 {
return nil
}
raw := x.Bits()
return C.go_openssl_BN_lebin2bn(wbase(raw), C.int(len(raw)*wordBytes), nil)
return C.go_openssl_BN_lebin2bn(wbase(x), C.int(len(x)*wordBytes), nil)
}

func bnToBig(bn C.GO_BIGNUM_PTR) *big.Int {
func bnToBig(bn C.GO_BIGNUM_PTR) BigInt {
if bn == nil {
return nil
}
raw := make([]big.Word, C.go_openssl_BN_num_bits(bn))
if C.go_openssl_BN_bn2lebinpad(bn, wbase(raw), C.int(len(raw)*wordBytes)) == 0 {
x := make(BigInt, C.go_openssl_BN_num_bits(bn))
if C.go_openssl_BN_bn2lebinpad(bn, wbase(x), C.int(len(x)*wordBytes)) == 0 {
panic("openssl: bignum conversion failed")
}
return new(big.Int).SetBits(raw)
return x
}

// noescape hides a pointer from escape analysis. noescape is
Expand Down
Loading

0 comments on commit 8a421b5

Please sign in to comment.