@microsoft/useragent-sdk
Version:
SDK for building decentralized identity wallets and enterprise agents.
181 lines (168 loc) • 9.8 kB
text/typescript
/*---------------------------------------------------------------------------------------------
* 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`);
}
}
}