Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add validation module #1

Merged
merged 1 commit into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.com/meshplus/bitxhub-core

go 1.13

require (
github.com/gogo/protobuf v1.3.1
github.com/hyperledger/fabric v2.0.1+incompatible
github.com/hyperledger/fabric-protos-go v0.0.0-20200330074707-cfe579e86986
github.com/meshplus/bitxhub-kit v1.0.0-rc2
github.com/meshplus/bitxhub-model v1.0.0-rc3
github.com/sirupsen/logrus v1.5.0
github.com/wasmerio/go-ext-wasm v0.3.1
)
759 changes: 759 additions & 0 deletions go.sum

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions validator/fabric_v14_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package validator

import (
"github.com/meshplus/bitxhub-core/validator/validatorlib"
"github.com/sirupsen/logrus"
)

// Validator is the instance that can use wasm to verify transaction validity
type FabV14Validator struct {
logger logrus.FieldLogger
}

// New a validator instance
func NewFabV14Validator(logger logrus.FieldLogger) *FabV14Validator {
return &FabV14Validator{
logger: logger,
}
}

// Verify will check whether the transaction info is valid
func (vlt *FabV14Validator) Verify(address, from string, proof, payload []byte, validators string) (bool, error) {
vInfo, err := validatorlib.UnmarshalValidatorInfo([]byte(validators))
if err != nil {
return false, err
}
err = validatorlib.ValidateV14(proof, payload, []byte(vInfo.Policy), vInfo.ConfByte, vInfo.Cid)
if err != nil {
return false, err
}

return true, nil
}
38 changes: 38 additions & 0 deletions validator/validation_engine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package validator

import (
"github.com/sirupsen/logrus"
)

const (
FabricRuleAddr = "0x00000000000000000000000000000000000000a0"
)

// Validator is the instance that can use wasm to verify transaction validity
type ValidationEngine struct {
ledger Ledger
logger logrus.FieldLogger
}

// New a validator instance
func NewValidationEngine(ledger Ledger, logger logrus.FieldLogger) *ValidationEngine {
return &ValidationEngine{
ledger: ledger,
logger: logger,
}
}

// Verify will check whether the transaction info is valid
func (ve *ValidationEngine) Validate(address, from string, proof, payload []byte, validators string) (bool, error) {
vlt := ve.getValidator(address)

return vlt.Verify(address, from, proof, payload, validators)
}

func (ve *ValidationEngine) getValidator(address string) Validator {
if address == FabricRuleAddr {
return NewFabV14Validator(ve.logger)
}

return NewWasmValidator(ve.ledger, ve.logger)
}
18 changes: 18 additions & 0 deletions validator/validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package validator

import "github.com/meshplus/bitxhub-kit/types"

// Engine runs for validation
type Engine interface {
Validate(address, from string, proof, payload []byte, validators string) (bool, error)
}

// Validator chooses specific method to verify transaction
type Validator interface {
Verify(address, from string, proof, payload []byte, validators string) (bool, error)
}

type Ledger interface {
// GetCode
GetCode(types.Address) []byte
}
39 changes: 39 additions & 0 deletions validator/validatorlib/complex_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package validatorlib

// #include <stdlib.h>
//
// extern int32_t fabric_validate_v14(void *context, long long proof_ptr, long long validator_ptr, long long payload_ptr);
import "C"
import (
"unsafe"

"github.com/wasmerio/go-ext-wasm/wasmer"
)

//export fabric_validate_v14
func fabric_validate_v14(context unsafe.Pointer, proof_ptr int64, validator_ptr int64, payload_ptr int64) int32 {
ctx := wasmer.IntoInstanceContext(context)
data := ctx.Data().(map[int]int)
memory := ctx.Memory()
proof := memory.Data()[proof_ptr : proof_ptr+int64(data[int(proof_ptr)])]
validator := memory.Data()[validator_ptr : validator_ptr+int64(data[int(validator_ptr)])]
payload := memory.Data()[payload_ptr : payload_ptr+int64(data[int(payload_ptr)])]
vInfo, err := UnmarshalValidatorInfo(validator)
if err != nil {
return 0
}
err = ValidateV14(proof, payload, []byte(vInfo.Policy), vInfo.ConfByte, vInfo.Cid)
if err != nil {
return 0
}

return 1
}

func (im *Imports) importFabricV14() {
var err error
im.imports, err = im.imports.Append("fabric_validate_v14", fabric_validate_v14, C.fabric_validate_v14)
if err != nil {
return
}
}
168 changes: 168 additions & 0 deletions validator/validatorlib/ecdsa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package validatorlib

// #include <stdlib.h>
//
// extern int32_t ecdsa_verify(void *context, long long sig_ptr, long long digest_ptr, long long pubkey_ptr, int32_t opt);
import "C"
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"fmt"
"math/big"
"unsafe"

"github.com/meshplus/bitxhub-kit/crypto/asym/secp256k1"
"github.com/wasmerio/go-ext-wasm/wasmer"
)

type AlgorithmOption string

const (
// Secp256k1 secp256k1 algorithm
Secp256k1 AlgorithmOption = "Secp256k1"
// Secp256r1 secp256r1 algorithm
Secp256r1 AlgorithmOption = "Secp256r1"
)

type PrivateKey struct {
K *ecdsa.PrivateKey
}

// PublicKey ECDSA public key.
// never new(PublicKey), use NewPublicKey()
type PublicKey struct {
k *ecdsa.PublicKey
}

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

//export ecdsa_verify
func ecdsa_verify(context unsafe.Pointer, sig_ptr int64, digest_ptr int64, pubkey_ptr int64, opt int32) int32 {
ctx := wasmer.IntoInstanceContext(context)
data := ctx.Data().(map[int]int)
memory := ctx.Memory()
signature := memory.Data()[sig_ptr : sig_ptr+70]
digest := memory.Data()[digest_ptr : digest_ptr+32]
pubkey := memory.Data()[pubkey_ptr : pubkey_ptr+int64(data[int(pubkey_ptr)])]
pemCert, _ := pem.Decode(pubkey)
var cert *x509.Certificate
cert, err := x509.ParseCertificate(pemCert.Bytes)
if err != nil {
return 0
}
pk := cert.PublicKey
r, s, err := unmarshalECDSASignature(signature)
if err != nil {
return 0
}
isValid := ecdsa.Verify(pk.(*ecdsa.PublicKey), digest, r, s)

if isValid {
return 1
} else {
return 0
}
}

func unmarshalECDSASignature(raw []byte) (*big.Int, *big.Int, error) {
sig := new(ECDSASignature)
_, err := asn1.Unmarshal(raw, sig)
if err != nil {
return nil, nil, fmt.Errorf("failed unmashalling signature [%s]", err)
}

// Validate sig
if sig.R == nil {
return nil, nil, fmt.Errorf("invalid signature, r must be different from nil")
}
if sig.S == nil {
return nil, nil, fmt.Errorf("invalid signature, s must be different from nil")
}

if sig.R.Sign() != 1 {
return nil, nil, fmt.Errorf("invalid signature, r must be larger than zero")
}
if sig.S.Sign() != 1 {
return nil, nil, fmt.Errorf("invalid signature, s must be larger than zero")
}

return sig.R, sig.S, nil
}

func (im *Imports) importECDSA() {
var err error
im.imports, err = im.imports.Append("ecdsa_verify", ecdsa_verify, C.ecdsa_verify)
if err != nil {
return
}
}

// Bytes returns a serialized, storable representation of this key
func (priv *PrivateKey) Bytes() ([]byte, error) {
if priv.K == nil {
return nil, fmt.Errorf("ECDSAPrivateKey.K is nil, please invoke FromBytes()")
}
r := make([]byte, 32)
a := priv.K.D.Bytes()
copy(r[32-len(a):], a)
return r, nil
}

func (pub *PublicKey) Bytes() ([]byte, error) {
x := pub.k.X.Bytes()
y := pub.k.Y.Bytes()
return bytes.Join(
[][]byte{{0x04},
make([]byte, 32-len(x)), x, // padding to 32 bytes
make([]byte, 32-len(y)), y,
}, nil), nil
}

func UnmarshalPrivateKey(data []byte, opt AlgorithmOption) (crypto.PrivateKey, error) {
if len(data) == 0 {
return nil, fmt.Errorf("empty private key data")
}
key := &PrivateKey{K: new(ecdsa.PrivateKey)}
key.K.D = big.NewInt(0)
key.K.D.SetBytes(data)
switch opt {
case Secp256k1:
key.K.Curve = secp256k1.S256()
case Secp256r1:
key.K.Curve = elliptic.P256()
default:
return nil, fmt.Errorf("unsupported algorithm option")
}

key.K.PublicKey.X, key.K.PublicKey.Y = key.K.Curve.ScalarBaseMult(data)

return key, nil
}

func UnmarshalPublicKey(data []byte, opt AlgorithmOption) (crypto.PublicKey, error) {
if len(data) == 0 {
return nil, fmt.Errorf("empty public key data")
}
key := &PublicKey{k: new(ecdsa.PublicKey)}
key.k.X = big.NewInt(0)
key.k.Y = big.NewInt(0)
if len(data) != 65 {
return nil, fmt.Errorf("public key data length is not 65")
}
key.k.X.SetBytes(data[1:33])
key.k.Y.SetBytes(data[33:])
switch opt {
case Secp256k1:
key.k.Curve = secp256k1.S256()
case Secp256r1:
key.k.Curve = elliptic.P256()
}
return key, nil
}
Loading