github.com/iotexproject/iotex-core@v1.14.1-rc1/ioctl/cmd/did/diddoc.go (about) 1 // Copyright (c) 2020 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package did 7 8 import ( 9 "crypto/sha256" 10 "encoding/json" 11 12 "github.com/btcsuite/btcutil/base58" 13 "github.com/ethereum/go-ethereum/common" 14 "github.com/ethereum/go-ethereum/crypto" 15 "github.com/pkg/errors" 16 ) 17 18 const ( 19 // DIDPrefix is the prefix string 20 DIDPrefix = "did:io:" 21 // DIDAuthType is the authentication type 22 DIDAuthType = "EcdsaSecp256k1VerificationKey2019" 23 // DIDOwner is the suffix string 24 DIDOwner = "#owner" 25 26 // KnownDIDContext known context for did 27 KnownDIDContext = "https://www.w3.org/ns/did/v1" 28 // Secp256k1DIDContext secp256k1 context for did 29 Secp256k1DIDContext = "https://w3id.org/security/suites/secp256k1-2019/v1" 30 ) 31 32 type ( 33 verificationMethod struct { 34 ID string `json:"id"` 35 Type string `json:"type"` 36 Controller string `json:"controller"` 37 PublicKeyBase58 string `json:"publicKeyBase58,omitempty"` 38 } 39 40 verificationMethodSet interface{} 41 42 serviceStruct struct { 43 ID string `json:"id,omitempty"` 44 Type string `json:"type,omitempty"` 45 ServiceEndpoint string `json:"serviceEndpoint,omitempty"` 46 } 47 48 // Doc is the DID document struct 49 Doc struct { 50 Context interface{} `json:"@context,omitempty"` 51 ID string `json:"id,omitempty"` 52 Controller string `json:"controller,omitempty"` 53 VerificationMethod []verificationMethod `json:"verificationMethod,omitempty"` 54 Authentication []verificationMethodSet `json:"authentication,omitempty"` 55 AssertionMethod []verificationMethodSet `json:"assertionMethod,omitempty"` 56 Service []serviceStruct `json:"service,omitempty"` 57 } 58 ) 59 60 // Owner did document owner 61 func (doc *Doc) Owner() common.Address { 62 return common.HexToAddress(doc.ID[7:]) 63 } 64 65 // Bytes did document bytes 66 func (doc *Doc) Bytes() ([]byte, error) { 67 return json.MarshalIndent(doc, "", " ") 68 } 69 70 // JSON did document json 71 func (doc *Doc) JSON() (string, error) { 72 data, err := doc.Bytes() 73 if err != nil { 74 return "", err 75 } 76 return string(data), nil 77 } 78 79 // Hash did document hash 80 func (doc *Doc) Hash() ([32]byte, error) { 81 data, err := doc.Bytes() 82 if err != nil { 83 return [32]byte{}, err 84 } 85 return sha256.Sum256(data), nil 86 } 87 88 // AddService add service to did document 89 func (doc *Doc) AddService(tag, serviceType, endpoint string) { 90 id := doc.ID + "#" + tag 91 if doc.Service == nil { 92 doc.Service = []serviceStruct{{ 93 ID: id, 94 Type: serviceType, 95 ServiceEndpoint: endpoint, 96 }} 97 return 98 } 99 for _, service := range doc.Service { 100 if service.ID == id { 101 service.Type = serviceType 102 service.ServiceEndpoint = endpoint 103 return 104 } 105 } 106 doc.Service = append(doc.Service, serviceStruct{ 107 ID: id, 108 Type: serviceType, 109 ServiceEndpoint: endpoint, 110 }) 111 } 112 113 // RemoveService remove service from did document 114 func (doc *Doc) RemoveService(tag string) error { 115 id := doc.ID + "#" + tag 116 if doc.Service == nil || len(doc.Service) == 0 { 117 return errors.New("service not exists") 118 } 119 services := make([]serviceStruct, len(doc.Service)-1) 120 count := 0 121 for i, service := range doc.Service { 122 if service.ID != id { 123 if count == len(services) { 124 return errors.New("service not exists") 125 } 126 services[count] = doc.Service[i] 127 count++ 128 } 129 } 130 doc.Service = services 131 return nil 132 } 133 134 // NewDIDDoc new did document by public key 135 func NewDIDDoc(publicKey []byte) (*Doc, error) { 136 pubKey, err := crypto.UnmarshalPubkey(publicKey) 137 if err != nil { 138 return nil, err 139 } 140 compressedPubKey := crypto.CompressPubkey(pubKey) 141 address := crypto.PubkeyToAddress(*pubKey) 142 if err != nil { 143 return nil, err 144 } 145 146 doc := &Doc{ 147 Context: []string{ 148 KnownDIDContext, 149 Secp256k1DIDContext, 150 }, 151 } 152 doc.ID = DIDPrefix + address.Hex() 153 key0 := doc.ID + "#key-0" 154 doc.VerificationMethod = []verificationMethod{{ 155 ID: key0, 156 Type: DIDAuthType, 157 Controller: doc.ID, 158 PublicKeyBase58: base58.Encode(compressedPubKey), 159 }} 160 doc.Authentication = []verificationMethodSet{key0} 161 doc.AssertionMethod = []verificationMethodSet{key0} 162 return doc, nil 163 }