UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

181 lines (168 loc) 9.8 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License in the project root for license information. *--------------------------------------------------------------------------------------------*/ import IVerificationResult from '../IVerificationResult'; import { ICryptoToken } from '../ICryptoToken'; import { IPayloadProtection } from '../IPayloadProtection'; import IPayloadProtectionOptions from '../IPayloadProtectionOptions'; import JwsToken from './jws/JwsToken'; import { ProtectionFormat } from '../../keyStore/ProtectionFormat'; import CryptoProtocolError from '../CryptoProtocolError'; import JoseConstants from './JoseConstants'; import JweToken from './jwe/JweToken'; import PublicKey from '../../keys/PublicKey'; import { IJwsSigningOptions, IJweEncryptionOptions } from './IJoseOptions'; /** * Class to implement the JOSE protocol. */ export default class JoseProtocol implements IPayloadProtection { /** * Signs contents using the given private key reference. * * @param signingKeyReference Reference to the signing key. * @param payload to sign. * @param format of the final signature. * @param options used for the signature. These options override the options provided in the constructor. * @returns Signed payload in requested format. */ public async sign (signingKeyReference: string, payload: Buffer, format: string, options: IPayloadProtectionOptions): Promise<ICryptoToken> { const jwsOptions: IJwsSigningOptions = JwsToken.fromPayloadProtectionOptions(options); const token: JwsToken = new JwsToken(jwsOptions); const protocolFormat: ProtectionFormat = this.getProtectionFormat(format); return JwsToken.toCryptoToken(protocolFormat, await token.sign(signingKeyReference, payload, protocolFormat), options); } /** * Verify the signature. * * @param validationKeys Public key to validate the signature. * @param payload that was signed * @param signature on payload * @param options used for the signature. These options override the options provided in the constructor. * @returns True if signature validated. */ public async verify (validationKeys: PublicKey[], _payload: Buffer, signature: ICryptoToken, options?: IPayloadProtectionOptions): Promise<IVerificationResult> { const jwsOptions: IJwsSigningOptions = JwsToken.fromPayloadProtectionOptions(<IPayloadProtectionOptions>options); const token: JwsToken = JwsToken.fromCryptoToken(signature, <IPayloadProtectionOptions>options); const result = await token.verify(validationKeys); return { result: result, reason: '' }; } /** * 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 encryption. These options override the options provided in the constructor. * @returns JweToken with encrypted payload. */ public async encrypt (recipients: PublicKey[], payload: Buffer, format: string, options: IPayloadProtectionOptions): Promise<ICryptoToken> { const jweOptions: IJweEncryptionOptions = JweToken.fromPayloadProtectionOptions(options); const token: JweToken = new JweToken(jweOptions); const protocolFormat: ProtectionFormat = this.getProtectionFormat(format); return JweToken.toCryptoToken(protocolFormat, await token.encrypt(recipients, payload.toString('utf8'), protocolFormat, jweOptions), options); } /** * Decrypt the content. * * @param decryptionKeyReference Reference to the decryption key. * @param token The crypto token to decrypt. * @param options used for the decryption. These options override the options provided in the constructor. * @returns Decrypted payload. */ public async decrypt (decryptionKeyReference: string, token: ICryptoToken, options: IPayloadProtectionOptions): Promise<Buffer> { const cipher: JweToken = JweToken.fromCryptoToken(token, options); (<any>cipher).options = JweToken.fromPayloadProtectionOptions(options); return await cipher.decrypt(decryptionKeyReference); } /** * Serialize a cryptographic token * @param token The crypto token to serialize. * @param format Specify the serialization format. If not specified, use default format. * @param options used for the decryption. These options override the options provided in the constructor. */ public serialize (token: ICryptoToken, format: string, options: IPayloadProtectionOptions): string { const protocolFormat: ProtectionFormat = this.getProtectionFormat(format); switch (protocolFormat) { case ProtectionFormat.JwsFlatJson: case ProtectionFormat.JwsCompactJson: case ProtectionFormat.JwsGeneralJson: const signature: JwsToken = JwsToken.fromCryptoToken(token, options); return signature.serialize(protocolFormat); case ProtectionFormat.JweFlatJson: case ProtectionFormat.JweCompactJson: case ProtectionFormat.JweGeneralJson: const cipher: JweToken = JweToken.fromCryptoToken(token, options); return cipher.serialize(protocolFormat); default: throw new CryptoProtocolError(JoseConstants.Jose, `Serialization format '${format}' is not supported`); } } /** * Deserialize a cryptographic token * @param token The crypto token to deserialize. * @param format Specify the serialization format. If not specified, use default format. * @param options used for the decryption. These options override the options provided in the constructor. */ public deserialize (token: string, format: string, options: IPayloadProtectionOptions): ICryptoToken { const protocolFormat: ProtectionFormat = this.getProtectionFormat(format); switch (protocolFormat) { case ProtectionFormat.JwsFlatJson: case ProtectionFormat.JwsCompactJson: case ProtectionFormat.JwsGeneralJson: const jwsProtectOptions = JwsToken.fromPayloadProtectionOptions(options); return JwsToken.toCryptoToken(protocolFormat, JwsToken.deserialize(token, jwsProtectOptions), options); case ProtectionFormat.JweFlatJson: case ProtectionFormat.JweCompactJson: case ProtectionFormat.JweGeneralJson: const jweProtectOptions = JweToken.fromPayloadProtectionOptions(options); return JweToken.toCryptoToken(protocolFormat, JweToken.deserialize(token, jweProtectOptions), options); default: throw new CryptoProtocolError(JoseConstants.Jose, `Serialization format '${format}' is not supported`); } } /** * Deserialize a cryptographic token * @param token The crypto token to deserialize. * @param options used for the token. These options override the options provided in the constructor. */ public static deserialize (token: string, options?: IPayloadProtectionOptions): ICryptoToken { const parts = token.split('.'); const protocol = new JoseProtocol(); if (parts.length === 3) { const deserializationOptions = options ? JwsToken.fromPayloadProtectionOptions(options) : <IJwsSigningOptions>{}; return JwsToken.toCryptoToken(ProtectionFormat.JwsCompactJson, JwsToken.deserialize(token, deserializationOptions), <IPayloadProtectionOptions>options); } else if (parts.length === 5) { const deserializationOptions = options ? JweToken.fromPayloadProtectionOptions(options) : <IJweEncryptionOptions>{}; return JweToken.toCryptoToken(ProtectionFormat.JweCompactJson, JweToken.deserialize(token, deserializationOptions), <IPayloadProtectionOptions>options); } const parsed = JSON.parse(token); if (parsed[JoseConstants.tokenSignatures] || parsed[JoseConstants.tokenSignature]) { const deserializationOptions = options ? JwsToken.fromPayloadProtectionOptions(options) : <IJwsSigningOptions>{}; return JwsToken.toCryptoToken(parsed[JoseConstants.tokenSignatures] ? ProtectionFormat.JwsGeneralJson : ProtectionFormat.JwsFlatJson, JwsToken.deserialize(token, deserializationOptions), <IPayloadProtectionOptions>options); } if (parsed[JoseConstants.tokenRecipients] || parsed[JoseConstants.tokenCiphertext]) { const deserializationOptions = options ? JweToken.fromPayloadProtectionOptions(options) : <IJweEncryptionOptions>{}; return JweToken.toCryptoToken(parsed[JoseConstants.tokenRecipients] ? ProtectionFormat.JweGeneralJson : ProtectionFormat.JweFlatJson, JweToken.deserialize(token, deserializationOptions), <IPayloadProtectionOptions>options); } throw new CryptoProtocolError(JoseConstants.Jose, 'Unrecognised token to deserialize'); } // Map string to protection format private getProtectionFormat(format: string): ProtectionFormat { switch(format.toLocaleLowerCase()) { case 'jwsflatjson': return ProtectionFormat.JwsFlatJson; case 'jwscompactjson': return ProtectionFormat.JwsCompactJson; case 'jwsgeneraljson': return ProtectionFormat.JwsGeneralJson; case 'jweflatjson': return ProtectionFormat.JweFlatJson; case 'jwecompactjson': return ProtectionFormat.JweCompactJson; case 'jwegeneraljson': return ProtectionFormat.JweGeneralJson; default: throw new CryptoProtocolError(JoseConstants.Jose, `Format '${format}' is not supported`); } } }