jsontokens
Advanced tools
Comparing version 3.0.0-alpha.0 to 3.0.0-alpha.1
@@ -9,4 +9,3 @@ # Changelog | ||
### Changed | ||
- Web Crypto API used for hashing, if available. Otherwise uses the Node.js `crypto` module. | ||
- Several functions are now async promises. | ||
- Added async functions that use Web Crypto API used for hashing, if available. Otherwise uses the Node.js `crypto` module. | ||
@@ -13,0 +12,0 @@ ## [2.0.3] |
/// <reference types="node" /> | ||
import { ec as EC, BNInput } from 'elliptic'; | ||
import KeyEncoder from 'key-encoder'; | ||
export declare class SECP256K1Client { | ||
static ec: EC; | ||
static algorithmName: string; | ||
static keyEncoder: KeyEncoder; | ||
constructor(); | ||
private static getHashFunction; | ||
static createHash(signingInput: string | Buffer): Promise<Buffer>; | ||
static loadPrivateKey(rawPrivateKey: string): EC.KeyPair; | ||
static loadPublicKey(rawPublicKey: string | Buffer): EC.KeyPair; | ||
static encodePublicKey(publicKey: string | Buffer, originalFormat: 'raw' | 'pem' | 'der', destinationFormat: 'raw' | 'pem' | 'der'): string; | ||
static derivePublicKey(privateKey: string, compressed?: boolean): string; | ||
@@ -15,0 +10,0 @@ static signHash(signingInputHash: string | Buffer, rawPrivateKey: string, format?: string): string; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const elliptic_1 = require("elliptic"); | ||
const key_encoder_1 = require("key-encoder"); | ||
const ecdsa_sig_formatter_1 = require("ecdsa-sig-formatter"); | ||
@@ -19,36 +9,2 @@ const errors_1 = require("../errors"); | ||
} | ||
static getHashFunction() { | ||
try { | ||
const isSubtleCryptoAvailable = typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined'; | ||
if (isSubtleCryptoAvailable) { | ||
// Use the W3C Web Crypto API if available (running in a web browser). | ||
return (input) => __awaiter(this, void 0, void 0, function* () { | ||
const buffer = typeof input === 'string' ? Buffer.from(input) : input; | ||
const hash = yield crypto.subtle.digest('SHA-256', buffer); | ||
return Buffer.from(hash); | ||
}); | ||
} | ||
else { | ||
// Otherwise try loading the Node.js `crypto` module (running in Node.js, or an older browser with a polyfill). | ||
const nodeCrypto = require('crypto'); | ||
if (!nodeCrypto.createHash) { | ||
const error = new Error('`crypto` module does not contain `createHash`'); | ||
console.error(error); | ||
throw error; | ||
} | ||
return input => Promise.resolve(nodeCrypto.createHash('sha256').update(input).digest()); | ||
} | ||
} | ||
catch (error) { | ||
console.error(error); | ||
const missingCryptoError = new Error('Crypto lib not found. Neither the global `crypto.subtle` Web Crypto API, ' + | ||
'nor the or the Node.js `require("crypto").createHash` module is available.'); | ||
console.error(missingCryptoError); | ||
throw missingCryptoError; | ||
} | ||
} | ||
static createHash(signingInput) { | ||
const hashFunction = this.getHashFunction(); | ||
return hashFunction(signingInput); | ||
} | ||
static loadPrivateKey(rawPrivateKey) { | ||
@@ -63,5 +19,2 @@ if (rawPrivateKey.length === 66) { | ||
} | ||
static encodePublicKey(publicKey, originalFormat, destinationFormat) { | ||
return SECP256K1Client.keyEncoder.encodePublic(publicKey, originalFormat, destinationFormat); | ||
} | ||
static derivePublicKey(privateKey, compressed = true) { | ||
@@ -125,8 +78,2 @@ if (typeof privateKey !== 'string') { | ||
SECP256K1Client.algorithmName = 'ES256K'; | ||
SECP256K1Client.keyEncoder = new key_encoder_1.default({ | ||
curveParameters: [1, 3, 132, 0, 10], | ||
privatePEMOptions: { label: 'EC PRIVATE KEY' }, | ||
publicPEMOptions: { label: 'PUBLIC KEY' }, | ||
curve: SECP256K1Client.ec | ||
}); | ||
//# sourceMappingURL=secp256k1.js.map |
export interface TokenInterface { | ||
header: { | ||
[key: string]: any; | ||
[key: string]: Json; | ||
alg?: string; | ||
@@ -8,3 +8,3 @@ typ?: string; | ||
payload: { | ||
[key: string]: any; | ||
[key: string]: Json; | ||
iss?: string; | ||
@@ -17,2 +17,5 @@ jti?: string; | ||
} | ||
export declare type Json = string | number | boolean | null | { | ||
[property: string]: Json; | ||
} | Json[]; | ||
export declare function decodeToken(token: string | TokenInterface): TokenInterface; |
@@ -1,5 +0,5 @@ | ||
export { TokenSigner, createUnsecuredToken } from './signer'; | ||
export { TokenVerifier } from './verifier'; | ||
export { decodeToken } from './decode'; | ||
export { MissingParametersError, InvalidTokenError } from './errors'; | ||
export { SECP256K1Client, cryptoClients } from './cryptoClients'; | ||
export * from './signer'; | ||
export * from './verifier'; | ||
export * from './decode'; | ||
export * from './errors'; | ||
export * from './cryptoClients'; |
"use strict"; | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var signer_1 = require("./signer"); | ||
exports.TokenSigner = signer_1.TokenSigner; | ||
exports.createUnsecuredToken = signer_1.createUnsecuredToken; | ||
var verifier_1 = require("./verifier"); | ||
exports.TokenVerifier = verifier_1.TokenVerifier; | ||
var decode_1 = require("./decode"); | ||
exports.decodeToken = decode_1.decodeToken; | ||
var errors_1 = require("./errors"); | ||
exports.MissingParametersError = errors_1.MissingParametersError; | ||
exports.InvalidTokenError = errors_1.InvalidTokenError; | ||
var cryptoClients_1 = require("./cryptoClients"); | ||
exports.SECP256K1Client = cryptoClients_1.SECP256K1Client; | ||
exports.cryptoClients = cryptoClients_1.cryptoClients; | ||
__export(require("./signer")); | ||
__export(require("./verifier")); | ||
__export(require("./decode")); | ||
__export(require("./errors")); | ||
__export(require("./cryptoClients")); | ||
//# sourceMappingURL=index.js.map |
@@ -0,3 +1,5 @@ | ||
/// <reference types="node" /> | ||
import { SECP256K1Client } from './cryptoClients'; | ||
export declare function createUnsecuredToken(payload: any): string; | ||
import { Json } from './decode'; | ||
export declare function createUnsecuredToken(payload: Json): string; | ||
export interface SignedToken { | ||
@@ -17,6 +19,10 @@ header: string[]; | ||
}; | ||
sign(payload: any): Promise<string>; | ||
sign(payload: any, expanded: undefined): Promise<string>; | ||
sign(payload: any, expanded: false, customHeader?: any): Promise<string>; | ||
sign(payload: any, expanded: true, customHeader?: any): Promise<SignedToken>; | ||
sign(payload: Json, expanded: true, customHeader?: Json): SignedToken; | ||
sign(payload: Json, expanded: false, customHeader?: Json): string; | ||
signAsync(payload: Json, expanded: true, customHeader?: Json): Promise<SignedToken>; | ||
signAsync(payload: Json, expanded: false, customHeader?: Json): Promise<string>; | ||
createWithSignedHash(payload: Json, expanded: boolean, header: { | ||
typ: string; | ||
alg: string; | ||
}, signingInput: string, signingInputHash: Buffer): SignedToken | string; | ||
} |
@@ -15,2 +15,3 @@ "use strict"; | ||
const errors_1 = require("./errors"); | ||
const sha256_1 = require("./cryptoClients/sha256"); | ||
function createSigningInput(payload, header) { | ||
@@ -55,3 +56,11 @@ const tokenParts = []; | ||
} | ||
sign(payload, expanded = false, customHeader = {}) { | ||
sign(payload, expanded, customHeader = {}) { | ||
// generate the token header | ||
const header = this.header(customHeader); | ||
// prepare the message to be signed | ||
const signingInput = createSigningInput(payload, header); | ||
const signingInputHash = sha256_1.hashSha256(signingInput); | ||
return this.createWithSignedHash(payload, expanded, header, signingInput, signingInputHash); | ||
} | ||
signAsync(payload, expanded = false, customHeader = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -62,23 +71,27 @@ // generate the token header | ||
const signingInput = createSigningInput(payload, header); | ||
const signingInputHash = yield this.cryptoClient.createHash(signingInput); | ||
// sign the message and add in the signature | ||
const signature = this.cryptoClient.signHash(signingInputHash, this.rawPrivateKey); | ||
if (expanded) { | ||
return { | ||
'header': [ | ||
base64url_1.default.encode(JSON.stringify(header)) | ||
], | ||
'payload': JSON.stringify(payload), | ||
'signature': [ | ||
signature | ||
] | ||
}; | ||
} | ||
else { | ||
return [signingInput, signature].join('.'); | ||
} | ||
const signingInputHash = yield sha256_1.hashSha256Async(signingInput); | ||
return this.createWithSignedHash(payload, expanded, header, signingInput, signingInputHash); | ||
}); | ||
} | ||
createWithSignedHash(payload, expanded, header, signingInput, signingInputHash) { | ||
// sign the message and add in the signature | ||
const signature = this.cryptoClient.signHash(signingInputHash, this.rawPrivateKey); | ||
if (expanded) { | ||
const signedToken = { | ||
'header': [ | ||
base64url_1.default.encode(JSON.stringify(header)) | ||
], | ||
'payload': JSON.stringify(payload), | ||
'signature': [ | ||
signature | ||
] | ||
}; | ||
return signedToken; | ||
} | ||
else { | ||
return [signingInput, signature].join('.'); | ||
} | ||
} | ||
} | ||
exports.TokenSigner = TokenSigner; | ||
//# sourceMappingURL=signer.js.map |
@@ -8,5 +8,8 @@ import { SECP256K1Client } from './cryptoClients'; | ||
constructor(signingAlgorithm: string, rawPublicKey: string); | ||
verify(token: string | SignedToken): Promise<boolean>; | ||
verifyCompact(token: string): Promise<boolean>; | ||
verifyExpanded(token: SignedToken): Promise<boolean>; | ||
verify(token: string | SignedToken): boolean; | ||
verifyAsync(token: string | SignedToken): Promise<boolean>; | ||
verifyCompact(token: string, async: false): boolean; | ||
verifyCompact(token: string, async: true): Promise<boolean>; | ||
verifyExpanded(token: SignedToken, async: false): boolean; | ||
verifyExpanded(token: SignedToken, async: true): Promise<boolean>; | ||
} |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -15,2 +6,3 @@ const base64url_1 = require("base64url"); | ||
const errors_1 = require("./errors"); | ||
const sha256_1 = require("./cryptoClients/sha256"); | ||
class TokenVerifier { | ||
@@ -34,18 +26,28 @@ constructor(signingAlgorithm, rawPublicKey) { | ||
if (typeof token === 'string') { | ||
return this.verifyCompact(token); | ||
return this.verifyCompact(token, false); | ||
} | ||
else if (typeof token === 'object') { | ||
return this.verifyExpanded(token); | ||
return this.verifyExpanded(token, false); | ||
} | ||
else { | ||
false; | ||
} | ||
} | ||
verifyAsync(token) { | ||
if (typeof token === 'string') { | ||
return this.verifyCompact(token, true); | ||
} | ||
else if (typeof token === 'object') { | ||
return this.verifyExpanded(token, true); | ||
} | ||
else { | ||
return Promise.resolve(false); | ||
} | ||
} | ||
verifyCompact(token) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// decompose the token into parts | ||
const tokenParts = token.split('.'); | ||
// calculate the signing input hash | ||
const signingInput = tokenParts[0] + '.' + tokenParts[1]; | ||
const signingInputHash = yield this.cryptoClient.createHash(signingInput); | ||
verifyCompact(token, async) { | ||
// decompose the token into parts | ||
const tokenParts = token.split('.'); | ||
// calculate the signing input hash | ||
const signingInput = tokenParts[0] + '.' + tokenParts[1]; | ||
const performVerify = (signingInputHash) => { | ||
// extract the signature as a DER array | ||
@@ -55,12 +57,18 @@ const derSignatureBuffer = this.cryptoClient.loadSignature(tokenParts[2]); | ||
return this.cryptoClient.verifyHash(signingInputHash, derSignatureBuffer, this.rawPublicKey); | ||
}); | ||
}; | ||
if (async) { | ||
return sha256_1.hashSha256Async(signingInput).then(signingInputHash => performVerify(signingInputHash)); | ||
} | ||
else { | ||
const signingInputHash = sha256_1.hashSha256(signingInput); | ||
return performVerify(signingInputHash); | ||
} | ||
} | ||
verifyExpanded(token) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const signingInput = [ | ||
token['header'].join('.'), | ||
base64url_1.default.encode(token['payload']) | ||
].join('.'); | ||
const signingInputHash = yield this.cryptoClient.createHash(signingInput); | ||
let verified = true; | ||
verifyExpanded(token, async) { | ||
const signingInput = [ | ||
token['header'].join('.'), | ||
base64url_1.default.encode(token['payload']) | ||
].join('.'); | ||
let verified = true; | ||
const performVerify = (signingInputHash) => { | ||
token['signature'].map((signature) => { | ||
@@ -74,3 +82,10 @@ const derSignatureBuffer = this.cryptoClient.loadSignature(signature); | ||
return verified; | ||
}); | ||
}; | ||
if (async) { | ||
return sha256_1.hashSha256Async(signingInput).then(signingInputHash => performVerify(signingInputHash)); | ||
} | ||
else { | ||
const signingInputHash = sha256_1.hashSha256(signingInput); | ||
return performVerify(signingInputHash); | ||
} | ||
} | ||
@@ -77,0 +92,0 @@ } |
{ | ||
"name": "jsontokens", | ||
"version": "3.0.0-alpha.0", | ||
"version": "3.0.0-alpha.1", | ||
"description": "node.js library for encoding, decoding, and verifying JSON Web Tokens (JWTs)", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"build": "tsc -p tsconfig.build.json", | ||
"build": "tsc -b tsconfig.build.json", | ||
"lint": "eslint --ext .ts ./src", | ||
@@ -42,14 +42,15 @@ "test": "npm run lint && jest", | ||
"@peculiar/webcrypto": "^1.0.21", | ||
"@types/jest": "^24.0.19", | ||
"@types/node": "^12.7.12", | ||
"@typescript-eslint/eslint-plugin": "^2.4.0", | ||
"@typescript-eslint/parser": "^2.4.0", | ||
"@types/jest": "^24.0.23", | ||
"@types/node": "^12.12.7", | ||
"@types/sha.js": "^2.4.0", | ||
"@typescript-eslint/eslint-plugin": "^2.7.0", | ||
"@typescript-eslint/parser": "^2.7.0", | ||
"codecov": "^3.6.1", | ||
"eslint": "^6.5.1", | ||
"eslint-plugin-jest": "^22.19.0", | ||
"eslint": "^6.6.0", | ||
"eslint-plugin-jest": "^23.0.3", | ||
"jest": "^24.9.0", | ||
"source-map-support": "^0.5.13", | ||
"source-map-support": "^0.5.16", | ||
"ts-jest": "^24.1.0", | ||
"ts-node": "^8.4.1", | ||
"typescript": "^3.6.4" | ||
"ts-node": "^8.5.0", | ||
"typescript": "^3.7.2" | ||
}, | ||
@@ -62,3 +63,3 @@ "dependencies": { | ||
"elliptic": "^6.4.1", | ||
"key-encoder": "^2.0.3" | ||
"sha.js": "^2.4.11" | ||
}, | ||
@@ -65,0 +66,0 @@ "files": [ |
import { ec as EC, BNInput } from 'elliptic' | ||
import KeyEncoder from 'key-encoder' | ||
import { derToJose, joseToDer } from 'ecdsa-sig-formatter' | ||
@@ -10,8 +9,2 @@ import { MissingParametersError } from '../errors' | ||
static algorithmName = 'ES256K' | ||
static keyEncoder = new KeyEncoder({ | ||
curveParameters: [1, 3, 132, 0, 10], | ||
privatePEMOptions: { label: 'EC PRIVATE KEY' }, | ||
publicPEMOptions: { label: 'PUBLIC KEY' }, | ||
curve: SECP256K1Client.ec | ||
}) | ||
@@ -21,37 +14,2 @@ constructor() { | ||
private static getHashFunction(): (signingInput: string | Buffer) => Promise<Buffer> { | ||
try { | ||
const isSubtleCryptoAvailable = typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined' | ||
if (isSubtleCryptoAvailable) { | ||
// Use the W3C Web Crypto API if available (running in a web browser). | ||
return async input => { | ||
const buffer = typeof input === 'string' ? Buffer.from(input) : input | ||
const hash = await crypto.subtle.digest('SHA-256', buffer) | ||
return Buffer.from(hash) | ||
} | ||
} else { | ||
// Otherwise try loading the Node.js `crypto` module (running in Node.js, or an older browser with a polyfill). | ||
const nodeCrypto = require('crypto') as typeof import('crypto') | ||
if (!nodeCrypto.createHash) { | ||
const error = new Error('`crypto` module does not contain `createHash`') | ||
console.error(error) | ||
throw error | ||
} | ||
return input => Promise.resolve(nodeCrypto.createHash('sha256').update(input).digest()) | ||
} | ||
} catch (error) { | ||
console.error(error) | ||
const missingCryptoError = new Error( | ||
'Crypto lib not found. Neither the global `crypto.subtle` Web Crypto API, ' + | ||
'nor the or the Node.js `require("crypto").createHash` module is available.') | ||
console.error(missingCryptoError) | ||
throw missingCryptoError | ||
} | ||
} | ||
static createHash(signingInput: string | Buffer): Promise<Buffer> { | ||
const hashFunction = this.getHashFunction() | ||
return hashFunction(signingInput) | ||
} | ||
static loadPrivateKey(rawPrivateKey: string) { | ||
@@ -68,7 +26,2 @@ if (rawPrivateKey.length === 66) { | ||
static encodePublicKey(publicKey: string | Buffer, originalFormat: 'raw' | 'pem' | 'der', destinationFormat: 'raw' | 'pem' | 'der') { | ||
return SECP256K1Client.keyEncoder.encodePublic( | ||
publicKey, originalFormat, destinationFormat) | ||
} | ||
static derivePublicKey(privateKey: string, compressed = true) { | ||
@@ -75,0 +28,0 @@ if (typeof privateKey !== 'string') { |
@@ -5,3 +5,3 @@ import base64url from 'base64url' | ||
header: { | ||
[key: string]: any; | ||
[key: string]: Json; | ||
alg?: string; | ||
@@ -11,3 +11,3 @@ typ?: string; | ||
payload: { | ||
[key: string]: any; | ||
[key: string]: Json; | ||
iss?: string; | ||
@@ -21,2 +21,10 @@ jti?: string; | ||
export type Json = | ||
| string | ||
| number | ||
| boolean | ||
| null | ||
| { [property: string]: Json } | ||
| Json[]; | ||
export function decodeToken(token: string | TokenInterface): TokenInterface { | ||
@@ -45,4 +53,4 @@ if (typeof token === 'string') { | ||
const allHeaders: any[] = [] | ||
token.header.map((headerValue: string) => { | ||
const allHeaders: any = []; | ||
(token.header as any).map((headerValue: string) => { | ||
const header = JSON.parse(base64url.decode(headerValue)) | ||
@@ -49,0 +57,0 @@ allHeaders.push(header) |
@@ -1,5 +0,5 @@ | ||
export { TokenSigner, createUnsecuredToken } from './signer' | ||
export { TokenVerifier } from './verifier' | ||
export { decodeToken } from './decode' | ||
export { MissingParametersError, InvalidTokenError } from './errors' | ||
export { SECP256K1Client, cryptoClients } from './cryptoClients' | ||
export * from './signer' | ||
export * from './verifier' | ||
export * from './decode' | ||
export * from './errors' | ||
export * from './cryptoClients' |
import base64url from 'base64url' | ||
import { cryptoClients, SECP256K1Client } from './cryptoClients' | ||
import { MissingParametersError } from './errors' | ||
import { Json } from './decode' | ||
import { hashSha256, hashSha256Async } from './cryptoClients/sha256' | ||
function createSigningInput(payload: any, header: any) { | ||
function createSigningInput(payload: Json, header: Json) { | ||
const tokenParts = [] | ||
@@ -23,3 +25,3 @@ | ||
export function createUnsecuredToken(payload: any) { | ||
export function createUnsecuredToken(payload: Json) { | ||
const header = {typ: 'JWT', alg: 'none'} | ||
@@ -62,8 +64,18 @@ return createSigningInput(payload, header) + '.' | ||
} | ||
sign(payload: Json, expanded: true, customHeader?: Json): SignedToken; | ||
sign(payload: Json, expanded: false, customHeader?: Json): string; | ||
sign(payload: Json, expanded: boolean, customHeader: Json = {}): SignedToken | string { | ||
// generate the token header | ||
const header = this.header(customHeader) | ||
sign(payload: any): Promise<string>; | ||
sign(payload: any, expanded: undefined): Promise<string>; | ||
sign(payload: any, expanded: false, customHeader?: any): Promise<string>; | ||
sign(payload: any, expanded: true, customHeader?: any): Promise<SignedToken>; | ||
async sign(payload: any, expanded: boolean = false, customHeader: any = {}): Promise<string | SignedToken> { | ||
// prepare the message to be signed | ||
const signingInput = createSigningInput(payload, header) | ||
const signingInputHash = hashSha256(signingInput) | ||
return this.createWithSignedHash(payload, expanded, header, signingInput, signingInputHash) | ||
} | ||
signAsync(payload: Json, expanded: true, customHeader?: Json): Promise<SignedToken>; | ||
signAsync(payload: Json, expanded: false, customHeader?: Json): Promise<string>; | ||
async signAsync(payload: Json, expanded: boolean = false, customHeader: Json = {}) { | ||
// generate the token header | ||
@@ -74,4 +86,14 @@ const header = this.header(customHeader) | ||
const signingInput = createSigningInput(payload, header) | ||
const signingInputHash = await this.cryptoClient.createHash(signingInput) | ||
const signingInputHash = await hashSha256Async(signingInput) | ||
return this.createWithSignedHash(payload, expanded, header, signingInput, signingInputHash) | ||
} | ||
createWithSignedHash( | ||
payload: Json, | ||
expanded: boolean, | ||
header: { typ: string; alg: string }, | ||
signingInput: string, | ||
signingInputHash: Buffer | ||
): | ||
SignedToken | string { | ||
// sign the message and add in the signature | ||
@@ -82,3 +104,3 @@ const signature = this.cryptoClient.signHash( | ||
if (expanded) { | ||
return { | ||
const signedToken: SignedToken = { | ||
'header': [ | ||
@@ -92,2 +114,3 @@ base64url.encode(JSON.stringify(header)) | ||
} | ||
return signedToken | ||
} else { | ||
@@ -94,0 +117,0 @@ return [signingInput, signature].join('.') |
import { SECP256K1Client as secp256k1 } from '../index' | ||
import { hashSha256Async } from '../cryptoClients/sha256' | ||
import * as webcrypto from '@peculiar/webcrypto' | ||
describe('SECP256k1 tests - node.js crypto', () => { | ||
describe('SECP256k1 tests', () => { | ||
runSECP256k1Tests() | ||
}) | ||
describe('SECP256k1 tests - web crypto', () => { | ||
beforeAll(() => { | ||
Object.defineProperty(global, 'crypto', { value: new webcrypto.Crypto() }) | ||
}) | ||
afterAll(() => { | ||
delete (global as any)['crypto'] | ||
}) | ||
runSECP256k1Tests() | ||
}) | ||
function runSECP256k1Tests() { | ||
@@ -55,3 +44,3 @@ const privateKey = '278a5de700e29faae8e40e366ec5012b5ec63d36ec77e8a2417154cc1d25383f' | ||
const hash = await secp256k1.createHash(message) | ||
const hash = await hashSha256Async(message) | ||
const signature = secp256k1.signHash(hash, privateKey, 'der') | ||
@@ -58,0 +47,0 @@ |
@@ -10,8 +10,38 @@ import base64url from 'base64url' | ||
describe('main tests - node.js crypto', () => { | ||
let origGlobalCrypto: { defined: boolean, value: any } | ||
beforeAll(() => { | ||
origGlobalCrypto = { | ||
defined: 'crypto' in global, | ||
value: (global as any)['crypto'] | ||
} | ||
delete (global as any)['crypto']; | ||
(global as any)['crypto'] = new webcrypto.Crypto() | ||
}) | ||
afterAll(() => { | ||
if (origGlobalCrypto.defined) { | ||
(global as any)['crypto'] = origGlobalCrypto.value | ||
} else { | ||
delete (global as any)['crypto'] | ||
} | ||
}) | ||
runMainTests() | ||
}) | ||
describe('main tests - sha.js crypto', () => { | ||
let origCreateHash: typeof import('crypto').createHash | ||
beforeAll(() => { | ||
const nodeCrypto = require('crypto') as typeof import('crypto') | ||
origCreateHash = nodeCrypto.createHash | ||
delete nodeCrypto.createHash | ||
}) | ||
afterAll(() => { | ||
const nodeCrypto = require('crypto') as typeof import('crypto') | ||
nodeCrypto.createHash = origCreateHash | ||
}) | ||
runMainTests() | ||
}) | ||
describe('main tests - web crypto', () => { | ||
beforeAll(() => { | ||
Object.defineProperty(global, 'crypto', { value: new webcrypto.Crypto() }) | ||
(global as any)['crypto'] = new webcrypto.Crypto() | ||
}) | ||
@@ -45,3 +75,5 @@ afterAll(() => { | ||
const token = await tokenSigner.sign(sampleDecodedToken.payload) | ||
const token = await tokenSigner.signAsync(sampleDecodedToken.payload, false) | ||
const token1 = tokenSigner.sign(sampleDecodedToken.payload, false) | ||
expect(token).toStrictEqual(token1) | ||
expect(token).toBeTruthy() | ||
@@ -62,3 +94,5 @@ expect(typeof token).toBe('string') | ||
const token = await tokenSigner.sign(sampleDecodedToken.payload, undefined, { test: 'TestHeader' }) | ||
const token = await tokenSigner.signAsync(sampleDecodedToken.payload, false, { test: 'TestHeader' }) | ||
const token1 = tokenSigner.sign(sampleDecodedToken.payload, false, { test: 'TestHeader' }) | ||
expect(token).toStrictEqual(token1) | ||
expect(token).toBeTruthy() | ||
@@ -92,9 +126,16 @@ expect(typeof token).toBe('string') | ||
const verified = await tokenVerifier.verify(sampleToken) | ||
const verified = await tokenVerifier.verifyAsync(sampleToken) | ||
const verified1 = await tokenVerifier.verify(sampleToken) | ||
expect(verified).toStrictEqual(verified1) | ||
expect(verified).toBe(true) | ||
const tokenSigner = new TokenSigner('ES256K', rawPrivateKey) | ||
const newToken = await tokenSigner.sign(sampleDecodedToken.payload) | ||
const newToken = await tokenSigner.signAsync(sampleDecodedToken.payload, false) | ||
const newToken1 = tokenSigner.sign(sampleDecodedToken.payload, false) | ||
expect(newToken).toStrictEqual(newToken1) | ||
expect(newToken).toBeTruthy() | ||
const newTokenVerified = await tokenVerifier.verify(newToken) | ||
const newTokenVerified = await tokenVerifier.verifyAsync(newToken) | ||
const newTokenVerified1 = tokenVerifier.verify(newToken) | ||
expect(newTokenVerified).toStrictEqual(newTokenVerified1) | ||
expect(newTokenVerified).toBe(true) | ||
@@ -113,12 +154,18 @@ }) | ||
const token = await tokenSigner.sign(sampleDecodedToken.payload, true) | ||
const token = await tokenSigner.signAsync(sampleDecodedToken.payload, true) | ||
const token1 = tokenSigner.sign(sampleDecodedToken.payload, true) | ||
expect(token).toStrictEqual(token1) | ||
expect(token).toBeTruthy() | ||
expect(typeof token).toBe('object') | ||
const verified = await tokenVerifier.verify(token) | ||
const verified = await tokenVerifier.verifyAsync(token) | ||
const verified1 = tokenVerifier.verify(token) | ||
expect(verified).toStrictEqual(verified1) | ||
expect(verified).toBe(true) | ||
const signedToken = await tokenSigner.sign(sampleDecodedToken.payload, true) | ||
const signedToken = await tokenSigner.signAsync(sampleDecodedToken.payload, true) | ||
const signedToken1 = tokenSigner.sign(sampleDecodedToken.payload, true) | ||
expect(signedToken).toStrictEqual(signedToken1) | ||
expect(signedToken).toBeTruthy() | ||
}) | ||
} |
@@ -5,2 +5,3 @@ import base64url from 'base64url' | ||
import { SignedToken } from './signer' | ||
import { hashSha256Async, hashSha256 } from './cryptoClients/sha256' | ||
@@ -30,8 +31,18 @@ export class TokenVerifier { | ||
verify(token: string | SignedToken): Promise<boolean> { | ||
verify(token: string | SignedToken): boolean { | ||
if (typeof token === 'string') { | ||
return this.verifyCompact(token) | ||
return this.verifyCompact(token, false) | ||
} else if (typeof token === 'object') { | ||
return this.verifyExpanded(token) | ||
return this.verifyExpanded(token, false) | ||
} else { | ||
false | ||
} | ||
} | ||
verifyAsync(token: string | SignedToken): Promise<boolean> { | ||
if (typeof token === 'string') { | ||
return this.verifyCompact(token, true) | ||
} else if (typeof token === 'object') { | ||
return this.verifyExpanded(token, true) | ||
} else { | ||
return Promise.resolve(false) | ||
@@ -41,3 +52,5 @@ } | ||
async verifyCompact(token: string): Promise<boolean> { | ||
verifyCompact(token: string, async: false): boolean | ||
verifyCompact(token: string, async: true): Promise<boolean> | ||
verifyCompact(token: string, async: boolean): boolean | Promise<boolean> { | ||
// decompose the token into parts | ||
@@ -48,13 +61,24 @@ const tokenParts = token.split('.') | ||
const signingInput = tokenParts[0] + '.' + tokenParts[1] | ||
const signingInputHash = await this.cryptoClient.createHash(signingInput) | ||
// extract the signature as a DER array | ||
const derSignatureBuffer = this.cryptoClient.loadSignature(tokenParts[2]) | ||
const performVerify = (signingInputHash: Buffer) => { | ||
// extract the signature as a DER array | ||
const derSignatureBuffer = this.cryptoClient.loadSignature(tokenParts[2]) | ||
// verify the signed hash | ||
return this.cryptoClient.verifyHash( | ||
signingInputHash, derSignatureBuffer, this.rawPublicKey) | ||
// verify the signed hash | ||
return this.cryptoClient.verifyHash( | ||
signingInputHash, derSignatureBuffer, this.rawPublicKey) | ||
} | ||
if (async) { | ||
return hashSha256Async(signingInput).then(signingInputHash => | ||
performVerify(signingInputHash)) | ||
} else { | ||
const signingInputHash = hashSha256(signingInput) | ||
return performVerify(signingInputHash) | ||
} | ||
} | ||
async verifyExpanded(token: SignedToken): Promise<boolean> { | ||
verifyExpanded(token: SignedToken, async: false): boolean; | ||
verifyExpanded(token: SignedToken, async: true): Promise<boolean>; | ||
verifyExpanded(token: SignedToken, async: boolean): boolean | Promise<boolean> { | ||
const signingInput = [ | ||
@@ -64,17 +88,24 @@ token['header'].join('.'), | ||
].join('.') | ||
const signingInputHash = await this.cryptoClient.createHash(signingInput) | ||
let verified = true | ||
token['signature'].map((signature: string) => { | ||
const derSignatureBuffer = this.cryptoClient.loadSignature(signature) | ||
const signatureVerified = this.cryptoClient.verifyHash( | ||
signingInputHash, derSignatureBuffer, this.rawPublicKey) | ||
if (!signatureVerified) { | ||
verified = false | ||
} | ||
}) | ||
const performVerify = (signingInputHash: Buffer) => { | ||
token['signature'].map((signature: string) => { | ||
const derSignatureBuffer = this.cryptoClient.loadSignature(signature) | ||
const signatureVerified = this.cryptoClient.verifyHash( | ||
signingInputHash, derSignatureBuffer, this.rawPublicKey) | ||
if (!signatureVerified) { | ||
verified = false | ||
} | ||
}) | ||
return verified | ||
} | ||
return verified | ||
if (async) { | ||
return hashSha256Async(signingInput).then(signingInputHash => | ||
performVerify(signingInputHash)) | ||
} else { | ||
const signingInputHash = hashSha256(signingInput) | ||
return performVerify(signingInputHash) | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
61777
40
1050
14
+ Addedsha.js@^2.4.11
+ Addedsha.js@2.4.11(transitive)
- Removedkey-encoder@^2.0.3
- Removedkey-encoder@2.0.3(transitive)