Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@dapperlabs/dappauth

Package Overview
Dependencies
Maintainers
3
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@dapperlabs/dappauth - npm Package Compare versions

Comparing version 1.0.1 to 2.0.0

ABIs/ERC1271.js

65

index.js
const Web3 = require('web3');
const ethUtil = require('ethereumjs-util');
const erc165 = require('./ABIs/ERC165');
const erc725Core = require('./ABIs/ERC725Core');
const ERC1271 = require('./ABIs/ERC1271');
const erc725CoreInterfaceID = '0xd202158d';
const erc725InterfaceID = '0xdc3d2a7b';
const erc725ActionPurpose = 2;
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
const ERC1271_MAGIC_VALUE = '0x1626ba7e';

@@ -15,3 +13,3 @@ module.exports = class DappAuth {

async isSignerActionableOnAddress(challenge, signature, address) {
async isAuthorizedSigner(challenge, signature, address) {
const challengeHash = ethUtil.hashPersonalMessage(

@@ -22,9 +20,4 @@ ethUtil.toBuffer(challenge),

// Get the address of whoever signed this message
const signatureParams = ethUtil.fromRpcSig(signature);
const recoveredKey = ethUtil.ecrecover(
challengeHash,
signatureParams.v,
signatureParams.r,
signatureParams.s,
);
const { v, r, s } = ethUtil.fromRpcSig(signature);
const recoveredKey = ethUtil.ecrecover(challengeHash, v, r, s);
const recoveredAddress = ethUtil.publicToAddress(recoveredKey);

@@ -41,48 +34,10 @@

// try smart-contract wallet
const isSupportedContract = await this.isSupportedContract(address);
if (!isSupportedContract) {
return false;
}
const erc1271CoreContract = new this.web3.eth.Contract(ERC1271, address);
const keyHasActionPurpose = await this.keyHasActionPurpose(
address,
recoveredKey,
);
return keyHasActionPurpose;
}
async keyHasActionPurpose(contractAddr, key) {
const erc725CoreContract = new this.web3.eth.Contract(
erc725Core,
contractAddr,
);
const keyHash = ethUtil.keccak(key);
return erc725CoreContract.methods
.keyHasPurpose(keyHash, erc725ActionPurpose)
const magicValue = await erc1271CoreContract.methods
.isValidSignature(ethUtil.keccak(challenge), signature) // we send just a regular hash, which then the smart contract hashes ontop to an erc191 hash
.call();
}
async isSupportedContract(contractAddr) {
const erc165Contract = new this.web3.eth.Contract(erc165, contractAddr);
const isSupportsERC725CoreInterface = await erc165Contract.methods
.supportsInterface(erc725CoreInterfaceID)
.call();
if (isSupportsERC725CoreInterface) {
return true;
}
const isSupportsERC725Interface = await erc165Contract.methods
.supportsInterface(erc725InterfaceID)
.call();
if (isSupportsERC725Interface) {
return true;
}
return false;
return magicValue === ERC1271_MAGIC_VALUE;
}
};
{
"name": "@dapperlabs/dappauth",
"version": "1.0.1",
"version": "2.0.0",
"description": "A util to prove actionable control ('ownership') over a public Ethereum address using eth_sign",

@@ -23,3 +23,5 @@ "keywords": [

"ethereumjs-util": "^6.0.0",
"web3": "^1.0.0-beta.36"
"web3": "^1.0.0-beta.36",
"ethereumjs-abi": "^0.6.6",
"safe-buffer": "^5.1.2"
},

@@ -26,0 +28,0 @@ "devDependencies": {

const ethUtil = require('ethereumjs-util');
const ethAbi = require('ethereumjs-abi');
const Buffer = require('safe-buffer').Buffer;
const utils = require('./utils');
const erc725CoreInterfaceID = '0xd202158d';
const erc725InterfaceID = '0xdc3d2a7b';
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
const ERC1271_METHOD_SIG = '1626ba7e';
module.exports = class MockContract {
constructor(options) {
this.isSupportsERC725CoreInterface = options.isSupportsERC725CoreInterface;
this.isSupportsERC725Interface = options.isSupportsERC725Interface;
this.actionableKey = options.actionableKey;
this.errorOnIsSupportedContract = options.errorOnIsSupportedContract;
this.errorOnKeyHasPurpose = options.errorOnKeyHasPurpose;
this.authorizedKey = options.authorizedKey;
this.address = options.address;
this.errorIsValidSignature = options.errorIsValidSignature;
}
static _true() {
return '0x0000000000000000000000000000000000000000000000000000000000000001';
return `0x${ERC1271_METHOD_SIG}00000000000000000000000000000000000000000000000000000000`; // a.k.a the "magic value".
}

@@ -23,8 +24,14 @@

// @param {String} methodCall
// @param {String} methodParams
// @return {String}
run(methodCall, methodParams) {
switch (methodCall) {
case '01ffc9a7':
return this._01ffc9a7(`0x${methodParams.substring(0, 4 * 2)}`);
case 'd202158d':
return this._d202158d(`0x${methodParams.substring(0, 32 * 2)}`);
case ERC1271_METHOD_SIG:
const [hash, signature] = ethAbi.rawDecode(
['bytes32', 'bytes'],
Buffer.from(methodParams, 'hex'),
);
return this._1626ba7e(hash, signature);
default:

@@ -35,29 +42,20 @@ throw new Error(`Unexpected method ${methodCall}`);

// "isSupportedContract" method call
_01ffc9a7(interfaceID) {
if (this.errorOnIsSupportedContract) {
throw new Error('isSupportedContract call returned an error');
// "isValidSignature" method call
// @param {Buffer} hash
// @param {Buffer} signature
// @return {String}
_1626ba7e(hash, signature) {
if (this.errorIsValidSignature) {
throw new Error('isValidSignature call returned an error');
}
if (
this.isSupportsERC725CoreInterface &&
interfaceID === erc725CoreInterfaceID
) {
return MockContract._true();
}
// Get the address of whoever signed this message
const { v, r, s } = ethUtil.fromRpcSig(signature);
const erc191MessageHash = utils.erc191MessageHash(hash, this.address);
const recoveredKey = ethUtil.ecrecover(erc191MessageHash, v, r, s);
const recoveredAddress = ethUtil.publicToAddress(recoveredKey);
if (this.isSupportsERC725Interface && interfaceID === erc725InterfaceID) {
return MockContract._true();
}
const expectedAddress = ethUtil.publicToAddress(this.authorizedKey);
return MockContract._false();
}
// "keyHasPurpose" method call
_d202158d(key) {
if (this.errorOnKeyHasPurpose) {
throw new Error('keyHasPurpose call returned an error');
}
if (key === ethUtil.bufferToHex(ethUtil.keccak(this.actionableKey))) {
if (recoveredAddress.toString() === expectedAddress.toString()) {
return MockContract._true();

@@ -64,0 +62,0 @@ }

const ethUtil = require('ethereumjs-util');
const crypto = require('crypto');
const assert = require('assert');

@@ -7,25 +6,24 @@ const DappAuth = require('..');

const ContractMock = require('./contract-mock');
const utils = require('./utils');
describe('dappauth', function() {
const keyA = generateRandomKey();
const keyB = generateRandomKey();
const keyC = generateRandomKey();
const keyA = utils.generateRandomKey();
const keyB = utils.generateRandomKey();
const keyC = utils.generateRandomKey();
const testCases = [
{
title:
'Direct-keyed wallets should have actionable control over their address',
title: 'External wallets should be authorized signers over their address',
isEOA: true,
challenge: 'foo',
challengeSign: 'foo',
signingKey: keyA,
authAddr: keyToAddress(keyA),
authAddr: utils.keyToAddress(keyA),
mockContract: {
isSupportsERC725CoreInterface: false,
isSupportsERC725Interface: false,
actionableKey: null,
errorOnIsSupportedContract: false,
errorOnKeyHasPurpose: false,
authorizedKey: null,
address: null,
errorIsValidSignature: false,
},
expectedIsSignerActionableOnAddressError: false,
expectedIsSignerActionableOnAddress: true,
expectedAuthorizedSignerError: false,
expectedAuthorizedSigner: true,
},

@@ -35,136 +33,79 @@

title:
'Direct-keyed wallets should NOT have actionable control when signing the wrong challenge',
'External wallets should NOT be authorized signers when signing the wrong challenge',
isEOA: true,
challenge: 'foo',
challengeSign: 'bar',
signingKey: keyA,
authAddr: keyToAddress(keyA),
authAddr: utils.keyToAddress(keyA),
mockContract: {
isSupportsERC725CoreInterface: false,
isSupportsERC725Interface: false,
actionableKey: null,
errorOnIsSupportedContract: false,
errorOnKeyHasPurpose: false,
authorizedKey: ethUtil.privateToPublic(keyC),
address: utils.keyToAddress(keyA),
errorIsValidSignature: false,
},
expectedIsSignerActionableOnAddressError: false,
expectedIsSignerActionableOnAddress: false,
expectedAuthorizedSignerError: false,
expectedAuthorizedSigner: false,
},
{
title:
'Direct-keyed wallets should NOT have actionable control over OTHER addresses',
'External wallets should NOT be authorized signers over OTHER addresses',
isEOA: true,
challenge: 'foo',
challengeSign: 'foo',
signingKey: keyA,
authAddr: keyToAddress(keyB),
authAddr: utils.keyToAddress(keyB),
mockContract: {
isSupportsERC725CoreInterface: false,
isSupportsERC725Interface: false,
actionableKey: null,
errorOnIsSupportedContract: false,
errorOnKeyHasPurpose: false,
authorizedKey: ethUtil.privateToPublic(keyC),
address: utils.keyToAddress(keyB),
errorIsValidSignature: false,
},
expectedIsSignerActionableOnAddressError: false,
expectedIsSignerActionableOnAddress: false,
expectedAuthorizedSignerError: false,
expectedAuthorizedSigner: false,
},
{
title:
'Smart-contract wallets with support for ERC725Core interface and action key should have actionable control over their address',
'Smart-contract wallets with a 1-of-1 correct internal key should be authorized signers over their address',
isEOA: false,
challenge: 'foo',
challengeSign: 'foo',
signingKey: keyB,
authAddr: keyToAddress(keyA),
authAddr: utils.keyToAddress(keyA),
mockContract: {
isSupportsERC725CoreInterface: true,
isSupportsERC725Interface: false,
actionableKey: ethUtil.privateToPublic(keyB),
errorOnIsSupportedContract: false,
errorOnKeyHasPurpose: false,
authorizedKey: ethUtil.privateToPublic(keyB),
address: utils.keyToAddress(keyA),
errorIsValidSignature: false,
},
expectedIsSignerActionableOnAddressError: false,
expectedIsSignerActionableOnAddress: true,
expectedAuthorizedSignerError: false,
expectedAuthorizedSigner: true,
},
{
title:
'Smart-contract wallets with support for ERC725 interface and action key should have actionable control over their address',
'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',
signingKey: keyB,
authAddr: keyToAddress(keyA),
authAddr: utils.keyToAddress(keyA),
mockContract: {
isSupportsERC725CoreInterface: false,
isSupportsERC725Interface: true,
actionableKey: ethUtil.privateToPublic(keyB),
errorOnIsSupportedContract: false,
errorOnKeyHasPurpose: false,
authorizedKey: ethUtil.privateToPublic(keyC),
address: utils.keyToAddress(keyA),
errorIsValidSignature: false,
},
expectedIsSignerActionableOnAddressError: false,
expectedIsSignerActionableOnAddress: true,
expectedAuthorizedSignerError: false,
expectedAuthorizedSigner: false,
},
{
title:
'Smart-contract wallets WITHOUT support for ERC725/ERC725Core interface and action key should NOT have actionable control over their address',
title: 'isAuthorizedSigner should error when smart-contract call errors',
isEOA: false,
challenge: 'foo',
challengeSign: 'foo',
signingKey: keyB,
authAddr: keyToAddress(keyA),
authAddr: utils.keyToAddress(keyA),
mockContract: {
isSupportsERC725CoreInterface: false,
isSupportsERC725Interface: false,
actionableKey: ethUtil.privateToPublic(keyB),
errorOnIsSupportedContract: false,
errorOnKeyHasPurpose: false,
authorizedKey: ethUtil.privateToPublic(keyB),
address: utils.keyToAddress(keyA),
errorIsValidSignature: true,
},
expectedIsSignerActionableOnAddressError: false,
expectedIsSignerActionableOnAddress: false,
expectedAuthorizedSignerError: true,
expectedAuthorizedSigner: false,
},
{
title:
'Smart-contract wallets with support for ERC725 interface and incorrect action key should have NO actionable control over their address',
challenge: 'foo',
challengeSign: 'foo',
signingKey: keyB,
authAddr: keyToAddress(keyA),
mockContract: {
isSupportsERC725CoreInterface: false,
isSupportsERC725Interface: true,
actionableKey: ethUtil.privateToPublic(keyC),
errorOnIsSupportedContract: false,
errorOnKeyHasPurpose: false,
},
expectedIsSignerActionableOnAddressError: false,
expectedIsSignerActionableOnAddress: false,
},
{
title:
'isSignerActionableOnAddress should error when smart-contract call errors',
challenge: 'foo',
challengeSign: 'foo',
signingKey: keyB,
authAddr: keyToAddress(keyA),
mockContract: {
isSupportsERC725CoreInterface: true,
isSupportsERC725Interface: false,
actionableKey: ethUtil.privateToPublic(keyB),
errorOnIsSupportedContract: true,
errorOnKeyHasPurpose: false,
},
expectedIsSignerActionableOnAddressError: true,
expectedIsSignerActionableOnAddress: false,
},
{
title:
'isSignerActionableOnAddress should error when smart-contract call errors',
challenge: 'foo',
challengeSign: 'foo',
signingKey: keyB,
authAddr: keyToAddress(keyA),
mockContract: {
isSupportsERC725CoreInterface: true,
isSupportsERC725Interface: false,
actionableKey: ethUtil.privateToPublic(keyB),
errorOnIsSupportedContract: false,
errorOnKeyHasPurpose: true,
},
expectedIsSignerActionableOnAddressError: true,
expectedIsSignerActionableOnAddress: false,
},
];

@@ -178,11 +119,16 @@

const signature = signPersonalMessage(
const signatureFunc = test.isEOA
? utils.signEOAPersonalMessage
: utils.signERC1654PersonalMessage;
const signature = signatureFunc(
test.challengeSign,
test.signingKey,
test.authAddr,
);
let isError = false;
let isSignerActionableOnAddress = false;
let isAuthorizedSigner = false;
try {
isSignerActionableOnAddress = await dappAuth.isSignerActionableOnAddress(
isAuthorizedSigner = await dappAuth.isAuthorizedSigner(
test.challenge,

@@ -196,26 +142,6 @@ signature,

assert.equal(isError, test.expectedIsSignerActionableOnAddressError);
if (!isError) {
assert.equal(
isSignerActionableOnAddress,
test.expectedIsSignerActionableOnAddress,
);
}
assert.equal(isError, test.expectedAuthorizedSignerError);
assert.equal(isAuthorizedSigner, test.expectedAuthorizedSigner);
}),
);
});
function generateRandomKey() {
return ethUtil.toBuffer(`0x${crypto.randomBytes(32).toString('hex')}`);
}
function signPersonalMessage(message, key) {
const messageHash = ethUtil.hashPersonalMessage(ethUtil.toBuffer(message));
const signature = ethUtil.ecsign(messageHash, key);
return ethUtil.toRpcSig(signature.v, signature.r, signature.s);
}
function keyToAddress(key) {
return ethUtil.bufferToHex(ethUtil.privateToAddress(key));
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc