-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from meshplus/feat/add-validator
feat: add validation module
- Loading branch information
Showing
13 changed files
with
1,493 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.