metamask / eth-sig-util Goto Github PK
View Code? Open in Web Editor NEWA collection of functions for signing and verifying data with Ethereum keys.
License: ISC License
A collection of functions for signing and verifying data with Ethereum keys.
License: ISC License
When I run npm install
, I got and error message:
npm ERR! Could not install from "node_modules/eth-sig-util/ethereumjs-abi@git+https:/github.com/ethereumjs/ethereumjs-abi.git" as it does not contain a package.json file.
In my package-lock.json
's eth-sig-util
section, I see:
"eth-sig-util": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
"requires": {
"ethereumjs-abi": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git",
"ethereumjs-util": "^5.1.1"
},
It works If I delete ethereumjs-abi@
before git+https
, but it shows up again if I run npm install
, not sure if it is a bug?
As a binary-heavy module with a lot of complicated parameters, this is a valuable module to add typescript to. In the past, we've failed to add a proper declaration file, so rather than take that half-measure, we should actually convert the module to TypeScript.
In case we add a bounty, we need well defined acceptance criteria. Those would be:
build
script which builds the typescript file into an output file.package.json
main
field should point to the built output.build
should be able to deterministically produce the same output.I was implementing signTypedData_v4
for our project, and I have used the tests in this repo to make sure our implementation was sound.
I then noticed that geth
provided their own https://github.com/ethereum/go-ethereum/blob/43c278cdf93d5469702fd1c2f570dbf3c1718ff0/signer/core/signed_data.go#L323 , so I plugged in the tests from this repo in geth
and noticed that the behavior was not consistent.
Upon further investigation, I noticed that currently the behavior of signTypedData_v4 is not according to
https://eips.ethereum.org/EIPS/eip-712 when it comes to encoding arrays.
The eip states:
The array values are encoded as the keccak256 hash of the
concatenated encodeData of their contents
The behavior instead was to encode array values as the keccak256 of the
concatenated keccak256 of the values.
This worked well for primary types, but not for struct, as encodeData
per spec is:
The encoding of a struct instance is enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ) , i.e. the concatenation of the
encoded member values in the order that they appear in the type.
Each encoded member value is exactly 32-byte long.
Instead, we are using basically hashStruct
instead of encodeData
Should match the functionality of parity_encryptMessage
:
https://github.com/paritytech/parity/wiki/JSONRPC-parity-module#parity_encryptmessage
We don't have much in the way of input validation for signTypedData
. This is dangerous because invalid inputs can result in non-standard encodings.
Some examples of this are:
uint
values are currently encoded as positive values. The negative is ignored.address
field can be used to store values far larger than an address.We should strictly validate input, so that users don't accidentally become reliant upon non-standard and non-portable encodings.
I found that message data with non-ASCII string signed by MetaMask can not pass the verification on-chain. Here is an example: it will pass the verification on-chain if title
is set to a ASCII string, i.e. English
, but failed in the following case.
const msgParams = JSON.stringify(
{
types:
{
EIP712Domain: [
{name: "name", type: "string"},
{name: "version", type: "string"},
{name: "chainId", type: "uint256"},
{name: "verifyingContract", type: "address"}
],
State: [
{name:"title",type:"string"}
]
},
//make sure to replace verifyingContract with address of deployed contract
primaryType: "State",
domain: {
name: "StringTest",
version: "1",
chainId: 1337,
verifyingContract: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
},
message: {
title: "にほんご"
}
})
Any suggestion?
There are two issues
node_modules/eth-sig-util/index.d.ts:16:5 - error TS2411: Property 'EIP712Domain' of type 'ITypedField[] | undefined' is not assignable to string index type 'ITypedField[]'.
16 EIP712Domain?: Array<ITypedField>
~~~~~~~~~~~~
I don't recall EIP712Domain
being optional in the Types in the spec.
Also
export interface ITypedValue {
[key: string]: string | number | ITypedValue | ITypedValue[]
}
should also allow for string[]
and number[]
I am trying to test an implementation of this
https://medium.com/metamask/scaling-web3-with-signtypeddata-91d6efc8b290
starting from your tests for eth-sig-util
at
https://github.com/MetaMask/eth-sig-util/blob/master/test/index.js
The problem is that you test sigUtil.recoverTypedSignatureLegacy
, but not sigUtil.recoverTypedSignature
.
If I try to test the former, signing the string with sigUtil.signTypedDataLegacy
, it fails during the execution of sigUtil.recoverTypedSignature
. If instead, I sign the string with sigUtil.signTypedData
, it fails during its execution.
It would be good if you add a test for sigUtil.recoverTypedSignature
and sigUtil.recoverTypedSignature
.
Anyway, which is the right way to sign a string with sigUtil
so that the address can be recovered using sigUtil.recoverTypedSignature
?
Thanks.
Our current decryption failure test is incorrect, and is being removed:
It tests if an absent variable can decrypt a payload, and so it fails for the wrong reason: An error is thrown because alice
does not exist, not because she cannot decrypt the variable.
Highlighted here:
#74 (comment)
Hello,
would you consider publishing an es5 version of the module as index.js
? Since ProviderEngine depends on this module, if any package in my node modules depends on ProviderEngine it will break a create-react-app's webpack build on it's Uglify step.
Also, please let me know any way I can help and I'd be happy to open a pull request :)
(ref of issue: https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#npm-run-build-fails-to-minify)
Currently I have a use-case for an off-chain application to verify ownership of an address (specifically a Discord Bot).
In this use-case, the bot gathers some parameters, sends the user to the URL of a webpage running a pure JS dapp which uses Metamask to sign a message. It then gives them a string to paste back into the bot, which the bot uses via the same JS code on the back-end to verify the signature.
When I do this, in the browser my code generates the message from 4 parameters, id, name, datestamp, and address.
When I pass those parameters to this code in browser, and sign, metamask pops up, everything looks good, signature works, and the verification code is able to verify the signature is valid just fine.
When I take the same signature, and same parameters and pass it to the exact same code running on NodeJS v12, the verification fails. The address it recovers is completely different.
I've attempted this with the Personal.sign, signTyped, and signTypedV3, all behave the same.
It is expected the behavior of the off-chain verification code would be the same regardless of which JS runtime it's executing in.
The following is code which reproduces the issue for me:
const Signer = {
EIP712DomainSchema: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "salt", type: "bytes32" },
],
DiscordAuthorizationSchema: [
{ name: "statement", type: "string" },
{ name: "id", type: "uint256" },
{ name: "name", type: "string" },
{ name: "date", type: "string" },
{ name: "address", type: "address" },
],
domainData: {
name: "Discord Address Authorization",
version: "1.0",
salt: "0xe2d857f4a3edcb9b78b4d503120433db1e3f6cdc2b7971ab739626c97e86a558"
},
buildStatementString: function (id, name, date, address) {
return (
"I Hereby verify that I am the owner of the Ethereum Address: " + address + "\n" +
"I authorize the use of this address by the Discord User ID: " + id + " Named: " + name + "\n" +
"Signed on Date: " + date
)
},
buildMessageData: function (id, name, date, address) {
return {
statement: this.buildStatementString(id, name, date, address),
id: id,
name: name,
date: date,
address: address
}
},
buildSigningPacket: function (id, name, date, address) {
return JSON.stringify({
types: {
EIP712Domain: this.EIP712DomainSchema,
DiscordAuthorization: this.DiscordAuthorizationSchema
},
domain: this.domainData,
primaryType: "DiscordAuthorization",
message: this.buildMessageData(id, name, date, address)
})
},
sign: function (provider, id, name, date, address, callback) {
provider.sendAsync({
method: "eth_signTypedData_v3",
params: [address, this.buildSigningPacket(id, name, date, address)],
from: address
}, callback)
},
verify: function (signature, id, name, date, address) {
var ethUtil = require('ethereumjs-util')
var sigUtil = require('eth-sig-util')
let recoveryParameters = { data: JSON.parse(this.buildSigningPacket(id, name, date, address)), sig: signature }
//console.log("RecoveryParameters:" + JSON.stringify(recoveryParameters))
let recovered = sigUtil.recoverTypedSignature(recoveryParameters)
let expectedAddress = ethUtil.toChecksumAddress(address)
let recoveredAddress = ethUtil.toChecksumAddress(recovered)
console.log("Expected Address:" + expectedAddress)
console.log("Recovered Address:" + recoveredAddress)
return expectedAddress === recoveredAddress
}
}
I am using the following versions:
NodeJS 12.16.1
eth-sig-util 2.5.3
Any help or recommendations would be greatly appreciated!
Thanks!
This library is using ethereumjs-abi
, which does not support the newer ABI_V2 spec and has been deprecated as a result: ethereumjs/ethereumjs-abi#86
ABI_V2 changed the way bytes
and string
params are encoded and I know from personal experience that ethereumjs-abi
does not decode newer messages properly. I don't know if the encoding side will work or not, but I would suggest considering a move to ethers
if this repo is still in development.
Jest give very good reports.
There is PR #32 for this
When I try to send a large number (ether field) for a signature, I get an error Assertion failed
var message = { user: "0x000000000000000000", ether: 100000000000000000, expire: 1559257200 };
How to avoid this? I need pass a Wei value with signed message.
TypedDataUtils.sign()
is really helpful for hashing data as per the EIP712 standard, but it might be better to give it a clearer name instead of sign
. Since it functions more like a hash function, instead of a cryptographic signing function, perhaps name it hash
, eip712Hash
, or something else instead?
/**
* Signs a typed message as per EIP-712 and returns its sha3 hash
*
* @param {Object} typedData - Types message data to sign
* @returns {string} - sha3 hash of the resulting signed message
*/
https://github.com/MetaMask/eth-sig-util/blob/master/index.js#L145
Circle CI runs for master
but does not run for PRs. It would be good to have Circle CI build incoming PRs.
Branch | Build failing 🚨 |
---|---|
Dependency |
tape
|
Current Version | 4.9.0 |
Type | devDependency |
This version is covered by your current version range and after updating it in your project the build failed.
tape is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.
The new version differs by 10 commits.
050b318
v4.9.1
73232c0
[Dev Deps] update js-yaml
8a2d29b
[Deps] update has
, for-each
, resolve
, object-inspect
c6f5313
[Tests] add eclint and eslint, to enforce a consistent style
45788a5
[Dev Deps] update concat-stream
ec4a71d
[fix] Fix bug in functionName regex during stack parsing
7261ccc
Merge pull request #433 from mcnuttandrew/add-trb
6cbc53e
Add tap-react-browser
9d501ff
[Dev Deps] use ~ for dev deps; update to latest nonbreaking
24e0a8d
Fix spelling of "parameterize"
See the full diff
There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.
Your Greenkeeper Bot 🌴
I noticed lately a new build 3.0.1 however this release still bundle tweetnacl-util 0.15.0
Is there any reason to not upgrade it to the 0.15.1 ?
Metamask uses now the 0.15.1 as it fixed a Firefox regexp bug as reported here :
MetaMask/metamask-extension#9042 (comment)
Thanks
Hi! I've created noble-secp256k1 one year ago. The goal was to create an extremely simple zero-dependency library. With all the optimizations, even though it's just <1kloc, it's actually faster than elliptic — which you're using right now:
noble#sign x 3,952 ops/sec
elliptic#sign x 1,808 ops/sec
noble#verify x 851 ops/sec
elliptic#verify x 812 ops/sec
The reasoning behind the creation was that more and more dependencies are used in all kinds of projects, and there is a huge need in reducing the number — because any dep could get hacked, and it becomes complex to verify the source code by hand. Check out the blog post: https://paulmillr.com/posts/noble-secp256k1-fast-ecc/
I strongly recommend switching to noble. I can prepare a pull request.
The doc for methods such as personalSign
state that data
should be hexEncoded:
msgParams should have a data key that is hex-encoded data to sign.
However, the tests use non-encoded data.
test('personalSign and recover', function (t) {
.....
const message = 'Hello, world!'
const msgParams = { data: message }
const signed = sigUtil.personalSign(privKey, msgParams)
})```
Currently, the ethereumjs-abi
dep for this package is:
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
This is extremely dangerous since if a ethereumjs
maintainer, pushes a breaking change to master
, everyone using eth-sig-utils
will be affected the next time they npm install
the package.
When pointing to a Github repo, you should alwys specify a git commit hash. This way, no matter what the maintainers do, the library always pulls in the intended version of the dep.
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
Readme.md is not reflecting methods exposed in index.js.
For example: getEncryptionPublicKey
Our current browser test tools are niche and unmaintained, and should be replaced with something more modern.
https://github.com/MetaMask/eth-sig-util#signtypeddata_v3-privatekeybuffer-msgparams
in readme, but there is no signTypedData_v3
What's the purpose to use ethUtil.stripHexPrefix
and ethUtil.intToHex
at the same time?
Isn't it duplicated?
Line 169 in 2790801
Need to add test & ensure we can sign bytes, as reported here:
MetaMask/metamask-extension#2826
This project is missing a LICENSE
file
I would like to see an Ethereum name or address added to the ethereum
field of the package.json file. This will allow consumers of the project to know a cannonical address to send funds to. For example, I'm building an application that will allow developers to automatically donate ETH to all of their npm dependencies that have Ethereum addresses in their package.json files: https://sustainus.io (not quite ready, alpha soon)
Hi, I'm upgrading to eth_signedTypedData_v3 (via https://medium.com/metamask/eip712-is-coming-what-to-expect-and-how-to-use-it-bb92fd1a7a26) and finding that recoverTypedSignature is expecting a different data structure than is specified in the new standard. Is there a plan to update this?
We already have some JS code that does this, will post it shortly.
I have experienced an error and issue very similar to this: web3/web3.js#3151
I am wondering if it has something to do with ethereumjs-util
?
Removing eth-sig-util
from my project fixed/alleviated this. I also tried updating to the latest stable version, but it did not help.
Node: v12.13.1
npm: 6.12.1
Darwin mac 19.0.0 Darwin Kernel Version 19.0.0: Wed Sep 25 20:18:50 PDT 2019; root:xnu-6153.11.26~2/RELEASE_X86_64 x86_64
The published package in NPM only contains the /dist/*
files but when debugging with i.e. the Chrome DevTools and stepping into the library, an error is thrown because the original /src/index.ts
file cannot be found and therefore the sourcemaps can't be used to display the proper source lines being executed.
Please update the NPM package to also include the /src
directory.
Note: To workaround this issue, just copied the /src
file into node_modules/eth-sig-util
and the DevTools are happy again.
The unit test coverage was recently dramatically expanded, but there are still a few gaps.
typedSignatureHash
tests for array inputs.personal_sign
isn't thoroughly tested in general (e.g. we only test '0x'-prefixed hex string inputs).A unnecessary global variable is introduced in https://github.com/MetaMask/eth-sig-util/blob/master/index.js#L151 (This fails in some mocha test in other projects)
This project would benefit from using the ethereum-cryptography package.
all the cryptographic primitives normally used when developing Javascript/TypeScript applications and tools for Ethereum
This would fix issues #100 and #72.
#105 is related. This will be the implementation used in ethereum-cryptography eventually, see ethereum/js-ethereum-cryptography#5.
https://github.com/MetaMask/eth-sig-util/blob/master/index.js#L438-L442
signTypedData: function (privateKey, msgParams) {
const message = TypedDataUtils.sign(msgParams.data, false)
const sig = ethUtil.ecsign(message, privateKey)
return ethUtil.bufferToHex(this.concatSig(sig.v, sig.r, sig.s))
},
Maybe I'm misunderstanding something, but it looks like the data is getting signed twice. The variable message
is actually the hash of a signature, then ecSign is used to sign it again
Hey there, I'm using eth-sig-util to do some signature validation on my backend. I recently redeployed the same code I had been using in a new project and found it was no longer working. This was because of an upgrade to eth-sig-util. I've gone ahead and created an example test that passes in 2.1.2
, but fails in 2.2.0
. I've narrowed it down to 0fbac01 as being the commit to break this. The signature and data blob here are from a real project using MetaMask v6.7.2 to do the signature.
test('broken recovery', (t) => {
t.plan(1);
const data = {
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "string"}
],
"authorization": [
{"name": "userid", "type": "uint256"},
{"name": "point", "type": "string"}
]
},
"domain": {
"name": "test.test",
"version": "1",
"chainId": 1,
"verifyingContract": "0x223c067f8cf28ae173ee5cafea60ca44c335fecb"
},
"primaryType": "authorization",
"message": {
"userid": 123,
"point": "~hatteb-tondys"
}
};
const sig = '0xab3b4a19d27b4f2ff9b2669888d1c3fee427b8c9ccabdeb91ef529a760aa593d00717dfa6f02d0ae0556f6c210ab5d5af41130d805d4659437d735172f7172c71c';
const expectedAddress = '0x529104532a9779ea9Eae0C1e325b3368e0F8add4';
const recoveredAddress = sigUtil.recoverTypedSignature({ data, sig });
t.equal(expectedAddress.toLowerCase(), recoveredAddress.toLocaleLowerCase());
});
Is this because of verifyingContract
being misinterpreted?
Right now, signTypedData will accept uint
and int
, but EIP 712 states that only integer types with their size are allowed.
Trying to use these should error, telling users that uint256
and int256
should be used instead when encountering these types.
Starting from th v8.0.x, Metamask-extension is able to provide a public encryption key and to decrypt content.
I tried to dynamically load the library in my browser DApp from:
https://cdn.jsdelivr.net/npm/[email protected]/index.min.js
However it appears that this package is not build as a browser friendly component (nodejs).
In my build chain I need to clone, browserified, tercerified and embed this library in my DApp in order to encrypt content with the Metamask provided public encryption key. Roughly 400KB.
Do you have any plan to package this component as a browser friendly component ?
Thanks
Every personal-sign message in MM starts with the user approving a readable text. Whether it's words, numbers or emojis, it's all just text, which is supposed to make sense somehow to the user approving it. It is therefore expected all these messages would be treated the same when hashed to the personal message format.
Unfortunately, this is not the case. What happens is that number formated messages and mixed characters messages are decoded from utf8 encoding. However, hex formated messages are have an exception, they are decoded with hex encoding.
The hex decoding seems to be coincidental. Otherwise, why wouldn't more text format, e.g. numbers, not be decoded in a special way also?
MetaMask uses this logic to sign personal sign messages:
Line 250 in 0b84950
personalSign: function (privateKey, msgParams) {
var message = ethUtil.toBuffer(msgParams.data)
var msgHash = ethUtil.hashPersonalMessage(message)
var sig = ethUtil.ecsign(msgHash, privateKey)
var serialized = ethUtil.bufferToHex(this.concatSig(sig.v, sig.r, sig.s))
return serialized
},
That logic works, but ethUtil.toBuffer
might behave differently than expected from the caller. That function will always receive a string in this case, but will try to auto-detect hex and then decode it as hex instead of utf8
https://github.com/ethereumjs/ethereumjs-util/blob/v6.0.0/index.js#L157
// ...
if (exports.isHexString(v)) {
v = Buffer.from(exports.padToEven(exports.stripHexPrefix(v)), 'hex')
} else {
v = Buffer.from(v)
}
// ...
The correct behaviour is debatable. I strongly believe hex should not be dealt differently. As the code in geth suggest, https://github.com/ethereum/go-ethereum/blob/0c5f8c078abca7dc5954e30f307495a5c41c5f6c/accounts/accounts.go#L193 the function that hases the message is called TextAndHash
, meaning the data is supposed to represent text.
In any case, changing the behaviour now will be too big of a breaking change for apps that rely on this.
At this point, we should just accept that hex-appearing texts are dealt differently than any other text format, including numbers.
Link "Available on NPM" ( https://www.npmjs.com/package/@metamask/eth-sig-util ) 404
Presumably related to this https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md
reference source: https://badges.greenkeeper.io/MetaMask/eth-sig-util.svg
I've tried this numerous times to use https://github.com/senarma/eth-auth, where I provide a valid Ethereum address as the MetaAddress URL parameter to generate a "challenge", and then make a subsequent request to the other API endpoint with both that "challenge" and the "signature", but it always recovers a different Ethereum address from the original one.
The line of code where it recovers the Ethereum address is here const recovered = await sigUtil.recoverTypedSignature({
, which uses eth-sig-util v2.0.2.
So I created a simplified version of that code as a simple script where you just follow a few simple steps in the README to replicate the issue with any Ethereum address:
https://github.com/ltfschoen/eth-auth-test
Any guidance would be greatly appreciated!
seems to throw on this line, because they have buffer
set to false in their package.json
browser
field
if (typeof Buffer.from !== 'undefined') {
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.