Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add CustomKey for a customized signing and verifying #52

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/jwk/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const Key = require('./key/base')
const CustomKey = require('./key/custom')
const importKey = require('./import')
const { generate, generateSync } = require('./generate')

module.exports.asKey = importKey
module.exports.generate = generate
module.exports.generateSync = generateSync
module.exports.isKey = input => input instanceof Key
module.exports.CustomKey = CustomKey

/* deprecated */
Object.defineProperty(module.exports, 'importKey', {
Expand Down
9 changes: 7 additions & 2 deletions lib/jwk/key/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const thumbprint = require('../thumbprint')
const errors = require('../../errors')

class Key {
constructor (keyObject, { alg, use, kid, key_ops: ops, x5c, x5t, 'x5t#S256': x5t256 } = {}) {
constructor (keyObject, { alg, use, kid, key_ops: ops, x5c, x5t, 'x5t#S256': x5t256, custom } = {}) {
if (use !== undefined) {
if (typeof use !== 'string' || !USES.has(use)) {
throw new TypeError('`use` must be either "sig" or "enc" string when provided')
Expand Down Expand Up @@ -76,6 +76,10 @@ class Key {
})
}

if (custom === undefined) {
custom = false
}

Object.defineProperties(this, {
[KEYOBJECT]: { value: isObject(keyObject) ? undefined : keyObject },
type: { value: keyObject.type },
Expand Down Expand Up @@ -129,7 +133,8 @@ class Key {
return this.thumbprint
},
configurable: true
}
},
custom: { value: custom }
})
}

Expand Down
50 changes: 50 additions & 0 deletions lib/jwk/key/custom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const {
JWK_MEMBERS, PUBLIC_MEMBERS, PRIVATE_MEMBERS
} = require('../../help/consts')

const Key = require('./base')

const CUSTOM_PUBLIC = new Set([])
Object.freeze(CUSTOM_PUBLIC)
const CUSTOM_PRIVATE = new Set([...CUSTOM_PUBLIC])
Object.freeze(CUSTOM_PRIVATE)

class CustomKeyObject {
constructor (params) {
params = params || {}

this._type = params.keyType
this._symmetricKeySize = params.symmetricKeySize || 0
}

get type () {
return this._type
}

get symmetricKeySize () {
return this._symmetricKeySize
}
}

// Custom Key Type
class CustomKey extends Key {
constructor (params) {
// { alg, use, kid, key_ops: ops, x5c, x5t, 'x5t#S256', keyType, symmetricKeySize } = {}
super(new CustomKeyObject(params), Object.assign(params || {}, { custom: true }))
this[JWK_MEMBERS]()
}

static get [PUBLIC_MEMBERS] () {
return CUSTOM_PUBLIC
}

static get [PRIVATE_MEMBERS] () {
return CUSTOM_PRIVATE
}

algorithms (operation, /* second argument is private API */ { use = this.use, alg = this.alg, key_ops: ops = this.key_ops } = {}) {
// Should override
}
}

module.exports = CustomKey
7 changes: 6 additions & 1 deletion lib/jws/sign.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ class Sign {

recipient.header = unprotectedHeader
recipient.protected = Object.keys(joseHeader.protected).length ? base64url.JSON.encode(joseHeader.protected) : ''
recipient.signature = base64url.encodeBuffer(sign(alg, key, Buffer.from(`${recipient.protected}.${i(this).payload}`)))

if (key.custom) {
recipient.signature = base64url.encodeBuffer(key.sign(alg, Buffer.from(`${recipient.protected}.${i(this).payload}`)))
} else {
recipient.signature = base64url.encodeBuffer(sign(alg, key, Buffer.from(`${recipient.protected}.${i(this).payload}`)))
}
}

/*
Expand Down
10 changes: 8 additions & 2 deletions lib/jws/verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,14 @@ const jwsVerify = (skipDisjointCheck, serialization, jws, key, { crit = [], comp

check(key, 'verify', alg)

if (!verify(alg, key, Buffer.from([prot, payload].join('.')), base64url.decodeToBuffer(signature))) {
throw new errors.JWSVerificationFailed()
if (key.custom) {
if (!key.verify(alg, Buffer.from([prot, payload].join('.')), base64url.decodeToBuffer(signature))) {
throw new errors.JWSVerificationFailed()
}
} else {
if (!verify(alg, key, Buffer.from([prot, payload].join('.')), base64url.decodeToBuffer(signature))) {
throw new errors.JWSVerificationFailed()
}
}

if (!combinedHeader.crit || !combinedHeader.crit.includes('b64') || combinedHeader.b64) {
Expand Down
7 changes: 7 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export namespace JWK {
x5t?: string;
'x5t#S256'?: string;

custom: boolean;

toPEM(private?: boolean, encoding?: pemEncodingOptions): string;

algorithms(operation?: keyOperation): Set<string>;
Expand Down Expand Up @@ -144,6 +146,11 @@ export namespace JWK {
toJWK(private?: boolean): JWKOctKey;
}

class CustomKey extends Key {
sign(alg: string, buffer: Buffer): Buffer;
verify(alg: string, buffer: Buffer): boolean;
}

type KeyInput = PrivateKeyInput | PublicKeyInput | string | Buffer;

function isKey(object: any): boolean;
Expand Down