github.com/iotexproject/iotex-core@v1.14.1-rc1/ioctl/cmd/did/common.go (about) 1 // Copyright (c) 2022 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 "bytes" 10 "crypto/ecdsa" 11 "encoding/json" 12 "fmt" 13 "io" 14 "math/big" 15 "net/http" 16 17 "github.com/ethereum/go-ethereum/common/hexutil" 18 "github.com/ethereum/go-ethereum/crypto" 19 "github.com/iotexproject/iotex-core/ioctl/cmd/account" 20 "github.com/iotexproject/iotex-core/ioctl/cmd/action" 21 "github.com/iotexproject/iotex-core/ioctl/output" 22 "github.com/iotexproject/iotex-core/ioctl/util" 23 "github.com/iotexproject/iotex-core/pkg/util/addrutil" 24 "golang.org/x/crypto/sha3" 25 ) 26 27 type ( 28 // Permit permit content for DID 29 Permit struct { 30 Separator string `json:"DOMAIN_SEPARATOR"` 31 PermitHash string `json:"permitHash"` 32 } 33 34 // Signature signature for typed message 35 Signature struct { 36 R string `json:"r"` 37 S string `json:"s"` 38 V uint64 `json:"v"` 39 } 40 41 // CreateRequest create DID request 42 CreateRequest struct { 43 Signature 44 PublicKey string `json:"publicKey"` 45 } 46 47 // ServiceAddRequest add service to DID request 48 ServiceAddRequest struct { 49 Signature 50 Tag string `json:"tag"` 51 Type string `json:"type"` 52 ServiceEndpoint string `json:"serviceEndpoint"` 53 } 54 55 // ServiceRemoveRequest remove service from DID request 56 ServiceRemoveRequest struct { 57 Signature 58 Tag string `json:"tag"` 59 } 60 ) 61 62 // getPermit fetch DID permit from resolver 63 func getPermit(endpoint, address string) (*Permit, error) { 64 resp, err := http.Get(endpoint + "/did/" + address + "/permit") 65 if err != nil { 66 return nil, err 67 } 68 defer resp.Body.Close() 69 70 var data Permit 71 err = json.NewDecoder(resp.Body).Decode(&data) 72 if err != nil { 73 return nil, err 74 } 75 return &data, nil 76 } 77 78 // signType sign typed message 79 func signType(key *ecdsa.PrivateKey, separator, hash string) (*Signature, error) { 80 separatorBytes, err := hexutil.Decode(separator) 81 if err != nil { 82 return nil, err 83 } 84 hashBytes, err := hexutil.Decode(hash) 85 if err != nil { 86 return nil, err 87 } 88 89 data := append([]byte{0x19, 0x01}, append(separatorBytes, hashBytes...)...) 90 sha := sha3.NewLegacyKeccak256() 91 sha.Write(data) 92 signHash := sha.Sum(nil) 93 94 sig, err := crypto.Sign(signHash, key) 95 if err != nil { 96 return nil, err 97 } 98 v := new(big.Int).SetBytes([]byte{sig[64] + 27}) 99 100 return &Signature{ 101 R: hexutil.Encode(sig[:32]), 102 S: hexutil.Encode(sig[32:64]), 103 V: v.Uint64(), 104 }, nil 105 } 106 107 // loadPrivateKey load private key and address from signer 108 func loadPrivateKey() (*ecdsa.PrivateKey, string, error) { 109 signer, err := action.Signer() 110 if err != nil { 111 return nil, "", output.NewError(output.InputError, "failed to get signer addr", err) 112 } 113 fmt.Printf("Enter password #%s:\n", signer) 114 password, err := util.ReadSecretFromStdin() 115 if err != nil { 116 return nil, "", output.NewError(output.InputError, "failed to get password", err) 117 } 118 pri, err := account.PrivateKeyFromSigner(signer, password) 119 if err != nil { 120 return nil, "", output.NewError(output.InputError, "failed to decrypt key", err) 121 } 122 ethAddress, err := addrutil.IoAddrToEvmAddr(signer) 123 if err != nil { 124 return nil, "", output.NewError(output.AddressError, "", err) 125 } 126 127 return pri.EcdsaPrivateKey().(*ecdsa.PrivateKey), ethAddress.String(), nil 128 } 129 130 // loadPublicKey load public key by private key 131 func loadPublicKey(key *ecdsa.PrivateKey) ([]byte, error) { 132 publicKey := key.Public() 133 publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) 134 if !ok { 135 return nil, output.NewError(output.ConvertError, "generate public key error", nil) 136 } 137 return crypto.FromECDSAPub(publicKeyECDSA), nil 138 } 139 140 // signPermit fetch permit and sign and return signature, publicKey and address 141 func signPermit(endpoint string) (*Signature, []byte, string, error) { 142 key, addr, err := loadPrivateKey() 143 if err != nil { 144 return nil, nil, "", err 145 } 146 147 publicKey, err := loadPublicKey(key) 148 if err != nil { 149 return nil, nil, "", err 150 } 151 152 permit, err := getPermit(endpoint, addr) 153 if err != nil { 154 return nil, nil, "", output.NewError(output.InputError, "failed to fetch permit", err) 155 } 156 signature, err := signType(key, permit.Separator, permit.PermitHash) 157 if err != nil { 158 return nil, nil, "", output.NewError(output.InputError, "failed to sign typed permit", err) 159 } 160 return signature, publicKey, addr, nil 161 } 162 163 // postToResolver post data to resolver 164 func postToResolver(url string, reqBytes []byte) error { 165 req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBytes)) 166 if err != nil { 167 return output.NewError(output.ConvertError, "failed to create request", err) 168 } 169 req.Header.Set("Content-Type", "application/json") 170 171 client := &http.Client{} 172 resp, err := client.Do(req) 173 if err != nil { 174 return output.NewError(output.NetworkError, "failed to post request", err) 175 } 176 defer resp.Body.Close() 177 178 body, err := io.ReadAll(resp.Body) 179 if err != nil { 180 return output.NewError(output.ConvertError, "failed to read response", err) 181 } 182 output.PrintResult(string(body)) 183 return nil 184 }