github.com/theQRL/go-zond@v0.2.1/signer/core/signed_data.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "mime" 25 26 "github.com/theQRL/go-zond/accounts" 27 "github.com/theQRL/go-zond/common" 28 "github.com/theQRL/go-zond/common/hexutil" 29 "github.com/theQRL/go-zond/crypto" 30 "github.com/theQRL/go-zond/signer/core/apitypes" 31 ) 32 33 // sign receives a request and produces a signature 34 func (api *SignerAPI) sign(req *SignDataRequest) (hexutil.Bytes, error) { 35 // We make the request prior to looking up if we actually have the account, to prevent 36 // account-enumeration via the API 37 res, err := api.UI.ApproveSignData(req) 38 if err != nil { 39 return nil, err 40 } 41 if !res.Approved { 42 return nil, ErrRequestDenied 43 } 44 // Look up the wallet containing the requested signer 45 account := accounts.Account{Address: req.Address.Address()} 46 wallet, err := api.am.Find(account) 47 if err != nil { 48 return nil, err 49 } 50 pw, err := api.lookupOrQueryPassword(account.Address, 51 "Password for signing", 52 fmt.Sprintf("Please enter password for signing data with account %s", account.Address.Hex())) 53 if err != nil { 54 return nil, err 55 } 56 // Sign the data with the wallet 57 signature, err := wallet.SignDataWithPassphrase(account, pw, req.ContentType, req.Rawdata) 58 if err != nil { 59 return nil, err 60 } 61 return signature, nil 62 } 63 64 // SignData signs the hash of the provided data, but does so differently 65 // depending on the content-type specified. 66 // 67 // Different types of validation occur. 68 func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) { 69 var req, err = api.determineSignatureFormat(ctx, contentType, addr, data) 70 if err != nil { 71 return nil, err 72 } 73 signature, err := api.sign(req) 74 if err != nil { 75 api.UI.ShowError(err.Error()) 76 return nil, err 77 } 78 return signature, nil 79 } 80 81 // determineSignatureFormat determines which signature method should be used based upon the mime type 82 // In the cases where it matters ensure that the charset is handled. The charset 83 // resides in the 'params' returned as the second returnvalue from mime.ParseMediaType 84 // charset, ok := params["charset"] 85 // As it is now, we accept any charset and just treat it as 'raw'. 86 // This method returns the mimetype for signing along with the request 87 func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (*SignDataRequest, error) { 88 var req *SignDataRequest 89 90 mediaType, _, err := mime.ParseMediaType(contentType) 91 if err != nil { 92 return nil, err 93 } 94 95 switch mediaType { 96 case apitypes.IntendedValidator.Mime: 97 // Data with an intended validator 98 validatorData, err := UnmarshalValidatorData(data) 99 if err != nil { 100 return nil, err 101 } 102 sighash, msg := SignTextValidator(validatorData) 103 messages := []*apitypes.NameValueType{ 104 { 105 Name: "This is a request to sign data intended for a particular validator (see EIP 191 version 0)", 106 Typ: "description", 107 Value: "", 108 }, 109 { 110 Name: "Intended validator address", 111 Typ: "address", 112 Value: validatorData.Address.String(), 113 }, 114 { 115 Name: "Application-specific data", 116 Typ: "hexdata", 117 Value: validatorData.Message, 118 }, 119 { 120 Name: "Full message for signing", 121 Typ: "hexdata", 122 Value: fmt.Sprintf("%#x", msg), 123 }, 124 } 125 req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash} 126 case apitypes.DataTyped.Mime: 127 // EIP-712 conformant typed data 128 var err error 129 req, err = typedDataRequest(data) 130 if err != nil { 131 return nil, err 132 } 133 default: // also case TextPlain.Mime: 134 // Calculates a Zond Dilithium signature for: 135 // hash = keccak256("\x19Zond Signed Message:\n${message length}${message}") 136 // We expect input to be a hex-encoded string 137 textData, err := fromHex(data) 138 if err != nil { 139 return nil, err 140 } 141 sighash, msg := accounts.TextAndHash(textData) 142 messages := []*apitypes.NameValueType{ 143 { 144 Name: "message", 145 Typ: accounts.MimetypeTextPlain, 146 Value: msg, 147 }, 148 } 149 req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash} 150 } 151 req.Address = addr 152 req.Meta = MetadataFromContext(ctx) 153 return req, nil 154 } 155 156 // SignTextValidator signs the given message which can be further recovered 157 // with the given validator. 158 // hash = keccak256("\x19\x00"${address}${data}). 159 func SignTextValidator(validatorData apitypes.ValidatorData) (hexutil.Bytes, string) { 160 msg := fmt.Sprintf("\x19\x00%s%s", string(validatorData.Address.Bytes()), string(validatorData.Message)) 161 return crypto.Keccak256([]byte(msg)), msg 162 } 163 164 // SignTypedData signs EIP-712 conformant typed data 165 // hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}") 166 // It returns 167 // - the signature, 168 // - and/or any error 169 func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData apitypes.TypedData) (hexutil.Bytes, error) { 170 signature, _, err := api.signTypedData(ctx, addr, typedData, nil) 171 return signature, err 172 } 173 174 // signTypedData is identical to the capitalized version, except that it also returns the hash (preimage) 175 // - the signature preimage (hash) 176 func (api *SignerAPI) signTypedData(ctx context.Context, addr common.MixedcaseAddress, 177 typedData apitypes.TypedData, validationMessages *apitypes.ValidationMessages) (hexutil.Bytes, hexutil.Bytes, error) { 178 req, err := typedDataRequest(typedData) 179 if err != nil { 180 return nil, nil, err 181 } 182 req.Address = addr 183 req.Meta = MetadataFromContext(ctx) 184 if validationMessages != nil { 185 req.Callinfo = validationMessages.Messages 186 } 187 signature, err := api.sign(req) 188 if err != nil { 189 api.UI.ShowError(err.Error()) 190 return nil, nil, err 191 } 192 return signature, req.Hash, nil 193 } 194 195 // fromHex tries to interpret the data as type string, and convert from 196 // hexadecimal to []byte 197 func fromHex(data any) ([]byte, error) { 198 if stringData, ok := data.(string); ok { 199 binary, err := hexutil.Decode(stringData) 200 return binary, err 201 } 202 return nil, fmt.Errorf("wrong type %T", data) 203 } 204 205 // typeDataRequest tries to convert the data into a SignDataRequest. 206 func typedDataRequest(data any) (*SignDataRequest, error) { 207 var typedData apitypes.TypedData 208 if td, ok := data.(apitypes.TypedData); ok { 209 typedData = td 210 } else { // Hex-encoded data 211 jsonData, err := fromHex(data) 212 if err != nil { 213 return nil, err 214 } 215 if err = json.Unmarshal(jsonData, &typedData); err != nil { 216 return nil, err 217 } 218 } 219 messages, err := typedData.Format() 220 if err != nil { 221 return nil, err 222 } 223 sighash, rawData, err := apitypes.TypedDataAndHash(typedData) 224 if err != nil { 225 return nil, err 226 } 227 return &SignDataRequest{ 228 ContentType: apitypes.DataTyped.Mime, 229 Rawdata: []byte(rawData), 230 Messages: messages, 231 Hash: sighash}, nil 232 } 233 234 // UnmarshalValidatorData converts the bytes input to typed data 235 func UnmarshalValidatorData(data interface{}) (apitypes.ValidatorData, error) { 236 raw, ok := data.(map[string]interface{}) 237 if !ok { 238 return apitypes.ValidatorData{}, errors.New("validator input is not a map[string]interface{}") 239 } 240 addrBytes, err := fromHex(raw["address"]) 241 if err != nil { 242 return apitypes.ValidatorData{}, fmt.Errorf("validator address error: %w", err) 243 } 244 if len(addrBytes) == 0 { 245 return apitypes.ValidatorData{}, errors.New("validator address is undefined") 246 } 247 messageBytes, err := fromHex(raw["message"]) 248 if err != nil { 249 return apitypes.ValidatorData{}, fmt.Errorf("message error: %w", err) 250 } 251 if len(messageBytes) == 0 { 252 return apitypes.ValidatorData{}, errors.New("message is undefined") 253 } 254 return apitypes.ValidatorData{ 255 Address: common.BytesToAddress(addrBytes), 256 Message: messageBytes, 257 }, nil 258 }