UNPKG

@dapperlabs/dappauth

Version:

A util to prove actionable control ('ownership') over a public Ethereum address using eth_sign

254 lines (233 loc) 7.9 kB
const ethUtil = require('ethereumjs-util'); const assert = require('assert'); const DappAuth = require('../index.js'); const utils = require('../utils.js'); const ProviderMock = require('./provider-mock.js'); const ContractMock = require('./contract-mock.js'); const testUtils = require('./test-utils.js'); describe('DappAuth', function() { const keyA = testUtils.generateRandomKey(); const keyB = testUtils.generateRandomKey(); const keyC = testUtils.generateRandomKey(); const testCases = [ { title: 'External wallets should be authorized signers over their address', isEOA: true, challenge: 'foo', challengeSign: 'foo', signingKeys: [keyA], authAddr: testUtils.keyToAddress(keyA), mockContract: { authorizedKey: null, address: null, errorIsValidSignature: false, }, expectedAuthorizedSignerError: false, expectedAuthorizedSigner: true, }, { title: 'External wallets should NOT be authorized signers when signing the wrong challenge', isEOA: true, challenge: 'foo', challengeSign: 'bar', signingKeys: [keyA], authAddr: testUtils.keyToAddress(keyA), mockContract: { authorizedKey: ethUtil.privateToPublic(keyC), address: testUtils.keyToAddress(keyA), errorIsValidSignature: false, }, expectedAuthorizedSignerError: false, expectedAuthorizedSigner: false, }, { title: 'External wallets should NOT be authorized signers over OTHER addresses', isEOA: true, challenge: 'foo', challengeSign: 'foo', signingKeys: [keyA], authAddr: testUtils.keyToAddress(keyB), mockContract: { authorizedKey: ethUtil.privateToPublic(keyC), address: testUtils.keyToAddress(keyB), errorIsValidSignature: false, }, expectedAuthorizedSignerError: false, expectedAuthorizedSigner: false, }, { title: 'Smart-contract wallets with a 1-of-1 correct internal key should be authorized signers over their address', isEOA: false, challenge: 'foo', challengeSign: 'foo', signingKeys: [keyB], authAddr: testUtils.keyToAddress(keyA), mockContract: { authorizedKey: ethUtil.privateToPublic(keyB), address: testUtils.keyToAddress(keyA), errorIsValidSignature: false, }, expectedAuthorizedSignerError: false, expectedAuthorizedSigner: true, }, { title: 'Smart-contract wallets with a 1-of-2 (multi-sig) correct internal key should be authorized signers over their address', isEOA: false, challenge: 'foo', challengeSign: 'foo', signingKeys: [keyB, keyC], authAddr: testUtils.keyToAddress(keyA), mockContract: { authorizedKey: ethUtil.privateToPublic(keyB), address: testUtils.keyToAddress(keyA), errorIsValidSignature: false, }, expectedAuthorizedSignerError: false, expectedAuthorizedSigner: true, }, { title: 'Smart-contract wallets with a 1-of-1 incorrect internal key should NOT be authorized signers over their address', isEOA: false, challenge: 'foo', challengeSign: 'foo', signingKeys: [keyB], authAddr: testUtils.keyToAddress(keyA), mockContract: { authorizedKey: ethUtil.privateToPublic(keyC), address: testUtils.keyToAddress(keyA), errorIsValidSignature: false, }, expectedAuthorizedSignerError: false, expectedAuthorizedSigner: false, }, { title: 'isAuthorizedSigner should error when smart-contract call errors', isEOA: false, challenge: 'foo', challengeSign: 'foo', signingKeys: [keyB], authAddr: testUtils.keyToAddress(keyA), mockContract: { authorizedKey: ethUtil.privateToPublic(keyB), address: testUtils.keyToAddress(keyA), errorIsValidSignature: true, }, expectedAuthorizedSignerError: true, expectedAuthorizedSigner: false, }, ]; testCases.forEach((test) => it(test.title, async function() { const dappAuth = new DappAuth( new ProviderMock(new ContractMock(test.mockContract)), ); const signatureFunc = test.isEOA ? testUtils.signEOAPersonalMessage : testUtils.signERC1654PersonalMessage; const signatures = `0x${test.signingKeys .map((signingKey) => ethUtil.stripHexPrefix( signatureFunc(test.challengeSign, signingKey, test.authAddr), ), ) .join('')}`; let isError = false; let isAuthorizedSigner = false; try { isAuthorizedSigner = await dappAuth.isAuthorizedSigner( test.challenge, signatures, test.authAddr, ); } catch (error) { isError = true; } assert.equal(isError, test.expectedAuthorizedSignerError); assert.equal(isAuthorizedSigner, test.expectedAuthorizedSigner); }), ); it('It should decode challenge as utf8 by default when decoding challenges', async function() { const dappAuth = new DappAuth( new ProviderMock( new ContractMock({ authorizedKey: null, address: null, errorIsValidSignature: false, }), ), ); const eoaHash = dappAuth._hashEOAPersonalMessage('foo'); assert.equal( `0x${eoaHash.toString('hex')}`, '0x76b2e96714d3b5e6eb1d1c509265430b907b44f72b2a22b06fcd4d96372b8565', ); const scHash = dappAuth._hashSCMessage('foo'); assert.equal( `0x${scHash.toString('hex')}`, '0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d', ); }); // See https://github.com/MetaMask/eth-sig-util/issues/60 it('It should decode challenge as hex if hex is detected when decoding challenges', async function() { const dappAuth = new DappAuth( new ProviderMock( new ContractMock({ authorizedKey: null, address: null, errorIsValidSignature: false, }), ), ); // result if 0xffff is decoded as hex: 13a6aa3102b2d639f36804a2d7c31469618fd7a7907c658a7b2aa91a06e31e47 // result if 0xffff is decoded as utf8: 247aefb5d2e5b17fca61f786c779f7388485460c13e51308f88b2ff84ffa6851 const eoaHash = dappAuth._hashEOAPersonalMessage('0xffff'); assert.equal( `0x${eoaHash.toString('hex')}`, '0x13a6aa3102b2d639f36804a2d7c31469618fd7a7907c658a7b2aa91a06e31e47', ); // result if 0xffff is decoded as hex: 06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a // result if 0xffff is decoded as utf8: f0443ea82539c5136844b0a175f544b7ee7bc0fc5ce940bad19f08eaf618af71 const scHash = dappAuth._hashSCMessage('0xffff'); assert.equal( `0x${scHash.toString('hex')}`, '0x06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a', ); }); // This test is needed for 100% coverage it('Invalid signature should fail', async function() { const dappAuth = new DappAuth( new ProviderMock( new ContractMock({ authorizedKey: null, address: null, errorIsValidSignature: false, }), ), ); const signatures = '0xinvalid-signature'; let isError = false; let isAuthorizedSigner = false; try { isAuthorizedSigner = await dappAuth.isAuthorizedSigner( 'foo', signatures, testUtils.keyToAddress(keyA), ); } catch (error) { isError = true; } assert.equal(isError, true); assert.equal(isAuthorizedSigner, false); }); }); describe('utils', function() { it('Should remove hex prefix if value is hex prefixed', function() { const value = 'foo'; assert.equal(utils.removeHexPrefix(value), 'foo'); }); });