UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

740 lines (648 loc) 29.3 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import CryptoFactory from '../../../plugin/CryptoFactory'; import PublicKey from '../../../keys/PublicKey'; import IJweGeneralJson, { JweHeader } from './IJweGeneralJson'; import { ProtectionFormat } from '../../../keyStore/ProtectionFormat'; import { IJweEncryptionOptions } from "../IJoseOptions"; import JweRecipient from './JweRecipient'; import { TSMap } from 'typescript-map' import JoseHelpers from '../JoseHelpers'; import { KeyType } from '../../../keys/KeyTypeFactory'; import JoseConstants from '../JoseConstants' import CryptoHelpers from '../../../utilities/CryptoHelpers'; import SubtleCryptoExtension from '../../../plugin/SubtleCryptoExtension'; import base64url from 'base64url'; import IJweRecipient from './IJweRecipient'; import ISubtleCryptoExtension from '../../../plugin/ISubtleCryptoExtension'; import CryptoProtocolError from '../../CryptoProtocolError'; import IJweFlatJson from './IJweFlatJson'; import IPayloadProtectionOptions from '../../IPayloadProtectionOptions'; import JoseProtocol from '../JoseProtocol'; import { ICryptoToken } from '../../ICryptoToken'; import { SubtleCrypto } from 'webcrypto-core' import JoseToken from '../JoseToken'; /** * Class for containing Jwe token operations. * This class hides the JOSE and crypto library dependencies to allow support for additional crypto algorithms. * Crypto calls always happen via CryptoFactory */ export default class JweToken implements IJweGeneralJson { /** * The protected header. */ public protected: JweHeader = new TSMap<string, string>(); /** * The unprotected header. */ public unprotected: JweHeader = new TSMap<string, string>(); /** * The initial vector. */ iv: Buffer = Buffer.from(''); /** * The additional authenticated data. */ public aad: Buffer = Buffer.from(''); /** * The encrypted data. */ public ciphertext: Buffer = Buffer.from(''); /** * The authentication tag used by GCM. */ public tag: Buffer = Buffer.from(''); /** * Signatures on content */ public recipients: JweRecipient[] = []; /** * Get the request serialization format */ public format: ProtectionFormat = ProtectionFormat.JweGeneralJson; // Options passed into the constructor private options: IJweEncryptionOptions | undefined; /** * Create an Jwe token object * @param options Set of Jwe token options */ constructor (options?: IJweEncryptionOptions) { this.options = options; } //#region serialization /** * Serialize a Jwe token object from a token * @param format Optional specify the serialization format. If not specified, use default format. */ public serialize (format?: ProtectionFormat): string { if (format === undefined) { format = this.format; } switch (format) { case ProtectionFormat.JweGeneralJson: return JweToken.serializeJweGeneralJson(this); case ProtectionFormat.JweCompactJson: return JweToken.serializeJweCompact(this); case ProtectionFormat.JweFlatJson: return JweToken.serializeJweFlatJson(this); } throw new CryptoProtocolError(JoseConstants.Jwe, `The format '${this.format}' is not supported`); } /** * Serialize a Jwe token object from a token in General Json format * @param token Jwe base object */ private static serializeJweGeneralJson (token: JweToken): string { let json: any = { recipients: [], aad: base64url.encode(token.aad), iv: base64url.encode(token.iv), ciphertext: base64url.encode(token.ciphertext), tag: base64url.encode(token.tag) } if (JoseHelpers.headerHasElements(token.protected)) { json.protected = JoseHelpers.encodeHeader(<JweHeader>token.protected); } if (JoseHelpers.headerHasElements(token.unprotected)) { json.unprotected = JoseHelpers.encodeHeader(<JweHeader>token.unprotected, false); } for (let inx = 0 ; inx < token.recipients.length ; inx++ ) { const recipient: any = { encrypted_key: base64url.encode(token.recipients[inx].encrypted_key) } if (JoseHelpers.headerHasElements(token.recipients[inx].header)) { recipient.header = JoseHelpers.encodeHeader(<JweHeader>token.recipients[inx].header, false); } json.recipients.push(recipient); } return JSON.stringify(json); } /** * Serialize a Jwe token object from a token in Flat Json format * @param token Jwe base object */ private static serializeJweFlatJson (token: JweToken): string { let json: any = { encrypted_key: base64url.encode(token.recipients[0].encrypted_key), aad: base64url.encode(token.aad), iv: base64url.encode(token.iv), ciphertext: base64url.encode(token.ciphertext), tag: base64url.encode(token.tag) } if (JoseHelpers.headerHasElements(token.protected)) { json.protected = JoseHelpers.encodeHeader(<JweHeader>token.protected); } if (JoseHelpers.headerHasElements(token.unprotected)) { json.unprotected = JoseHelpers.encodeHeader(<JweHeader>token.unprotected); } if (JoseHelpers.headerHasElements(token.recipients[0].header)) { json.header = JoseHelpers.encodeHeader(<JweHeader>token.recipients[0].header, false); } return JSON.stringify(json); } /** * Serialize a Jwe token object from a token in Compact format * @param token Jwe base object */ private static serializeJweCompact (token: JweToken): string { let encodedProtected = ''; if (JoseHelpers.headerHasElements(token.protected)) { encodedProtected = JoseHelpers.encodeHeader(<JweHeader>token.protected); } const encryptedKey = base64url.encode(token.recipients[0].encrypted_key); const iv = base64url.encode(token.iv); const cipher = base64url.encode(token.ciphertext); const tag = base64url.encode(token.tag); return `${encodedProtected}.${encryptedKey}.${iv}.${cipher}.${tag}`; } //#endregion //#region deserialization /** * Deserialize a Jwe token object */ public static deserialize (token: string, options?: IJweEncryptionOptions): JweToken { const jweToken = new JweToken(options); // check for JWE compact format if (typeof token === 'string') { const parts = token.split('.'); if (parts.length === 5) { jweToken.protected = JweToken.setProtected(parts[0]); const recipient = new JweRecipient(); recipient.encrypted_key = base64url.toBuffer(parts[1]); jweToken.recipients = [recipient]; jweToken.ciphertext = base64url.toBuffer(parts[3]); jweToken.iv = base64url.toBuffer(parts[2]); jweToken.tag = base64url.toBuffer(parts[4]); jweToken.aad = base64url.toBuffer(parts[0]); return jweToken; } } else { throw new CryptoProtocolError(JoseConstants.Jwe, `The presented object is not deserializable.`); } // Flat or general format let jsonObject: any; try { jsonObject = JSON.parse(token); } catch (error) { throw new CryptoProtocolError(JoseConstants.Jwe, `The presented object is not deserializable and is no compact format.`); } // Try to handle token as IJweGeneralJSon let decodeStatus: { result: boolean, reason: string } = jweToken.setGeneralParts(<IJweGeneralJson>jsonObject); if (decodeStatus.result) { return jweToken; } else { console.debug(`Failed parsing as IJweGeneralJSon. Reason: ${decodeStatus.reason}`) } // Try to handle token as IJweFlatJson decodeStatus = jweToken.setFlatParts(<IJweFlatJson>jsonObject); if (decodeStatus.result) { return jweToken; } else { console.debug(`Failed parsing as IJweFlatJson. Reason: ${decodeStatus.reason}`); } throw new CryptoProtocolError(JoseConstants.Jwe, `The content does not represent a valid jwe token`); } /** * Try to parse the input token and set the properties of this JswToken * @param content Alledged IJweGeneralJSon token * @returns true if valid token was parsed */ private setGeneralParts(content: IJweGeneralJson): {result: boolean, reason: string} { if (content) { if (content.recipients) { this.ciphertext = base64url.toBuffer(<string><any>content.ciphertext); this.aad = base64url.toBuffer(<string><any>content.aad); this.iv = base64url.toBuffer(<string><any>content.iv); if (content.protected) { this.protected = JweToken.setProtected(content.protected); } if (content.unprotected && <any>content.unprotected !== 'e30') { this.unprotected = JweToken.setUnprotected(content.unprotected); } this.tag = base64url.toBuffer(<string><any>content.tag); this.recipients = []; for (let inx = 0; inx < content.recipients.length; inx ++) { const recipient = new JweRecipient(); recipient.encrypted_key = base64url.toBuffer(<string><any>content.recipients[inx].encrypted_key); if (content.recipients[inx].header) { recipient.header = JweToken.setUnprotected(<any>content.recipients[inx].header); } this.recipients.push(recipient); } } else { // manadatory field return {result: false, reason: 'missing recipients'}; } return this.isValidToken(); } return {result: false, reason: 'no content passed'}; } /** * Try to parse the input token and set the properties of this JswToken * @param content Alledged IJweFlatJson token * @returns true if valid token was parsed */ private setFlatParts(content: IJweFlatJson): {result: boolean, reason: string} { if (content) { const recipient = new JweRecipient(); if (content.ciphertext) { this.ciphertext = base64url.toBuffer(<any>content.ciphertext); } else { // manadatory field return {result: false, reason: 'missing ciphertext'}; } if (content.encrypted_key) { recipient.encrypted_key = base64url.toBuffer(<any>content.encrypted_key); } else { // manadatory field return {result: false, reason: 'missing encrypted_key'}; } if (content.iv) { this.iv = base64url.toBuffer(<any>content.iv); } else { // manadatory field return {result: false, reason: 'missing iv'}; } if (content.aad) { this.aad = base64url.toBuffer(<any>content.aad); } if (JoseHelpers.headerHasElements(content.protected)) { this.protected = JweToken.setProtected(<JweHeader>content.protected); } if (JoseHelpers.headerHasElements(content.unprotected)) { this.unprotected = JweToken.setUnprotected(<JweHeader>content.unprotected); } if (JoseHelpers.headerHasElements(content.header)) { recipient.header = JweToken.setUnprotected(<JweHeader>content.header); } this.recipients = [recipient]; return this.isValidToken(); } return {result: false, reason: 'no content passed'}; } /** * Check if a valid token was found after decoding */ private isValidToken(): {result: boolean, reason: string} { if (!this.ciphertext) { return {result: false, reason: 'missing ciphertext'}; } if (!this.iv) { return {result: false, reason: 'missing iv'}; } if (!this.recipients) { return {result: false, reason: 'missing recipients'}; } if (this.recipients.length == 0) { return {result: false, reason: 'recipients array is empty'}; } for (let inx = 0; inx < this.recipients.length; inx++) { const recipient = this.recipients[inx]; if (!recipient.encrypted_key) { return {result: false, reason: `recipient ${inx} is missing encrypted_key`}; } if (!this.protected && !recipient.header) { return {result: false, reason: `recipient ${inx} is missing header and protected is also missing`}; } } return {result: true, reason: ''}; } //#endregion //#region options section /** * Get the CryptoFactory to be used * @param newOptions Options passed in after the constructure * @param manadatory True if property needs to be defined */ private getCryptoFactory(newOptions?: IJweEncryptionOptions, manadatory: boolean = true): CryptoFactory { return JoseHelpers.getOptionsProperty<CryptoFactory>('cryptoFactory', this.options, newOptions, manadatory); } /** * Get the key encryption key for testing * @param newOptions Options passed in after the constructure * @param manadatory True if property needs to be defined */ private getContentEncryptionKey(newOptions?: IJweEncryptionOptions, manadatory: boolean = true): Buffer { return JoseHelpers.getOptionsProperty<Buffer>('contentEncryptionKey', this.options, newOptions, manadatory); } /** * Get the initial vector for testing * @param newOptions Options passed in after the constructure * @param manadatory True if property needs to be defined */ private getInitialVector(newOptions?: IJweEncryptionOptions, manadatory: boolean = true): Buffer { return JoseHelpers.getOptionsProperty<Buffer>('initialVector', this.options, newOptions, manadatory); } /** * Get the content encryption algorithm from the options * @param newOptions Options passed in after the constructure * @param manadatory True if property needs to be defined */ private getContentEncryptionAlgorithm(newOptions?: IJweEncryptionOptions, manadatory: boolean = true): string { return JoseHelpers.getOptionsProperty<string>('contentEncryptionAlgorithm', this.options, newOptions, manadatory); } //#endregion //#region encryption functions /** * Encrypt content using the given public keys in JWK format. * The key type enforces the key encryption algorithm. * The options can override certain algorithm choices. * * @param recipients List of recipients' public keys. * @param payload to encrypt. * @param format of the final serialization. * @param options used for the signature. These options override the options provided in the constructor. * @returns JweToken with encrypted payload. */ // tslint:disable-next-line: max-func-body-length public async encrypt (recipients: PublicKey[], payload: string, format: ProtectionFormat, options?: IJweEncryptionOptions): Promise<JweToken> { const cryptoFactory: CryptoFactory = this.getCryptoFactory(options); const contentEncryptionAlgorithm = this.getContentEncryptionAlgorithm(options, false) || JoseConstants.AesGcm256; this.format = format; // encoded protected header let encodedProtected: string = ''; // Set the resulting token const jweToken: JweToken = new JweToken(options || this.options); // Get the encryptor extensions const encryptor = new SubtleCryptoExtension(cryptoFactory); let randomGenerator = CryptoHelpers.jwaToWebCrypto(contentEncryptionAlgorithm); const generator: SubtleCrypto = CryptoHelpers.getSubtleCryptoForAlgorithm(cryptoFactory, randomGenerator); // Set the content encryption key let contentEncryptionKey: Buffer = this.getContentEncryptionKey(options, false); if (!contentEncryptionKey) { const key = await generator.generateKey(randomGenerator, true, ['encrypt']); const jwk: any = await generator.exportKey('jwk', <CryptoKey>key); contentEncryptionKey = base64url.toBuffer(jwk.k); } // Set the initial vector jweToken.iv = this.getInitialVector(options, false); if (!jweToken.iv) { const key = await generator.generateKey(randomGenerator, true, ['encrypt']); const jwk: any = await generator.exportKey('jwk', <CryptoKey>key); jweToken.iv = base64url.toBuffer(jwk.k); } // Needs to be improved when alg is not provided. // Decide key encryption algorithm based on given JWK. let publicKey: PublicKey = recipients[0]; for (let key of recipients) { if (key.alg === JoseConstants.RsaOaep256) { publicKey = key; break; } } let keyEncryptionAlgorithm: string | undefined = publicKey.alg; if (!keyEncryptionAlgorithm) { if (publicKey.kty == KeyType.EC) { throw new Error('EC encryption not implemented'); } else { // Default RSA algorithm keyEncryptionAlgorithm = JoseConstants.RsaOaep256; } } // tslint:disable: no-backbone-get-set-outside-model jweToken.unprotected = this.getUnprotected(options) || new TSMap<string, string>(); jweToken.protected = this.getProtected(options) || new TSMap<string, string>(); jweToken.protected.set(JoseConstants.Alg, <string>keyEncryptionAlgorithm); jweToken.protected.set(JoseConstants.Enc, <string>contentEncryptionAlgorithm); if (publicKey.kid) { jweToken.protected.set(JoseConstants.Kid, publicKey.kid); } encodedProtected = JoseHelpers.headerHasElements(jweToken.protected) ? JoseHelpers.encodeHeader(jweToken.protected) : ''; // Set aad as the protected header jweToken.aad = base64url.toBuffer(encodedProtected); for (let inx = 0 ; inx < recipients.length; inx ++) { // Set the recipients structure const jweRecipient = new JweRecipient(); jweToken.recipients.push(jweRecipient); // Decide key encryption algorithm based on given JWK. publicKey = recipients[inx]; // Get key encrypter and encrypt the content key jweRecipient.encrypted_key = Buffer.from( await encryptor.encryptByJwk( CryptoHelpers.jwaToWebCrypto(<string>keyEncryptionAlgorithm), publicKey, contentEncryptionKey)); const header = new TSMap<string, string>([ [JoseConstants.Kid, <string>publicKey.kid] ]); jweRecipient.header = JweToken.setHeader(header); } // encrypt content const contentEncryptorKey: JsonWebKey = { k: base64url.encode(contentEncryptionKey), alg: contentEncryptionAlgorithm, kty: 'oct' }; const encodedAad = base64url.encode(jweToken.aad); const cipherText = await encryptor.encryptByJwk( CryptoHelpers.jwaToWebCrypto(contentEncryptionAlgorithm, new Uint8Array(jweToken.iv), new Uint8Array(Buffer.from(encodedAad))), contentEncryptorKey, Buffer.from(payload)); jweToken.tag = Buffer.from(cipherText, cipherText.byteLength - 16); jweToken.ciphertext = Buffer.from(cipherText, 0 , cipherText.byteLength - 16); return jweToken; } //#endregion //#region decryption functions /** * Decrypt the content. * * @param decryptionKeyReference Reference to the decryption key. * @param options used for the signature. These options override the options provided in the constructor. * @returns Signed payload in compact Jwe format. */ // tslint:disable-next-line: max-func-body-length public async decrypt (decryptionKeyReference: string, options?: IJweEncryptionOptions): Promise<Buffer> { const cryptoFactory: CryptoFactory = this.getCryptoFactory(options); // Get the encryptor extensions const decryptor = new SubtleCryptoExtension(cryptoFactory); // get decryption public key let jwk: PublicKey = (await cryptoFactory.keyStore.get(decryptionKeyReference, true)).getKey<PublicKey>(); // Get the encrypted key // Check if kid matches let contentEncryptionKey: Buffer | undefined; if (jwk.kid) { for (let inx = 0 ; inx < this.recipients.length ; inx ++) { const recipient = this.recipients[inx]; if (recipient.header) { const headerKid = recipient.header.get(JoseConstants.Kid); if (headerKid && headerKid === jwk.kid) { if (contentEncryptionKey = await this.decryptContentEncryptionKey(recipient, decryptor, decryptionKeyReference)) { break; } } } } } if (!contentEncryptionKey) { // try to decrypt every key for (let inx = 0, length = this.recipients.length ; inx < length ; inx ++) { const recipient = this.recipients[inx]; if (contentEncryptionKey = await this.decryptContentEncryptionKey(recipient, decryptor, decryptionKeyReference)) { break; } } } if (!contentEncryptionKey) { throw new CryptoProtocolError(JoseConstants.Jwe, 'Cannot decrypt the content encryption key because of missing key'); } // Decrypt content const contentEncryptionAlgorithm = this.protected.get(JoseConstants.Enc); const iv = new Uint8Array(this.iv); const encodedAad = base64url.encode(this.aad); const aad = new Uint8Array(Buffer.from(encodedAad)); const algorithm = CryptoHelpers.jwaToWebCrypto(contentEncryptionAlgorithm, iv, aad); const contentJwk: JsonWebKey = { kty: 'oct', alg: contentEncryptionAlgorithm, k: base64url.encode(contentEncryptionKey) }; const plaintext = await decryptor.decryptByJwk(algorithm, contentJwk, Buffer.concat([this.ciphertext, this.tag])); return Buffer.from(plaintext); } private async decryptContentEncryptionKey(recipient: IJweRecipient, decryptor: ISubtleCryptoExtension, decryptionKeyReference: string): Promise<Buffer> { let keyDecryptionAlgorithm = ''; if (!recipient.header) { keyDecryptionAlgorithm = this.protected.get(JoseConstants.Alg); } else { keyDecryptionAlgorithm = recipient.header.get(JoseConstants.Alg) || this.protected.get(JoseConstants.Alg); } const algorithm = CryptoHelpers.jwaToWebCrypto(keyDecryptionAlgorithm); return Buffer.from(await decryptor.decryptByKeyStore(algorithm, decryptionKeyReference, recipient.encrypted_key)); } //#endregion /** * Get the default protected header to be used from the options * @param newOptions Options passed in after the constructure * @param mandatory True if property needs to be defined */ private getProtected(newOptions?: IJweEncryptionOptions, mandatory: boolean = false): JweHeader { return JoseHelpers.getOptionsProperty<JweHeader>('protected', this.options, newOptions, mandatory); } /** * Get the default header to be used from the options * @param newOptions Options passed in after the constructure * @param mandatory True if property needs to be defined */ public getUnprotected(newOptions?: IJweEncryptionOptions, mandatory: boolean = false): JweHeader { return JoseHelpers.getOptionsProperty<JweHeader>('unprotected', this.options, newOptions, mandatory); } /** * Get the default header to be used from the options * @param newOptions Options passed in after the constructure * @param mandatory True if property needs to be defined */ public getHeader(newOptions?: IJweEncryptionOptions, mandatory: boolean = false): JweHeader { return JoseHelpers.getOptionsProperty<JweHeader>('header', this.options, newOptions, mandatory); } /** * Convert a @class ICryptoToken into a @class JweToken * @param cryptoToken to convert * @param protectOptions options for the token */ public static fromCryptoToken(cryptoToken: ICryptoToken, protectOptions: IPayloadProtectionOptions): JweToken { const options = JweToken.fromPayloadProtectionOptions(protectOptions); const token = new JweToken(options); token.protected = cryptoToken.has(JoseConstants.tokenProtected) ? cryptoToken.get(JoseConstants.tokenProtected) : undefined; token.unprotected = cryptoToken.has(JoseConstants.tokenUnprotected) ? cryptoToken.get(JoseConstants.tokenUnprotected) : undefined; token.format = <ProtectionFormat>cryptoToken.get(JoseConstants.tokenFormat); token.aad = cryptoToken.get(JoseConstants.tokenAad); token.iv = cryptoToken.get(JoseConstants.tokenIv); token.ciphertext = cryptoToken.get(JoseConstants.tokenCiphertext); token.tag = cryptoToken.get(JoseConstants.tokenTag); token.recipients = <JweRecipient[]>cryptoToken.get(JoseConstants.tokenRecipients); return token; } /** * Convert a @class JweToken into a @class ICryptoToken * @param protocolFormat format of the token * @param jweToken to convert * @param options used for the encryption. These options override the options provided in the constructor. */ public static toCryptoToken(protocolFormat: ProtectionFormat, jweToken: JweToken, options: IPayloadProtectionOptions): ICryptoToken { const cryptoToken = new JoseToken(options); if (jweToken.protected) { cryptoToken.set(JoseConstants.tokenProtected, jweToken.protected); } if (jweToken.unprotected) { cryptoToken.set(JoseConstants.tokenUnprotected, jweToken.unprotected); } cryptoToken.set(JoseConstants.tokenFormat, protocolFormat); cryptoToken.set(JoseConstants.tokenAad, jweToken.aad); cryptoToken.set(JoseConstants.tokenIv, jweToken.iv); cryptoToken.set(JoseConstants.tokenCiphertext, jweToken.ciphertext); cryptoToken.set(JoseConstants.tokenTag, jweToken.tag); cryptoToken.set(JoseConstants.tokenRecipients, jweToken.recipients); return cryptoToken; } /** * Convert a @class IPayloadProtectionOptions into a @class IJweEncryptionOptions * @param protectOptions to convert */ public static fromPayloadProtectionOptions(protectOptions: IPayloadProtectionOptions): IJweEncryptionOptions { return <IJweEncryptionOptions>{ cryptoFactory: protectOptions.cryptoFactory, protected: protectOptions.options.has(JoseConstants.optionProtectedHeader) ? <JweHeader>protectOptions.options.get(JoseConstants.optionProtectedHeader) : undefined, header: protectOptions.options.has(JoseConstants.optionHeader) ? <JweHeader>protectOptions.options.get(JoseConstants.optionHeader) : undefined, kidPrefix: protectOptions.options.has(JoseConstants.optionKidPrefix) ? <JweHeader>protectOptions.options.get(JoseConstants.optionKidPrefix) : undefined, contentEncryptionAlgorithm: protectOptions.options.get(JoseConstants.optionContentEncryptionAlgorithm) }; } /** * Convert a @class IPayloadProtectionOptions into a @class IJweEncryptionOptions * @param encryptionOptions to convert */ public static toPayloadProtectionOptions(encryptionOptions: IJweEncryptionOptions): IPayloadProtectionOptions { const protectOptions = { cryptoFactory: encryptionOptions.cryptoFactory, payloadProtection: new JoseProtocol(), options: new TSMap<string, any>(), contentEncryptionAlgorithm: encryptionOptions.contentEncryptionAlgorithm }; if (encryptionOptions.header) { protectOptions.options.set(JoseConstants.optionHeader, encryptionOptions.header); } if (encryptionOptions.protected) { protectOptions.options.set(JoseConstants.optionProtectedHeader, encryptionOptions.protected); } if (encryptionOptions.kidPrefix) { protectOptions.options.set(JoseConstants.optionKidPrefix, encryptionOptions.kidPrefix); } return protectOptions; } /** * Set the header in the recipients object * @param header to set on the JweToken recipients object */ private static setHeader(header: string | JweHeader) { if (typeof header === 'string') { return new TSMap<string, string>().fromJSON(JSON.parse(header)); } return header; } /** * Set the unprotected header * @param unprotectedHeader to set on the JweToken object */ private static setUnprotected(unprotectedHeader: string | JweHeader) { if (typeof unprotectedHeader === 'string') { return new TSMap<string, string>().fromJSON(JSON.parse(unprotectedHeader)); } return unprotectedHeader; } /** * Set the protected header * @param protectedHeader to set on the JweToken object */ private static setProtected(protectedHeader: string | JweHeader) { if (typeof protectedHeader === 'string') { const json = base64url.decode(protectedHeader); return new TSMap<string, string>().fromJSON(JSON.parse(json)); } return protectedHeader; } }