Skip to content

Commit

Permalink
v0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
rjrodger committed May 19, 2020
1 parent b78ef72 commit 8dd30ff
Show file tree
Hide file tree
Showing 10 changed files with 707 additions and 843 deletions.
9 changes: 3 additions & 6 deletions apikey-doc.js
@@ -1,33 +1,30 @@
module.exports = function(seneca,util) {
module.exports = function (seneca, util) {
var Joi = util.Joi

return {
generate_key: {
desc: 'Generate a new API key.',
reply_desc: {
ok: '`true` if successful',
key: 'key string'
key: 'key string',
},
validate: {
owner: Joi.string().required(),
scope: Joi.string().default('default'),

},
},


verify_key: {
desc: 'Verify an API key.',
reply_desc: {
ok: '`true` if verified',
why: 'explanation code'
why: 'explanation code',
},
validate: {
owner: Joi.string().required(),
scope: Joi.string().default('default'),
key: Joi.string().required(),
},
},

}
}
97 changes: 47 additions & 50 deletions apikey.js
Expand Up @@ -7,13 +7,11 @@ const Crypto = require('crypto')

const intern = (apikey.intern = make_intern())


module.exports = apikey

module.exports.errors = {}
module.exports.doc = require('./apikey-doc')


module.exports.defaults = {
test: false,
keysize: 32, // does not include tag
Expand All @@ -24,36 +22,33 @@ module.exports.defaults = {

salt: {
bytelen: 16,
format: 'hex'
format: 'hex',
},

pepper: '',
generate_salt: intern.generate_salt

generate_salt: intern.generate_salt,
}

function apikey(options) {
var seneca = this
var ctx = intern.make_ctx({}, options)


// TODO: generate owner id from db id
// TODO: publish keys, accept keys



seneca
.fix('sys:apikey')
.message('generate:key', intern.make_msg('generate_key', ctx))
.message('verify:key', intern.make_msg('verify_key', ctx))
}


function make_intern() {
return {
make_msg: function (msg_fn, ctx) {
Assert(msg_fn)
Assert(ctx)

return require('./lib/' + msg_fn)(ctx)
},

Expand All @@ -73,101 +68,104 @@ function make_intern() {
hash_version: '001',

// Base64 padding length
base64padlen: intern.calculate_base64padlen(options.keysize)
base64padlen: intern.calculate_base64padlen(options.keysize),
},
initial_ctx
)
},

make_hash: async function(spec) {
make_hash: async function (spec) {
Assert(spec)

var start = process.hrtime()

spec.salt = spec.salt || spec.options.generate_salt(spec.options)

var out = {
ok: true,
pass: intern.runhash({
src: intern.make_key_src(spec),
rounds: spec.options.rounds
rounds: spec.options.rounds,
}),
salt: spec.salt
salt: spec.salt,
}

var dur = process.hrtime(start)
out.tn_gen = dur[0] * 1e9 + dur[1]

return out
},

verify_hash: async function(spec) {
verify_hash: async function (spec) {
Assert(spec)

var start = process.hrtime()

var hash = intern.runhash({
src: intern.make_key_src(spec),
rounds: spec.options.rounds
rounds: spec.options.rounds,
})

var out = {
ok: hash === spec.pass
ok: hash === spec.pass,
}

out.why = out.ok ? void 0 : 'no-match'

var dur = process.hrtime(start)
out.tn_vfy = dur[0] * 1e9 + dur[1]

return out
},


runhash: function(spec) {
runhash: function (spec) {
var out = spec.src

for (var i = 0; i < spec.rounds; i++) {
var shasum = Crypto.createHash('sha512')
shasum.update(out, 'utf8')
out = shasum.digest('hex')
}

return out
},


make_key_src: function(spec) {
make_key_src: function (spec) {
var fullcore =
'~'+
spec.tag+'~'+
spec.version+'~'+
spec.core+'~'+
spec.scope+'~'+
spec.owner+
'~'
'~' +
spec.tag +
'~' +
spec.version +
'~' +
spec.core +
'~' +
spec.scope +
'~' +
spec.owner +
'~'

// NOTE: remain compatible with @seneca/user
return spec.options.pepper+fullcore+spec.salt
return spec.options.pepper + fullcore + spec.salt
},

make_key_id: function(owner, tag) {
return owner+'~'+tag
make_key_id: function (owner, tag) {
return owner + '~' + tag
},

generate_salt: function(options) {
generate_salt: function (options) {
return Crypto.randomBytes(options.salt.bytelen).toString(
options.salt.format
)
},

// base64 padding is removed, string is URL-safed
generate_core: async function(keysize, base64padlen) {
var core = (await Util.promisify(Crypto.randomBytes)(keysize))
.toString('base64')
var out = core.substring(0, core.length-base64padlen)
out = out.replace(/\+/g,'-')
out = out.replace(/\//g,'_')
generate_core: async function (keysize, base64padlen) {
var core = (await Util.promisify(Crypto.randomBytes)(keysize)).toString(
'base64'
)
var out = core.substring(0, core.length - base64padlen)
out = out.replace(/\+/g, '-')
out = out.replace(/\//g, '_')
return out
},

Expand All @@ -181,13 +179,12 @@ function make_intern() {
return out
},
*/



// NOTE: https://stackoverflow.com/questions/13378815/base64-length-calculation/13378842
calculate_base64padlen: function(keysize) {
var base64len = 4*keysize/3
var padlen = Math.round(3*(Math.ceil(base64len)-base64len))
calculate_base64padlen: function (keysize) {
var base64len = (4 * keysize) / 3
var padlen = Math.round(3 * (Math.ceil(base64len) - base64len))
return padlen
}
},
}
}
22 changes: 10 additions & 12 deletions lib/generate_key.js
Expand Up @@ -5,48 +5,46 @@ const Assert = require('assert')

const Nid = require('nid')


module.exports = (ctx) => {
Assert(ctx)

const intern = ctx.intern
const options = ctx.options
const base64padlen = ctx.base64padlen

// Internal version, not encoded in key
var version = ctx.hash_version

var make_tag = Nid({length:options.tagsize})
var make_tag = Nid({ length: options.tagsize })


return async function generate_key(msg) {
var seneca = this

// unique identifier for owner
var owner = msg.owner

// text string naming application dependent scope
var scope = msg.scope

var out = { ok: false }

var core = await intern.generate_core(options.keysize, base64padlen)

var tag = make_tag()

var key = tag+'.'+core
var key = tag + '.' + core

var hashres = await intern.make_hash({
seneca,
options,
tag,
version,
core,
scope,
owner
owner,
})

if(!hashres.ok) {
if (!hashres.ok) {
out.why = hashres.why
return out
}
Expand All @@ -64,15 +62,15 @@ module.exports = (ctx) => {
tn_gen: hashres.tn_gen,
tn_vfy_hi: 0, // verify high water mark
tn_vfy_lo: Number.MAX_SAFE_INTEGER, // verify low water mark

// Count passes and fails when verifying
n_pass_key: 0,
n_fail_key: 0,
})

out.ok = true
out.key = key

return out
}
}

0 comments on commit 8dd30ff

Please sign in to comment.