From 30c5c661c096c09e4232fc06ab9442fa66ff47d3 Mon Sep 17 00:00:00 2001 From: lucas7788 Date: Thu, 9 Apr 2020 17:10:31 +0800 Subject: [PATCH 1/5] add claim support --- claim.go | 145 ++++++++++++++++++++++++++++++++++++++++ claim_test.go | 30 +++++++++ native_contract.go | 125 ++++++++++++++++++++++++++++++++++ native_contract_test.go | 38 +++++++++++ ont_sdk_test.go | 8 +-- 5 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 claim.go create mode 100644 claim_test.go diff --git a/claim.go b/claim.go new file mode 100644 index 0000000..f727254 --- /dev/null +++ b/claim.go @@ -0,0 +1,145 @@ +package ontology_go_sdk + +import ( + "encoding/base64" + "encoding/json" + "github.com/ontio/ontology/common" + "time" +) + +type Claim struct { + context string + id string + claim map[string]interface{} + claimStr string +} + +func NewClaim(controller *Controller, ctx string, clmMap map[string]interface{}, + metadata map[string]string, clmRevMap map[string]interface{}, publicKeyId string, expireTime int64) (*Claim, error) { + iss := metadata["Issuer"] + sub := metadata["Subject"] + header := NewHeader(publicKeyId) + payload, err := NewPayload("v1.0", iss, sub, time.Now().Unix(), expireTime, ctx, clmMap, clmRevMap) + if err != nil { + return nil, err + } + headerBs, err := header.getJson() + if err != nil { + return nil, err + } + payloadBs, err := payload.getJson() + if err != nil { + return nil, err + } + headerStr := base64.StdEncoding.EncodeToString(headerBs) + payloadStr := base64.StdEncoding.EncodeToString(payloadBs) + sig, err := controller.Sign([]byte(headerStr + "." + payloadStr)) + if err != nil { + return nil, err + } + claimStr := headerStr + "." + payloadStr + "." + base64.StdEncoding.EncodeToString(sig) + return &Claim{ + context: ctx, + claimStr: claimStr, + }, nil +} + +func (this *Claim) GetClaimStr() string { + return this.claimStr +} + +type Header struct { + Alg string `json:"alg"` + Typ string `json:"typ"` + Kid string `json:"kid"` +} + +func NewHeader(kid string) *Header { + return &Header{ + Alg: "ONT-ES256", + Typ: "JWT-X", + Kid: kid, + } +} +func (this *Header) getJson() ([]byte, error) { + bs, err := json.Marshal(this) + if err != nil { + return nil, err + } + return bs, nil +} + +type Payload struct { + Ver string `json:"ver"` + Iss string `json:"iss"` + Sub string `json:"sub"` + Iat int64 `json:"iat"` + Exp int64 `json:"exp"` + Jti string `json:"jti"` + Context string `json:"@context"` + ClmMap map[string]interface{} `json:"clm"` + ClmRevMap map[string]interface{} `json:"clm-rev"` +} + +func NewPayload(ver, iss, sub string, iat, exp int64, ctx string, clmMap, clmRevMap map[string]interface{}) (*Payload, error) { + payload := &Payload{ + Ver: ver, + Iss: iss, + Sub: sub, + Iat: iat, + Exp: exp, + Context: ctx, + ClmMap: clmMap, + ClmRevMap: clmRevMap, + } + pbs, err := json.Marshal(payload) + if err != nil { + return nil, err + } + payload.Jti = common.ToHexString(pbs) + return payload, nil +} + +func (this *Payload) getJson() ([]byte, error) { + bs, err := json.Marshal(this) + if err != nil { + return nil, err + } + return bs, nil +} + +type MetaData struct { + createTime string + meta map[string]string +} + +func (this *MetaData) GetJson() interface{} { + this.meta["CreateTime"] = this.createTime + + return this.meta +} + +type SignatureInfo struct { + format string + algorithm string + value []byte + publicKeyId string +} + +func NewSignatureInfo(publicKeyId string, val []byte) *SignatureInfo { + return &SignatureInfo{ + format: "pgp", + algorithm: "ECDSAwithSHA256", + value: val, + publicKeyId: publicKeyId, + } +} + +func (this *SignatureInfo) getJson() map[string]interface{} { + signature := make(map[string]interface{}) + signature["Format"] = this.format + signature["Algorithm"] = this.algorithm + signature["Value"] = this.value + signature["PublicKeyId"] = this.publicKeyId + return signature +} diff --git a/claim_test.go b/claim_test.go new file mode 100644 index 0000000..5636bc5 --- /dev/null +++ b/claim_test.go @@ -0,0 +1,30 @@ +package ontology_go_sdk + +import ( + "testing" + + "fmt" + "github.com/stretchr/testify/assert" + "time" +) + +func TestNewClaim(t *testing.T) { + wallet := NewWallet("./wallet.dat") + pwd := []byte("111111") + acct, err := wallet.NewDefaultSettingIdentity(pwd) + assert.Nil(t, err) + clmMap := map[string]interface{}{ + "1111": "1111", + } + metadata := map[string]string{ + "2222": "2222", + } + clmRevMap := map[string]interface{}{ + "333": "333", + } + con,err := acct.controllers[0].GetController(pwd) + assert.Nil(t, err) + claim, err := NewClaim(con, "", clmMap, metadata, clmRevMap, "", time.Now().Unix()+20) + assert.Nil(t, err) + fmt.Println(claim.claimStr) +} diff --git a/native_contract.go b/native_contract.go index ff01ac8..cbdc6a6 100644 --- a/native_contract.go +++ b/native_contract.go @@ -19,9 +19,12 @@ package ontology_go_sdk import ( "bytes" + "encoding/base64" "encoding/hex" + "encoding/json" "fmt" "github.com/ontio/ontology-crypto/keypair" + "github.com/ontio/ontology-crypto/signature" sdkcom "github.com/ontio/ontology-go-sdk/common" "github.com/ontio/ontology-go-sdk/utils" "github.com/ontio/ontology/common" @@ -30,6 +33,9 @@ import ( cutils "github.com/ontio/ontology/core/utils" "github.com/ontio/ontology/smartcontract/service/native/global_params" "github.com/ontio/ontology/smartcontract/service/native/ont" + "strconv" + "strings" + "time" ) var ( @@ -665,6 +671,125 @@ func (this *OntId) RegIDWithPublicKey(gasPrice, gasLimit uint64, payer *Account, return this.ontSdk.SendTransaction(tx) } +func (this *OntId) CreateOntIdClaim(ontidController *Controller, context string, + metaData map[string]string, claimMap, clmRevMap map[string]interface{}, expire int64) (string, error) { + if ontidController == nil || context == "" || claimMap == nil || metaData == nil || + clmRevMap == nil || expire < 0 { + return "", fmt.Errorf("[CreateOntIdClaim] param should not be nil") + } + curr := time.Now().Unix() + if expire < curr { + return "", fmt.Errorf("[CreateOntIdClaim] expire:%d should not be less than current time: %d", expire, curr) + } + issuerDid := metaData["Issuer"] + receiverDid := metaData["Subject"] + if issuerDid == "" || receiverDid == "" { + return "", fmt.Errorf("issuer ontid and receiver ontid should not be nil") + } + issuerDdo, err := this.GetDDO(issuerDid) + if err != nil { + return "", fmt.Errorf("[CreateOntIdClaim] GetDDO issuerDid failed: %s, ontid: %s", err, issuerDid) + } + owners := issuerDdo.Owners + if owners == nil { + return "", fmt.Errorf("[CreateOntIdClaim] not exist cliam issuer") + } + pk := common.ToHexString(keypair.SerializePublicKey(ontidController.PublicKey)) + pubkeyId := "" + for _, owner := range owners { + if owner.Value == pk { + pubkeyId = owner.PubKeyId + break + } + } + if pubkeyId == "" { + return "", fmt.Errorf("not found publickeyid") + } + receiverDidStr := strings.Split(receiverDid, ":") + if len(receiverDidStr) != 3 { + return "", fmt.Errorf("[CreateOntIdClaim]receiverDid is wrong") + } + clai, err := NewClaim(ontidController, context, claimMap, metaData, clmRevMap, pubkeyId, expire) + if err != nil { + return "", fmt.Errorf("[CreateOntIdClaim] NewClaim failed: %s", err) + } + return clai.GetClaimStr(), nil +} + +func (this *OntId) VerifyOntIdClaim(claimStr string) (bool, error) { + if claimStr == "" { + return false, fmt.Errorf("[VerifyOntIdClaim]param is nil") + } + arr := strings.Split(claimStr, ".") + if len(arr) != 3 { + return false, fmt.Errorf("[VerifyOntIdClaim] param is wrong") + } + payloadBytes, err := base64.StdEncoding.DecodeString(arr[1]) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] base64 decode failed: %s", err) + } + payload := &Payload{} + err = json.Unmarshal(payloadBytes, payload) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] Unmarshal payload failed: %s", err) + } + issuerDid := payload.Iss + issArr := strings.Split(issuerDid, ":") + if len(issArr) != 3 { + return false, fmt.Errorf("[VerifyOntIdClaim] payload iss is wrong") + } + issuerDdo, err := this.GetDDO(issuerDid) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] GetDDO failed: %s", err) + } + owners := issuerDdo.Owners + if owners == nil { + return false, fmt.Errorf("[VerifyOntIdClaim] owners is nil, issuerDid: %s", issuerDid) + } + signatureBytes, err := base64.StdEncoding.DecodeString(arr[2]) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] sig base64 decode failed: %s, sig: %s", err, arr[2]) + } + headerBytes, err := base64.StdEncoding.DecodeString(arr[0]) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] header base64 decode failed: %s, header: %s", err, arr[0]) + } + header := &Header{} + err = json.Unmarshal(headerBytes, header) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] header Unmarshal failed: %s, header: %s", err, arr[0]) + } + kid := header.Kid + idArr := strings.Split(kid, "#keys-") + if len(idArr) < 2 { + return false, fmt.Errorf("[VerifyOntIdClaim] header kid is wrong: %s", kid) + } + idStr := idArr[1] + id, err := strconv.Atoi(idStr) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] Atoi is failed: %s, id: %s", err, idStr) + } + id -= 1 + if id < 0 || id >= len(owners) { + return false, fmt.Errorf("[VerifyOntIdClaim] error id: %d", id) + } + pubkeyStr := owners[id].Value + data := arr[0] + "." + arr[1] + pkBs, err := common.HexToBytes(pubkeyStr) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] publickey error: %s", err) + } + pk, err := keypair.DeserializePublicKey(pkBs) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] DeserializePublicKey failed: %s", err) + } + sig, err := signature.Deserialize(signatureBytes) + if err != nil { + return false, fmt.Errorf("[VerifyOntIdClaim] sig Deserialize failed: %s", err) + } + return signature.Verify(pk, []byte(data), sig), nil +} + func (this *OntId) NewRegIDWithAttributesTransaction(gasPrice, gasLimit uint64, ontId string, pubKey keypair.PublicKey, attributes []*DDOAttribute) (*types.MutableTransaction, error) { type regIDWithAttribute struct { OntId string diff --git a/native_contract_test.go b/native_contract_test.go index 2ec82fd..5adeb27 100644 --- a/native_contract_test.go +++ b/native_contract_test.go @@ -21,6 +21,7 @@ import ( "encoding/hex" "fmt" "github.com/ontio/ontology-crypto/keypair" + "github.com/stretchr/testify/assert" "testing" "time" ) @@ -349,3 +350,40 @@ func TestOntId_Recovery(t *testing.T) { return } } + +func TestOntId_CreateOntIdClaim(t *testing.T) { + testIdentity, err := testWallet.NewDefaultSettingIdentity(testPasswd) + assert.Nil(t, err) + testIdentity2, err := testWallet.NewDefaultSettingIdentity(testPasswd) + assert.Nil(t, err) + payer, err := testWallet.NewDefaultSettingAccount(testPasswd) + assert.Nil(t, err) + + testOntSdk.NewRpcClient().SetAddress("http://127.0.0.1:20336") + + controller, err := testIdentity.controllers[0].GetController(testPasswd) + _, err = testOntSdk.Native.OntId.RegIDWithPublicKey(0, 20000, payer, payer, testIdentity.ID, controller) + controller2, err := testIdentity2.controllers[0].GetController(testPasswd) + _, err = testOntSdk.Native.OntId.RegIDWithPublicKey(0, 20000, payer, payer, testIdentity.ID, controller2) + assert.Nil(t, err) + time.Sleep(6 * time.Second) + ddo, err := testOntSdk.Native.OntId.GetDDO(testIdentity.ID) + assert.NotNil(t, ddo) + assert.Nil(t, err) + ddo, err = testOntSdk.Native.OntId.GetDDO(testIdentity.ID) + assert.Nil(t, err) + assert.NotNil(t, ddo) + metaData := map[string]string{ + "Issuer": testIdentity.ID, + "Subject": testIdentity2.ID, + } + clmRevMap := map[string]interface{}{ + "typ": "AttestContract", + "addr": testIdentity.ID, + } + claim, err := testOntSdk.Native.OntId.CreateOntIdClaim(controller, "claim:context", metaData, clmRevMap, clmRevMap, time.Now().Unix()+1000) + assert.Nil(t, err) + boo, err := testOntSdk.Native.OntId.VerifyOntIdClaim(claim) + assert.Nil(t, err) + assert.True(t, boo) +} diff --git a/ont_sdk_test.go b/ont_sdk_test.go index ecac085..2d4cc57 100644 --- a/ont_sdk_test.go +++ b/ont_sdk_test.go @@ -49,13 +49,9 @@ var ( func init() { var err error - testWallet, err = testOntSdk.OpenWallet("./wallet.dat") - if err != nil { - fmt.Printf("OpenWallet err: %s\n", err) - return - } + testWallet = NewWallet("./wallet.dat") testOntSdk = NewOntologySdk() - testOntSdk.NewRpcClient().SetAddress(testNetUrl) + testOntSdk.NewRpcClient().SetAddress("http://127.0.0.1:20336") testDefAcc, err = testWallet.GetDefaultAccount(testPasswd) if err != nil { fmt.Printf("GetDefaultAccount err: %s\n", err) From 7e2430d8602243d8339c662b1960c62c72646e64 Mon Sep 17 00:00:00 2001 From: lucas7788 Date: Fri, 10 Apr 2020 14:55:04 +0800 Subject: [PATCH 2/5] add verifyClaim test --- native_contract_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/native_contract_test.go b/native_contract_test.go index 5adeb27..bbcd893 100644 --- a/native_contract_test.go +++ b/native_contract_test.go @@ -386,4 +386,14 @@ func TestOntId_CreateOntIdClaim(t *testing.T) { boo, err := testOntSdk.Native.OntId.VerifyOntIdClaim(claim) assert.Nil(t, err) assert.True(t, boo) + fmt.Println("claim:", claim) +} + +func TestOntId_VerifyOntIdClaim(t *testing.T) { + testOntSdk.NewRpcClient().SetAddress("http://127.0.0.1:20336") + //generate by java-sdk + claimStr := "eyJraWQiOiJkaWQ6b250OkFTejlOZENCVUdEclpZVGhuY2hGZkp0ZVFWcnUyUDNtcXEja2V5cy0xIiwidHlwIjoiSldULVgiLCJhbGciOiJPTlQtRVMyNTYifQ==.eyJjbG0tcmV2Ijp7Iklzc3VlciI6ImRpZDpvbnQ6QVN6OU5kQ0JVR0RyWllUaG5jaEZmSnRlUVZydTJQM21xcSIsIlN1YmplY3QiOiJkaWQ6b250OkFhcnJNQnkxaUdKU1o1VG1VUUNvak55VlZUdWdpUExQaWsifSwic3ViIjoiZGlkOm9udDpBYXJyTUJ5MWlHSlNaNVRtVVFDb2pOeVZWVHVnaVBMUGlrIiwidmVyIjoidjEuMCIsImNsbSI6eyIkcmVmIjoiJC5jbG0tcmV2In0sImlzcyI6ImRpZDpvbnQ6QVN6OU5kQ0JVR0RyWllUaG5jaEZmSnRlUVZydTJQM21xcSIsImV4cCI6MTU4NjQ5OTEwNCwiaWF0IjoxNTg2NDk4MTA1LCJAY29udGV4dCI6ImNsYWltOmNvbnRleHQiLCJqdGkiOiI1MzlhMzlmNWYyY2E1NzRlNTdkMjY2NzRiMDBhZTc5ZTBkODdiYjExMTNmODBlZWNmZDFkZDhjNThhOTNiM2NjIn0=.AZ1jo4XYus7+ovFK5FKr3l5GxJihfDUPlsiOhY4vyiRf283L8AYG7fIguE2HLUEDLIE7rGxc6jnU8/ts77MLo6U=" + res, err := testOntSdk.Native.OntId.VerifyOntIdClaim(claimStr) + assert.Nil(t, err) + assert.True(t, res) } From f4682ba42003f2fed63205096ac657c3a51ccfe6 Mon Sep 17 00:00:00 2001 From: lucas7788 Date: Mon, 13 Apr 2020 10:51:21 +0800 Subject: [PATCH 3/5] fix test --- native_contract_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/native_contract_test.go b/native_contract_test.go index bbcd893..0602560 100644 --- a/native_contract_test.go +++ b/native_contract_test.go @@ -352,6 +352,7 @@ func TestOntId_Recovery(t *testing.T) { } func TestOntId_CreateOntIdClaim(t *testing.T) { + return testIdentity, err := testWallet.NewDefaultSettingIdentity(testPasswd) assert.Nil(t, err) testIdentity2, err := testWallet.NewDefaultSettingIdentity(testPasswd) @@ -390,6 +391,7 @@ func TestOntId_CreateOntIdClaim(t *testing.T) { } func TestOntId_VerifyOntIdClaim(t *testing.T) { + return testOntSdk.NewRpcClient().SetAddress("http://127.0.0.1:20336") //generate by java-sdk claimStr := "eyJraWQiOiJkaWQ6b250OkFTejlOZENCVUdEclpZVGhuY2hGZkp0ZVFWcnUyUDNtcXEja2V5cy0xIiwidHlwIjoiSldULVgiLCJhbGciOiJPTlQtRVMyNTYifQ==.eyJjbG0tcmV2Ijp7Iklzc3VlciI6ImRpZDpvbnQ6QVN6OU5kQ0JVR0RyWllUaG5jaEZmSnRlUVZydTJQM21xcSIsIlN1YmplY3QiOiJkaWQ6b250OkFhcnJNQnkxaUdKU1o1VG1VUUNvak55VlZUdWdpUExQaWsifSwic3ViIjoiZGlkOm9udDpBYXJyTUJ5MWlHSlNaNVRtVVFDb2pOeVZWVHVnaVBMUGlrIiwidmVyIjoidjEuMCIsImNsbSI6eyIkcmVmIjoiJC5jbG0tcmV2In0sImlzcyI6ImRpZDpvbnQ6QVN6OU5kQ0JVR0RyWllUaG5jaEZmSnRlUVZydTJQM21xcSIsImV4cCI6MTU4NjQ5OTEwNCwiaWF0IjoxNTg2NDk4MTA1LCJAY29udGV4dCI6ImNsYWltOmNvbnRleHQiLCJqdGkiOiI1MzlhMzlmNWYyY2E1NzRlNTdkMjY2NzRiMDBhZTc5ZTBkODdiYjExMTNmODBlZWNmZDFkZDhjNThhOTNiM2NjIn0=.AZ1jo4XYus7+ovFK5FKr3l5GxJihfDUPlsiOhY4vyiRf283L8AYG7fIguE2HLUEDLIE7rGxc6jnU8/ts77MLo6U=" From c1989ab8601ba029343611f99c66da7421effaf8 Mon Sep 17 00:00:00 2001 From: lucas7788 Date: Mon, 13 Apr 2020 13:03:09 +0800 Subject: [PATCH 4/5] add license --- claim.go | 17 +++++++++++++++++ claim_test.go | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/claim.go b/claim.go index f727254..39455b8 100644 --- a/claim.go +++ b/claim.go @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 The ontology Authors + * This file is part of The ontology library. + * + * The ontology is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The ontology is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with The ontology. If not, see . + */ package ontology_go_sdk import ( diff --git a/claim_test.go b/claim_test.go index 5636bc5..3e71177 100644 --- a/claim_test.go +++ b/claim_test.go @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2018 The ontology Authors + * This file is part of The ontology library. + * + * The ontology is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The ontology is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with The ontology. If not, see . + */ package ontology_go_sdk import ( From 842e75f99d49266d5c6dd1269102413be83e479b Mon Sep 17 00:00:00 2001 From: lucas7788 Date: Mon, 13 Apr 2020 13:19:19 +0800 Subject: [PATCH 5/5] fmt --- claim_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/claim_test.go b/claim_test.go index 3e71177..f97300d 100644 --- a/claim_test.go +++ b/claim_test.go @@ -39,7 +39,7 @@ func TestNewClaim(t *testing.T) { clmRevMap := map[string]interface{}{ "333": "333", } - con,err := acct.controllers[0].GetController(pwd) + con, err := acct.controllers[0].GetController(pwd) assert.Nil(t, err) claim, err := NewClaim(con, "", clmMap, metadata, clmRevMap, "", time.Now().Unix()+20) assert.Nil(t, err)