diff --git a/README.md b/README.md index 3bbc25b..2af452c 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ in the below methods, `keys` is an object of the following form: ``` js { + "feedType": "ed25519" "curve": "ed25519", "public": ".ed25519", "private": ".ed25519", @@ -53,11 +54,15 @@ when stored in a file, the file also contains a comment warning the reader about safe private key security. Comment lines are prefixed with `#` after removing them the result is valid JSON. +The `curve` property is a legacy property used to denote the feed type and may be deprecated in the future. + ### hash (data, encoding) => id Returns the sha256 hash of a given data. If encoding is not provided then it is assumed to be _binary_. -### getTag (ssb_id) => tag -The SSB ids contain a tag at the end. This function returns it. +### getFeedType (ssbId) => feedType + +Each SSB ID contains a feed type at the end. This function returns it. + So if you have a string like `@gaQw6zD4pHrg8zmrqku24zTSAINhRg=.ed25519` this function would return `ed25519`. This is useful as SSB start providing features for different encryption methods and cyphers. @@ -77,22 +82,24 @@ variations and parts `loadOrCreate` (async), `load`, `create` `createSync` `loadSync`. But since you only need to load once, using the combined function is easiest. -`keys` is an object as described in [`keys`](#keys) section. - ### loadOrCreate (filename, cb) If a sync file access method is not available, `loadOrCreate` can be called with a callback. that callback will be called with `cb(null, keys)`. If loading the keys errored, new keys are created. -### generate(curve, seed) => keys +### generate(feedType, seed) => keys generate a key, with optional seed. -curve defaults to `ed25519` (and no other type is currently supported) + +feedType defaults to `ed25519` but also supports `ed25519.test` + seed should be a 32 byte buffer. `keys` is an object as described in [`keys`](#keys) section. +New feed types can be added with `ssbKeys.use()`. + ### signObj(keys, hmac_key?, obj) signs a javascript object, and then adds a signature property to it. @@ -109,7 +116,6 @@ The fine details of the signature format are described in the [protocol guide](h verify a signed object. `hmac_key` must be the same value as passed to `signObj`. - ### box(content, recipients) => boxed encrypt a message content to many recipients. msg will be JSON encoded, then encrypted @@ -145,15 +151,11 @@ symmetrically encrypt an object with `key` (a buffer) symmetrically decrypt an object with `key` (a buffer) -### LICENSE - -MIT - - - - - +### use(feedType, { generate, sign, verify }) => ssbKeys +add new feed type to be used with `ssbKeys.generate()` +### LICENSE +MIT diff --git a/index.js b/index.js index ae5f3ba..4bb9c08 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,7 @@ 'use strict' var sodium = require('chloride') - var pb = require('private-box') - var u = require('./util') - var isBuffer = Buffer.isBuffer //UTILS @@ -22,57 +19,85 @@ var hmac = sodium.crypto_auth exports.hash = u.hash -exports.getTag = u.getTag +exports.getFeedType = u.getFeedType +exports.getTag = u.getFeedType // deprecated function isObject (o) { return 'object' === typeof o } -function isFunction (f) { - return 'function' === typeof f -} - function isString(s) { return 'string' === typeof s } -var curves = {} -curves.ed25519 = require('./sodium') +const feedTypes = { + ed25519: require('./sodium'), + 'ed25519.test': require('./sodium') +} + +exports.use = (name, object) => { + if (typeof name !== 'string' || name.length === 0) { + throw new Error(`Invalid name: "${name}", expected string with non-zero length`) + } + + const requiredMethods = [ + 'generate', + 'sign', + 'verify' + ] + + const isNotObject = typeof object !== 'object' + const isInvalidObject = isNotObject || requiredMethods.some(methodName => + typeof object[methodName] !== 'function' + ) + + if (isInvalidObject) { + const expectedMethods = requiredMethods.join(', ') + console.log(object) + throw new Error(`Invalid object. Missing required methods, expected: ${expectedMethods}`) + } + + if (feedTypes[name] != null) { + throw new Error(`Duplicate feed type: "${name}"`) + } + + feedTypes[name] = object +} -function getCurve(keys) { - var curve = keys.curve +function getFeedType(keys) { + let { feedType } = keys + feedType = feedType || keys.curve - if(!keys.curve && isString(keys.public)) + if(!feedType && isString(keys.public)) keys = keys.public - if(!curve && isString(keys)) - curve = u.getTag(keys) + if(!feedType && isString(keys)) + feedType = u.getFeedType(keys) - if(!curves[curve]) { - throw new Error( - 'unkown curve:' + curve + - ' expected: '+Object.keys(curves) - ) + if(!feedTypes[feedType]) { + throw new Error(`unkown feed type: "${feedType}", expected: "${Object.keys(feedTypes)}"`) } - return curve + return feedType } //this should return a key pair: -// {curve: curve, public: Buffer, private: Buffer} +// { feedType: string, curve: string, public: Buffer, private: Buffer} +exports.generate = function (feedType, seed) { + feedType = feedType || 'ed25519' -exports.generate = function (curve, seed) { - curve = curve || 'ed25519' + if(feedTypes[feedType] == null) + throw new Error(`unknown feed type: "${feedType}"`) - if(!curves[curve]) - throw new Error('unknown curve:'+curve) - - return u.keysToJSON(curves[curve].generate(seed), curve) + return u.keysToJSON(feedTypes[feedType].generate(seed), feedType) } //import functions for loading/saving keys from storage var storage = require('./storage')(exports.generate) -for(var key in storage) exports[key] = storage[key] +exports.load = storage.load +exports.loadSync = storage.loadSync +exports.create = storage.create +exports.createSync = storage.createSync exports.loadOrCreate = function (filename, cb) { @@ -96,14 +121,17 @@ exports.loadOrCreateSync = function (filename) { function sign (keys, msg) { if(isString(msg)) - msg = new Buffer(msg) + msg = Buffer.from(msg) if(!isBuffer(msg)) throw new Error('msg should be buffer') - var curve = getCurve(keys) + var feedType = getFeedType(keys) - return curves[curve] + const prefix = feedTypes[feedType] .sign(u.toBuffer(keys.private || keys), msg) - .toString('base64')+'.sig.'+curve + .toString('base64') + const suffix = `.sig.${feedType}` + + return prefix + suffix } @@ -112,10 +140,10 @@ function sign (keys, msg) { function verify (keys, sig, msg) { if(isObject(sig)) throw new Error('signature should be base64 string, did you mean verifyObj(public, signed_obj)') - return curves[getCurve(keys)].verify( + return feedTypes[getFeedType(keys)].verify( u.toBuffer(keys.public || keys), u.toBuffer(sig), - isBuffer(msg) ? msg : new Buffer(msg) + isBuffer(msg) ? msg : Buffer.from(msg) ) } @@ -124,7 +152,7 @@ function verify (keys, sig, msg) { exports.signObj = function (keys, hmac_key, obj) { if(!obj) obj = hmac_key, hmac_key = null var _obj = clone(obj) - var b = new Buffer(JSON.stringify(_obj, null, 2)) + var b = Buffer.from(JSON.stringify(_obj, null, 2)) if(hmac_key) b = hmac(b, u.toBuffer(hmac_key)) _obj.signature = sign(keys, b) return _obj @@ -135,13 +163,13 @@ exports.verifyObj = function (keys, hmac_key, obj) { obj = clone(obj) var sig = obj.signature delete obj.signature - var b = new Buffer(JSON.stringify(obj, null, 2)) + var b = Buffer.from(JSON.stringify(obj, null, 2)) if(hmac_key) b = hmac(b, u.toBuffer(hmac_key)) return verify(keys, sig, b) } exports.box = function (msg, recipients) { - msg = new Buffer(JSON.stringify(msg)) + msg = Buffer.from(JSON.stringify(msg)) recipients = recipients.map(function (keys) { return sodium.crypto_sign_ed25519_pk_to_curve25519(u.toBuffer(keys.public || keys)) @@ -163,7 +191,9 @@ exports.unboxBody = function (boxed, key) { var msg = pb.multibox_open_body(boxed, key) try { return JSON.parse(''+msg) - } catch (_) { } + } catch (_) { + return undefined + } } exports.unbox = function (boxed, keys) { @@ -174,8 +204,9 @@ exports.unbox = function (boxed, keys) { try { var msg = pb.multibox_open(boxed, sk) return JSON.parse(''+msg) - } catch (_) { } - return + } catch (_) { + return undefined + } } exports.secretBox = function secretBox (data, key) { @@ -188,4 +219,4 @@ exports.secretUnbox = function secretUnbox (ctxt, key) { var ptxt = sodium.crypto_secretbox_open_easy(ctxt, key.slice(0, 24), key) if(!ptxt) return return JSON.parse(ptxt.toString()) -} \ No newline at end of file +} diff --git a/local-storage.js b/local-storage.js index 1fc2bc8..63d133d 100644 --- a/local-storage.js +++ b/local-storage.js @@ -1,5 +1,4 @@ 'use strict' -var u = require('./util') function isFunction (f) { return 'function' == typeof f @@ -7,8 +6,8 @@ function isFunction (f) { module.exports = function (generate) { - function create (filename, curve, legacy) { - var keys = generate(curve, legacy) + function create (filename, feedType, legacy) { + var keys = generate(feedType, legacy) localStorage[filename] = JSON.stringify(keys) return keys } @@ -19,12 +18,12 @@ module.exports = function (generate) { return { createSync: create, - create: function(filename, curve, legacy, cb) { + create: function(filename, feedType, legacy, cb) { if(isFunction(legacy)) cb = legacy, legacy = null - if(isFunction(curve)) - cb = curve, curve = null - cb(null, create(filename, curve, legacy)) + if(isFunction(feedType)) + cb = feedType, feedType = null + cb(null, create(filename, feedType, legacy)) }, loadSync: load, load: function (filename, cb) { diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..bcc9880 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,264 @@ +{ + "name": "ssb-keys", + "version": "7.2.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "chloride": { + "version": "2.2.14", + "resolved": "https://registry.npmjs.org/chloride/-/chloride-2.2.14.tgz", + "integrity": "sha512-Jp3kpDIO4MlcJCFi4jER9P7k3sAVvIwbe4QJtM9Nkp43e/GQ/98HU1wJS6NdU6cbzfGrKWmMdRB+VNRrCynzfw==", + "requires": { + "is-electron": "^2.2.0", + "sodium-browserify": "^1.2.7", + "sodium-browserify-tweetnacl": "^0.2.5", + "sodium-chloride": "^1.1.2", + "sodium-native": "^2.1.6" + } + }, + "chloride-test": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/chloride-test/-/chloride-test-1.2.4.tgz", + "integrity": "sha512-9vhoi1qXSBPn6//ZxIgSe3M2QhKHzIPZQzmrZgmPADsqW0Jxpe3db1e7aGSRUMXbxAQ04SfypdT8dGaSvIvKDw==", + "requires": { + "json-buffer": "^2.0.11" + } + }, + "deep-equal": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz", + "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=", + "dev": true + }, + "defined": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", + "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=", + "dev": true + }, + "ed2curve": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/ed2curve/-/ed2curve-0.1.4.tgz", + "integrity": "sha1-lKRCSLuH2jXbDv968KpXYWgRf1k=", + "requires": { + "tweetnacl": "0.x.x" + } + }, + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dev": true, + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "optional": true + }, + "is-electron": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.0.tgz", + "integrity": "sha512-SpMppC2XR3YdxSzczXReBjqs2zGscWQpBIKqwXYBFic0ERaxNVgwLCHwOLZeESfdJQjX0RDvrJ1lBXX2ij+G1Q==" + }, + "json-buffer": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-2.0.11.tgz", + "integrity": "sha1-PkQf2jCYvo0eMXGtWRvGKjPi1V8=" + }, + "libsodium": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.5.tgz", + "integrity": "sha512-0YVU2QJc5sDR5HHkGCaliYImS7pGeXi11fiOfm4DirBd96PJVZIn3LJa06ZOFjLNsWkL3UbNjYhLRUOABPL9vw==" + }, + "libsodium-wrappers": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.5.tgz", + "integrity": "sha512-QE9Q+FxLLGdJRiJTuC2GB3LEHZeHX/VcbMQeNPdAixEKo86JPy6bOWND1XmMLu0tjWUu0xIY0YpJYQApxIZwbQ==", + "requires": { + "libsodium": "0.7.5" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "dev": true, + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "optional": true + }, + "node-gyp-build": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz", + "integrity": "sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==", + "optional": true + }, + "object-inspect": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-0.4.0.tgz", + "integrity": "sha1-9RV8EWwUVbJDsG7pdwM5LFrYn+w=", + "dev": true + }, + "private-box": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/private-box/-/private-box-0.3.0.tgz", + "integrity": "sha512-zsK6DDEC+cnNiunYamcVbx4ZCLbKnzTOZa09K4Pj3/tH3nQFPUO9K2QoYy4kfxLqmoyw6RPDtACN9OYviMQZ2Q==", + "requires": { + "chloride": "^2.2.9" + } + }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "dev": true, + "requires": { + "through": "~2.3.4" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "sha.js": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.5.tgz", + "integrity": "sha1-J9Fx78yCoRi5ljn/WBZgJCtQbnw=", + "requires": { + "inherits": "^2.0.1" + } + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "sodium-browserify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sodium-browserify/-/sodium-browserify-1.3.0.tgz", + "integrity": "sha512-1KRS6Oew3X13AIZhbmGF0YBdt2pQdafJMfv83OZHWbzxG92YBBnN8HYx/VKmYB4xCe90eidNaDJWBEFw/o3ahw==", + "requires": { + "libsodium-wrappers": "^0.7.4", + "sha.js": "2.4.5", + "sodium-browserify-tweetnacl": "^0.2.5", + "tweetnacl": "^0.14.1" + } + }, + "sodium-browserify-tweetnacl": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/sodium-browserify-tweetnacl/-/sodium-browserify-tweetnacl-0.2.6.tgz", + "integrity": "sha512-ZnEI26hdluilpYY28Xc4rc1ALfmEp2TWihkJX6Mdtw0z9RfHfpZJU7P8DoKbN1HcBdU9aJmguFZs7igE8nLJPg==", + "requires": { + "chloride-test": "^1.1.0", + "ed2curve": "^0.1.4", + "sha.js": "^2.4.8", + "tweetnacl": "^1.0.1", + "tweetnacl-auth": "^0.3.0" + }, + "dependencies": { + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", + "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + } + } + }, + "sodium-chloride": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sodium-chloride/-/sodium-chloride-1.1.2.tgz", + "integrity": "sha512-8AVzr9VHueXqfzfkzUA0aXe/Q4XG3UTmhlP6Pt+HQc5bbAPIJFo7ZIMh9tvn+99QuiMcyDJdYumegGAczl0N+g==" + }, + "sodium-native": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-2.4.2.tgz", + "integrity": "sha512-qwHcUnzFpRSGSm6F49j/h5SnxPFBgSNdDwZkAqjvuAoHQIVBFOXYb+oCUTJV80K5hRqSYCihpbX06vbrtPbilg==", + "optional": true, + "requires": { + "ini": "^1.3.5", + "nan": "^2.4.0", + "node-gyp-build": "^3.0.0" + } + }, + "tape": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-3.6.1.tgz", + "integrity": "sha1-SJPdU+KApfWMDOswwsDrs7zVHh8=", + "dev": true, + "requires": { + "deep-equal": "~0.2.0", + "defined": "~0.0.0", + "glob": "~3.2.9", + "inherits": "~2.0.1", + "object-inspect": "~0.4.0", + "resumer": "~0.0.0", + "through": "~2.3.4" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "tweetnacl-auth": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tweetnacl-auth/-/tweetnacl-auth-0.3.1.tgz", + "integrity": "sha1-t1vC3xVkm7hOi5qjwGacbEvODSU=", + "requires": { + "tweetnacl": "0.x.x" + } + } + } +} diff --git a/sodium.js b/sodium.js index c948074..2e878bb 100644 --- a/sodium.js +++ b/sodium.js @@ -2,15 +2,11 @@ var sodium = require('chloride') module.exports = { - - curves: ['ed25519'], - generate: function (seed) { - if(!seed) sodium.randombytes(seed = new Buffer(32)) + if(!seed) sodium.randombytes(seed = Buffer.alloc(32)) var keys = seed ? sodium.crypto_sign_seed_keypair(seed) : sodium.crypto_sign_keypair() return { - curve: 'ed25519', public: keys.publicKey, //so that this works with either sodium @@ -18,15 +14,12 @@ module.exports = { private: keys.privateKey || keys.secretKey } }, - sign: function (privateKey, message) { return sodium.crypto_sign_detached(message, privateKey) }, - verify: function (publicKey, sig, message) { return sodium.crypto_sign_verify_detached(sig, message, publicKey) } - } diff --git a/storage.js b/storage.js index f07387e..3a51e27 100644 --- a/storage.js +++ b/storage.js @@ -51,7 +51,7 @@ module.exports = function (generate) { function reconstructKeys(keyfile) { var privateKey = keyfile - .replace(/\s*\#[^\n]*/g, '') + .replace(/\s*#[^\n]*/g, '') .split('\n').filter(empty).join('') //if the key is in JSON format, we are good. @@ -63,7 +63,7 @@ module.exports = function (generate) { } exports.load = function(filename, cb) { - filename = toFile(filename, 'secret') + filename = toFile(filename) fs.readFile(filename, 'ascii', function(err, privateKeyStr) { if (err) return cb(err) var keys @@ -78,14 +78,14 @@ module.exports = function (generate) { return reconstructKeys(fs.readFileSync(filename, 'ascii')) } - exports.create = function(filename, curve, legacy, cb) { + exports.create = function(filename, feedType, legacy, cb) { if(isFunction(legacy)) cb = legacy, legacy = null - if(isFunction(curve)) - cb = curve, curve = null + if(isFunction(feedType)) + cb = feedType, feedType = null filename = toFile(filename) - var keys = generate(curve) + var keys = generate(feedType) var keyfile = constructKeys(keys, legacy) mkdirp(path.dirname(filename), function (err) { if(err) return cb(err) @@ -96,9 +96,9 @@ module.exports = function (generate) { }) } - exports.createSync = function(filename, curve, legacy) { + exports.createSync = function(filename, feedType, legacy) { filename = toFile(filename) - var keys = generate(curve) + var keys = generate(feedType) var keyfile = constructKeys(keys, legacy) mkdirp.sync(path.dirname(filename)) fs.writeFileSync(filename, keyfile, {mode: 0x100, flag: 'wx'}) diff --git a/test/box-unbox.js b/test/box-unbox.js index 2c9f05f..d2134ef 100644 --- a/test/box-unbox.js +++ b/test/box-unbox.js @@ -2,7 +2,6 @@ var tape = require('tape') var ssbkeys = require('../') tape('box, unbox', function (t) { - var alice = ssbkeys.generate() var bob = ssbkeys.generate() @@ -14,9 +13,7 @@ tape('box, unbox', function (t) { }) tape('return undefined for invalid content', function (t) { - var alice = ssbkeys.generate() - var bob = ssbkeys.generate() var msg = ssbkeys.unbox('this is invalid content', alice.private) t.equal(msg, undefined) diff --git a/test/fs.js b/test/fs.js index 9fc89bc..fad69dc 100644 --- a/test/fs.js +++ b/test/fs.js @@ -1,6 +1,5 @@ var tape = require('tape') var ssbkeys = require('../') -var crypto = require('crypto') var path = '/tmp/ssb-keys_'+Date.now() var fs = require('fs') diff --git a/test/index.js b/test/index.js index 3924ef1..cc7997e 100644 --- a/test/index.js +++ b/test/index.js @@ -70,18 +70,18 @@ tape('sign and verify a hmaced object javascript object', function (t) { hmac_key = hmac_key.toString('base64') hmac_key2 = hmac_key2.toString('base64') - var keys = ssbkeys.generate() - var sig = ssbkeys.signObj(keys.private, hmac_key, obj) + var otherKeys = ssbkeys.generate() + var otherSig = ssbkeys.signObj(otherKeys.private, hmac_key, obj) console.log(sig) t.ok(sig) //verify must be passed the key to correctly verify - t.notOk(ssbkeys.verifyObj(keys, sig)) - t.notOk(ssbkeys.verifyObj({public: keys.public}, sig)) + t.notOk(ssbkeys.verifyObj(otherKeys, otherSig)) + t.notOk(ssbkeys.verifyObj({public: otherKeys.public}, otherSig)) t.ok(ssbkeys.verifyObj(keys, hmac_key, sig)) - t.ok(ssbkeys.verifyObj({public: keys.public}, hmac_key, sig)) + t.ok(ssbkeys.verifyObj({public: otherKeys.public}, hmac_key, otherSig)) //a different hmac_key fails to verify - t.notOk(ssbkeys.verifyObj(keys, hmac_key2, sig)) - t.notOk(ssbkeys.verifyObj({public: keys.public}, hmac_key2, sig)) + t.notOk(ssbkeys.verifyObj(otherKeys, hmac_key2, otherSig)) + t.notOk(ssbkeys.verifyObj({public: otherKeys.public}, hmac_key2, otherSig)) t.end() @@ -108,15 +108,32 @@ tape('ed25519 id === "@" ++ pubkey', function (t) { }) - - - - - - - - - - - +tape('alternative "test" feed type', function (t) { + const newFeedType = { + name: 'test', + object: { + generate: () => { + return { + name: newFeedType.name, + public: crypto.randomBytes(32).toString('hex'), + private: crypto.randomBytes(32).toString('hex') + } + }, + sign: (privateKey, message) => message, + verify: () => true + } + } + + ssbkeys.use(newFeedType.name, newFeedType.object) + + const keys = ssbkeys.generate(newFeedType.name) + t.assert(keys.id.endsWith(newFeedType.name), 'using new test feed type') + + const signedFoo = ssbkeys.signObj(keys, 'foo') + t.assert(signedFoo, 'foo', 'signature works') + + const isValid = ssbkeys.verifyObj(keys, 'foo') + t.assert(isValid, true, 'verification works') + t.end() +}) diff --git a/util.js b/util.js index f4e71d1..628f22c 100644 --- a/util.js +++ b/util.js @@ -4,8 +4,8 @@ var cl = require('chloride') exports.hash = function (data, enc) { data = ( 'string' === typeof data && enc == null - ? new Buffer(data, 'binary') - : new Buffer(data, enc) + ? Buffer.from(data, 'binary') + : Buffer.from(data, enc) ) return cl.crypto_hash_sha256(data).toString('base64')+'.sha256' } @@ -14,32 +14,35 @@ exports.hasSigil = function hasSigil (s) { return /^(@|%|&)/.test(s) } -function tag (key, tag) { - if(!tag) throw new Error('no tag for:' + key.toString('base64')) - return key.toString('base64')+'.' + tag.replace(/^\./, '') +function setFeedType (key, feedType) { + if(!feedType) throw new Error('no feed type for:' + key.toString('base64')) + return key.toString('base64')+'.' + feedType.replace(/^\./, '') } -exports.keysToJSON = function keysToJSON(keys, curve) { - curve = (keys.curve || curve) +exports.keysToJSON = function keysToJSON(keys, feedType) { + feedType = keys.feedType || feedType - var pub = tag(keys.public, curve) + var pub = setFeedType(keys.public, feedType) return { - curve: curve, + curve: feedType, // deprecated + feedType, public: pub, - private: keys.private ? tag(keys.private, curve) : undefined, + private: keys.private ? setFeedType(keys.private, feedType) : undefined, id: '@' + pub } } -exports.getTag = function getTag (string) { +exports.getFeedType = function getFeedType (string) { var i = string.indexOf('.') return string.substring(i+1) } +exports.getSuffix = exports.getFeedType // deprecated + exports.toBuffer = function (buf) { if(buf == null) return buf if(Buffer.isBuffer(buf)) return buf var i = buf.indexOf('.') var start = (exports.hasSigil(buf)) ? 1 : 0 - return new Buffer(buf.substring(start, ~i ? i : buf.length), 'base64') + return Buffer.from(buf.substring(start, ~i ? i : buf.length), 'base64') }