Skip to content

Commit

Permalink
Merge pull request #1 from meshplus/feat/add-validator
Browse files Browse the repository at this point in the history
feat: add validation module
  • Loading branch information
0xforever9 authored Apr 21, 2020
2 parents 0b7e14f + 45b77de commit 1957b1b
Show file tree
Hide file tree
Showing 13 changed files with 1,493 additions and 0 deletions.
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

0 comments on commit 1957b1b

Please sign in to comment.