Skip to content

Commit

Permalink
refactor: DRY
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Aug 4, 2021
1 parent b0a5b17 commit daf1c29
Show file tree
Hide file tree
Showing 22 changed files with 158 additions and 319 deletions.
6 changes: 3 additions & 3 deletions lib/general/decode.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module.exports = (token, /* second arg is private API */ { parse = true } = {})
const { 0: version, 1: purpose, 2: payload, 3: footer, length } = token.split('.')

if (length !== 3 && length !== 4) {
throw new PasetoInvalid('token value is not a PASETO formatted value')
throw new PasetoInvalid('token is not a PASETO formatted value')
}

if (version !== 'v1' && version !== 'v2' && version !== 'v3' && version !== 'v4') {
Expand All @@ -37,8 +37,8 @@ module.exports = (token, /* second arg is private API */ { parse = true } = {})
let raw
try {
raw = decode(payload).subarray(0, -sigLength)
} catch (err) {
throw new PasetoInvalid('token value is not a PASETO formatted value')
} catch {
throw new PasetoInvalid('token is not a PASETO formatted value')
}

if (!parse) {
Expand Down
56 changes: 56 additions & 0 deletions lib/help/consume.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const assert = require('assert')

const { PasetoInvalid } = require('../errors')
const assertPayload = require('./assert_payload')
const { decode } = require('./base64url')
const parse = require('./parse_paseto_payload')

function pre(h, token) {
if (typeof token !== 'string') {
throw new TypeError(`token must be a string, got: ${typeof token}`)
}

if (token.substr(0, h.length) !== h) {
throw new PasetoInvalid(`token is not a ${h.substr(0, h.length - 1)} PASETO`)
}

let { 0: raw, 1: f = '', length } = token.substr(h.length).split('.')

try {
assert(length <= 2)
raw = decode(raw)
f = decode(f)
} catch {
throw new PasetoInvalid('token is not a PASETO formatted value')
}

return { raw, f }
}

function post(version, buffer, options, complete, m, f, purpose) {
if (buffer) {
if (Object.keys(options).length !== 0) {
throw new TypeError('options cannot contain claims when options.buffer is true')
}
if (complete) {
return { payload: m, footer: f.length ? f : undefined, version, purpose }
}

return m
}

const payload = parse(m)

assertPayload(options, payload)

if (complete) {
return { payload, footer: f.length ? f : undefined, version, purpose }
}

return payload
}

module.exports = {
post,
pre,
}
2 changes: 1 addition & 1 deletion lib/help/parse_paseto_payload.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = (payload) => {
const parsed = JSON.parse(payload)
assert(isObject(parsed))
return parsed
} catch (err) {
} catch {
throw new PasetoInvalid('All PASETO payloads MUST be a JSON object')
}
}
31 changes: 5 additions & 26 deletions lib/help/verify.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,14 @@
const { PasetoInvalid, PasetoVerificationFailed } = require('../errors')
const { PasetoVerificationFailed } = require('../errors')

const { decode } = require('./base64url')
const { verify } = require('./crypto_worker')
const pae = require('./pae')
const { pre } = require('./consume')

module.exports = async function verifyPaseto(h, token, alg, sigLength, key, i, eo) {
if (typeof token !== 'string') {
throw new TypeError('token must be a string')
}

if (token.substr(0, h.length) !== h) {
throw new PasetoInvalid(`token is not a ${h.slice(0, -1)} token`)
}

const { 0: b64ms, 1: b64f, length } = token.substr(h.length).split('.')
if (length !== 1 && length !== 2) {
throw new PasetoInvalid('token value is not a PASETO formatted value')
}

let f
let ms

try {
ms = decode(b64ms)
f = decode(b64f || '')
} catch (err) {
throw new PasetoInvalid('token value is not a PASETO formatted value')
}
const { raw, f } = pre(h, token)

const m = ms.subarray(0, -sigLength)
const s = ms.subarray(-sigLength)
const m = raw.subarray(0, -sigLength)
const s = raw.subarray(-sigLength)
const m2 = pae(eo, h, m, f, i)

if (!(await verify(alg, m2, key, s))) {
Expand Down
45 changes: 3 additions & 42 deletions lib/v1/decrypt.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
const { decode } = require('../help/base64url')
const { 'v1.local-decrypt': decrypt } = require('../help/crypto_worker')
const { PasetoInvalid } = require('../errors')
const assertPayload = require('../help/assert_payload')
const checkKey = require('../help/symmetric_key_check').bind(undefined, 'v1.local')
const parse = require('../help/parse_paseto_payload')
const { pre, post } = require('../help/consume')

const h = 'v1.local.'

Expand All @@ -12,45 +9,9 @@ module.exports = async function v1Decrypt(
key,
{ complete = false, buffer = false, ...options } = {},
) {
if (typeof token !== 'string') {
throw new TypeError(`token must be a string, got: ${typeof token}`)
}

const { raw, f } = pre(h, token)
key = checkKey(key)

if (token.substr(0, h.length) !== h) {
throw new PasetoInvalid('token is not a v1.local PASETO')
}

const { 0: b64, 1: b64f = '', length } = token.substr(h.length).split('.')
if (length > 2) {
throw new PasetoInvalid('token value is not a PASETO formatted value')
}

const f = decode(b64f)
const raw = decode(b64)
const k = key.export()

const m = await decrypt(raw, f, k)

if (buffer) {
if (Object.keys(options).length !== 0) {
throw new TypeError('options cannot contain claims when options.buffer is true')
}
if (complete) {
return { payload: m, footer: f.length ? f : undefined, version: 'v1', purpose: 'local' }
}

return m
}

const payload = parse(m)

assertPayload(options, payload)

if (complete) {
return { payload, footer: f.length ? f : undefined, version: 'v1', purpose: 'local' }
}

return payload
return post('v1', buffer, options, complete, m, f, 'local')
}
23 changes: 2 additions & 21 deletions lib/v1/verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ const {
createPublicKey,
} = require('crypto')

const assertPayload = require('../help/assert_payload')
const parse = require('../help/parse_paseto_payload')
const verify = require('../help/verify')
const isKeyObject = require('../help/is_key_object')
const { post } = require('../help/consume')

function checkKey(key) {
if (!isKeyObject(key) || key.type === 'private') {
Expand Down Expand Up @@ -45,23 +44,5 @@ module.exports = async function v1Verify(
saltLength,
})

if (buffer) {
if (Object.keys(options).length !== 0) {
throw new TypeError('options cannot contain claims when options.buffer is true')
}
if (complete) {
return { payload: m, footer, version: 'v1', purpose: 'public' }
}

return m
}

const payload = parse(m)
assertPayload(options, payload)

if (complete) {
return { payload, footer, version: 'v1', purpose: 'public' }
}

return payload
return post('v1', buffer, options, complete, m, footer, 'public')
}
50 changes: 50 additions & 0 deletions lib/v2/key.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,54 @@ const isKeyObject = require('../help/is_key_object')

const generateKeyPair = promisify(crypto.generateKeyPair)

function _checkPrivateKey(v, key) {
if (Buffer.isBuffer(key)) {
try {
key = bytesToKeyObject(key)
} catch {}
}

if (!isKeyObject(key)) {
try {
key = crypto.createPrivateKey(key)
} catch {}
}

if (!isKeyObject(key)) {
throw new TypeError('invalid key provided')
}

if (key.type !== 'private' || key.asymmetricKeyType !== 'ed25519') {
throw new TypeError(`${v}.public signing key must be a private ed25519 key`)
}

return key
}

function _checkPublicKey(v, key) {
if (Buffer.isBuffer(key)) {
try {
key = bytesToKeyObject(key)
} catch {}
}

if (!isKeyObject(key) || key.type === 'private') {
try {
key = crypto.createPublicKey(key)
} catch {}
}

if (!isKeyObject(key)) {
throw new TypeError('invalid key provided')
}

if (key.type !== 'public' || key.asymmetricKeyType !== 'ed25519') {
throw new TypeError(`${v}.public verify key must be a public ed25519 key`)
}

return key
}

async function _generateKey(v, purpose) {
switch (purpose) {
case 'public': {
Expand Down Expand Up @@ -78,6 +126,8 @@ function keyObjectToBytes(...args) {
}

module.exports = {
_checkPrivateKey,
_checkPublicKey,
_generateKey,
_keyObjectToBytes,
bytesToKeyObject,
Expand Down
29 changes: 2 additions & 27 deletions lib/v2/sign.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,9 @@
const { createPrivateKey } = require('crypto')

const checkFooter = require('../help/check_footer')
const checkPayload = require('../help/check_payload')
const sign = require('../help/sign')
const isKeyObject = require('../help/is_key_object')
const { bytesToKeyObject } = require('./key')

function checkKey(key) {
if (Buffer.isBuffer(key)) {
try {
key = bytesToKeyObject(key)
} catch {}
}

if (!isKeyObject(key)) {
try {
key = createPrivateKey(key)
} catch {}
}
const { _checkPrivateKey } = require('./key')

if (!isKeyObject(key)) {
throw new TypeError('invalid key provided')
}

if (key.type !== 'private' || key.asymmetricKeyType !== 'ed25519') {
throw new TypeError('v2.public signing key must be a private ed25519 key')
}

return key
}
const checkKey = _checkPrivateKey.bind(undefined, 'v2')

module.exports = async function v2Sign(payload, key, { footer, ...options } = {}) {
const m = checkPayload(payload, options)
Expand Down
52 changes: 4 additions & 48 deletions lib/v2/verify.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,8 @@
const { createPublicKey } = require('crypto')

const assertPayload = require('../help/assert_payload')
const parse = require('../help/parse_paseto_payload')
const verify = require('../help/verify')
const isKeyObject = require('../help/is_key_object')
const { bytesToKeyObject } = require('./key')

function checkKey(key) {
if (Buffer.isBuffer(key)) {
try {
key = bytesToKeyObject(key)
} catch {}
}

if (!isKeyObject(key) || key.type === 'private') {
try {
key = createPublicKey(key)
} catch {}
}

if (!isKeyObject(key)) {
throw new TypeError('invalid key provided')
}

if (key.type !== 'public' || key.asymmetricKeyType !== 'ed25519') {
throw new TypeError('v2.public verify key must be a public ed25519 key')
}
const { _checkPublicKey } = require('./key')
const { post } = require('../help/consume')

return key
}
const checkKey = _checkPublicKey.bind(undefined, 'v2')

module.exports = async function v2Verify(
token,
Expand All @@ -39,23 +13,5 @@ module.exports = async function v2Verify(

const { m, footer } = await verify('v2.public.', token, undefined, 64, key)

if (buffer) {
if (Object.keys(options).length !== 0) {
throw new TypeError('options cannot contain claims when options.buffer is true')
}
if (complete) {
return { payload: m, footer, version: 'v2', purpose: 'public' }
}

return m
}

const payload = parse(m)
assertPayload(options, payload)

if (complete) {
return { payload, footer, version: 'v2', purpose: 'public' }
}

return payload
return post('v2', buffer, options, complete, m, footer, 'public')
}
Loading

0 comments on commit daf1c29

Please sign in to comment.