From a3cd17204b8fdb87e6dcfdae40ea4920dd983809 Mon Sep 17 00:00:00 2001 From: siovanus Date: Thu, 2 Jul 2020 16:05:09 +0800 Subject: [PATCH] add credential api (#114) * add claim api * update * update claim * update * add jwt * rename from claim to credential * update * update * update gas price --- .gitignore | 3 +- README.md | 106 ++++++ cred.go | 709 +++++++++++++++++++++++++++++++++++++++ cred_jwt.go | 549 ++++++++++++++++++++++++++++++ cred_jwt_test.go | 243 ++++++++++++++ cred_test.go | 205 +++++++++++ examples/wasm_example.go | 4 +- go.mod | 1 + go.sum | 2 + oep4/oep4_test.go | 6 +- ont_sdk.go | 19 +- ont_sdk_test.go | 20 +- 12 files changed, 1848 insertions(+), 19 deletions(-) create mode 100644 cred.go create mode 100644 cred_jwt.go create mode 100644 cred_jwt_test.go create mode 100644 cred_test.go diff --git a/.gitignore b/.gitignore index 987a817..51d82be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ cscope* vendor/* glide.lock -.idea/ \ No newline at end of file +.idea/ +wallet.dat \ No newline at end of file diff --git a/README.md b/README.md index 2d02563..99ac4fa 100644 --- a/README.md +++ b/README.md @@ -1055,6 +1055,112 @@ ontSdk.Native.OntId.GetDocumentJson(ontId string) ([]byte, error) ``` `ontId`: ONT ID +### 2.6 Credential API + +#### 2.6.1 GenSignReq + +``` +ontSdk.Credential.GenSignReq(credentialSubject interface{}, ontId string, signer *Account) (*Request, error) +``` +`credentialSubject`: [credentialSubject of Credential](https://www.w3.org/TR/vc-data-model/#credential-subject) + +`ontId`: holder ONT ID + +`signer`: signer account + +#### 2.6.2 VerifySignReq + +``` +ontSdk.Credential.VerifySignReq(request *Request) error +``` +`request`: result of GenSignReq + +#### 2.6.3 CreateCredential + +``` +ontSdk.Credential.CreateCredential(contexts []string, types []string, credentialSubject interface{}, issuerId string, expirationDateTimestamp int64, signer *Account) (*VerifiableCredential, uint32, error) +``` +`contexts`: [definition](https://www.w3.org/TR/vc-data-model/#contexts) + +`types`: [definition](https://www.w3.org/TR/vc-data-model/#types) + +`credentialSubject`: [credentialSubject of Credential](https://www.w3.org/TR/vc-data-model/#credential-subject) + +`issuerId`: ONT ID of issuer + +`expirationDateTimestamp`: unix of expiration date timestamp + +`signer`: signer account + +#### 2.6.4 CommitCredential + +``` +ontSdk.Credential.CommitCredential(gasPrice, gasLimit uint64, credentialId, issuerId, holderId string, index uint32, signer, payer *Account) (common.Uint256, error) +``` +`credentialId`: Id of credential + +`issuerId`: ONT ID of issuer + +`holderId`: ONT ID of holder + +`index`: key index of issuer used to sign tx + +`signer`: signer account + +#### 2.6.5 VerifyCredibleOntId + +``` +ontSdk.Credential.VerifyCredibleOntId(credibleOntIds []string, credential *VerifiableCredential) error +``` +`credibleOntIds`: credible ONT ID list + +`credential`: [definition](https://www.w3.org/TR/vc-data-model/) + +#### 2.6.6 VerifyNotExpired + +``` +ontSdk.Credential.VerifyNotExpired(credential *VerifiableCredential) error +``` +`credential`: [definition](https://www.w3.org/TR/vc-data-model/) + +#### 2.6.7 VerifyIssuerSignature + +``` +ontSdk.Credential.VerifyIssuerSignature(credential *VerifiableCredential) error +``` +`credential`: [definition](https://www.w3.org/TR/vc-data-model/) + +#### 2.6.8 VerifyStatus + +``` +ontSdk.Credential.VerifyStatus(credential *VerifiableCredential) error +``` +`credential`: [definition](https://www.w3.org/TR/vc-data-model/) + +#### 2.6.9 CreatePresentation + +``` +ontSdk.Credential.CreatePresentation(credentials []*VerifiableCredential, contexts, types []string, holder string, signers []*Account) (*Presentation, error) +``` +`credentials`: credential list + +`contexts`: [definition](https://www.w3.org/TR/vc-data-model/#contexts) + +`types`: [definition](https://www.w3.org/TR/vc-data-model/#types) + +`holder`: ONTID of holder + +`signers`: signer accounts + +#### 2.6.10 VerifyPresentation + +``` +ontSdk.Credential.VerifyPresentation(presentation *Presentation, credibleOntIds []string) error +``` +`presentation`: [definition](https://www.w3.org/TR/vc-data-model/#presentations-0) + +`credibleOntIds`: credible ONT ID list + # Contributing Can I contribute patches to the Ontology project? diff --git a/cred.go b/cred.go new file mode 100644 index 0000000..0a36ba7 --- /dev/null +++ b/cred.go @@ -0,0 +1,709 @@ +/* + * 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 ( + "encoding/hex" + "encoding/json" + "fmt" + "reflect" + "strings" + "time" + + "github.com/ontio/ontology-crypto/keypair" + "github.com/ontio/ontology/common" + "github.com/ontio/ontology/core/signature" + "github.com/satori/go.uuid" +) + +const ( + CREDENTIAL_STATUS_TYPE CredentialStatusType = "AttestContract" + PROOF_PURPOSE ProofPurpose = "assertionMethod" + UUID_PREFIX = "urn:uuid:" +) + +type CredentialStatusType string +type ProofPurpose string + +var DefaultContext = []string{"https://www.w3.org/2018/credentials/v1", "https://ontid.ont.io/credentials/v1"} +var DefaultCredentialType = []string{"VerifiableCredential"} +var DefaultPresentationType = []string{"VerifiablePresentation"} + +type Credential struct { + credRecordContractAddress common.Address + ontSdk *OntologySdk +} + +type Request struct { + CredentialSubject interface{} `json:"credentialSubject,omitempty"` + OntId string `json:"ontId,omitempty"` + Proof *Proof `json:"proof,omitempty"` +} + +type CredentialStatus struct { + Id string `json:"id"` + Type CredentialStatusType `json:"type"` +} + +type Proof struct { + Type string `json:"type,omitempty"` + Created string `json:"created,omitempty"` + Challenge string `json:"challenge,omitempty"` + Domain interface{} `json:"domain,omitempty"` + ProofPurpose ProofPurpose `json:"proofPurpose,omitempty"` + VerificationMethod string `json:"verificationMethod,omitempty"` + Hex string `json:"hex,omitempty"` + Jws string `json:"jws,omitempty"` +} + +type VerifiableCredential struct { + Context []string `json:"@context,omitempty"` + Id string `json:"id,omitempty"` + Type []string `json:"type,omitempty"` + Issuer interface{} `json:"issuer,omitempty"` + IssuanceDate string `json:"issuanceDate,omitempty"` + ExpirationDate string `json:"expirationDate,omitempty"` + CredentialSubject interface{} `json:"credentialSubject,omitempty"` + CredentialStatus *CredentialStatus `json:"credentialStatus,omitempty"` + Proof *Proof `json:"proof,omitempty"` +} + +type VerifiablePresentation struct { + Context []string `json:"@context,omitempty"` + Id string `json:"id,omitempty"` + Type []string `json:"type,omitempty"` + VerifiableCredential []*VerifiableCredential `json:"verifiableCredential,omitempty"` + Holder interface{} `json:"holder,omitempty"` + Proof []*Proof `json:"proof,omitempty"` +} + +type PublicKeyList []*PublicKey + +type PublicKey struct { + Id string `json:"id"` + Type string `json:"type"` + PublicKeyHex string `json:"publicKeyHex"` +} + +func newCredential(ontSdk *OntologySdk) *Credential { + return &Credential{ + ontSdk: ontSdk, + } +} + +func (this *Credential) GenSignReq(credentialSubject interface{}, ontId string, signer *Account) (*Request, error) { + request := &Request{ + CredentialSubject: credentialSubject, + OntId: ontId, + } + var domain interface{} + proof, err := this.createProof(ontId, signer, "", domain, time.Now().UTC().Unix()) + if err != nil { + return nil, fmt.Errorf("GenSignReq, this.CreateProof error: %s", err) + } + request.Proof = proof + + msg, err := json.Marshal(request) + if err != nil { + return nil, fmt.Errorf("GenSignReq, json.Marshal error: %s", err) + } + sig, err := signer.Sign(msg) + if err != nil { + return nil, fmt.Errorf("GenSignReq, signer.Sign error: %s", err) + } + request.Proof.Hex = hex.EncodeToString(sig) + + return request, nil +} + +func (this *Credential) VerifySignReq(request *Request) error { + msg, err := GenRequestMsg(request) + if err != nil { + return fmt.Errorf("VerifySignReq, hex.DecodeString signature error: %s", err) + } + sig, err := hex.DecodeString(request.Proof.Hex) + if err != nil { + return fmt.Errorf("VerifySignReq, hex.DecodeString signature error: %s", err) + } + + publicKeyList, err := this.GetPublicKeyList(request.OntId) + if err != nil { + return fmt.Errorf("VerifySignReq, this.GetPublicKeyList error: %s", err) + } + for _, v := range publicKeyList { + data, err := hex.DecodeString(v.PublicKeyHex) + if err != nil { + return fmt.Errorf("VerifySignReq, hex.DecodeString public key error: %s", err) + } + pk, err := keypair.DeserializePublicKey(data) + if err != nil { + return fmt.Errorf("VerifySignReq, keypair.DeserializePublicKey error: %s", err) + } + err = signature.Verify(pk, msg, sig) + if err == nil { + return nil + } + } + return fmt.Errorf("VerifySignReq failed") +} + +func (this *Credential) CreateCredential(contexts []string, types []string, credentialSubject interface{}, issuerId interface{}, + expirationDateTimestamp int64, challenge string, domain interface{}, signer *Account) (*VerifiableCredential, error) { + credential := new(VerifiableCredential) + credential.Id = UUID_PREFIX + uuid.NewV4().String() + credential.Context = append(DefaultContext, contexts...) + credential.Type = append(DefaultCredentialType, types...) + credential.Issuer = issuerId + + now := time.Now().UTC().Unix() + issuanceDate := time.Unix(now, 0).UTC().Format("2006-01-02T15:04:05Z") + credential.IssuanceDate = issuanceDate + + if expirationDateTimestamp != 0 { + expirationDate := time.Unix(expirationDateTimestamp, 0).UTC().Format("2006-01-02T15:04:05Z") + credential.ExpirationDate = expirationDate + if now > expirationDateTimestamp { + return nil, fmt.Errorf("CreateCredential, now is after expirationDateTimestamp") + } + } + credential.CredentialSubject = credentialSubject + + credentialStatus := &CredentialStatus{ + Id: this.credRecordContractAddress.ToHexString(), + Type: CREDENTIAL_STATUS_TYPE, + } + credential.CredentialStatus = credentialStatus + + // create proof + _, ontId, err := getOntId(issuerId) + if err != nil { + return nil, fmt.Errorf("CreateCredential, getOntId error: %s", err) + } + proof, err := this.createProof(ontId, signer, challenge, domain, now) + if err != nil { + return nil, fmt.Errorf("CreateCredential, this.CreateProof error: %s", err) + } + credential.Proof = proof + + msg, err := json.Marshal(credential) + if err != nil { + return nil, fmt.Errorf("CreateCredential, json.Marshal credential error: %s", err) + } + sig, err := signer.Sign(msg) + if err != nil { + return nil, fmt.Errorf("CreateCredential, signer.Sign error: %s", err) + } + credential.Proof.Hex = hex.EncodeToString(sig) + + return credential, nil +} + +func (this *Credential) GetPublicKeyId(ontId string, publicKeyHex string) (uint32, *PublicKey, error) { + publicKeyList, err := this.GetPublicKeyList(ontId) + if err != nil { + return 0, nil, fmt.Errorf("GetPublicKeyId, this.GetPublicKeyList error: %s", err) + } + + for i, v := range publicKeyList { + if v.PublicKeyHex == publicKeyHex { + return uint32(i + 1), v, nil + } + } + return 0, nil, fmt.Errorf("GetPublicKeyId, record not found") +} + +func (this *Credential) GetPublicKey(ontId string, Id string) (string, error) { + publicKeyList, err := this.GetPublicKeyList(ontId) + if err != nil { + return "", fmt.Errorf("GetPublicKeyId, this.GetPublicKeyList error: %s", err) + } + + for _, v := range publicKeyList { + if v.Id == Id { + return v.PublicKeyHex, nil + } + } + return "", fmt.Errorf("GetPublicKeyId, record not found") +} + +func (this *Credential) GetPublicKeyList(ontId string) (PublicKeyList, error) { + publicKeys, err := this.ontSdk.Native.OntId.GetPublicKeysJson(ontId) + if err != nil { + return nil, fmt.Errorf("GetPublicKeyList, this.ontSdk.Native.OntId.GetPublicKeysJson error: %s", err) + } + + var publicKeyList PublicKeyList + err = json.Unmarshal(publicKeys, &publicKeyList) + if err != nil { + return nil, fmt.Errorf("GetPublicKeyList, json.Unmarshal publicKeyList error: %s", err) + } + + return publicKeyList, nil +} + +func (this *Credential) CommitCredential(contractAddress common.Address, gasPrice, gasLimit uint64, credentialId, issuerId, + holderId string, signer, payer *Account) (common.Uint256, error) { + index, _, err := this.GetPublicKeyId(holderId, hex.EncodeToString(keypair.SerializePublicKey(signer.GetPublicKey()))) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("CommitCredential, this.GetPublicKeyId error: %s", err) + } + params := []interface{}{"Commit", []interface{}{credentialId, issuerId, index, holderId}} + txHash, err := this.ontSdk.NeoVM.InvokeNeoVMContract(gasPrice, gasLimit, payer, signer, contractAddress, params) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("CommitCredential, this.ontSdk.NeoVM.InvokeNeoVMContract error: %s", err) + } + return txHash, nil +} + +func (this *Credential) revokeCredential(contractAddress common.Address, gasPrice, gasLimit uint64, credentialId, ontId string, index uint32, + signer, payer *Account) (common.Uint256, error) { + params := []interface{}{"Revoke", []interface{}{credentialId, ontId, index}} + txHash, err := this.ontSdk.NeoVM.InvokeNeoVMContract(gasPrice, gasLimit, payer, signer, contractAddress, params) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("revokeCredential, this.ontSdk.NeoVM.InvokeNeoVMContract error: %s", err) + } + return txHash, nil +} + +func (this *Credential) RemoveCredential(gasPrice, gasLimit uint64, credential *VerifiableCredential, ontId string, + signer, payer *Account) (common.Uint256, error) { + index, _, err := this.GetPublicKeyId(ontId, hex.EncodeToString(keypair.SerializePublicKey(signer.GetPublicKey()))) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RemoveCredential, this.GetPublicKeyId error: %s", err) + } + if credential.CredentialStatus.Type != CREDENTIAL_STATUS_TYPE { + return common.UINT256_EMPTY, fmt.Errorf("RemoveCredential, credential status %s not match", credential.CredentialStatus.Type) + } + contractAddress, err := common.AddressFromHexString(credential.CredentialStatus.Id) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RemoveCredential, common.AddressFromHexString error: %s", err) + } + params := []interface{}{"Remove", []interface{}{credential.Id, ontId, index}} + txHash, err := this.ontSdk.NeoVM.InvokeNeoVMContract(gasPrice, gasLimit, payer, signer, contractAddress, params) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RemoveCredential, this.ontSdk.NeoVM.InvokeNeoVMContract error: %s", err) + } + return txHash, nil +} + +func (this *Credential) VerifyCredibleOntId(credibleOntIds []string, credential *VerifiableCredential) error { + for _, v := range credibleOntIds { + if credential.Issuer == v { + return nil + } + } + return fmt.Errorf("VerifyCredibleOntId failed") +} + +func (this *Credential) VerifyDate(credential *VerifiableCredential) error { + now := time.Now().UTC() + if credential.ExpirationDate != "" { + expirationDate, err := time.Parse("2006-01-02T15:04:05Z", credential.ExpirationDate) + if err != nil { + return fmt.Errorf("VerifyDate error: %s", err) + } + if now.Unix() > expirationDate.Unix() { + return fmt.Errorf("VerifyDate expirationDate failed") + } + } + + issuanceDate, err := time.Parse("2006-01-02T15:04:05Z", credential.IssuanceDate) + if err != nil { + return fmt.Errorf("VerifyDate error: %s", err) + } + if now.Unix() < issuanceDate.Unix() { + return fmt.Errorf("VerifyDate issuanceDate failed") + } + return nil +} + +func (this *Credential) VerifyIssuerSignature(credential *VerifiableCredential) error { + msg, err := GenCredentialMsg(credential) + if err != nil { + return fmt.Errorf("VerifyIssuerSignature, GenCredentialMsg error: %s", err) + } + _, ontId, err := getOntId(credential.Issuer) + if err != nil { + return fmt.Errorf("VerifyIssuerSignature, getOntId error: %s", err) + } + err = this.verifyProof(ontId, credential.Proof, msg) + if err != nil { + return fmt.Errorf("VerifyIssuerSignature, this.VerifyProof error: %s", err) + } + return nil +} + +func (this *Credential) VerifyStatus(credential *VerifiableCredential) error { + if credential.CredentialStatus.Type != CREDENTIAL_STATUS_TYPE { + return fmt.Errorf("VerifyStatus, credential status %s not match", credential.CredentialStatus.Type) + } + contractAddress, err := common.AddressFromHexString(credential.CredentialStatus.Id) + if err != nil { + return fmt.Errorf("VerifyStatus, common.AddressFromHexString error: %s", err) + } + status, err := this.getCredentialStatus(contractAddress, credential.Id) + if err != nil { + return fmt.Errorf("VerifyStatus, this.getCredentialStatus error: %s", err) + } + if status != 1 { + return fmt.Errorf("VerifyStatus failed") + } + return nil +} + +func (this *Credential) getCredentialStatus(contractAddress common.Address, credentialId string) (uint64, error) { + params := []interface{}{"GetStatus", []interface{}{credentialId}} + preExecResult, err := this.ontSdk.NeoVM.PreExecInvokeNeoVMContract(contractAddress, params) + if err != nil { + return 0, fmt.Errorf("getCredentialStatus, this.ontSdk.NeoVM.PreExecInvokeNeoVMContract error: %s", err) + } + r, err := preExecResult.Result.ToInteger() + if err != nil { + return 0, fmt.Errorf("getCredentialStatus, preExecResult.Result.ToInteger error: %s", err) + } + return r.Uint64(), nil +} + +func (this *Credential) CreatePresentation(credentials []*VerifiableCredential, contexts, types []string, holder interface{}, + signerOntIds, challenge []string, domain []interface{}, signers []*Account) (*VerifiablePresentation, error) { + presentation := new(VerifiablePresentation) + presentation.Id = UUID_PREFIX + uuid.NewV4().String() + presentation.Context = append(DefaultContext, contexts...) + presentation.Type = append(DefaultPresentationType, types...) + presentation.Holder = holder + presentation.VerifiableCredential = credentials + + if !(len(signerOntIds) == len(challenge) && len(signerOntIds) == len(domain) && len(signerOntIds) == len(signers)) { + return nil, fmt.Errorf("input params error") + } + now := time.Now().UTC().Unix() + proofs := make([]*Proof, 0) + for i := range signerOntIds { + // create proof + proof, err := this.createProof(signerOntIds[i], signers[i], challenge[i], domain[i], now) + if err != nil { + return nil, fmt.Errorf("CreatePresentation, this.CreateProof error: %s", err) + } + presentation.Proof = []*Proof{proof} + msg, err := json.Marshal(presentation) + if err != nil { + return nil, fmt.Errorf("CreatePresentation, json.Marshal msg error: %s", err) + } + sig, err := signers[i].Sign(msg) + if err != nil { + return nil, fmt.Errorf("CreatePresentation, signer.Sign error: %s", err) + } + proof.Hex = hex.EncodeToString(sig) + proofs = append(proofs, proof) + } + presentation.Proof = proofs + return presentation, nil +} + +func (this *Credential) VerifyPresentationProof(presentation *VerifiablePresentation, index int) (string, error) { + msg, err := GenPresentationMsg(presentation) + if err != nil { + return "", fmt.Errorf("VerifyPresentationProof, GenPresentationMsg error: %s", err) + } + ontId := parseOntId(presentation.Proof[index].VerificationMethod) + err = this.verifyProof(ontId, presentation.Proof[index], msg) + if err != nil { + return "", fmt.Errorf("VerifyPresentationProof, this.VerifyProof error: %s", err) + } + return ontId, nil +} + +func (this *Credential) JsonCred2JWT(cred *VerifiableCredential) (string, error) { + is, ontId, err := getOntId(cred.Issuer) + if err != nil { + return "", fmt.Errorf("JsonCred2JWT, getOntId issuer error: %s", err) + } + header, err := makeJWTHeader(cred.Proof.Type, cred.Proof.VerificationMethod) + if err != nil { + return "", fmt.Errorf("JsonCred2JWT, makeJWTHeader error: %s", err) + } + + cs, sub, err := getOntId(cred.CredentialSubject) + if err != nil { + return "", fmt.Errorf("JsonCred2JWT, getOntId credentialSubject error: %s", err) + } + + proof := cred.Proof + proof.VerificationMethod = "" + proof.Type = "" + vc := &VC{ + Context: cred.Context, + Type: cred.Type, + Issuer: is, + CredentialSubject: cs, + CredentialStatus: cred.CredentialStatus, + Proof: proof, + } + issuanceDate, err := time.Parse("2006-01-02T15:04:05Z", cred.IssuanceDate) + if err != nil { + return "", fmt.Errorf("JsonCred2JWT, time.Parse issuanceDate error: %s", err) + } + expirationDate, err := time.Parse("2006-01-02T15:04:05Z", cred.ExpirationDate) + if err != nil { + return "", fmt.Errorf("JsonCred2JWT, time.Parse expirationDate error: %s", err) + } + payload := &Payload{ + Sub: sub, + Jti: cred.Id, + Iss: ontId, + Nbf: issuanceDate.Unix(), + Iat: issuanceDate.Unix(), + Exp: expirationDate.Unix(), + VC: vc, + } + + credential := &JWTCredential{ + Header: header, + Payload: payload, + } + if cred.Proof.Jws == "" { + return "", fmt.Errorf("JsonCred2JWT, Jws signature is empty") + } + credential.Jws = cred.Proof.Jws + return credential.ToString() +} + +func (this *Credential) JsonPresentation2JWT(presentation *VerifiablePresentation, proof *Proof) (string, error) { + hd, ontId, err := getOntId(presentation.Holder) + if err != nil { + return "", fmt.Errorf("JsonPresentation2JWT, getOntId holder error: %s", err) + } + header, err := makeJWTHeader(proof.Type, proof.VerificationMethod) + if err != nil { + return "", fmt.Errorf("JsonPresentation2JWT, makeJWTHeader error: %s", err) + } + + proof.VerificationMethod = "" + proof.Type = "" + // make credentials + var credentials []string + for _, v := range presentation.VerifiableCredential { + JWTCred, err := this.JsonCred2JWT(v) + if err != nil { + return "", fmt.Errorf("JsonPresentation2JWT, this.JsonCred2JWT error: %s", err) + } + credentials = append(credentials, JWTCred) + } + vp := &VP{ + Context: presentation.Context, + Type: presentation.Type, + VerifiableCredential: credentials, + Holder: hd, + Proof: proof, + } + payload := &Payload{ + Aud: proof.Domain, + Nonce: proof.Challenge, + Jti: presentation.Id, + Iss: ontId, + VP: vp, + } + + JWTPresentation := &JWTCredential{ + Header: header, + Payload: payload, + } + if proof.Jws == "" { + return "", fmt.Errorf("JsonPresentation2JWT, Jws signature is empty") + } + JWTPresentation.Jws = proof.Jws + return JWTPresentation.ToString() +} + +func (this *Credential) verifyProof(ontId string, proof *Proof, msg []byte) error { + sig, err := hex.DecodeString(proof.Hex) + if err != nil { + return fmt.Errorf("VerifyProof, hex.DecodeString signature error: %s", err) + } + + publicKeyHex, err := this.GetPublicKey(ontId, proof.VerificationMethod) + if err != nil { + return fmt.Errorf("VerifyProof, this.GetPublicKey error: %s", err) + } + data, err := hex.DecodeString(publicKeyHex) + if err != nil { + return fmt.Errorf("VerifyProof, hex.DecodeString public key error: %s", err) + } + pk, err := keypair.DeserializePublicKey(data) + if err != nil { + return fmt.Errorf("VerifyProof, keypair.DeserializePublicKey error: %s", err) + } + + return signature.Verify(pk, msg, sig) +} + +func (this *Credential) RevokeCredentialByHolder(gasPrice, gasLimit uint64, credential *VerifiableCredential, holder string, + signer, payer *Account) (common.Uint256, error) { + if credential.CredentialStatus.Type != CREDENTIAL_STATUS_TYPE { + return common.UINT256_EMPTY, fmt.Errorf("RevokeCredentialByHolder, credential status %s not match", credential.CredentialStatus.Type) + } + contractAddress, err := common.AddressFromHexString(credential.CredentialStatus.Id) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RevokeCredentialByHolder, common.AddressFromHexString error: %s", err) + } + + index, _, err := this.GetPublicKeyId(holder, hex.EncodeToString(keypair.SerializePublicKey(signer.GetPublicKey()))) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RevokeCredentialByHolder, this.GetPublicKeyId error: %s", err) + } + + return this.revokeCredential(contractAddress, gasPrice, gasLimit, credential.Id, holder, index, signer, payer) +} + +func (this *Credential) RevokeCredentialByIssuer(gasPrice, gasLimit uint64, credentialId string, issuer string, + signer, payer *Account) (common.Uint256, error) { + index, _, err := this.GetPublicKeyId(issuer, hex.EncodeToString(keypair.SerializePublicKey(signer.GetPublicKey()))) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RevokeCredentialByIssuer, this.GetPublicKeyId error: %s", err) + } + + return this.revokeCredential(this.credRecordContractAddress, gasPrice, gasLimit, credentialId, issuer, index, signer, payer) +} + +func parseOntId(raw string) string { + return strings.Split(raw, "#")[0] +} + +type OntIdObject struct { + Id string `json:"id"` +} + +func genIdObject(ontId string, object interface{}) (interface{}, error) { + b, err := json.Marshal(object) + if err != nil { + return nil, fmt.Errorf("genIdObject, json.Marshal object error: %s", err) + } + m := make(map[string]interface{}) + if err := json.Unmarshal(b, &m); err != nil { + return nil, fmt.Errorf("genIdObject, json.Unmarshal object error: %s", err) + } + if m == nil { + m = make(map[string]interface{}) + } + m["id"] = ontId + + if len(m) == 1 { + return ontId, nil + } + j, err := json.Marshal(m) + if err != nil { + return nil, fmt.Errorf("genIdObject, json.Marshal map object error: %s", err) + } + var r interface{} + if err := json.Unmarshal(j, &r); err != nil { + return nil, fmt.Errorf("genIdObject, json.Unmarshal result error: %s", err) + } + return r, nil +} + +func getOntId(raw interface{}) (interface{}, string, error) { + t := reflect.TypeOf(raw).Kind() + if t != reflect.Struct && t != reflect.String { + return raw, "", nil + } + r, ok := raw.(string) + if ok { + return nil, r, nil + } + b, err := json.Marshal(raw) + if err != nil { + return nil, "", fmt.Errorf("getOntId, json.Marshal error: %s", err) + } + ontIdObject := new(OntIdObject) + err = json.Unmarshal(b, ontIdObject) + if err != nil { + return nil, "", fmt.Errorf("getOntId, json.Unmarshal error: %s", err) + } + m := make(map[string]interface{}) + if err := json.Unmarshal(b, &m); err == nil { + return nil, "", fmt.Errorf("getOntId, json.Unmarshal result error: %s", err) + } + delete(m, "id") + var result interface{} + mb, err := json.Marshal(m) + if err != nil { + return nil, "", fmt.Errorf("getOntId, json.Marshal map error: %s", err) + } + err = json.Unmarshal(mb, &result) + if err != nil { + return nil, "", fmt.Errorf("getOntId, json.Unmarshal result error: %s", err) + } + return result, ontIdObject.Id, nil +} + +func (this *Credential) createProof(ontId string, signer *Account, challenge string, domain interface{}, now int64) (*Proof, error) { + issuanceDate := time.Unix(now, 0).UTC().Format("2006-01-02T15:04:05Z") + // get public key id + _, pkInfo, err := this.GetPublicKeyId(ontId, hex.EncodeToString(keypair.SerializePublicKey(signer.GetPublicKey()))) + if err != nil { + return nil, fmt.Errorf("createProof, this.GetPublicKeyId error: %s", err) + } + proof := &Proof{ + Type: pkInfo.Type, + Created: issuanceDate, + Challenge: challenge, + Domain: domain, + ProofPurpose: PROOF_PURPOSE, + VerificationMethod: pkInfo.Id, + } + return proof, nil +} + +func GenRequestMsg(request *Request) ([]byte, error) { + sign := request.Proof.Hex + request.Proof.Hex = "" + msg, err := json.Marshal(request) + if err != nil { + return nil, fmt.Errorf("GenRequestMsg, json.Marshal error: %s", err) + } + request.Proof.Hex = sign + return msg, nil +} + +func GenCredentialMsg(credentials *VerifiableCredential) ([]byte, error) { + sign := credentials.Proof.Hex + credentials.Proof.Hex = "" + msg, err := json.Marshal(credentials) + if err != nil { + return nil, fmt.Errorf("GenCredentialsMsg, json.Marshal error: %s", err) + } + credentials.Proof.Hex = sign + return msg, nil +} + +func GenPresentationMsg(presentation *VerifiablePresentation) ([]byte, error) { + var signs []string + for i := range presentation.Proof { + signs = append(signs, presentation.Proof[i].Hex) + presentation.Proof[i].Hex = "" + } + msg, err := json.Marshal(presentation) + if err != nil { + return nil, fmt.Errorf("GenPresentationMsg, json.Marshal error: %s", err) + } + for i := range presentation.Proof { + presentation.Proof[i].Hex = signs[i] + } + return msg, nil +} diff --git a/cred_jwt.go b/cred_jwt.go new file mode 100644 index 0000000..f2bbdb4 --- /dev/null +++ b/cred_jwt.go @@ -0,0 +1,549 @@ +/* + * 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 ( + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/ontio/ontology/core/signature" + "strings" + "time" + + "github.com/ontio/ontology-crypto/keypair" + "github.com/ontio/ontology/common" + uuid "github.com/satori/go.uuid" +) + +const ( + HEADER_TYPE = "JWT" +) + +var JWTSignType = map[string]string{ + "EcdsaSecp224r1VerificationKey2019": "ES224", + "EcdsaSecp256r1VerificationKey2019": "ES256", + "EcdsaSecp384r1VerificationKey2019": "ES384", + "EcdsaSecp521r1VerificationKey2019": "ES512", + "EcdsaSecp256k1VerificationKey2019": "ES256K", + "Ed25519VerificationKey2018": "EdDSA", + "SM2VerificationKey2019": "SM", +} + +var JsonSignType = map[string]string{ + "ES224": "EcdsaSecp224r1VerificationKey2019", + "ES256": "EcdsaSecp256r1VerificationKey2019", + "ES384": "EcdsaSecp384r1VerificationKey2019", + "ES512": "EcdsaSecp521r1VerificationKey2019", + "ES256K": "EcdsaSecp256k1VerificationKey2019", + "EdDSA": "Ed25519VerificationKey2018", + "SM": "SM2VerificationKey2019", +} + +type VerifiableCredentialJWT struct { + Header Header + Payload Payload + Signature []byte +} + +type Header struct { + Alg string `json:"alg,omitempty"` + Kid string `json:"kid,omitempty"` + Typ string `json:"typ,omitempty"` +} + +type VC struct { + Context []string `json:"@context,omitempty"` + Type []string `json:"type,omitempty"` + Issuer interface{} `json:"issuer,omitempty"` + CredentialSubject interface{} `json:"credentialSubject,omitempty"` + CredentialStatus *CredentialStatus `json:"credentialStatus,omitempty"` + Proof *Proof `json:"proof,omitempty"` +} + +type VP struct { + Context []string `json:"@context,omitempty"` + Type []string `json:"type,omitempty"` + VerifiableCredential []string `json:"verifiableCredential,omitempty"` + Holder interface{} `json:"holder,omitempty"` + Proof *Proof `json:"proof,omitempty"` +} + +type Payload struct { + Iss string `json:"iss,omitempty"` + Sub string `json:"sub,omitempty"` + Aud interface{} `json:"aud,omitempty"` + Exp int64 `json:"exp,omitempty"` + Nbf int64 `json:"nbf,omitempty"` + Iat int64 `json:"iat,omitempty"` + Jti string `json:"jti,omitempty"` + Nonce string `json:"nonce,omitempty"` + VC *VC `json:"vc,omitempty"` + VP *VP `json:"vp,omitempty"` +} + +type JWTCredential struct { + Header *Header `json:"header"` + Payload *Payload `json:"payload"` + Jws string `json:"jws"` +} + +func (this *Credential) CreateJWTCredential(contexts []string, types []string, credentialSubject interface{}, issuerId interface{}, + expirationDateTimestamp int64, challenge string, domain interface{}, signer *Account) (string, error) { + is, ontId, err := getOntId(issuerId) + if err != nil { + return "", fmt.Errorf("CreateJWTCredential, getOntId error: %s", err) + } + // get public key id + _, pkInfo, err := this.GetPublicKeyId(ontId, hex.EncodeToString(keypair.SerializePublicKey(signer.GetPublicKey()))) + if err != nil { + return "", fmt.Errorf("CreateJWTCredential, this.GetPublicKeyId error: %s", err) + } + header, err := makeJWTHeader(pkInfo.Type, pkInfo.Id) + if err != nil { + return "", fmt.Errorf("CreateJWTCredential, makeJWTHeader error: %s", err) + } + + cs, sub, err := getOntId(credentialSubject) + if err != nil { + return "", fmt.Errorf("CreateJWTCredential, getOntId error: %s", err) + } + + now := time.Now().UTC().Unix() + proof, err := this.createProof(ontId, signer, challenge, domain, now) + if err != nil { + return "", fmt.Errorf("CreateJWTCredential, this.CreateProof error: %s", err) + } + proof.VerificationMethod = "" + proof.Type = "" + vc := &VC{ + Context: append(DefaultContext, contexts...), + Type: append(DefaultCredentialType, types...), + Issuer: is, + CredentialSubject: cs, + CredentialStatus: &CredentialStatus{ + Id: this.credRecordContractAddress.ToHexString(), + Type: CREDENTIAL_STATUS_TYPE, + }, + Proof: proof, + } + payload := &Payload{ + Sub: sub, + Jti: UUID_PREFIX + uuid.NewV4().String(), + Iss: ontId, + Nbf: now, + Iat: now, + VC: vc, + } + if expirationDateTimestamp != 0 { + payload.Exp = expirationDateTimestamp + } + + credential := &JWTCredential{ + Header: header, + Payload: payload, + } + signData, err := credential.SignData() + if err != nil { + return "", fmt.Errorf("CreateJWTCredential, credential.SignData error: %s", err) + } + sign, err := signer.Sign(signData) + if err != nil { + return "", fmt.Errorf("CreateJWTCredential, signer.Sign error: %s", err) + } + credential.Jws = base64.StdEncoding.EncodeToString(sign) + return credential.ToString() +} + +func (this *Credential) VerifyJWTCredibleOntId(credibleOntIds []string, credential string) error { + JWTCredential, err := DeserializeJWT(credential) + if err != nil { + return fmt.Errorf("VerifyJWTCredibleOntId, DeserializeJWT error: %s", err) + } + + for _, v := range credibleOntIds { + if JWTCredential.Payload.Iss == v { + return nil + } + } + return fmt.Errorf("VerifyJWTCredibleOntId failed") +} + +func (this *Credential) VerifyJWTDate(credential string) error { + JWTCredential, err := DeserializeJWT(credential) + if err != nil { + return fmt.Errorf("VerifyJWTDate, DeserializeJWT error: %s", err) + } + + now := time.Now().UTC() + if JWTCredential.Payload.Exp != 0 { + if now.Unix() > JWTCredential.Payload.Exp { + return fmt.Errorf("VerifyJWTDate expirationDate failed") + } + } + + if JWTCredential.Payload.Nbf != 0 { + if now.Unix() < JWTCredential.Payload.Nbf { + return fmt.Errorf("VerifyJWTDate issuanceDate nbf failed") + } + } + if JWTCredential.Payload.Iat != 0 { + if now.Unix() < JWTCredential.Payload.Iat { + return fmt.Errorf("VerifyJWTDate issuanceDate iat failed") + } + } + return nil +} + +func (this *Credential) VerifyJWTIssuerSignature(credential string) error { + JWTCredential, err := DeserializeJWT(credential) + if err != nil { + return fmt.Errorf("VerifyJWTIssuerSignature, DeserializeJWT error: %s", err) + } + + msg, err := JWTCredential.SignData() + if err != nil { + return fmt.Errorf("VerifyJWTIssuerSignature, JWTCredential.SignData error: %s", err) + } + err = this.verifyJWSProof(JWTCredential.Payload.Iss, JWTCredential.Header.Kid, msg, JWTCredential.Jws) + if err != nil { + return fmt.Errorf("VerifyJWTIssuerSignature, this.VerifyJWSProof error: %s", err) + } + return nil +} + +func (this *Credential) verifyJWSProof(ontId, kid string, msg []byte, jws string) error { + sig, err := base64.StdEncoding.DecodeString(jws) + if err != nil { + return fmt.Errorf("VerifyJWSProof, base64.StdEncoding.DecodeString jws error: %s", err) + } + + publicKeyHex, err := this.GetPublicKey(ontId, kid) + if err != nil { + return fmt.Errorf("VerifyJWSProof, this.GetPublicKey error: %s", err) + } + data, err := hex.DecodeString(publicKeyHex) + if err != nil { + return fmt.Errorf("VerifyJWSProof, hex.DecodeString public key error: %s", err) + } + pk, err := keypair.DeserializePublicKey(data) + if err != nil { + return fmt.Errorf("VerifyJWSProof, keypair.DeserializePublicKey error: %s", err) + } + + return signature.Verify(pk, msg, sig) +} + +func (this *Credential) VerifyJWTStatus(credential string) error { + JWTCredential, err := DeserializeJWT(credential) + if err != nil { + return fmt.Errorf("VerifyJWTStatus, DeserializeJWT error: %s", err) + } + + if JWTCredential.Payload.VC.CredentialStatus.Type != CREDENTIAL_STATUS_TYPE { + return fmt.Errorf("VerifyJWTStatus, credential status %s not match", JWTCredential.Payload.VC.CredentialStatus.Type) + } + contractAddress, err := common.AddressFromHexString(JWTCredential.Payload.VC.CredentialStatus.Id) + if err != nil { + return fmt.Errorf("VerifyJWTStatus, common.AddressFromHexString error: %s", err) + } + status, err := this.getCredentialStatus(contractAddress, JWTCredential.Payload.Jti) + if err != nil { + return fmt.Errorf("VerifyJWTStatus, this.GetCredentialStatus error: %s", err) + } + if status != 1 { + return fmt.Errorf("VerifyJWTStatus failed") + } + return nil +} + +func (this *Credential) RevokeJWTCredentialByHolder(gasPrice, gasLimit uint64, credential string, holder string, + signer, payer *Account) (common.Uint256, error) { + JWTCredential, err := DeserializeJWT(credential) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RevokeJWTCredentialByHolder, DeserializeJWT error: %s", err) + } + if JWTCredential.Payload.VC.CredentialStatus.Type != CREDENTIAL_STATUS_TYPE { + return common.UINT256_EMPTY, fmt.Errorf("RevokeJWTCredentialByHolder, credential status %s not match", JWTCredential.Payload.VC.CredentialStatus.Type) + } + contractAddress, err := common.AddressFromHexString(JWTCredential.Payload.VC.CredentialStatus.Id) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RevokeJWTCredentialByHolder, common.AddressFromHexString error: %s", err) + } + + index, _, err := this.GetPublicKeyId(holder, hex.EncodeToString(keypair.SerializePublicKey(signer.GetPublicKey()))) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RevokeJWTCredentialByHolder, this.GetPublicKeyId error: %s", err) + } + + return this.revokeCredential(contractAddress, gasPrice, gasLimit, JWTCredential.Payload.Jti, holder, index, signer, payer) +} + +func (this *Credential) RemoveJWTCredential(gasPrice, gasLimit uint64, credential string, holder string, + signer, payer *Account) (common.Uint256, error) { + JWTCredential, err := DeserializeJWT(credential) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RemoveJWTCredential, DeserializeJWT error: %s", err) + } + + index, _, err := this.GetPublicKeyId(holder, hex.EncodeToString(keypair.SerializePublicKey(signer.GetPublicKey()))) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RemoveJWTCredential, this.GetPublicKeyId error: %s", err) + } + if JWTCredential.Payload.VC.CredentialStatus.Type != CREDENTIAL_STATUS_TYPE { + return common.UINT256_EMPTY, fmt.Errorf("RemoveJWTCredential, credential status %s not match", JWTCredential.Payload.VC.CredentialStatus.Type) + } + contractAddress, err := common.AddressFromHexString(JWTCredential.Payload.VC.CredentialStatus.Id) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RemoveJWTCredential, common.AddressFromHexString error: %s", err) + } + params := []interface{}{"Remove", []interface{}{JWTCredential.Payload.Jti, holder, index}} + txHash, err := this.ontSdk.NeoVM.InvokeNeoVMContract(gasPrice, gasLimit, payer, signer, contractAddress, params) + if err != nil { + return common.UINT256_EMPTY, fmt.Errorf("RemoveJWTCredential, this.ontSdk.NeoVM.InvokeNeoVMContract error: %s", err) + } + return txHash, nil +} + +func (this *Credential) CreateJWTPresentation(credentials []string, contexts, types []string, holder interface{}, + nonce string, aud interface{}, signer *Account) (string, error) { + hd, ontId, err := getOntId(holder) + if err != nil { + return "", fmt.Errorf("CreateJWTPresentation, getOntId error: %s", err) + } + // get public key id + _, pkInfo, err := this.GetPublicKeyId(ontId, hex.EncodeToString(keypair.SerializePublicKey(signer.GetPublicKey()))) + if err != nil { + return "", fmt.Errorf("CreateJWTPresentation, this.GetPublicKeyId error: %s", err) + } + header, err := makeJWTHeader(pkInfo.Type, pkInfo.Id) + if err != nil { + return "", fmt.Errorf("CreateJWTPresentation, makeJWTHeader error: %s", err) + } + + now := time.Now().UTC().Unix() + var domain interface{} + proof, err := this.createProof(ontId, signer, "", domain, now) + if err != nil { + return "", fmt.Errorf("CreateJWTPresentation, this.CreateProof error: %s", err) + } + proof.VerificationMethod = "" + proof.Type = "" + // check credentials + for _, v := range credentials { + _, err := DeserializeJWT(v) + if err != nil { + return "", fmt.Errorf("CreateJWTPresentation, DeserializeJWT error: %s", err) + } + } + vp := &VP{ + Context: append(DefaultContext, contexts...), + Type: append(DefaultCredentialType, types...), + VerifiableCredential: credentials, + Holder: hd, + Proof: proof, + } + payload := &Payload{ + Aud: aud, + Nonce: nonce, + Jti: UUID_PREFIX + uuid.NewV4().String(), + Iss: ontId, + VP: vp, + } + + presentation := &JWTCredential{ + Header: header, + Payload: payload, + } + signData, err := presentation.SignData() + if err != nil { + return "", fmt.Errorf("CreateJWTPresentation, presentation.SignData error: %s", err) + } + sign, err := signer.Sign(signData) + if err != nil { + return "", fmt.Errorf("CreateJWTPresentation, signer.Sign error: %s", err) + } + presentation.Jws = base64.StdEncoding.EncodeToString(sign) + return presentation.ToString() +} + +func (this *Credential) JWTCred2Json(jwtCred string) (*VerifiableCredential, error) { + JWTCredential, err := DeserializeJWT(jwtCred) + if err != nil { + return nil, fmt.Errorf("JWTCred2Json, DeserializeJWT error: %s", err) + } + + if JWTCredential.Payload.VC == nil { + return nil, fmt.Errorf("JWTCred2Json, JWTCredential is not a credential error: %s", err) + } + + credential := new(VerifiableCredential) + credential.Id = JWTCredential.Payload.Jti + credential.Context = JWTCredential.Payload.VC.Context + credential.Type = JWTCredential.Payload.VC.Type + + issuer, err := genIdObject(JWTCredential.Payload.Iss, JWTCredential.Payload.VC.Issuer) + if err != nil { + return nil, fmt.Errorf("JWTCred2Json, genIdObject error: %s", err) + } + credential.Issuer = issuer + + t := JWTCredential.Payload.Iat + if JWTCredential.Payload.Iat == 0 { + t = JWTCredential.Payload.Nbf + } + issuanceDate := time.Unix(t, 0).UTC().Format("2006-01-02T15:04:05Z") + credential.IssuanceDate = issuanceDate + + expirationDate := time.Unix(JWTCredential.Payload.Exp, 0).UTC().Format("2006-01-02T15:04:05Z") + credential.ExpirationDate = expirationDate + credential.CredentialSubject = JWTCredential.Payload.VC.CredentialSubject + credential.CredentialStatus = JWTCredential.Payload.VC.CredentialStatus + + // create proof + credential.Proof = JWTCredential.Payload.VC.Proof + credential.Proof.VerificationMethod = JWTCredential.Header.Kid + credential.Proof.Type = JsonSignType[JWTCredential.Header.Alg] + credential.Proof.Challenge = JWTCredential.Payload.Nonce + credential.Proof.Domain = JWTCredential.Payload.Aud + credential.Proof.Jws = JWTCredential.Jws + + return credential, nil +} + +func (this *Credential) JWTPresentation2Json(jwtPresentation string) (*VerifiablePresentation, error) { + JWTPresentation, err := DeserializeJWT(jwtPresentation) + if err != nil { + return nil, fmt.Errorf("JWTPresentation2Json, JWTPresentation.Deserialization error: %s", err) + } + + if JWTPresentation.Payload.VP == nil { + return nil, fmt.Errorf("JWTPresentation2Json, JWTPresentation is not a presentation error: %s", err) + } + + presentation := new(VerifiablePresentation) + presentation.Id = JWTPresentation.Payload.Jti + presentation.Context = JWTPresentation.Payload.VP.Context + presentation.Type = JWTPresentation.Payload.VP.Type + + holder, err := genIdObject(JWTPresentation.Payload.Iss, JWTPresentation.Payload.VP.Holder) + if err != nil { + return nil, fmt.Errorf("JWTCred2Json, genIdObject error: %s", err) + } + presentation.Holder = holder + + for _, v := range JWTPresentation.Payload.VP.VerifiableCredential { + cred, err := this.JWTCred2Json(v) + if err != nil { + return nil, fmt.Errorf("JWTPresentation2Json, this.JWTCred2Json error: %s", err) + } + presentation.VerifiableCredential = append(presentation.VerifiableCredential, cred) + } + + // create proof + proof := new(Proof) + proof = JWTPresentation.Payload.VP.Proof + proof.VerificationMethod = JWTPresentation.Header.Kid + proof.Type = JsonSignType[JWTPresentation.Header.Alg] + proof.Challenge = JWTPresentation.Payload.Nonce + proof.Domain = JWTPresentation.Payload.Aud + proof.Jws = JWTPresentation.Jws + + presentation.Proof = append(presentation.Proof, proof) + return presentation, nil +} + +func (cred *JWTCredential) ToString() (string, error) { + headerb, err := json.Marshal(cred.Header) + if err != nil { + return "", fmt.Errorf("SignData, json.Marshal header error: %s", err) + } + headerString := base64.StdEncoding.EncodeToString(headerb) + + payloadb, err := json.Marshal(cred.Payload) + if err != nil { + return "", fmt.Errorf("SignData, json.Marshal payload error: %s", err) + } + payloadString := base64.StdEncoding.EncodeToString(payloadb) + + return headerString + "." + payloadString + "." + cred.Jws, nil +} + +func (cred *JWTCredential) SignData() ([]byte, error) { + headerb, err := json.Marshal(cred.Header) + if err != nil { + return nil, fmt.Errorf("SignData, json.Marshal header error: %s", err) + } + headerString := base64.StdEncoding.EncodeToString(headerb) + + payloadb, err := json.Marshal(cred.Payload) + if err != nil { + return nil, fmt.Errorf("SignData, json.Marshal payload error: %s", err) + } + payloadString := base64.StdEncoding.EncodeToString(payloadb) + + signData := headerString + "." + payloadString + return []byte(signData), nil +} + +func DeserializeJWT(jwt string) (*JWTCredential, error) { + slice := strings.Split(jwt, ".") + if len(slice) != 3 { + return nil, fmt.Errorf("JWTCredential Deserialization, length of elem is not 3") + } + headerb, err := base64.StdEncoding.DecodeString(slice[0]) + if err != nil { + return nil, fmt.Errorf("JWTCredential Deserialization, base64.StdEncoding.DecodeString header error: %s", err) + } + header := new(Header) + err = json.Unmarshal(headerb, header) + if err != nil { + return nil, fmt.Errorf("JWTCredential Deserialization, json.Unmarshal header error: %s", err) + } + + payload := new(Payload) + payloadb, err := base64.StdEncoding.DecodeString(slice[1]) + if err != nil { + return nil, fmt.Errorf("JWTCredential Deserialization, base64.StdEncoding.DecodeString payload error: %s", err) + } + err = json.Unmarshal(payloadb, payload) + if err != nil { + return nil, fmt.Errorf("JWTCredential Deserialization, json.Unmarshal payload error: %s", err) + } + + jws := slice[2] + r := &JWTCredential{ + Header: header, + Payload: payload, + Jws: jws, + } + return r, nil +} + +func makeJWTHeader(proofType, verificationMethod string) (*Header, error) { + if proofType == "" || verificationMethod == "" { + return nil, fmt.Errorf("makeJWTHeader, proof is illegal") + } + header := &Header{ + Alg: JWTSignType[proofType], + Typ: HEADER_TYPE, + Kid: verificationMethod, + } + return header, nil +} diff --git a/cred_jwt_test.go b/cred_jwt_test.go new file mode 100644 index 0000000..7f65a62 --- /dev/null +++ b/cred_jwt_test.go @@ -0,0 +1,243 @@ +/* + * 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 ( + "encoding/json" + "fmt" + "github.com/ontio/ontology/common" + "testing" + "time" +) + +func TestJWTCredential(t *testing.T) { + Init() + + testOntSdk.SetCredContractAddress("52df370680de17bc5d4262c446f102a0ee0d6312") + issuer, err := testWallet.NewDefaultSettingIdentity(testPasswd) + if err != nil { + t.Errorf("TestJWTCredential NewDefaultSettingIdentity error:%s", err) + return + } + holder, err := testWallet.NewDefaultSettingIdentity(testPasswd) + if err != nil { + t.Errorf("TestJWTCredential NewDefaultSettingIdentity error:%s", err) + return + } + + _, err = testOntSdk.Native.OntId.RegIDWithPublicKey(testGasPrice, testGasLimit, testDefAcc, issuer.ID, testDefAcc) + if err != nil { + t.Errorf("TestJWTCredential RegIDWithPublicKey error:%s", err) + return + } + _, err = testOntSdk.Native.OntId.RegIDWithPublicKey(testGasPrice, testGasLimit, testDefAcc, holder.ID, testDefAcc) + if err != nil { + t.Errorf("TestJWTCredential RegIDWithPublicKey error:%s", err) + return + } + testOntSdk.WaitForGenerateBlock(30 * time.Second) + + credentialSubject := RelationshipCredential( + []*Relationship{{"did:example:ebfeb1f712ebc6f1c276e12ec21", "Jayden Doe", "did:example:c276e12ec21ebfeb1f712ebc6f1"}, + {"did:example:c276e12ec21ebfeb1f712ebc6f1", "Morgan Doe", "did:example:ebfeb1f712ebc6f1c276e12ec21"}}, + ) + //var credentialSubject2 interface{} + request, err := testOntSdk.Credential.GenSignReq(credentialSubject, holder.ID, testDefAcc) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.GenSignReq error:%s", err) + return + } + + err = testOntSdk.Credential.VerifySignReq(request) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.VerifySignReq error:%s", err) + return + } + + contexts := []string{"context1", "context2"} + types := []string{"RelationshipCredential"} + expirationDate := time.Now().UTC().Unix() + 86400 + s, err := testOntSdk.Credential.CreateJWTCredential(contexts, types, credentialSubject, issuer.ID, expirationDate, + "", "", testDefAcc) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.CreateCredential error:%s", err) + return + } + fmt.Println("JWTcredential is: ", s) + cred, err := DeserializeJWT(s) + if err != nil { + t.Errorf("TestJWTCredential DeserializeJWT error:%s", err) + return + } + + contractAddress, err := common.AddressFromHexString(cred.Payload.VC.CredentialStatus.Id) + if err != nil { + t.Errorf("TestJWTCredential common.AddressFromHexString:%s", err) + return + } + txHash, err := testOntSdk.Credential.CommitCredential(contractAddress, 2500, 20000, cred.Payload.Jti, + issuer.ID, holder.ID, testDefAcc, testDefAcc) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.CommitCredential error:%s", err) + return + } + fmt.Println("txHash 1 is: ", txHash.ToHexString()) + testOntSdk.WaitForGenerateBlock(30 * time.Second) + + err = testOntSdk.Credential.VerifyJWTCredibleOntId([]string{issuer.ID}, s) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.VerifyCredibleOntId error:%s", err) + return + } + err = testOntSdk.Credential.VerifyJWTDate(s) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.VerifyDate error:%s", err) + return + } + err = testOntSdk.Credential.VerifyJWTIssuerSignature(s) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.VerifyIssuerSignature credential error:%s", err) + return + } + err = testOntSdk.Credential.VerifyJWTStatus(s) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.VerifyStatus error:%s", err) + return + } + + ps, err := testOntSdk.Credential.CreateJWTPresentation([]string{s}, contexts, types, holder.ID, + "", "", testDefAcc) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.CreatePresentation error:%s", err) + return + } + fmt.Println("JWTPresentation is: ", ps) + + err = testOntSdk.Credential.VerifyJWTIssuerSignature(ps) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.VerifyIssuerSignature presentation error:%s", err) + return + } + + credential, err := testOntSdk.Credential.JWTCred2Json(s) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.JWTCred2Json error:%s", err) + return + } + credentialJson, err := json.Marshal(credential) + if err != nil { + t.Errorf("TestJWTCredential json.Marshal credential error:%s", err) + return + } + fmt.Println("credential is: ", string(credentialJson)) + + presentation, err := testOntSdk.Credential.JWTPresentation2Json(ps) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.JWTPresentation2Json error:%s", err) + return + } + presentationJson, err := json.Marshal(presentation) + if err != nil { + t.Errorf("TestJWTCredential json.Marshal presentation error:%s", err) + return + } + fmt.Println("presentation is: ", string(presentationJson)) + + s2, err := testOntSdk.Credential.JsonCred2JWT(credential) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.JsonCred2JWT error:%s", err) + return + } + fmt.Println("JWTcredential2 is: ", s2) + + ps2, err := testOntSdk.Credential.JsonPresentation2JWT(presentation, presentation.Proof[0]) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.JsonCred2JWT error:%s", err) + return + } + fmt.Println("JWTPresentation2 is: ", ps2) + + credential2, err := testOntSdk.Credential.JWTCred2Json(s2) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.JWTCred2Json error:%s", err) + return + } + credentialJson2, err := json.Marshal(credential2) + if err != nil { + t.Errorf("TestJWTCredential json.Marshal credential error:%s", err) + return + } + fmt.Println("credential2 is: ", string(credentialJson2)) + + presentation2, err := testOntSdk.Credential.JWTPresentation2Json(ps2) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.JWTPresentation2Json error:%s", err) + return + } + presentationJson2, err := json.Marshal(presentation2) + if err != nil { + t.Errorf("TestJWTCredential json.Marshal presentation error:%s", err) + return + } + fmt.Println("presentation2 is: ", string(presentationJson2)) + + txHash, err = testOntSdk.Credential.RevokeJWTCredentialByHolder(2500, 20000, s, holder.ID, testDefAcc, testDefAcc) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.RevokeCredentialByHolder error:%s", err) + return + } + fmt.Println("txHash 2 is: ", txHash.ToHexString()) + testOntSdk.WaitForGenerateBlock(30 * time.Second) + + txHash, err = testOntSdk.Credential.RemoveJWTCredential(2500, 20000, s, holder.ID, testDefAcc, testDefAcc) + if err != nil { + t.Errorf("TestJWTCredential testOntSdk.Credential.RevokeCredentialByHolder error:%s", err) + return + } + fmt.Println("txHash 3 is: ", txHash.ToHexString()) +} + +//func TestVerifyStringJWT(t *testing.T) { +// Init() +// +// credibleOntIds := []string{"did:ont:AJ4C9aTYxTGUhEpaZdPjFSqCqzMCqJDRUd", +// "did:ont:AVe4zVZzteo6HoLpdBwpKNtDXLjJBzB9fv"} +// jwtCred := "eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDpvbnQ6QUo0QzlhVFl4VEdVaEVwYVpkUGpGU3FDcXpNQ3FKRFJVZCNrZXlzLTIiLCJ0eXAiOiJKV1QifQ==.eyJpc3MiOiJkaWQ6b250OkFKNEM5YVRZeFRHVWhFcGFaZFBqRlNxQ3F6TUNxSkRSVWQiLCJzdWIiOiJkaWQ6b250OjExMTExMSIsImV4cCI6MTU5MzUwODg0NywibmJmIjoxNTkzNDIyNDQ3LCJpYXQiOjE1OTM0MjI0NDcsImp0aSI6InVybjp1dWlkOjBjNjM5NzIwLWRjNGEtNGE1YS1hZTJmLTI4ZDgzNDg2ZDc4ZCIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly9vbnRpZC5vbnQuaW8vY3JlZGVudGlhbHMvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlJlbGF0aW9uc2hpcENyZWRlbnRpYWwiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsibmFtZSI6IkJvYiIsInNwb3VzZSI6IkFsaWNlIn0sImNyZWRlbnRpYWxTdGF0dXMiOnsiaWQiOiI1MmRmMzcwNjgwZGUxN2JjNWQ0MjYyYzQ0NmYxMDJhMGVlMGQ2MzEyIiwidHlwZSI6IkF0dGVzdENvbnRyYWN0In0sInByb29mIjp7ImNyZWF0ZWQiOiIyMDIwLTA2LTI5VDE3OjIwOjQ3WiIsInByb29mUHVycG9zZSI6ImFzc2VydGlvbk1ldGhvZCJ9fX0=.AZJzqbjmW8g5GNLn9QTgFGA86d4DIPd8A/rkoj5M+VNMsF76VteiHLy/j1srO8rORX36Xzp6ajIZ6NIMmBRH6M8=" +// err := testOntSdk.Credential.VerifyJWTCredibleOntId(credibleOntIds, jwtCred) +// if err != nil { +// t.Fatal(err) +// } +// err = testOntSdk.Credential.VerifyJWTDate(jwtCred) +// if err != nil { +// t.Fatal(err) +// } +// err = testOntSdk.Credential.VerifyJWTIssuerSignature(jwtCred) +// if err != nil { +// t.Fatal(err) +// } +// err = testOntSdk.Credential.VerifyJWTStatus(jwtCred) +// if err != nil { +// t.Fatal(err) +// } +// jwtPresentation := "eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDpvbnQ6QVZlNHpWWnp0ZW82SG9McGRCd3BLTnREWExqSkJ6QjlmdiNrZXlzLTIiLCJ0eXAiOiJKV1QifQ==.eyJpc3MiOiJkaWQ6b250OkFWZTR6Vlp6dGVvNkhvTHBkQndwS050RFhMakpCekI5ZnYiLCJhdWQiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbSJdLCJqdGkiOiJ1cm46dXVpZDo1ODU3ZDdiMS04ZDM5LTRmMzktOTYwNi0xNGRjNjIwZjExYzciLCJub25jZSI6ImQxYjIzZDMuLi4zZDIzZDMyZDIiLCJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vb250aWQub250LmlvL2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiIsIkNyZWRlbnRpYWxNYW5hZ2VyUHJlc2VudGF0aW9uIl0sInZlcmlmaWFibGVDcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJbVJwWkRwdmJuUTZRVW8wUXpsaFZGbDRWRWRWYUVWd1lWcGtVR3BHVTNGRGNYcE5RM0ZLUkZKVlpDTnJaWGx6TFRJaUxDSjBlWEFpT2lKS1YxUWlmUT09LmV5SnBjM01pT2lKa2FXUTZiMjUwT2tGS05FTTVZVlJaZUZSSFZXaEZjR0ZhWkZCcVJsTnhRM0Y2VFVOeFNrUlNWV1FpTENKemRXSWlPaUprYVdRNmIyNTBPakV4TVRFeE1TSXNJbVY0Y0NJNk1UVTVNelV3T0RnME55d2libUptSWpveE5Ua3pOREl5TkRRM0xDSnBZWFFpT2pFMU9UTTBNakkwTkRjc0ltcDBhU0k2SW5WeWJqcDFkV2xrT2pCak5qTTVOekl3TFdSak5HRXROR0UxWVMxaFpUSm1MVEk0WkRnek5EZzJaRGM0WkNJc0luWmpJanA3SWtCamIyNTBaWGgwSWpwYkltaDBkSEJ6T2k4dmQzZDNMbmN6TG05eVp5OHlNREU0TDJOeVpXUmxiblJwWVd4ekwzWXhJaXdpYUhSMGNITTZMeTl2Ym5ScFpDNXZiblF1YVc4dlkzSmxaR1Z1ZEdsaGJITXZkakVpWFN3aWRIbHdaU0k2V3lKV1pYSnBabWxoWW14bFEzSmxaR1Z1ZEdsaGJDSXNJbEpsYkdGMGFXOXVjMmhwY0VOeVpXUmxiblJwWVd3aVhTd2lZM0psWkdWdWRHbGhiRk4xWW1wbFkzUWlPbnNpYm1GdFpTSTZJa0p2WWlJc0luTndiM1Z6WlNJNklrRnNhV05sSW4wc0ltTnlaV1JsYm5ScFlXeFRkR0YwZFhNaU9uc2lhV1FpT2lJMU1tUm1NemN3Tmpnd1pHVXhOMkpqTldRME1qWXlZelEwTm1ZeE1ESmhNR1ZsTUdRMk16RXlJaXdpZEhsd1pTSTZJa0YwZEdWemRFTnZiblJ5WVdOMEluMHNJbkJ5YjI5bUlqcDdJbU55WldGMFpXUWlPaUl5TURJd0xUQTJMVEk1VkRFM09qSXdPalEzV2lJc0luQnliMjltVUhWeWNHOXpaU0k2SW1GemMyVnlkR2x2YmsxbGRHaHZaQ0o5ZlgwPS5BWkp6cWJqbVc4ZzVHTkxuOVFUZ0ZHQTg2ZDRESVBkOEEvcmtvajVNK1ZOTXNGNzZWdGVpSEx5L2oxc3JPOHJPUlgzNlh6cDZhaklaNk5JTW1CUkg2TTg9IiwiZXlKaGJHY2lPaUpGVXpJMU5pSXNJbXRwWkNJNkltUnBaRHB2Ym5RNlFVbzBRemxoVkZsNFZFZFZhRVZ3WVZwa1VHcEdVM0ZEY1hwTlEzRktSRkpWWkNOclpYbHpMVElpTENKMGVYQWlPaUpLVjFRaWZRPT0uZXlKcGMzTWlPaUprYVdRNmIyNTBPa0ZLTkVNNVlWUlplRlJIVldoRmNHRmFaRkJxUmxOeFEzRjZUVU54U2tSU1ZXUWlMQ0psZUhBaU9qRTFPVE0xTURnNE5EY3NJbTVpWmlJNk1UVTVNelF5TWpRMk5pd2lhV0YwSWpveE5Ua3pOREl5TkRZMkxDSnFkR2tpT2lKMWNtNDZkWFZwWkRvelpXRXhaamd4TWkweU1tUTVMVFE0WTJVdFlXSTNNeTFqTURWbE5qWTJObUpoTURNaUxDSjJZeUk2ZXlKQVkyOXVkR1Y0ZENJNld5Sm9kSFJ3Y3pvdkwzZDNkeTUzTXk1dmNtY3ZNakF4T0M5amNtVmtaVzUwYVdGc2N5OTJNU0lzSW1oMGRIQnpPaTh2YjI1MGFXUXViMjUwTG1sdkwyTnlaV1JsYm5ScFlXeHpMM1l4SWwwc0luUjVjR1VpT2xzaVZtVnlhV1pwWVdKc1pVTnlaV1JsYm5ScFlXd2lMQ0pTWld4aGRHbHZibk5vYVhCRGNtVmtaVzUwYVdGc0lsMHNJbWx6YzNWbGNpSTZleUp1WVcxbElqb2lhWE56ZFdWeUluMHNJbU55WldSbGJuUnBZV3hUZFdKcVpXTjBJanBiZXlKcFpDSTZJbVJwWkRwdmJuUTZNVEV4TVRFeElpd2libUZ0WlNJNkltaGxJaXdpYzNCdmRYTmxJam9pYzJobEluMWRMQ0pqY21Wa1pXNTBhV0ZzVTNSaGRIVnpJanA3SW1sa0lqb2lOVEprWmpNM01EWTRNR1JsTVRkaVl6VmtOREkyTW1NME5EWm1NVEF5WVRCbFpUQmtOak14TWlJc0luUjVjR1VpT2lKQmRIUmxjM1JEYjI1MGNtRmpkQ0o5TENKd2NtOXZaaUk2ZXlKamNtVmhkR1ZrSWpvaU1qQXlNQzB3TmkweU9WUXhOem95TVRvd05sb2lMQ0p3Y205dlpsQjFjbkJ2YzJVaU9pSmhjM05sY25ScGIyNU5aWFJvYjJRaWZYMTkuQVoyK1dWS3cxU2VUaG44d3loQlZpaVpZSmdramM4clc5SkI1WThrSHRNL1hrRmV6blRrcTUxd3poM1lpTWovYm80TjRNRDNpRkVwc1lHZ0VPMlVsRzc0PSJdLCJwcm9vZiI6eyJjcmVhdGVkIjoiMjAyMC0wNi0yOVQxNzoyMToyNVoiLCJwcm9vZlB1cnBvc2UiOiJhc3NlcnRpb25NZXRob2QifX19.AXtE9ZnANmyoy72CBhImXl2/SDIohriYdjavOyn9uv8h8OBkSyBo/l898N3vxhlSxlXHnLfyRzJ8aGGICtWiKC4=" +// err = testOntSdk.Credential.VerifyJWTIssuerSignature(jwtPresentation) +// if err != nil { +// t.Fatal(err) +// } +//} diff --git a/cred_test.go b/cred_test.go new file mode 100644 index 0000000..73d5afb --- /dev/null +++ b/cred_test.go @@ -0,0 +1,205 @@ +/* + * 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 ( + "encoding/json" + "fmt" + "github.com/ontio/ontology/common" + "testing" + "time" +) + +type RelationshipCredential []*Relationship + +type Relationship struct { + Id string `json:"id"` + Name string `json:"name"` + Spouse string `json:"spouse"` +} + +func TestCredential(t *testing.T) { + Init() + + testOntSdk.SetCredContractAddress("52df370680de17bc5d4262c446f102a0ee0d6312") + issuer, err := testWallet.NewDefaultSettingIdentity(testPasswd) + if err != nil { + t.Errorf("TestCredential NewDefaultSettingIdentity error:%s", err) + return + } + holder, err := testWallet.NewDefaultSettingIdentity(testPasswd) + if err != nil { + t.Errorf("TestCredential NewDefaultSettingIdentity error:%s", err) + return + } + + _, err = testOntSdk.Native.OntId.RegIDWithPublicKey(testGasPrice, testGasLimit, testDefAcc, issuer.ID, testDefAcc) + if err != nil { + t.Errorf("TestCredential RegIDWithPublicKey error:%s", err) + return + } + _, err = testOntSdk.Native.OntId.RegIDWithPublicKey(testGasPrice, testGasLimit, testDefAcc, holder.ID, testDefAcc) + if err != nil { + t.Errorf("TestCredential RegIDWithPublicKey error:%s", err) + return + } + testOntSdk.WaitForGenerateBlock(30 * time.Second) + + credentialSubject := RelationshipCredential( + []*Relationship{{"did:example:ebfeb1f712ebc6f1c276e12ec21", "Jayden Doe", "did:example:c276e12ec21ebfeb1f712ebc6f1"}, + {"did:example:c276e12ec21ebfeb1f712ebc6f1", "Morgan Doe", "did:example:ebfeb1f712ebc6f1c276e12ec21"}}, + ) + //var credentialSubject2 interface{} + request, err := testOntSdk.Credential.GenSignReq(credentialSubject, holder.ID, testDefAcc) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.GenSignReq error:%s", err) + return + } + + err = testOntSdk.Credential.VerifySignReq(request) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.VerifySignReq error:%s", err) + return + } + + contexts := []string{"context1", "context2"} + types := []string{"RelationshipCredential"} + expirationDate := time.Now().UTC().Unix() + 86400 + credential, err := testOntSdk.Credential.CreateCredential(contexts, types, credentialSubject, issuer.ID, expirationDate, + "", "", testDefAcc) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.CreateCredential error:%s", err) + return + } + credentialJson, err := json.Marshal(credential) + if err != nil { + t.Errorf("TestCredential json.Marshal credential error:%s", err) + return + } + fmt.Println("credential is: ", string(credentialJson)) + + contractAddress, err := common.AddressFromHexString(credential.CredentialStatus.Id) + if err != nil { + t.Errorf("TestCredential common.AddressFromHexString:%s", err) + return + } + txHash, err := testOntSdk.Credential.CommitCredential(contractAddress, 2500, 20000, credential.Id, issuer.ID, holder.ID, testDefAcc, testDefAcc) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.CommitCredential error:%s", err) + return + } + fmt.Println("txHash 1 is: ", txHash.ToHexString()) + testOntSdk.WaitForGenerateBlock(30 * time.Second) + + err = testOntSdk.Credential.VerifyCredibleOntId([]string{issuer.ID}, credential) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.VerifyCredibleOntId error:%s", err) + return + } + err = testOntSdk.Credential.VerifyDate(credential) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.VerifyDate error:%s", err) + return + } + err = testOntSdk.Credential.VerifyIssuerSignature(credential) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.VerifyIssuerSignature error:%s", err) + return + } + err = testOntSdk.Credential.VerifyStatus(credential) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.VerifyStatus error:%s", err) + return + } + + presentation, err := testOntSdk.Credential.CreatePresentation([]*VerifiableCredential{credential}, contexts, types, holder.ID, + []string{issuer.ID}, []string{""}, []interface{}{""}, []*Account{testDefAcc}) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.CreatePresentation error:%s", err) + return + } + presentationJson, err := json.Marshal(presentation) + if err != nil { + t.Errorf("TestCredential json.Marshal presentation error:%s", err) + return + } + fmt.Println("presentation is: ", string(presentationJson)) + + for i := range presentation.Proof { + _, err = testOntSdk.Credential.VerifyPresentationProof(presentation, i) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.VerifyPresentationProof error:%s", err) + return + } + } + + txHash, err = testOntSdk.Credential.RevokeCredentialByHolder(2500, 20000, credential, holder.ID, testDefAcc, testDefAcc) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.RevokeCredentialByHolder error:%s", err) + return + } + fmt.Println("txHash 2 is: ", txHash.ToHexString()) + testOntSdk.WaitForGenerateBlock(30 * time.Second) + + txHash, err = testOntSdk.Credential.RemoveCredential(2500, 20000, credential, holder.ID, testDefAcc, testDefAcc) + if err != nil { + t.Errorf("TestCredential testOntSdk.Credential.RevokeCredentialByHolder error:%s", err) + return + } + fmt.Println("txHash 3 is: ", txHash.ToHexString()) +} + +//func TestVerifyJSONCred(t *testing.T) { +// Init() +// +// credibleOntIds := []string{"did:ont:AJ4C9aTYxTGUhEpaZdPjFSqCqzMCqJDRUd", +// "did:ont:AVe4zVZzteo6HoLpdBwpKNtDXLjJBzB9fv"} +// credString := "{\"@context\":[\"https://www.w3.org/2018/credentials/v1\",\"https://ontid.ont.io/credentials/v1\"],\"id\":\"urn:uuid:5375555d-9ea2-4a4b-8318-11c28c9bde48\",\"type\":[\"VerifiableCredential\",\"RelationshipCredential\"],\"issuer\":\"did:ont:AJ4C9aTYxTGUhEpaZdPjFSqCqzMCqJDRUd\",\"issuanceDate\":\"2020-06-29T10:11:36Z\",\"expirationDate\":\"2020-06-30T10:11:36Z\",\"credentialSubject\":[{\"id\":\"did:ont:111111\",\"name\":\"Bob\",\"spouse\":\"Alice\"}],\"credentialStatus\":{\"id\":\"52df370680de17bc5d4262c446f102a0ee0d6312\",\"type\":\"AttestContract\"},\"proof\":{\"type\":\"EcdsaSecp256r1VerificationKey2019\",\"created\":\"2020-06-29T10:11:36Z\",\"proofPurpose\":\"assertionMethod\",\"verificationMethod\":\"did:ont:AJ4C9aTYxTGUhEpaZdPjFSqCqzMCqJDRUd#keys-2\",\"hex\":\"01d899e082ad8644cc01ade2f2eeb9b66443cc518c5d56e5a8ef7a562592a189ef19e2a883eb2d6dd4c4817e8b1841663e56b7cb66aba5dfd2f4f777faeda40bf7\"}}" +// credential := &VerifiableCredential{} +// if err := json.Unmarshal([]byte(credString), credential); err != nil { +// t.Fatal(err) +// } +// err := testOntSdk.Credential.VerifyCredibleOntId(credibleOntIds, credential) +// if err != nil { +// t.Fatal(err) +// } +// err = testOntSdk.Credential.VerifyDate(credential) +// if err != nil { +// t.Fatal(err) +// } +// err = testOntSdk.Credential.VerifyIssuerSignature(credential) +// if err != nil { +// t.Fatal(err) +// } +// err = testOntSdk.Credential.VerifyStatus(credential) +// if err != nil { +// t.Fatal(err) +// } +// presentationString := "{\"@context\":[\"https://www.w3.org/2018/credentials/v1\",\"https://ontid.ont.io/credentials/v1\"],\"id\":\"urn:uuid:7d0f6ffd-28d5-4954-a92f-38ca6062c746\",\"type\":[\"VerifiablePresentation\",\"CredentialManagerPresentation\"],\"verifiableCredential\":[{\"@context\":[\"https://www.w3.org/2018/credentials/v1\",\"https://ontid.ont.io/credentials/v1\"],\"id\":\"urn:uuid:5375555d-9ea2-4a4b-8318-11c28c9bde48\",\"type\":[\"VerifiableCredential\",\"RelationshipCredential\"],\"issuer\":\"did:ont:AJ4C9aTYxTGUhEpaZdPjFSqCqzMCqJDRUd\",\"issuanceDate\":\"2020-06-29T10:11:36Z\",\"expirationDate\":\"2020-06-30T10:11:36Z\",\"credentialSubject\":[{\"id\":\"did:ont:111111\",\"name\":\"Bob\",\"spouse\":\"Alice\"}],\"credentialStatus\":{\"id\":\"52df370680de17bc5d4262c446f102a0ee0d6312\",\"type\":\"AttestContract\"},\"proof\":{\"type\":\"EcdsaSecp256r1VerificationKey2019\",\"created\":\"2020-06-29T10:11:36Z\",\"proofPurpose\":\"assertionMethod\",\"verificationMethod\":\"did:ont:AJ4C9aTYxTGUhEpaZdPjFSqCqzMCqJDRUd#keys-2\",\"hex\":\"01d899e082ad8644cc01ade2f2eeb9b66443cc518c5d56e5a8ef7a562592a189ef19e2a883eb2d6dd4c4817e8b1841663e56b7cb66aba5dfd2f4f777faeda40bf7\"}},{\"@context\":[\"https://www.w3.org/2018/credentials/v1\",\"https://ontid.ont.io/credentials/v1\"],\"id\":\"urn:uuid:3076ea1d-ef58-425e-8849-82f218ab906e\",\"type\":[\"VerifiableCredential\",\"RelationshipCredential\"],\"issuer\":{\"id\":\"did:ont:AJ4C9aTYxTGUhEpaZdPjFSqCqzMCqJDRUd\",\"name\":\"issuer\"},\"issuanceDate\":\"2020-06-29T10:11:55Z\",\"expirationDate\":\"2020-06-30T10:11:36Z\",\"credentialSubject\":[{\"id\":\"did:ont:111111\",\"name\":\"he\",\"spouse\":\"she\"}],\"credentialStatus\":{\"id\":\"52df370680de17bc5d4262c446f102a0ee0d6312\",\"type\":\"AttestContract\"},\"proof\":{\"type\":\"EcdsaSecp256r1VerificationKey2019\",\"created\":\"2020-06-29T10:11:55Z\",\"proofPurpose\":\"assertionMethod\",\"verificationMethod\":\"did:ont:AJ4C9aTYxTGUhEpaZdPjFSqCqzMCqJDRUd#keys-2\",\"hex\":\"018691f221e88ed5aae4eca1f991fb82c3ea1fe04d637ee03bc42b9118ae4511a66f992ddac6c678f5f472ddb4052929747267a64286f11155318e2b53e8f86af1\"}}],\"holder\":\"did:ont:AVe4zVZzteo6HoLpdBwpKNtDXLjJBzB9fv\",\"proof\":[{\"type\":\"EcdsaSecp256r1VerificationKey2019\",\"created\":\"2020-06-29T10:12:14Z\",\"challenge\":\"d1b23d3...3d23d32d2\",\"domain\":[\"https://example.com\"],\"proofPurpose\":\"assertionMethod\",\"verificationMethod\":\"did:ont:AVe4zVZzteo6HoLpdBwpKNtDXLjJBzB9fv#keys-2\",\"hex\":\"01673ba58f0a4d03120713c8b81865dcf52be29d516d7f4420e9d490191d97f843475f9b06d2f45b7d9cb7c80edfa95e82ea5272e781dcda335cbe4837d5a836cd\"}]}" +// presentation := &VerifiablePresentation{} +// if err := json.Unmarshal([]byte(presentationString), presentation); err != nil { +// t.Fatal(err) +// } +// for i := range presentation.Proof { +// _, err = testOntSdk.Credential.VerifyPresentationProof(presentation, i) +// if err != nil { +// t.Fatal(err) +// } +// } +//} diff --git a/examples/wasm_example.go b/examples/wasm_example.go index 6a40e6c..b907532 100644 --- a/examples/wasm_example.go +++ b/examples/wasm_example.go @@ -31,7 +31,7 @@ import ( func main() { fmt.Println("==========================start============================") //testUrl := "http://127.0.0.1:20336" - testUrl := "http://polaris1.ont.io:20336" + testUrl := "http://polaris2.ont.io:20336" //initialize ontsdk ontSdk := sdk.NewOntologySdk() //suppose you already start up a local wasm ontology node @@ -71,7 +71,7 @@ func main() { codeHash := common.ToHexString(code) //=========================================== - gasprice := uint64(500) + gasprice := uint64(2500) invokegaslimit := uint64(200000) deploygaslimit := uint64(200000000) // deploy the wasm contract diff --git a/go.mod b/go.mod index 8fd02cd..068272e 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/ontio/go-bip32 v0.0.0-20190520025953-d3cea6894a2b github.com/ontio/ontology v1.8.2 github.com/ontio/ontology-crypto v1.0.7 + github.com/satori/go.uuid v1.2.0 github.com/stretchr/testify v1.4.0 github.com/tyler-smith/go-bip39 v1.0.2 golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 diff --git a/go.sum b/go.sum index a86c8e3..600e50c 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/oep4/oep4_test.go b/oep4/oep4_test.go index 8b25b65..be56634 100644 --- a/oep4/oep4_test.go +++ b/oep4/oep4_test.go @@ -37,7 +37,7 @@ func TestOep4(t *testing.T) { t.Fatal(err) } ontSdk := ontology_go_sdk.NewOntologySdk() - ontSdk.NewRpcClient().SetAddress("http://polaris1.ont.io:20336") + ontSdk.NewRpcClient().SetAddress("http://polaris2.ont.io:20336") oep4 := NewOep4(contractAddr, ontSdk) name, err := oep4.Name() if err != nil { @@ -86,7 +86,7 @@ func TestOep4(t *testing.T) { t.Fatal(err) } amount := big.NewInt(1000) - gasPrice := uint64(500) + gasPrice := uint64(2500) gasLimit := uint64(500000) transferTx, err := oep4.Transfer(acc, multiSignAddr, amount, nil, gasPrice, gasLimit) if err != nil { @@ -157,7 +157,7 @@ func TestOep4_FetchTxTransferEvent(t *testing.T) { addr, _ := common.AddressParseFromBytes(bs) fmt.Println(addr.ToBase58()) ontSdk := ontology_go_sdk.NewOntologySdk() - ontSdk.NewRpcClient().SetAddress("http://polaris1.ont.io:20336") + ontSdk.NewRpcClient().SetAddress("http://polaris2.ont.io:20336") oep4 := NewOep4(contractAddr, ontSdk) res, _ := oep4.FetchTxTransferEvent("8074fabad95400c6705478593f2b2fce865aa356c166e63214d8a9af036ee739") fmt.Println(res) diff --git a/ont_sdk.go b/ont_sdk.go index 14f714e..34cd297 100644 --- a/ont_sdk.go +++ b/ont_sdk.go @@ -48,9 +48,10 @@ func init() { //OntologySdk is the main struct for user type OntologySdk struct { client.ClientMgr - Native *NativeContract - NeoVM *NeoVMContract - WasmVM *WasmVMContract + Native *NativeContract + NeoVM *NeoVMContract + WasmVM *WasmVMContract + Credential *Credential } //NewOntologySdk return OntologySdk. @@ -62,6 +63,8 @@ func NewOntologySdk() *OntologySdk { ontSdk.NeoVM = neoVM wasmVM := newWasmVMContract(ontSdk) ontSdk.WasmVM = wasmVM + credential := newCredential(ontSdk) + ontSdk.Credential = credential return ontSdk } @@ -78,6 +81,16 @@ func (this *OntologySdk) OpenWallet(walletFile string) (*Wallet, error) { return OpenWallet(walletFile) } +//OpenWallet return a wallet instance +func (this *OntologySdk) SetCredContractAddress(addr string) error { + address, err := common.AddressFromHexString(addr) + if err != nil { + return fmt.Errorf("SetCredContractAddress error: %s", err) + } + this.Credential.credRecordContractAddress = address + return nil +} + func ParseNativeTxPayload(raw []byte) (map[string]interface{}, error) { tx, err := types.TransactionFromRawBytes(raw) if err != nil { diff --git a/ont_sdk_test.go b/ont_sdk_test.go index ecac085..4136088 100644 --- a/ont_sdk_test.go +++ b/ont_sdk_test.go @@ -42,9 +42,9 @@ var ( testWallet *Wallet testPasswd = []byte("123456") testDefAcc *Account - testGasPrice = uint64(500) + testGasPrice = uint64(2500) testGasLimit = uint64(20000) - testNetUrl = "http://polaris1.ont.io:20336" + testNetUrl = "http://polaris2.ont.io:20336" ) func init() { @@ -79,9 +79,9 @@ func TestParseNativeTxPayload(t *testing.T) { for i := 0; i < 1; i++ { transfers = append(transfers, state) } - _, err = testOntSdk.Native.Ont.NewMultiTransferTransaction(500, 20000, transfers) + _, err = testOntSdk.Native.Ont.NewMultiTransferTransaction(2500, 20000, transfers) assert.Nil(t, err) - _, err = testOntSdk.Native.Ont.NewTransferFromTransaction(500, 20000, acc.Address, acc.Address, acc.Address, 20) + _, err = testOntSdk.Native.Ont.NewTransferFromTransaction(2500, 20000, acc.Address, acc.Address, acc.Address, 20) assert.Nil(t, err) } @@ -237,7 +237,7 @@ func TestOntologySdk_ParseNativeTxPayload2(t *testing.T) { acc3, err := NewAccountFromPrivateKey(pri3, signature.SHA256withECDSA) amount := 1000000000 - txFrom, err := testOntSdk.Native.Ont.NewTransferFromTransaction(500, 20000, acc.Address, acc2.Address, acc3.Address, uint64(amount)) + txFrom, err := testOntSdk.Native.Ont.NewTransferFromTransaction(2500, 20000, acc.Address, acc2.Address, acc3.Address, uint64(amount)) assert.Nil(t, err) tx, err := txFrom.IntoImmutable() assert.Nil(t, err) @@ -276,7 +276,7 @@ func TestOntologySdk_ParseNativeTxPayload(t *testing.T) { assert.Nil(t, err) amount := uint64(1000000000) - tx, err := testOntSdk.Native.Ont.NewTransferTransaction(500, 20000, acc.Address, acc2.Address, amount) + tx, err := testOntSdk.Native.Ont.NewTransferTransaction(2500, 20000, acc.Address, acc2.Address, amount) assert.Nil(t, err) tx2, err := tx.IntoImmutable() @@ -290,7 +290,7 @@ func TestOntologySdk_ParseNativeTxPayload(t *testing.T) { assert.Equal(t, amount, states[0].Value) assert.Equal(t, "transfer", res["functionName"].(string)) - transferFrom, err := testOntSdk.Native.Ont.NewTransferFromTransaction(500, 20000, acc.Address, acc2.Address, acc3.Address, 10) + transferFrom, err := testOntSdk.Native.Ont.NewTransferFromTransaction(2500, 20000, acc.Address, acc2.Address, acc3.Address, 10) transferFrom2, err := transferFrom.IntoImmutable() r, err := ParseNativeTxPayload(transferFrom2.ToArray()) assert.Nil(t, err) @@ -300,7 +300,7 @@ func TestOntologySdk_ParseNativeTxPayload(t *testing.T) { assert.Equal(t, acc2.Address.ToBase58(), rp.From) assert.Equal(t, uint64(10), rp.Value) - ongTransfer, err := testOntSdk.Native.Ong.NewTransferTransaction(uint64(500), uint64(20000), acc.Address, acc2.Address, 100000000) + ongTransfer, err := testOntSdk.Native.Ong.NewTransferTransaction(uint64(2500), uint64(20000), acc.Address, acc2.Address, 100000000) assert.Nil(t, err) ongTx, err := ongTransfer.IntoImmutable() assert.Nil(t, err) @@ -388,7 +388,7 @@ func TestOntologySdk_GetTxData(t *testing.T) { testOntSdk = NewOntologySdk() testWallet, _ = testOntSdk.OpenWallet("./wallet.dat") acc, _ := testWallet.GetAccountByAddress("AXdmdzbyf3WZKQzRtrNQwAR91ZxMUfhXkt", testPasswd) - tx, _ := testOntSdk.Native.Ont.NewTransferTransaction(500, 10000, acc.Address, acc.Address, 100) + tx, _ := testOntSdk.Native.Ont.NewTransferTransaction(2500, 10000, acc.Address, acc.Address, 100) testOntSdk.SignToTransaction(tx, acc) tx2, _ := tx.IntoImmutable() sink := common.NewZeroCopySink(nil) @@ -477,7 +477,7 @@ func TestOng_WithDrawONG(t *testing.T) { return } fmt.Printf("Address:%s UnboundONG:%d\n", testDefAcc.Address.ToBase58(), unboundONG) - _, err = testOntSdk.Native.Ong.WithdrawONG(500, 20000, nil, testDefAcc, unboundONG) + _, err = testOntSdk.Native.Ong.WithdrawONG(2500, 20000, nil, testDefAcc, unboundONG) if err != nil { t.Errorf("WithDrawONG error:%s", err) return