eth-sig-util
Advanced tools
Comparing version 2.5.3 to 2.5.4
948
index.js
@@ -1,467 +0,473 @@ | ||
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var ethUtil = require("ethereumjs-util"); | ||
var ethAbi = require("ethereumjs-abi"); | ||
var nacl = require("tweetnacl"); | ||
var naclUtil = require("tweetnacl-util"); | ||
var TYPED_MESSAGE_SCHEMA = { | ||
type: 'object', | ||
properties: { | ||
types: { | ||
type: 'object', | ||
additionalProperties: { | ||
type: 'array', | ||
items: { | ||
type: 'object', | ||
properties: { | ||
name: { type: 'string' }, | ||
type: { type: 'string' }, | ||
}, | ||
required: ['name', 'type'], | ||
}, | ||
}, | ||
const { Buffer } = require('buffer') | ||
const ethUtil = require('ethereumjs-util') | ||
const ethAbi = require('ethereumjs-abi') | ||
const nacl = require('tweetnacl') | ||
nacl.util = require('tweetnacl-util') | ||
const TYPED_MESSAGE_SCHEMA = { | ||
type: 'object', | ||
properties: { | ||
types: { | ||
type: 'object', | ||
additionalProperties: { | ||
type: 'array', | ||
items: { | ||
type: 'object', | ||
properties: { | ||
name: { type: 'string' }, | ||
type: { type: 'string' }, | ||
}, | ||
required: ['name', 'type'], | ||
}, | ||
primaryType: { type: 'string' }, | ||
domain: { type: 'object' }, | ||
message: { type: 'object' }, | ||
}, | ||
}, | ||
required: ['types', 'primaryType', 'domain', 'message'], | ||
}; | ||
exports.TYPED_MESSAGE_SCHEMA = TYPED_MESSAGE_SCHEMA; | ||
primaryType: { type: 'string' }, | ||
domain: { type: 'object' }, | ||
message: { type: 'object' }, | ||
}, | ||
required: ['types', 'primaryType', 'domain', 'message'], | ||
} | ||
/** | ||
* A collection of utility functions used for signing typed data | ||
*/ | ||
var TypedDataUtils = { | ||
/** | ||
* Encodes an object by encoding and concatenating each of its members | ||
* | ||
* @param {string} primaryType - Root type | ||
* @param {Object} data - Object to encode | ||
* @param {Object} types - Type definitions | ||
* @returns {Buffer} - Encoded representation of an object | ||
*/ | ||
encodeData: function (primaryType, data, types, useV4) { | ||
var _this = this; | ||
if (useV4 === void 0) { useV4 = true; } | ||
var encodedTypes = ['bytes32']; | ||
var encodedValues = [this.hashType(primaryType, types)]; | ||
if (useV4) { | ||
var encodeField_1 = function (name, type, value) { | ||
if (types[type] !== undefined) { | ||
// eslint-disable-next-line no-eq-null | ||
return ['bytes32', value == null ? | ||
'0x0000000000000000000000000000000000000000000000000000000000000000' : | ||
ethUtil.sha3(_this.encodeData(type, value, types, useV4))]; | ||
} | ||
if (value === undefined) { | ||
throw new Error("missing value for field " + name + " of type " + type); | ||
} | ||
if (type === 'bytes') { | ||
return ['bytes32', ethUtil.sha3(value)]; | ||
} | ||
if (type === 'string') { | ||
// convert string to buffer - prevents ethUtil from interpreting strings like '0xabcd' as hex | ||
if (typeof value === 'string') { | ||
value = Buffer.from(value, 'utf8'); | ||
} | ||
return ['bytes32', ethUtil.sha3(value)]; | ||
} | ||
if (type.lastIndexOf(']') === type.length - 1) { | ||
var parsedType_1 = type.slice(0, type.lastIndexOf('[')); | ||
var typeValuePairs = value.map(function (item) { return encodeField_1(name, parsedType_1, item); }); | ||
return ['bytes32', ethUtil.sha3(ethAbi.rawEncode(typeValuePairs.map(function (_a) { | ||
var t = _a[0]; | ||
return t; | ||
}), typeValuePairs.map(function (_a) { | ||
var v = _a[1]; | ||
return v; | ||
})))]; | ||
} | ||
return [type, value]; | ||
}; | ||
for (var _i = 0, _a = types[primaryType]; _i < _a.length; _i++) { | ||
var field = _a[_i]; | ||
var _b = encodeField_1(field.name, field.type, data[field.name]), type = _b[0], value = _b[1]; | ||
encodedTypes.push(type); | ||
encodedValues.push(value); | ||
} | ||
const TypedDataUtils = { | ||
/** | ||
* Encodes an object by encoding and concatenating each of its members | ||
* | ||
* @param {string} primaryType - Root type | ||
* @param {Object} data - Object to encode | ||
* @param {Object} types - Type definitions | ||
* @returns {string} - Encoded representation of an object | ||
*/ | ||
encodeData (primaryType, data, types, useV4 = true) { | ||
const encodedTypes = ['bytes32'] | ||
const encodedValues = [this.hashType(primaryType, types)] | ||
if (useV4) { | ||
const encodeField = (name, type, value) => { | ||
if (types[type] !== undefined) { | ||
return ['bytes32', value === null || value === undefined ? | ||
'0x0000000000000000000000000000000000000000000000000000000000000000' : | ||
ethUtil.keccak(this.encodeData(type, value, types, useV4))] | ||
} | ||
else { | ||
for (var _c = 0, _d = types[primaryType]; _c < _d.length; _c++) { | ||
var field = _d[_c]; | ||
var value = data[field.name]; | ||
if (value !== undefined) { | ||
if (field.type === 'bytes') { | ||
encodedTypes.push('bytes32'); | ||
value = ethUtil.sha3(value); | ||
encodedValues.push(value); | ||
} | ||
else if (field.type === 'string') { | ||
encodedTypes.push('bytes32'); | ||
// convert string to buffer - prevents ethUtil from interpreting strings like '0xabcd' as hex | ||
if (typeof value === 'string') { | ||
value = Buffer.from(value, 'utf8'); | ||
} | ||
value = ethUtil.sha3(value); | ||
encodedValues.push(value); | ||
} | ||
else if (types[field.type] !== undefined) { | ||
encodedTypes.push('bytes32'); | ||
value = ethUtil.sha3(this.encodeData(field.type, value, types, useV4)); | ||
encodedValues.push(value); | ||
} | ||
else if (field.type.lastIndexOf(']') === field.type.length - 1) { | ||
throw new Error('Arrays currently unimplemented in encodeData'); | ||
} | ||
else { | ||
encodedTypes.push(field.type); | ||
encodedValues.push(value); | ||
} | ||
} | ||
} | ||
if (value === undefined) { | ||
throw new Error(`missing value for field ${name} of type ${type}`) | ||
} | ||
return ethAbi.rawEncode(encodedTypes, encodedValues); | ||
}, | ||
/** | ||
* Encodes the type of an object by encoding a comma delimited list of its members | ||
* | ||
* @param {string} primaryType - Root type to encode | ||
* @param {Object} types - Type definitions | ||
* @returns {string} - Encoded representation of the type of an object | ||
*/ | ||
encodeType: function (primaryType, types) { | ||
var result = ''; | ||
var deps = this.findTypeDependencies(primaryType, types).filter(function (dep) { return dep !== primaryType; }); | ||
deps = [primaryType].concat(deps.sort()); | ||
for (var _i = 0, deps_1 = deps; _i < deps_1.length; _i++) { | ||
var type = deps_1[_i]; | ||
var children = types[type]; | ||
if (!children) { | ||
throw new Error("No type definition specified: " + type); | ||
} | ||
result += type + "(" + types[type].map(function (_a) { | ||
var name = _a.name, t = _a.type; | ||
return t + " " + name; | ||
}).join(',') + ")"; | ||
if (type === 'bytes') { | ||
return ['bytes32', ethUtil.keccak(value)] | ||
} | ||
return result; | ||
}, | ||
/** | ||
* Finds all types within a type definition object | ||
* | ||
* @param {string} primaryType - Root type | ||
* @param {Object} types - Type definitions | ||
* @param {Array} results - current set of accumulated types | ||
* @returns {Array} - Set of all types found in the type definition | ||
*/ | ||
findTypeDependencies: function (primaryType, types, results) { | ||
if (results === void 0) { results = []; } | ||
primaryType = primaryType.match(/^\w*/u)[0]; | ||
if (results.includes(primaryType) || types[primaryType] === undefined) { | ||
return results; | ||
if (type === 'string') { | ||
// convert string to buffer - prevents ethUtil from interpreting strings like '0xabcd' as hex | ||
if (typeof value === 'string') { | ||
value = Buffer.from(value, 'utf8') | ||
} | ||
return ['bytes32', ethUtil.keccak(value)] | ||
} | ||
results.push(primaryType); | ||
for (var _i = 0, _a = types[primaryType]; _i < _a.length; _i++) { | ||
var field = _a[_i]; | ||
for (var _b = 0, _c = this.findTypeDependencies(field.type, types, results); _b < _c.length; _b++) { | ||
var dep = _c[_b]; | ||
!results.includes(dep) && results.push(dep); | ||
} | ||
if (type.lastIndexOf(']') === type.length - 1) { | ||
const parsedType = type.slice(0, type.lastIndexOf('[')) | ||
const typeValuePairs = value.map((item) => encodeField(name, parsedType, item)) | ||
return ['bytes32', ethUtil.keccak(ethAbi.rawEncode( | ||
typeValuePairs.map(([_type]) => _type), | ||
typeValuePairs.map(([, _value]) => _value), | ||
))] | ||
} | ||
return results; | ||
}, | ||
/** | ||
* Hashes an object | ||
* | ||
* @param {string} primaryType - Root type | ||
* @param {Object} data - Object to hash | ||
* @param {Object} types - Type definitions | ||
* @returns {Buffer} - Hash of an object | ||
*/ | ||
hashStruct: function (primaryType, data, types, useV4) { | ||
if (useV4 === void 0) { useV4 = true; } | ||
return ethUtil.sha3(this.encodeData(primaryType, data, types, useV4)); | ||
}, | ||
/** | ||
* Hashes the type of an object | ||
* | ||
* @param {string} primaryType - Root type to hash | ||
* @param {Object} types - Type definitions | ||
* @returns {Buffer} - Hash of an object | ||
*/ | ||
hashType: function (primaryType, types) { | ||
return ethUtil.sha3(this.encodeType(primaryType, types)); | ||
}, | ||
/** | ||
* Removes properties from a message object that are not defined per EIP-712 | ||
* | ||
* @param {Object} data - typed message object | ||
* @returns {Object} - typed message object with only allowed fields | ||
*/ | ||
sanitizeData: function (data) { | ||
var sanitizedData = {}; | ||
for (var key in TYPED_MESSAGE_SCHEMA.properties) { | ||
if (data[key]) { | ||
sanitizedData[key] = data[key]; | ||
return [type, value] | ||
} | ||
for (const field of types[primaryType]) { | ||
const [type, value] = encodeField(field.name, field.type, data[field.name]) | ||
encodedTypes.push(type) | ||
encodedValues.push(value) | ||
} | ||
} else { | ||
for (const field of types[primaryType]) { | ||
let value = data[field.name] | ||
if (value !== undefined) { | ||
if (field.type === 'bytes') { | ||
encodedTypes.push('bytes32') | ||
value = ethUtil.keccak(value) | ||
encodedValues.push(value) | ||
} else if (field.type === 'string') { | ||
encodedTypes.push('bytes32') | ||
// convert string to buffer - prevents ethUtil from interpreting strings like '0xabcd' as hex | ||
if (typeof value === 'string') { | ||
value = Buffer.from(value, 'utf8') | ||
} | ||
value = ethUtil.keccak(value) | ||
encodedValues.push(value) | ||
} else if (types[field.type] !== undefined) { | ||
encodedTypes.push('bytes32') | ||
value = ethUtil.keccak(this.encodeData(field.type, value, types, useV4)) | ||
encodedValues.push(value) | ||
} else if (field.type.lastIndexOf(']') === field.type.length - 1) { | ||
throw new Error('Arrays currently unimplemented in encodeData') | ||
} else { | ||
encodedTypes.push(field.type) | ||
encodedValues.push(value) | ||
} | ||
} | ||
if ('types' in sanitizedData) { | ||
sanitizedData.types = __assign({ EIP712Domain: [] }, sanitizedData.types); | ||
} | ||
return sanitizedData; | ||
}, | ||
/** | ||
* Signs a typed message as per EIP-712 and returns its sha3 hash | ||
* | ||
* @param {Object} typedData - Types message data to sign | ||
* @returns {Buffer} - sha3 hash of the resulting signed message | ||
*/ | ||
sign: function (typedData, useV4) { | ||
if (useV4 === void 0) { useV4 = true; } | ||
var sanitizedData = this.sanitizeData(typedData); | ||
var parts = [Buffer.from('1901', 'hex')]; | ||
parts.push(this.hashStruct('EIP712Domain', sanitizedData.domain, sanitizedData.types, useV4)); | ||
if (sanitizedData.primaryType !== 'EIP712Domain') { | ||
parts.push(this.hashStruct(sanitizedData.primaryType, sanitizedData.message, sanitizedData.types, useV4)); | ||
} | ||
return ethUtil.sha3(Buffer.concat(parts)); | ||
}, | ||
}; | ||
exports.TypedDataUtils = TypedDataUtils; | ||
function concatSig(v, r, s) { | ||
var rSig = ethUtil.fromSigned(r); | ||
var sSig = ethUtil.fromSigned(s); | ||
var vSig = ethUtil.bufferToInt(v); | ||
var rStr = padWithZeroes(ethUtil.toUnsigned(rSig).toString('hex'), 64); | ||
var sStr = padWithZeroes(ethUtil.toUnsigned(sSig).toString('hex'), 64); | ||
var vStr = ethUtil.stripHexPrefix(ethUtil.intToHex(vSig)); | ||
return ethUtil.addHexPrefix(rStr.concat(sStr, vStr)).toString('hex'); | ||
} | ||
} | ||
return ethAbi.rawEncode(encodedTypes, encodedValues) | ||
}, | ||
/** | ||
* Encodes the type of an object by encoding a comma delimited list of its members | ||
* | ||
* @param {string} primaryType - Root type to encode | ||
* @param {Object} types - Type definitions | ||
* @returns {string} - Encoded representation of the type of an object | ||
*/ | ||
encodeType (primaryType, types) { | ||
let result = '' | ||
let deps = this.findTypeDependencies(primaryType, types).filter((dep) => dep !== primaryType) | ||
deps = [primaryType].concat(deps.sort()) | ||
for (const type of deps) { | ||
const children = types[type] | ||
if (!children) { | ||
throw new Error(`No type definition specified: ${type}`) | ||
} | ||
result += `${type}(${types[type].map(({ name, type: _type }) => `${_type} ${name}`).join(',')})` | ||
} | ||
return result | ||
}, | ||
/** | ||
* Finds all types within a type defintion object | ||
* | ||
* @param {string} primaryType - Root type | ||
* @param {Object} types - Type definitions | ||
* @param {Array} results - current set of accumulated types | ||
* @returns {Array} - Set of all types found in the type definition | ||
*/ | ||
findTypeDependencies (primaryType, types, results = []) { | ||
const [firstWord] = primaryType.match(/^\w*/u) | ||
primaryType = firstWord | ||
if (results.includes(primaryType) || types[primaryType] === undefined) { | ||
return results | ||
} | ||
results.push(primaryType) | ||
for (const field of types[primaryType]) { | ||
for (const dep of this.findTypeDependencies(field.type, types, results)) { | ||
!results.includes(dep) && results.push(dep) | ||
} | ||
} | ||
return results | ||
}, | ||
/** | ||
* Hashes an object | ||
* | ||
* @param {string} primaryType - Root type | ||
* @param {Object} data - Object to hash | ||
* @param {Object} types - Type definitions | ||
* @returns {string} - Hash of an object | ||
*/ | ||
hashStruct (primaryType, data, types, useV4 = true) { | ||
return ethUtil.keccak(this.encodeData(primaryType, data, types, useV4)) | ||
}, | ||
/** | ||
* Hashes the type of an object | ||
* | ||
* @param {string} primaryType - Root type to hash | ||
* @param {Object} types - Type definitions | ||
* @returns {string} - Hash of an object | ||
*/ | ||
hashType (primaryType, types) { | ||
return ethUtil.keccak(this.encodeType(primaryType, types)) | ||
}, | ||
/** | ||
* Removes properties from a message object that are not defined per EIP-712 | ||
* | ||
* @param {Object} data - typed message object | ||
* @returns {Object} - typed message object with only allowed fields | ||
*/ | ||
sanitizeData (data) { | ||
const sanitizedData = {} | ||
for (const key of Object.keys(TYPED_MESSAGE_SCHEMA.properties)) { | ||
data[key] && (sanitizedData[key] = data[key]) | ||
} | ||
if (sanitizedData.types) { | ||
sanitizedData.types = { EIP712Domain: [], ...sanitizedData.types } | ||
} | ||
return sanitizedData | ||
}, | ||
/** | ||
* Signs a typed message as per EIP-712 and returns its keccak hash | ||
* | ||
* @param {Object} typedData - Types message data to sign | ||
* @returns {string} - keccak hash of the resulting signed message | ||
*/ | ||
sign (typedData, useV4 = true) { | ||
const sanitizedData = this.sanitizeData(typedData) | ||
const parts = [Buffer.from('1901', 'hex')] | ||
parts.push(this.hashStruct('EIP712Domain', sanitizedData.domain, sanitizedData.types, useV4)) | ||
if (sanitizedData.primaryType !== 'EIP712Domain') { | ||
parts.push(this.hashStruct(sanitizedData.primaryType, sanitizedData.message, sanitizedData.types, useV4)) | ||
} | ||
return ethUtil.keccak(Buffer.concat(parts)) | ||
}, | ||
} | ||
exports.concatSig = concatSig; | ||
function normalize(input) { | ||
module.exports = { | ||
TYPED_MESSAGE_SCHEMA, | ||
TypedDataUtils, | ||
concatSig (v, r, s) { | ||
const rSig = ethUtil.fromSigned(r) | ||
const sSig = ethUtil.fromSigned(s) | ||
const vSig = ethUtil.bufferToInt(v) | ||
const rStr = padWithZeroes(ethUtil.toUnsigned(rSig).toString('hex'), 64) | ||
const sStr = padWithZeroes(ethUtil.toUnsigned(sSig).toString('hex'), 64) | ||
const vStr = ethUtil.stripHexPrefix(ethUtil.intToHex(vSig)) | ||
return ethUtil.addHexPrefix(rStr.concat(sStr, vStr)).toString('hex') | ||
}, | ||
normalize (input) { | ||
if (!input) { | ||
return undefined; | ||
return undefined | ||
} | ||
if (typeof input === 'number') { | ||
var buffer = ethUtil.toBuffer(input); | ||
input = ethUtil.bufferToHex(buffer); | ||
const buffer = ethUtil.toBuffer(input) | ||
input = ethUtil.bufferToHex(buffer) | ||
} | ||
if (typeof input !== 'string') { | ||
var msg = 'eth-sig-util.normalize() requires hex string or integer input.'; | ||
msg += " received " + typeof input + ": " + input; | ||
throw new Error(msg); | ||
let msg = 'eth-sig-util.normalize() requires hex string or integer input.' | ||
msg += ` received ${typeof input}: ${input}` | ||
throw new Error(msg) | ||
} | ||
return ethUtil.addHexPrefix(input.toLowerCase()); | ||
} | ||
exports.normalize = normalize; | ||
function personalSign(privateKey, msgParams) { | ||
var message = ethUtil.toBuffer(msgParams.data); | ||
var msgHash = ethUtil.hashPersonalMessage(message); | ||
var sig = ethUtil.ecsign(msgHash, privateKey); | ||
var serialized = ethUtil.bufferToHex(concatSig(sig.v, sig.r, sig.s)); | ||
return serialized; | ||
} | ||
exports.personalSign = personalSign; | ||
function recoverPersonalSignature(msgParams) { | ||
var publicKey = getPublicKeyFor(msgParams); | ||
var sender = ethUtil.publicToAddress(publicKey); | ||
var senderHex = ethUtil.bufferToHex(sender); | ||
return senderHex; | ||
} | ||
exports.recoverPersonalSignature = recoverPersonalSignature; | ||
function extractPublicKey(msgParams) { | ||
var publicKey = getPublicKeyFor(msgParams); | ||
return "0x" + publicKey.toString('hex'); | ||
} | ||
exports.extractPublicKey = extractPublicKey; | ||
function externalTypedSignatureHash(typedData) { | ||
var hashBuffer = typedSignatureHash(typedData); | ||
return ethUtil.bufferToHex(hashBuffer); | ||
} | ||
exports.typedSignatureHash = externalTypedSignatureHash; | ||
function signTypedDataLegacy(privateKey, msgParams) { | ||
var msgHash = typedSignatureHash(msgParams.data); | ||
var sig = ethUtil.ecsign(msgHash, privateKey); | ||
return ethUtil.bufferToHex(concatSig(sig.v, sig.r, sig.s)); | ||
} | ||
exports.signTypedDataLegacy = signTypedDataLegacy; | ||
function recoverTypedSignatureLegacy(msgParams) { | ||
var msgHash = typedSignatureHash(msgParams.data); | ||
var publicKey = recoverPublicKey(msgHash, msgParams.sig); | ||
var sender = ethUtil.publicToAddress(publicKey); | ||
return ethUtil.bufferToHex(sender); | ||
} | ||
exports.recoverTypedSignatureLegacy = recoverTypedSignatureLegacy; | ||
function encrypt(receiverPublicKey, msgParams, version) { | ||
return ethUtil.addHexPrefix(input.toLowerCase()) | ||
}, | ||
personalSign (privateKey, msgParams) { | ||
const message = ethUtil.toBuffer(msgParams.data) | ||
const msgHash = ethUtil.hashPersonalMessage(message) | ||
const sig = ethUtil.ecsign(msgHash, privateKey) | ||
const serialized = ethUtil.bufferToHex(this.concatSig(sig.v, sig.r, sig.s)) | ||
return serialized | ||
}, | ||
recoverPersonalSignature (msgParams) { | ||
const publicKey = getPublicKeyFor(msgParams) | ||
const sender = ethUtil.publicToAddress(publicKey) | ||
const senderHex = ethUtil.bufferToHex(sender) | ||
return senderHex | ||
}, | ||
extractPublicKey (msgParams) { | ||
const publicKey = getPublicKeyFor(msgParams) | ||
return `0x${publicKey.toString('hex')}` | ||
}, | ||
typedSignatureHash (typedData) { | ||
const hashBuffer = typedSignatureHash(typedData) | ||
return ethUtil.bufferToHex(hashBuffer) | ||
}, | ||
signTypedDataLegacy (privateKey, msgParams) { | ||
const msgHash = typedSignatureHash(msgParams.data) | ||
const sig = ethUtil.ecsign(msgHash, privateKey) | ||
return ethUtil.bufferToHex(this.concatSig(sig.v, sig.r, sig.s)) | ||
}, | ||
recoverTypedSignatureLegacy (msgParams) { | ||
const msgHash = typedSignatureHash(msgParams.data) | ||
const publicKey = recoverPublicKey(msgHash, msgParams.sig) | ||
const sender = ethUtil.publicToAddress(publicKey) | ||
return ethUtil.bufferToHex(sender) | ||
}, | ||
encrypt (receiverPublicKey, msgParams, version) { | ||
switch (version) { | ||
case 'x25519-xsalsa20-poly1305': { | ||
if (typeof msgParams.data !== 'string') { | ||
throw new Error('Cannot detect secret message, message params should be of the form {data: "secret message"} '); | ||
} | ||
// generate ephemeral keypair | ||
var ephemeralKeyPair = nacl.box.keyPair(); | ||
// assemble encryption parameters - from string to UInt8 | ||
var pubKeyUInt8Array = void 0; | ||
try { | ||
pubKeyUInt8Array = naclUtil.decodeBase64(receiverPublicKey); | ||
} | ||
catch (err) { | ||
throw new Error('Bad public key'); | ||
} | ||
var msgParamsUInt8Array = naclUtil.decodeUTF8(msgParams.data); | ||
var nonce = nacl.randomBytes(nacl.box.nonceLength); | ||
// encrypt | ||
var encryptedMessage = nacl.box(msgParamsUInt8Array, nonce, pubKeyUInt8Array, ephemeralKeyPair.secretKey); | ||
// handle encrypted data | ||
var output = { | ||
version: 'x25519-xsalsa20-poly1305', | ||
nonce: naclUtil.encodeBase64(nonce), | ||
ephemPublicKey: naclUtil.encodeBase64(ephemeralKeyPair.publicKey), | ||
ciphertext: naclUtil.encodeBase64(encryptedMessage), | ||
}; | ||
// return encrypted msg data | ||
return output; | ||
case 'x25519-xsalsa20-poly1305': { | ||
if (typeof msgParams.data === 'undefined') { | ||
throw new Error('Cannot detect secret message, message params should be of the form {data: "secret message"} ') | ||
} | ||
default: | ||
throw new Error('Encryption type/version not supported'); | ||
// generate ephemeral keypair | ||
const ephemeralKeyPair = nacl.box.keyPair() | ||
// assemble encryption parameters - from string to UInt8 | ||
let pubKeyUInt8Array | ||
try { | ||
pubKeyUInt8Array = nacl.util.decodeBase64(receiverPublicKey) | ||
} catch (err) { | ||
throw new Error('Bad public key') | ||
} | ||
const msgParamsUInt8Array = nacl.util.decodeUTF8(msgParams.data) | ||
const nonce = nacl.randomBytes(nacl.box.nonceLength) | ||
// encrypt | ||
const encryptedMessage = nacl.box(msgParamsUInt8Array, nonce, pubKeyUInt8Array, ephemeralKeyPair.secretKey) | ||
// handle encrypted data | ||
const output = { | ||
version: 'x25519-xsalsa20-poly1305', | ||
nonce: nacl.util.encodeBase64(nonce), | ||
ephemPublicKey: nacl.util.encodeBase64(ephemeralKeyPair.publicKey), | ||
ciphertext: nacl.util.encodeBase64(encryptedMessage), | ||
} | ||
// return encrypted msg data | ||
return output | ||
} | ||
default: | ||
throw new Error('Encryption type/version not supported') | ||
} | ||
} | ||
exports.encrypt = encrypt; | ||
function encryptSafely(receiverPublicKey, msgParams, version) { | ||
var DEFAULT_PADDING_LENGTH = Math.pow(2, 11); | ||
var NACL_EXTRA_BYTES = 16; | ||
var data = msgParams.data; | ||
}, | ||
encryptSafely (receiverPublicKey, msgParams, version) { | ||
const DEFAULT_PADDING_LENGTH = (2 ** 11) | ||
const NACL_EXTRA_BYTES = 16 | ||
const { data } = msgParams | ||
if (!data) { | ||
throw new Error('Cannot encrypt empty msg.data'); | ||
throw new Error('Cannot encrypt empty msg.data') | ||
} | ||
if (typeof data === 'object' && 'toJSON' in data) { | ||
// remove toJSON attack vector | ||
// TODO, check all possible children | ||
throw new Error('Cannot encrypt with toJSON property. Please remove toJSON property'); | ||
if (typeof data === 'object' && data.toJSON) { | ||
// remove toJSON attack vector | ||
// TODO, check all possible children | ||
throw new Error('Cannot encrypt with toJSON property. Please remove toJSON property') | ||
} | ||
// add padding | ||
var dataWithPadding = { | ||
data: data, | ||
padding: '', | ||
}; | ||
const dataWithPadding = { | ||
data, | ||
padding: '', | ||
} | ||
// calculate padding | ||
var dataLength = Buffer.byteLength(JSON.stringify(dataWithPadding), 'utf-8'); | ||
var modVal = dataLength % DEFAULT_PADDING_LENGTH; | ||
var padLength = 0; | ||
const dataLength = Buffer.byteLength(JSON.stringify(dataWithPadding), 'utf-8') | ||
const modVal = (dataLength % DEFAULT_PADDING_LENGTH) | ||
let padLength = 0 | ||
// Only pad if necessary | ||
if (modVal > 0) { | ||
padLength = DEFAULT_PADDING_LENGTH - modVal - NACL_EXTRA_BYTES; // nacl extra bytes | ||
padLength = (DEFAULT_PADDING_LENGTH - modVal) - NACL_EXTRA_BYTES // nacl extra bytes | ||
} | ||
dataWithPadding.padding = '0'.repeat(padLength); | ||
var paddedMsgParams = { data: JSON.stringify(dataWithPadding) }; | ||
return encrypt(receiverPublicKey, paddedMsgParams, version); | ||
} | ||
exports.encryptSafely = encryptSafely; | ||
function decrypt(encryptedData, receiverPrivateKey) { | ||
dataWithPadding.padding = '0'.repeat(padLength) | ||
const paddedMsgParams = { data: JSON.stringify(dataWithPadding) } | ||
return this.encrypt(receiverPublicKey, paddedMsgParams, version) | ||
}, | ||
decrypt (encryptedData, receiverPrivateKey) { | ||
switch (encryptedData.version) { | ||
case 'x25519-xsalsa20-poly1305': { | ||
// string to buffer to UInt8Array | ||
var recieverPrivateKeyUint8Array = nacl_decodeHex(receiverPrivateKey); | ||
var recieverEncryptionPrivateKey = nacl.box.keyPair.fromSecretKey(recieverPrivateKeyUint8Array).secretKey; | ||
// assemble decryption parameters | ||
var nonce = naclUtil.decodeBase64(encryptedData.nonce); | ||
var ciphertext = naclUtil.decodeBase64(encryptedData.ciphertext); | ||
var ephemPublicKey = naclUtil.decodeBase64(encryptedData.ephemPublicKey); | ||
// decrypt | ||
var decryptedMessage = nacl.box.open(ciphertext, nonce, ephemPublicKey, recieverEncryptionPrivateKey); | ||
// return decrypted msg data | ||
var output = void 0; | ||
try { | ||
output = naclUtil.encodeUTF8(decryptedMessage); | ||
} | ||
catch (err) { | ||
throw new Error('Decryption failed.'); | ||
} | ||
if (output) { | ||
return output; | ||
} | ||
throw new Error('Decryption failed.'); | ||
case 'x25519-xsalsa20-poly1305': { | ||
// string to buffer to UInt8Array | ||
const recieverPrivateKeyUint8Array = nacl_decodeHex(receiverPrivateKey) | ||
const recieverEncryptionPrivateKey = nacl.box.keyPair.fromSecretKey(recieverPrivateKeyUint8Array).secretKey | ||
// assemble decryption parameters | ||
const nonce = nacl.util.decodeBase64(encryptedData.nonce) | ||
const ciphertext = nacl.util.decodeBase64(encryptedData.ciphertext) | ||
const ephemPublicKey = nacl.util.decodeBase64(encryptedData.ephemPublicKey) | ||
// decrypt | ||
const decryptedMessage = nacl.box.open(ciphertext, nonce, ephemPublicKey, recieverEncryptionPrivateKey) | ||
// return decrypted msg data | ||
let output | ||
try { | ||
output = nacl.util.encodeUTF8(decryptedMessage) | ||
} catch (err) { | ||
throw new Error('Decryption failed.') | ||
} | ||
default: | ||
throw new Error('Encryption type/version not supported.'); | ||
if (output) { | ||
return output | ||
} | ||
throw new Error('Decryption failed.') | ||
} | ||
default: | ||
throw new Error('Encryption type/version not supported.') | ||
} | ||
} | ||
exports.decrypt = decrypt; | ||
function decryptSafely(encryptedData, receiverPrivateKey) { | ||
var dataWithPadding = JSON.parse(decrypt(encryptedData, receiverPrivateKey)); | ||
return dataWithPadding.data; | ||
} | ||
exports.decryptSafely = decryptSafely; | ||
function getEncryptionPublicKey(privateKey) { | ||
var privateKeyUint8Array = nacl_decodeHex(privateKey); | ||
var encryptionPublicKey = nacl.box.keyPair.fromSecretKey(privateKeyUint8Array).publicKey; | ||
return naclUtil.encodeBase64(encryptionPublicKey); | ||
} | ||
exports.getEncryptionPublicKey = getEncryptionPublicKey; | ||
/** | ||
* A generic entry point for all typed data methods to be passed, includes a version parameter. | ||
*/ | ||
function signTypedMessage(privateKey, msgParams, version) { | ||
if (version === void 0) { version = 'V4'; } | ||
}, | ||
decryptSafely (encryptedData, receiverPrivateKey) { | ||
const dataWithPadding = JSON.parse(this.decrypt(encryptedData, receiverPrivateKey)) | ||
return dataWithPadding.data | ||
}, | ||
getEncryptionPublicKey (privateKey) { | ||
const privateKeyUint8Array = nacl_decodeHex(privateKey) | ||
const encryptionPublicKey = nacl.box.keyPair.fromSecretKey(privateKeyUint8Array).publicKey | ||
return nacl.util.encodeBase64(encryptionPublicKey) | ||
}, | ||
/** | ||
* A generic entry point for all typed data methods to be passed, includes a version parameter. | ||
*/ | ||
signTypedMessage (privateKey, msgParams, version = 'V4') { | ||
switch (version) { | ||
case 'V1': | ||
return signTypedDataLegacy(privateKey, msgParams); | ||
case 'V3': | ||
return signTypedData(privateKey, msgParams); | ||
case 'V4': | ||
default: | ||
return signTypedData_v4(privateKey, msgParams); | ||
case 'V1': | ||
return this.signTypedDataLegacy(privateKey, msgParams) | ||
case 'V3': | ||
return this.signTypedData(privateKey, msgParams) | ||
case 'V4': | ||
default: | ||
return this.signTypedData_v4(privateKey, msgParams) | ||
} | ||
} | ||
exports.signTypedMessage = signTypedMessage; | ||
function recoverTypedMessage(msgParams, version) { | ||
if (version === void 0) { version = 'V4'; } | ||
}, | ||
recoverTypedMessage (msgParams, version = 'V4') { | ||
switch (version) { | ||
case 'V1': | ||
return recoverTypedSignatureLegacy(msgParams); | ||
case 'V3': | ||
return recoverTypedSignature(msgParams); | ||
case 'V4': | ||
default: | ||
return recoverTypedSignature_v4(msgParams); | ||
case 'V1': | ||
return this.recoverTypedSignatureLegacy(msgParams) | ||
case 'V3': | ||
return this.recoverTypedSignature(msgParams) | ||
case 'V4': | ||
default: | ||
return this.recoverTypedSignature_v4(msgParams) | ||
} | ||
}, | ||
signTypedData (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)) | ||
}, | ||
signTypedData_v4 (privateKey, msgParams) { | ||
const message = TypedDataUtils.sign(msgParams.data) | ||
const sig = ethUtil.ecsign(message, privateKey) | ||
return ethUtil.bufferToHex(this.concatSig(sig.v, sig.r, sig.s)) | ||
}, | ||
recoverTypedSignature (msgParams) { | ||
const message = TypedDataUtils.sign(msgParams.data, false) | ||
const publicKey = recoverPublicKey(message, msgParams.sig) | ||
const sender = ethUtil.publicToAddress(publicKey) | ||
return ethUtil.bufferToHex(sender) | ||
}, | ||
recoverTypedSignature_v4 (msgParams) { | ||
const message = TypedDataUtils.sign(msgParams.data) | ||
const publicKey = recoverPublicKey(message, msgParams.sig) | ||
const sender = ethUtil.publicToAddress(publicKey) | ||
return ethUtil.bufferToHex(sender) | ||
}, | ||
} | ||
exports.recoverTypedMessage = recoverTypedMessage; | ||
function signTypedData(privateKey, msgParams) { | ||
var message = TypedDataUtils.sign(msgParams.data, false); | ||
var sig = ethUtil.ecsign(message, privateKey); | ||
return ethUtil.bufferToHex(concatSig(sig.v, sig.r, sig.s)); | ||
} | ||
exports.signTypedData = signTypedData; | ||
function signTypedData_v4(privateKey, msgParams) { | ||
var message = TypedDataUtils.sign(msgParams.data); | ||
var sig = ethUtil.ecsign(message, privateKey); | ||
return ethUtil.bufferToHex(concatSig(sig.v, sig.r, sig.s)); | ||
} | ||
exports.signTypedData_v4 = signTypedData_v4; | ||
function recoverTypedSignature(msgParams) { | ||
var message = TypedDataUtils.sign(msgParams.data, false); | ||
var publicKey = recoverPublicKey(message, msgParams.sig); | ||
var sender = ethUtil.publicToAddress(publicKey); | ||
return ethUtil.bufferToHex(sender); | ||
} | ||
exports.recoverTypedSignature = recoverTypedSignature; | ||
function recoverTypedSignature_v4(msgParams) { | ||
var message = TypedDataUtils.sign(msgParams.data); | ||
var publicKey = recoverPublicKey(message, msgParams.sig); | ||
var sender = ethUtil.publicToAddress(publicKey); | ||
return ethUtil.bufferToHex(sender); | ||
} | ||
exports.recoverTypedSignature_v4 = recoverTypedSignature_v4; | ||
/** | ||
@@ -471,45 +477,57 @@ * @param typedData - Array of data along with types, as per EIP712. | ||
*/ | ||
function typedSignatureHash(typedData) { | ||
var error = new Error('Expect argument to be non-empty array'); | ||
if (typeof typedData !== 'object' || !('length' in typedData) || !typedData.length) { | ||
throw error; | ||
function typedSignatureHash (typedData) { | ||
const error = new Error('Expect argument to be non-empty array') | ||
if (typeof typedData !== 'object' || !typedData.length) { | ||
throw error | ||
} | ||
const data = typedData.map(function (e) { | ||
return e.type === 'bytes' ? ethUtil.toBuffer(e.value) : e.value | ||
}) | ||
const types = typedData.map(function (e) { | ||
return e.type | ||
}) | ||
const schema = typedData.map(function (e) { | ||
if (!e.name) { | ||
throw error | ||
} | ||
var data = typedData.map(function (e) { | ||
return e.type === 'bytes' ? ethUtil.toBuffer(e.value) : e.value; | ||
}); | ||
var types = typedData.map(function (e) { | ||
return e.type; | ||
}); | ||
var schema = typedData.map(function (e) { | ||
if (!e.name) { | ||
throw error; | ||
} | ||
return e.type + " " + e.name; | ||
}); | ||
return ethAbi.soliditySHA3(['bytes32', 'bytes32'], [ | ||
ethAbi.soliditySHA3(new Array(typedData.length).fill('string'), schema), | ||
ethAbi.soliditySHA3(types, data), | ||
]); | ||
return `${e.type} ${e.name}` | ||
}) | ||
return ethAbi.soliditySHA3( | ||
['bytes32', 'bytes32'], | ||
[ | ||
ethAbi.soliditySHA3(new Array(typedData.length).fill('string'), schema), | ||
ethAbi.soliditySHA3(types, data), | ||
], | ||
) | ||
} | ||
function recoverPublicKey(hash, sig) { | ||
var signature = ethUtil.toBuffer(sig); | ||
var sigParams = ethUtil.fromRpcSig(signature); | ||
return ethUtil.ecrecover(hash, sigParams.v, sigParams.r, sigParams.s); | ||
function recoverPublicKey (hash, sig) { | ||
const signature = ethUtil.toBuffer(sig) | ||
const sigParams = ethUtil.fromRpcSig(signature) | ||
return ethUtil.ecrecover(hash, sigParams.v, sigParams.r, sigParams.s) | ||
} | ||
function getPublicKeyFor(msgParams) { | ||
var message = ethUtil.toBuffer(msgParams.data); | ||
var msgHash = ethUtil.hashPersonalMessage(message); | ||
return recoverPublicKey(msgHash, msgParams.sig); | ||
function getPublicKeyFor (msgParams) { | ||
const message = ethUtil.toBuffer(msgParams.data) | ||
const msgHash = ethUtil.hashPersonalMessage(message) | ||
return recoverPublicKey(msgHash, msgParams.sig) | ||
} | ||
function padWithZeroes(number, length) { | ||
var myString = "" + number; | ||
while (myString.length < length) { | ||
myString = "0" + myString; | ||
} | ||
return myString; | ||
function padWithZeroes (number, length) { | ||
let myString = `${number}` | ||
while (myString.length < length) { | ||
myString = `0${myString}` | ||
} | ||
return myString | ||
} | ||
// converts hex strings to the Uint8Array format used by nacl | ||
function nacl_decodeHex(msgHex) { | ||
var msgBase64 = Buffer.from(msgHex, 'hex').toString('base64'); | ||
return naclUtil.decodeBase64(msgBase64); | ||
function nacl_decodeHex (msgHex) { | ||
const msgBase64 = (Buffer.from(msgHex, 'hex')).toString('base64') | ||
return nacl.util.decodeBase64(msgBase64) | ||
} | ||
{ | ||
"name": "eth-sig-util", | ||
"version": "2.5.3", | ||
"version": "2.5.4", | ||
"description": "A few useful functions for signing ethereum data", | ||
@@ -8,10 +8,7 @@ "main": "index.js", | ||
"index.js", | ||
"index.d.ts", | ||
"utils/" | ||
], | ||
"scripts": { | ||
"build": "tsc --project .", | ||
"lint": "eslint . --ext .ts,.js", | ||
"test": "npm run build && node test/index.js", | ||
"prepublishOnly": "npm run build" | ||
"lint": "eslint .", | ||
"test": "node test/index.js" | ||
}, | ||
@@ -33,7 +30,5 @@ "repository": { | ||
"dependencies": { | ||
"buffer": "^5.2.1", | ||
"elliptic": "^6.4.0", | ||
"ethereumjs-abi": "0.6.5", | ||
"ethereumjs-abi": "0.6.8", | ||
"ethereumjs-util": "^5.1.1", | ||
"tweetnacl": "^1.0.0", | ||
"tweetnacl": "^1.0.3", | ||
"tweetnacl-util": "^0.15.0" | ||
@@ -43,10 +38,6 @@ }, | ||
"@metamask/eslint-config": "^2.0.0", | ||
"@types/node": "^10.17.13", | ||
"@typescript-eslint/eslint-plugin": "^2.20.0", | ||
"@typescript-eslint/parser": "^2.20.0", | ||
"eslint": "^6.8.0", | ||
"eslint-plugin-import": "^2.20.1", | ||
"tape": "^4.9.1", | ||
"typescript": "3.7.4" | ||
"tape": "^4.9.1" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
4
4
22107
4
461
2
+ Added@types/bn.js@4.11.6(transitive)
+ Addedethereumjs-abi@0.6.8(transitive)
+ Addedethereumjs-util@6.2.1(transitive)
- Removedbuffer@^5.2.1
- Removedelliptic@^6.4.0
- Removedbase64-js@1.5.1(transitive)
- Removedbuffer@5.7.1(transitive)
- Removedethereumjs-abi@0.6.5(transitive)
- Removedethereumjs-util@4.5.1(transitive)
- Removedieee754@1.2.1(transitive)
Updatedethereumjs-abi@0.6.8
Updatedtweetnacl@^1.0.3