From db4b6ed97bb401b2b3557296acca21b458186978 Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Tue, 9 Jul 2019 14:49:18 -0700 Subject: [PATCH 01/11] Add API changes to readme --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d805190..6730152 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ var keys = ssbkeys.loadOrCreateSync(filename) }*/ //but for testing, .generate() is useful. -var keys = ssbkeys.generate() +var keys = ssbkeys.generate('feedType') /* => { id: String, public: String, @@ -34,7 +34,7 @@ ssbkeys.verifyObj(k, hmac_key, obj) // => true ## api -### loadOrCreateSync (filename) +### loadOrCreateSync (filename, feedType) Load a file containing the your private key. the file will also contain a comment with a warning about keeping the file secret. @@ -47,10 +47,12 @@ variations and parts `loadOrCreate` (async), `load`, `create` `createSync` `loadSync`. But since you only need to load once, using the combined function is easiest. -### generate(curve, seed) +The `feedType` argument is optional and defaults to `ed25519`. + +### generate(feedType, seed) 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. ### signObj(keys, hmac_key?, obj) From 5171233209c97d156366e731aacec3a8dbb95211 Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Tue, 30 Jul 2019 14:11:33 -0700 Subject: [PATCH 02/11] Refactor syntax for clarity --- index.js | 14 +++++++------- local-storage.js | 1 - storage.js | 2 +- test/box-unbox.js | 1 - test/fs.js | 1 - test/index.js | 14 +++++++------- 6 files changed, 15 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index ae5f3ba..a9d7376 100644 --- a/index.js +++ b/index.js @@ -28,9 +28,6 @@ function isObject (o) { return 'object' === typeof o } -function isFunction (f) { - return 'function' === typeof f -} function isString(s) { return 'string' === typeof s @@ -163,7 +160,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 +173,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 +188,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..b237726 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 diff --git a/storage.js b/storage.js index f07387e..b8b717d 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. diff --git a/test/box-unbox.js b/test/box-unbox.js index 2c9f05f..1ac5aae 100644 --- a/test/box-unbox.js +++ b/test/box-unbox.js @@ -16,7 +16,6 @@ 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..e57a37c 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() From 79c0e9859fa9044d0627d6db97f3b81536462b9c Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 1 Aug 2019 11:45:57 -0700 Subject: [PATCH 03/11] Add new feed types --- README.md | 16 +++++++--- index.js | 81 +++++++++++++++++++++++++++++++----------------- local-storage.js | 12 +++---- sodium.js | 8 +---- storage.js | 12 +++---- test/index.js | 39 ++++++++++++++++------- util.js | 18 +++++------ 7 files changed, 113 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 3bbc25b..3f1c0af 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ in the below methods, `keys` is an object of the following form: ``` js { - "curve": "ed25519", + "feedType": "ed25519", "public": ".ed25519", "private": ".ed25519", "id": "@.ed25519" @@ -56,8 +56,8 @@ Comment lines are prefixed with `#` after removing them the result is valid JSON ### 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. @@ -85,14 +85,16 @@ If a sync file access method is not available, `loadOrCreate` can be called with 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) +feed type defaults to `ed25519` (and no other type is currently supported) 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. @@ -145,6 +147,10 @@ symmetrically encrypt an object with `key` (a buffer) symmetrically decrypt an object with `key` (a buffer) +### use(feedTypeName, { 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 a9d7376..38a3ffe 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,8 +19,6 @@ var hmac = sodium.crypto_auth exports.hash = u.hash -exports.getTag = u.getTag - function isObject (o) { return 'object' === typeof o } @@ -33,38 +28,63 @@ function isString(s) { return 'string' === typeof s } -var curves = {} -curves.ed25519 = require('./sodium') +const feedTypes = { + ed25519: 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.every(methodName => { + typeof object[methodName] === 'function' + }) -function getCurve(keys) { - var curve = keys.curve + if (isInvalidObject) { + const expectedMethods = requiredMethods.join(', ') + throw new Error(`Invalid objectMissing required methods, expected: ${expectedMethods}`) + } - if(!keys.curve && isString(keys.public)) + if (feedTypes[name] != null) { + throw new Error(`Duplicate feed type: "${name}"`) + } + + feedTypes[name] = object +} + +function getFeedType(keys) { + let { feedType } = keys + + if(!keys.feedType && isString(keys.public)) keys = keys.public - if(!curve && isString(keys)) - curve = u.getTag(keys) + if(!feedType && isString(keys)) + feedType = u.getSuffix(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, 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 @@ -96,11 +116,14 @@ function sign (keys, msg) { msg = new Buffer(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 } @@ -109,7 +132,7 @@ 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) diff --git a/local-storage.js b/local-storage.js index b237726..63d133d 100644 --- a/local-storage.js +++ b/local-storage.js @@ -6,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 } @@ -18,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/sodium.js b/sodium.js index c948074..1bd8837 100644 --- a/sodium.js +++ b/sodium.js @@ -2,15 +2,12 @@ var sodium = require('chloride') module.exports = { - - curves: ['ed25519'], - generate: function (seed) { if(!seed) sodium.randombytes(seed = new Buffer(32)) var keys = seed ? sodium.crypto_sign_seed_keypair(seed) : sodium.crypto_sign_keypair() return { - curve: 'ed25519', + feedType: 'ed25519', public: keys.publicKey, //so that this works with either sodium @@ -18,15 +15,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 b8b717d..6a0043a 100644 --- a/storage.js +++ b/storage.js @@ -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/index.js b/test/index.js index e57a37c..cc7997e 100644 --- a/test/index.js +++ b/test/index.js @@ -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..8d85295 100644 --- a/util.js +++ b/util.js @@ -14,24 +14,24 @@ 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 feedType 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, + 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.getSuffix = function (string) { var i = string.indexOf('.') return string.substring(i+1) } From a0ee48afb34ca093f22651e256f18d373a8cc24c Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 1 Aug 2019 12:01:08 -0700 Subject: [PATCH 04/11] Remove unnecessary feedType declaration --- sodium.js | 1 - 1 file changed, 1 deletion(-) diff --git a/sodium.js b/sodium.js index 1bd8837..195fbf8 100644 --- a/sodium.js +++ b/sodium.js @@ -7,7 +7,6 @@ module.exports = { var keys = seed ? sodium.crypto_sign_seed_keypair(seed) : sodium.crypto_sign_keypair() return { - feedType: 'ed25519', public: keys.publicKey, //so that this works with either sodium From a3e16116644a37a0f556652209a9f62f5434d5c5 Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 22 Aug 2019 13:33:57 -0700 Subject: [PATCH 05/11] Refactor to use `curve` rather than `feedType` --- README.md | 8 +- index.js | 40 +++---- local-storage.js | 12 +-- package-lock.json | 264 ++++++++++++++++++++++++++++++++++++++++++++++ storage.js | 12 +-- util.js | 16 +-- 6 files changed, 308 insertions(+), 44 deletions(-) create mode 100644 package-lock.json diff --git a/README.md b/README.md index 3f1c0af..db93ce8 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ in the below methods, `keys` is an object of the following form: ``` js { - "feedType": "ed25519", + "curve": "ed25519", "public": ".ed25519", "private": ".ed25519", "id": "@.ed25519" @@ -56,7 +56,7 @@ Comment lines are prefixed with `#` after removing them the result is valid JSON ### hash (data, encoding) => id Returns the sha256 hash of a given data. If encoding is not provided then it is assumed to be _binary_. -### getFeedType (ssbId) => feedType +### getFeedType (ssbId) => curve 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. @@ -85,7 +85,7 @@ If a sync file access method is not available, `loadOrCreate` can be called with callback. that callback will be called with `cb(null, keys)`. If loading the keys errored, new keys are created. -### generate(feedType, seed) => keys +### generate(curve, seed) => keys generate a key, with optional seed. feed type defaults to `ed25519` (and no other type is currently supported) @@ -147,7 +147,7 @@ symmetrically encrypt an object with `key` (a buffer) symmetrically decrypt an object with `key` (a buffer) -### use(feedTypeName, { generate, sign, verify }) => ssbKeys +### use(curveName, { generate, sign, verify }) => ssbKeys add new feed type to be used with `ssbKeys.generate()` diff --git a/index.js b/index.js index 38a3ffe..36803bc 100644 --- a/index.js +++ b/index.js @@ -28,7 +28,7 @@ function isString(s) { return 'string' === typeof s } -const feedTypes = { +const curves = { ed25519: require('./sodium'), } @@ -53,38 +53,38 @@ exports.use = (name, object) => { throw new Error(`Invalid objectMissing required methods, expected: ${expectedMethods}`) } - if (feedTypes[name] != null) { + if (curves[name] != null) { throw new Error(`Duplicate feed type: "${name}"`) } - feedTypes[name] = object + curves[name] = object } function getFeedType(keys) { - let { feedType } = keys + let { curve } = keys - if(!keys.feedType && isString(keys.public)) + if(!keys.curve && isString(keys.public)) keys = keys.public - if(!feedType && isString(keys)) - feedType = u.getSuffix(keys) + if(!curve && isString(keys)) + curve = u.getSuffix(keys) - if(!feedTypes[feedType]) { - throw new Error(`unkown feed type: "${feedType}", expected: "${Object.keys(feedTypes)}"`) + if(!curves[curve]) { + throw new Error(`unkown feed type: "${curve}", expected: "${Object.keys(curves)}"`) } - return feedType + return curve } //this should return a key pair: -// { feedType: string, public: Buffer, private: Buffer} -exports.generate = function (feedType, seed) { - feedType = feedType || 'ed25519' +// { curve: string, public: Buffer, private: Buffer} +exports.generate = function (curve, seed) { + curve = curve || 'ed25519' - if(feedTypes[feedType] == null) - throw new Error(`unknown feed type: "${feedType}"`) + if(curves[curve] == null) + throw new Error(`unknown feed type: "${curve}"`) - return u.keysToJSON(feedTypes[feedType].generate(seed), feedType) + return u.keysToJSON(curves[curve].generate(seed), curve) } //import functions for loading/saving keys from storage @@ -116,12 +116,12 @@ function sign (keys, msg) { msg = new Buffer(msg) if(!isBuffer(msg)) throw new Error('msg should be buffer') - var feedType = getFeedType(keys) + var curve = getFeedType(keys) - const prefix = feedTypes[feedType] + const prefix = curves[curve] .sign(u.toBuffer(keys.private || keys), msg) .toString('base64') - const suffix = `.sig.${feedType}` + const suffix = `.sig.${curve}` return prefix + suffix @@ -132,7 +132,7 @@ 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 feedTypes[getFeedType(keys)].verify( + return curves[getFeedType(keys)].verify( u.toBuffer(keys.public || keys), u.toBuffer(sig), isBuffer(msg) ? msg : new Buffer(msg) diff --git a/local-storage.js b/local-storage.js index 63d133d..b237726 100644 --- a/local-storage.js +++ b/local-storage.js @@ -6,8 +6,8 @@ function isFunction (f) { module.exports = function (generate) { - function create (filename, feedType, legacy) { - var keys = generate(feedType, legacy) + function create (filename, curve, legacy) { + var keys = generate(curve, legacy) localStorage[filename] = JSON.stringify(keys) return keys } @@ -18,12 +18,12 @@ module.exports = function (generate) { return { createSync: create, - create: function(filename, feedType, legacy, cb) { + create: function(filename, curve, legacy, cb) { if(isFunction(legacy)) cb = legacy, legacy = null - if(isFunction(feedType)) - cb = feedType, feedType = null - cb(null, create(filename, feedType, legacy)) + if(isFunction(curve)) + cb = curve, curve = null + cb(null, create(filename, curve, 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/storage.js b/storage.js index 6a0043a..b8b717d 100644 --- a/storage.js +++ b/storage.js @@ -78,14 +78,14 @@ module.exports = function (generate) { return reconstructKeys(fs.readFileSync(filename, 'ascii')) } - exports.create = function(filename, feedType, legacy, cb) { + exports.create = function(filename, curve, legacy, cb) { if(isFunction(legacy)) cb = legacy, legacy = null - if(isFunction(feedType)) - cb = feedType, feedType = null + if(isFunction(curve)) + cb = curve, curve = null filename = toFile(filename) - var keys = generate(feedType) + var keys = generate(curve) 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, feedType, legacy) { + exports.createSync = function(filename, curve, legacy) { filename = toFile(filename) - var keys = generate(feedType) + var keys = generate(curve) var keyfile = constructKeys(keys, legacy) mkdirp.sync(path.dirname(filename)) fs.writeFileSync(filename, keyfile, {mode: 0x100, flag: 'wx'}) diff --git a/util.js b/util.js index 8d85295..7c0637b 100644 --- a/util.js +++ b/util.js @@ -14,19 +14,19 @@ exports.hasSigil = function hasSigil (s) { return /^(@|%|&)/.test(s) } -function setFeedType (key, feedType) { - if(!feedType) throw new Error('no feedType for:' + key.toString('base64')) - return key.toString('base64')+'.' + feedType.replace(/^\./, '') +function setFeedType (key, curve) { + if(!curve) throw new Error('no curve for:' + key.toString('base64')) + return key.toString('base64')+'.' + curve.replace(/^\./, '') } -exports.keysToJSON = function keysToJSON(keys, feedType) { - feedType = keys.feedType || feedType +exports.keysToJSON = function keysToJSON(keys, curve) { + curve = keys.curve || curve - var pub = setFeedType(keys.public, feedType) + var pub = setFeedType(keys.public, curve) return { - feedType, + curve, public: pub, - private: keys.private ? setFeedType(keys.private, feedType) : undefined, + private: keys.private ? setFeedType(keys.private, curve) : undefined, id: '@' + pub } } From 153469761de5532d6e3571b012024fdcccb7b0f9 Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 22 Aug 2019 13:38:51 -0700 Subject: [PATCH 06/11] Fix backward method check logic --- index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 36803bc..16d0bc8 100644 --- a/index.js +++ b/index.js @@ -44,13 +44,14 @@ exports.use = (name, object) => { ] const isNotObject = typeof object !== 'object' - const isInvalidObject = isNotObject || requiredMethods.every(methodName => { - typeof object[methodName] === 'function' - }) + const isInvalidObject = isNotObject || requiredMethods.some(methodName => + typeof object[methodName] !== 'function' + ) if (isInvalidObject) { const expectedMethods = requiredMethods.join(', ') - throw new Error(`Invalid objectMissing required methods, expected: ${expectedMethods}`) + console.log(object) + throw new Error(`Invalid object. Missing required methods, expected: ${expectedMethods}`) } if (curves[name] != null) { From fb4be5cf71b345d153dd1e64b903e48e53f0cd62 Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 26 Sep 2019 12:33:33 -0700 Subject: [PATCH 07/11] Change dependency resolution to be static --- index.js | 7 +++++-- storage.js | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index ae5f3ba..0b46fed 100644 --- a/index.js +++ b/index.js @@ -72,7 +72,10 @@ exports.generate = function (curve, seed) { //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) { @@ -188,4 +191,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/storage.js b/storage.js index f07387e..d5c45ee 100644 --- a/storage.js +++ b/storage.js @@ -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 From da1aa08d3b45d397d6b840a140f22030ea5fe7be Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 26 Sep 2019 12:42:30 -0700 Subject: [PATCH 08/11] Finish transition to feedType with `getTag()` compatibility --- README.md | 4 ++-- index.js | 19 ++++++++----------- local-storage.js | 1 - sodium.js | 2 +- storage.js | 2 +- test/box-unbox.js | 3 --- test/fs.js | 1 - test/index.js | 4 ++-- util.js | 18 +++++++++--------- 9 files changed, 23 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 9467357..0caa2af 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,8 @@ Comment lines are prefixed with `#` after removing them the result is valid JSON ### 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 (ssb_id) => feedType +The SSB ids contain 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. diff --git a/index.js b/index.js index 0b46fed..6c9f6cf 100644 --- a/index.js +++ b/index.js @@ -22,16 +22,13 @@ 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 } @@ -46,7 +43,7 @@ function getCurve(keys) { keys = keys.public if(!curve && isString(keys)) - curve = u.getTag(keys) + curve = u.getFeedType(keys) if(!curves[curve]) { throw new Error( @@ -99,7 +96,7 @@ 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) @@ -118,7 +115,7 @@ function verify (keys, sig, msg) { return curves[getCurve(keys)].verify( u.toBuffer(keys.public || keys), u.toBuffer(sig), - isBuffer(msg) ? msg : new Buffer(msg) + isBuffer(msg) ? msg : Buffer.from(msg) ) } @@ -127,7 +124,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 @@ -138,13 +135,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)) diff --git a/local-storage.js b/local-storage.js index 1fc2bc8..b237726 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 diff --git a/sodium.js b/sodium.js index c948074..d833e2a 100644 --- a/sodium.js +++ b/sodium.js @@ -6,7 +6,7 @@ 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 { diff --git a/storage.js b/storage.js index d5c45ee..7eab70a 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. 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..47f066b 100644 --- a/test/index.js +++ b/test/index.js @@ -70,8 +70,8 @@ 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) + keys = ssbkeys.generate() + sig = ssbkeys.signObj(keys.private, hmac_key, obj) console.log(sig) t.ok(sig) //verify must be passed the key to correctly verify diff --git a/util.js b/util.js index f4e71d1..fa436e5 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,24 +14,24 @@ 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 feedType (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) - var pub = tag(keys.public, curve) + var pub = feedType(keys.public, curve) return { curve: curve, public: pub, - private: keys.private ? tag(keys.private, curve) : undefined, + private: keys.private ? feedType(keys.private, curve) : undefined, id: '@' + pub } } -exports.getTag = function getTag (string) { +exports.getFeedType = function getFeedType (string) { var i = string.indexOf('.') return string.substring(i+1) } @@ -41,5 +41,5 @@ exports.toBuffer = function (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') } From 37bcf09f4ac22173637933d9a4962d6694b075d5 Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 26 Sep 2019 12:57:52 -0700 Subject: [PATCH 09/11] Re-add ed25519.test feed type --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 6735c98..38fb7bf 100644 --- a/index.js +++ b/index.js @@ -32,6 +32,7 @@ function isString(s) { const curves = { ed25519: require('./sodium'), + 'ed25519.test': require('./sodium') } exports.use = (name, object) => { From 4b892583b9a340d0b4618c38a6d42e0a282a9a95 Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 26 Sep 2019 13:08:25 -0700 Subject: [PATCH 10/11] Finish rename from `curve` to `feedType` --- README.md | 7 +++++-- index.js | 41 +++++++++++++++++++++-------------------- local-storage.js | 12 ++++++------ storage.js | 12 ++++++------ util.js | 17 +++++++++-------- 5 files changed, 47 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index ee1b895..077042a 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,6 +54,8 @@ 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_. @@ -85,7 +88,7 @@ If a sync file access method is not available, `loadOrCreate` can be called with 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. @@ -148,7 +151,7 @@ symmetrically encrypt an object with `key` (a buffer) symmetrically decrypt an object with `key` (a buffer) -### use(curveName, { generate, sign, verify }) => ssbKeys +### use(feedType, { generate, sign, verify }) => ssbKeys add new feed type to be used with `ssbKeys.generate()` diff --git a/index.js b/index.js index 38fb7bf..4bb9c08 100644 --- a/index.js +++ b/index.js @@ -30,7 +30,7 @@ function isString(s) { return 'string' === typeof s } -const curves = { +const feedTypes = { ed25519: require('./sodium'), 'ed25519.test': require('./sodium') } @@ -57,38 +57,39 @@ exports.use = (name, object) => { throw new Error(`Invalid object. Missing required methods, expected: ${expectedMethods}`) } - if (curves[name] != null) { + if (feedTypes[name] != null) { throw new Error(`Duplicate feed type: "${name}"`) } - curves[name] = object + feedTypes[name] = object } function getFeedType(keys) { - let { curve } = 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.getFeedType(keys) + if(!feedType && isString(keys)) + feedType = u.getFeedType(keys) - if(!curves[curve]) { - throw new Error(`unkown feed type: "${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: string, public: Buffer, private: Buffer} -exports.generate = function (curve, seed) { - curve = curve || 'ed25519' +// { feedType: string, curve: string, public: Buffer, private: Buffer} +exports.generate = function (feedType, seed) { + feedType = feedType || 'ed25519' - if(curves[curve] == null) - throw new Error(`unknown feed type: "${curve}"`) + if(feedTypes[feedType] == null) + throw new Error(`unknown feed type: "${feedType}"`) - return u.keysToJSON(curves[curve].generate(seed), curve) + return u.keysToJSON(feedTypes[feedType].generate(seed), feedType) } //import functions for loading/saving keys from storage @@ -123,12 +124,12 @@ function sign (keys, msg) { msg = Buffer.from(msg) if(!isBuffer(msg)) throw new Error('msg should be buffer') - var curve = getFeedType(keys) + var feedType = getFeedType(keys) - const prefix = curves[curve] + const prefix = feedTypes[feedType] .sign(u.toBuffer(keys.private || keys), msg) .toString('base64') - const suffix = `.sig.${curve}` + const suffix = `.sig.${feedType}` return prefix + suffix @@ -139,7 +140,7 @@ 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[getFeedType(keys)].verify( + return feedTypes[getFeedType(keys)].verify( u.toBuffer(keys.public || keys), u.toBuffer(sig), isBuffer(msg) ? msg : Buffer.from(msg) diff --git a/local-storage.js b/local-storage.js index b237726..63d133d 100644 --- a/local-storage.js +++ b/local-storage.js @@ -6,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 } @@ -18,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/storage.js b/storage.js index 7eab70a..3a51e27 100644 --- a/storage.js +++ b/storage.js @@ -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/util.js b/util.js index bf6d560..628f22c 100644 --- a/util.js +++ b/util.js @@ -14,19 +14,20 @@ exports.hasSigil = function hasSigil (s) { return /^(@|%|&)/.test(s) } -function setFeedType (key, curve) { - if(!curve) throw new Error('no curve for:' + key.toString('base64')) - return key.toString('base64')+'.' + curve.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 = setFeedType(keys.public, curve) + var pub = setFeedType(keys.public, feedType) return { - curve, + curve: feedType, // deprecated + feedType, public: pub, - private: keys.private ? setFeedType(keys.private, curve) : undefined, + private: keys.private ? setFeedType(keys.private, feedType) : undefined, id: '@' + pub } } From 22a9efbca3d0ad1d0cbf194a3fa68c3c107abfc0 Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Thu, 26 Sep 2019 13:11:53 -0700 Subject: [PATCH 11/11] Remove broken code from readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 077042a..2af452c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ var keys = ssbkeys.loadOrCreateSync(filename) }*/ //but for testing, .generate() is useful. -var keys = ssbkeys.generate('feedType') +var keys = ssbkeys.generate() /* => { id: String, public: String,