tink-crypto
Version:
A multi-language, cross-platform library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.
238 lines • 26.9 kB
JavaScript
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
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());
});
};
import { InvalidArgumentsException } from '../exception/invalid_arguments_exception';
import { SecurityException } from '../exception/security_exception';
import * as Random from '../subtle/random';
import * as PrimitiveSet from './primitive_set';
import { PbKeyData, PbKeyMaterialType, PbKeyset, PbKeysetKey, PbKeyStatusType } from './proto';
import { bytesAsU8 } from './proto_shims';
import * as Registry from './registry';
import * as Util from './util';
/**
* Keyset handle provide abstracted access to Keysets, to limit the exposure of
* actual protocol buffers that hold sensitive key material.
*
* @final
*/
export class KeysetHandle {
constructor(keyset) {
Util.validateKeyset(keyset);
this.keyset_ = keyset;
}
/**
* Returns a primitive that uses key material from this keyset handle. If
* opt_customKeyManager is defined then the provided key manager is used to
* instantiate primitives. Otherwise key manager from Registry is used.
*/
getPrimitive(primitiveType, opt_customKeyManager) {
return __awaiter(this, void 0, void 0, function* () {
if (!primitiveType) {
throw new InvalidArgumentsException('primitive type must be non-null');
}
const primitiveSet = yield this.getPrimitiveSet(primitiveType, opt_customKeyManager);
return Registry.wrap(primitiveSet);
});
}
/**
* Creates a set of primitives corresponding to the keys with status Enabled
* in the given keysetHandle, assuming all the correspoding key managers are
* present (keys with status different from Enabled are skipped). If provided
* uses customKeyManager instead of registered key managers for keys supported
* by the customKeyManager.
*
* Visible for testing.
*/
getPrimitiveSet(primitiveType, opt_customKeyManager) {
return __awaiter(this, void 0, void 0, function* () {
const primitiveSet = new PrimitiveSet.PrimitiveSet(primitiveType);
const keys = this.keyset_.getKeyList();
const keysLength = keys.length;
for (let i = 0; i < keysLength; i++) {
const key = keys[i];
if (key.getStatus() === PbKeyStatusType.ENABLED) {
const keyData = key.getKeyData();
if (!keyData) {
throw new SecurityException('Key data has to be non null.');
}
let primitive;
if (opt_customKeyManager &&
opt_customKeyManager.getKeyType() === keyData.getTypeUrl()) {
primitive =
yield opt_customKeyManager.getPrimitive(primitiveType, keyData);
}
else {
primitive = yield Registry.getPrimitive(primitiveType, keyData);
}
const entry = primitiveSet.addPrimitive(primitive, key);
if (key.getKeyId() === this.keyset_.getPrimaryKeyId()) {
primitiveSet.setPrimary(entry);
}
}
}
return primitiveSet;
});
}
/**
* Encrypts the underlying keyset with the provided masterKeyAead wnd writes
* the resulting encryptedKeyset to the given writer which must be non-null.
*
*
*/
write(writer, masterKeyAead) {
return __awaiter(this, void 0, void 0, function* () {
// TODO implement
throw new SecurityException('KeysetHandle -- write: Not implemented yet.');
});
}
/**
* Writes this keyset using `writer` if and only if the keyset doesn't contain
* any secret key material.
*
* This can be used to persist public keysets or envelope encryption keysets.
* Use `CleartextKeysetHandle` to persist keysets containing secret key
* material.
*/
writeNoSecret(writer) {
assertNoSecretKeyMaterial(this.keyset_);
return writer.encodeBinary(this.keyset_);
}
/**
* Returns the keyset held by this KeysetHandle.
*
*/
getKeyset() {
return this.keyset_;
}
/**
* If the managed keyset contains private keys, returns a `KeysetHandle` of
* the public keys.
*/
getPublicKeysetHandle() {
const publicKeyset = new PbKeyset();
for (const key of this.keyset_.getKeyList()) {
publicKeyset.addKey(key.clone().setKeyData(createPublicKeyData(nonNull('Key data', key.getKeyData()))));
}
publicKeyset.setPrimaryKeyId(this.keyset_.getPrimaryKeyId());
return new KeysetHandle(publicKeyset);
}
}
function nonNull(desc, value) {
if (value == null) {
throw new SecurityException(`${desc} has to be non null.`);
}
return value;
}
function createPublicKeyData(privateKeyData) {
if (privateKeyData.getKeyMaterialType() !==
PbKeyData.KeyMaterialType.ASYMMETRIC_PRIVATE) {
throw new SecurityException('The keyset contains a non-private key');
}
return Registry.getPublicKeyData(privateKeyData.getTypeUrl(), bytesAsU8(privateKeyData.getValue()));
}
/**
* Validates that `keyset` doesn't contain any secret key material.
*
* @throws SecurityException if `keyset` contains secret key material.
*/
function assertNoSecretKeyMaterial(keyset) {
for (const key of keyset.getKeyList()) {
const keyData = nonNull('Key data', key.getKeyData());
if (isSecretKeyMaterialType(keyData.getKeyMaterialType())) {
throw new SecurityException('Keyset contains secret key material.');
}
}
}
/** Returns true if the key material type is secret. */
function isSecretKeyMaterialType(type) {
return type === PbKeyMaterialType.UNKNOWN_KEYMATERIAL ||
type === PbKeyMaterialType.SYMMETRIC ||
type === PbKeyMaterialType.ASYMMETRIC_PRIVATE;
}
/**
* Creates a KeysetHandle from an encrypted keyset obtained via reader, using
* masterKeyAead to decrypt the keyset.
*
*
*/
export function read(reader, masterKeyAead) {
return __awaiter(this, void 0, void 0, function* () {
// TODO implement
throw new SecurityException('KeysetHandle -- read: Not implemented yet.');
});
}
/**
* Returns a new KeysetHandle that contains a single new key generated
* according to keyTemplate.
*
*
*/
export function generateNew(keyTemplate) {
return __awaiter(this, void 0, void 0, function* () {
// TODO(thaidn): move this to a key manager.
const keyset = yield generateNewKeyset_(keyTemplate);
return new KeysetHandle(keyset);
});
}
/**
* Generates a new Keyset that contains a single new key generated
* according to keyTemplate.
*
*/
function generateNewKeyset_(keyTemplate) {
return __awaiter(this, void 0, void 0, function* () {
const key = (new PbKeysetKey())
.setStatus(PbKeyStatusType.ENABLED)
.setOutputPrefixType(keyTemplate.getOutputPrefixType());
const keyId = generateNewKeyId_();
key.setKeyId(keyId);
const keyData = yield Registry.newKeyData(keyTemplate);
key.setKeyData(keyData);
const keyset = new PbKeyset();
keyset.addKey(key);
keyset.setPrimaryKeyId(keyId);
return keyset;
});
}
/**
* Generates a new random key ID.
*
* @return The key ID.
*/
function generateNewKeyId_() {
const bytes = Random.randBytes(4);
let value = 0;
for (let i = 0; i < bytes.length; i++) {
value += (bytes[i] & 255) << i * 8;
}
// Make sure the key ID is a positive integer smaller than 2^32.
return Math.abs(value) % Math.pow(2, 32);
}
/**
* Creates a KeysetHandle from a keyset, obtained via reader, which
* must contain no secret key material.
*
* This can be used to load public keysets or envelope encryption keysets.
* Users that need to load cleartext keysets can use CleartextKeysetHandle.
*
*/
export function readNoSecret(reader) {
if (reader === null) {
throw new SecurityException('Reader has to be non-null.');
}
const keyset = reader.read();
assertNoSecretKeyMaterial(keyset);
return new KeysetHandle(keyset);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5c2V0X2hhbmRsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2ludGVybmFsL2tleXNldF9oYW5kbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRzs7Ozs7Ozs7OztBQUdILE9BQU8sRUFBQyx5QkFBeUIsRUFBQyxNQUFNLDBDQUEwQyxDQUFDO0FBQ25GLE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLGlDQUFpQyxDQUFDO0FBQ2xFLE9BQU8sS0FBSyxNQUFNLE1BQU0sa0JBQWtCLENBQUM7QUFLM0MsT0FBTyxLQUFLLFlBQVksTUFBTSxpQkFBaUIsQ0FBQztBQUNoRCxPQUFPLEVBQUMsU0FBUyxFQUFFLGlCQUFpQixFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsZUFBZSxFQUFnQixNQUFNLFNBQVMsQ0FBQztBQUM1RyxPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0sZUFBZSxDQUFDO0FBQ3hDLE9BQU8sS0FBSyxRQUFRLE1BQU0sWUFBWSxDQUFDO0FBQ3ZDLE9BQU8sS0FBSyxJQUFJLE1BQU0sUUFBUSxDQUFDO0FBRS9COzs7OztHQUtHO0FBQ0gsTUFBTSxPQUFPLFlBQVk7SUFHdkIsWUFBWSxNQUFnQjtRQUMxQixJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzVCLElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0csWUFBWSxDQUNkLGFBQWtDLEVBQ2xDLG9CQUFvRDs7WUFDdEQsSUFBSSxDQUFDLGFBQWEsRUFBRTtnQkFDbEIsTUFBTSxJQUFJLHlCQUF5QixDQUFDLGlDQUFpQyxDQUFDLENBQUM7YUFDeEU7WUFDRCxNQUFNLFlBQVksR0FDZCxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsYUFBYSxFQUFFLG9CQUFvQixDQUFDLENBQUM7WUFDcEUsT0FBTyxRQUFRLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FB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LEVBQUUsQ0FBQyxFQUFFLEVBQUU7UUFDckMsS0FBSyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7S0FDcEM7SUFFRCxnRUFBZ0U7SUFDaEUsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLFNBQUEsQ0FBQyxFQUFJLEVBQUUsQ0FBQSxDQUFDO0FBQ25DLENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLFlBQVksQ0FBQyxNQUFvQjtJQUMvQyxJQUFJLE1BQU0sS0FBSyxJQUFJLEVBQUU7UUFDbkIsTUFBTSxJQUFJLGlCQUFpQixDQUFDLDRCQUE0QixDQUFDLENBQUM7S0FDM0Q7SUFDRCxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDN0IseUJBQXlCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDbEMsT0FBTyxJQUFJLFlBQVksQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUNsQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMjAgR29vZ2xlIExMQ1xuICogU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcbiAqL1xuXG5pbXBvcnQge0FlYWR9IGZyb20gJy4uL2FlYWQvaW50ZXJuYWwvYWVhZCc7XG5pbXBvcnQge0ludmFsaWRBcmd1bWVudHNFeGNlcHRpb259IGZyb20gJy4uL2V4Y2VwdGlvbi9pbnZhbGlkX2FyZ3VtZW50c19leGNlcHRpb24nO1xuaW1wb3J0IHtTZWN1cml0eUV4Y2VwdGlvbn0gZnJvbSAnLi4vZXhjZXB0aW9uL3NlY3VyaXR5X2V4Y2VwdGlvbic7XG5pbXBvcnQgKiBhcyBSYW5kb20gZnJvbSAnLi4vc3VidGxlL3JhbmRvbSc7XG5cbmltcG9ydCAqIGFzIEtleU1hbmFnZXIgZnJvbSAnLi9rZXlfbWFuYWdlcic7XG5pbXBvcnQge0tleXNldFJlYWRlcn0gZnJvbSAnLi9rZXlzZXRfcmVhZGVyJztcbmltcG9ydCB7S2V5c2V0V3JpdGVyfSBmcm9tICcuL2tleXNldF93cml0ZXInO1xuaW1wb3J0ICogYXMgUHJpbWl0aXZlU2V0IGZyb20gJy4vcHJpbWl0aXZlX3NldCc7XG5pbXBvcnQge1BiS2V5RGF0YSwgUGJLZXlNYXRlcmlhbFR5cGUsIFBiS2V5c2V0LCBQYktleXNldEtleSwgUGJLZXlTdGF0dXNUeXBlLCBQYktleVRlbXBsYXRlfSBmcm9tICcuL3Byb3RvJztcbmltcG9ydCB7Ynl0ZXNBc1U4fSBmcm9tICcuL3Byb3RvX3NoaW1zJztcbmltcG9ydCAqIGFzIFJlZ2lzdHJ5IGZyb20gJy4vcmVnaXN0cnknO1xuaW1wb3J0ICogYXMgVXRpbCBmcm9tICcuL3V0aWwnO1xuXG4vKipcbiAqIEtleXNldCBoYW5kbGUgcHJvdmlkZSBhYnN0cmFjdGVkIGFjY2VzcyB0byBLZXlzZXRzLCB0byBsaW1pdCB0aGUgZXhwb3N1cmUgb2ZcbiAqIGFjdHVhbCBwcm90b2NvbCBidWZmZXJzIHRoYXQgaG9sZCBzZW5zaXRpdmUga2V5IG1hdGVyaWFsLlxuICpcbiAqIEBmaW5hbFxuICovXG5leHBvcnQgY2xhc3MgS2V5c2V0SGFuZGxlIHtcbiAgcHJpdmF0ZSByZWFkb25seSBrZXlzZXRfOiBQYktleXNldDtcblxuICBjb25zdHJ1Y3RvcihrZXlzZXQ6IFBiS2V5c2V0KSB7XG4gICAgVXRpbC52YWxpZGF0ZUtleXNldChrZXlzZXQpO1xuICAgIHRoaXMua2V5c2V0XyA9IGtleXNldDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGEgcHJpbWl0aXZlIHRoYXQgdXNlcyBrZXkgbWF0ZXJpYWwgZnJvbSB0aGlzIGtleXNldCBoYW5kbGUuIElmXG4gICAqIG9wdF9jdXN0b21LZXlNYW5hZ2VyIGlzIGRlZmluZWQgdGhlbiB0aGUgcHJvdmlkZWQga2V5IG1hbmFnZXIgaXMgdXNlZCB0b1xuICAgKiBpbnN0YW50aWF0ZSBwcmltaXRpdmVzLiBPdGhlcndpc2Uga2V5IG1hbmFnZXIgZnJvbSBSZWdpc3RyeSBpcyB1c2VkLlxuICAgKi9cbiAgYXN5bmMgZ2V0UHJpbWl0aXZlPFA+KFxuICAgICAgcHJpbWl0aXZlVHlwZTogVXRpbC5Db25zdHJ1Y3RvcjxQPixcbiAgICAgIG9wdF9jdXN0b21LZXlNYW5hZ2VyPzogS2V5TWFuYWdlci5LZXlNYW5hZ2VyPFA+fG51bGwpOiBQcm9taXNlPFA+IHtcbiAgICBpZiAoIXByaW1pdGl2ZVR5cGUpIHtcbiAgICAgIHRocm93IG5ldyBJbnZhbGlkQXJndW1lbnRzRXhjZXB0aW9uKCdwcmltaXRpdmUgdHlwZSBtdXN0IGJlIG5vbi1udWxsJyk7XG4gICAgfVxuICAgIGNvbnN0IHByaW1pdGl2ZVNldCA9XG4gICAgICAgIGF3YWl0IHRoaXMuZ2V0UHJpbWl0aXZlU2V0KHByaW1pdGl2ZVR5cGUsIG9wdF9jdXN0b21LZXlNYW5hZ2VyKTtcbiAgICByZXR1cm4gUmVnaXN0cnkud3JhcChwcmltaXRpdmVTZXQpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBzZXQgb2YgcHJpbWl0aXZlcyBjb3JyZXNwb25kaW5nIHRvIHRoZSBrZXlzIHdpdGggc3RhdHVzIEVuYWJsZWRcbiAgICogaW4gdGhlIGdpdmVuIGtleXNldEhhbmRsZSwgYXNzdW1pbmcgYWxsIHRoZSBjb3JyZXNwb2Rpbmcga2V5IG1hbmFnZXJzIGFyZVxuICAgKiBwcmVzZW50IChrZXlzIHdpdGggc3RhdHVzIGRpZmZlcmVudCBmcm9tIEVuYWJsZWQgYXJlIHNraXBwZWQpLiBJZiBwcm92aWRlZFxuICAgKiB1c2VzIGN1c3RvbUtleU1hbmFnZXIgaW5zdGVhZCBvZiByZWdpc3RlcmVkIGtleSBtYW5hZ2VycyBmb3Iga2V5cyBzdXBwb3J0ZWRcbiAgICogYnkgdGhlIGN1c3RvbUtleU1hbmFnZXIuXG4gICAqXG4gICAqIFZpc2libGUgZm9yIHRlc3RpbmcuXG4gICAqL1xuICBhc3luYyBnZXRQcmltaXRpdmVTZXQ8UD4oXG4gICAgICBwcmltaXRpdmVUeXBlOiBVdGlsLkNvbnN0cnVjdG9yPFA+LFxuICAgICAgb3B0X2N1c3RvbUtleU1hbmFnZXI/OiBLZXlNYW5hZ2VyLktleU1hbmFnZXI8UD58XG4gICAgICBudWxsKTogUHJvbWlzZTxQcmltaXRpdmVTZXQuUHJpbWl0aXZlU2V0PFA+PiB7XG4gICAgY29uc3QgcHJpbWl0aXZlU2V0ID0gbmV3IFByaW1pdGl2ZVNldC5QcmltaXRpdmVTZXQ8UD4ocHJpbWl0aXZlVHlwZSk7XG4gICAgY29uc3Qga2V5cyA9IHRoaXMua2V5c2V0Xy5nZXRLZXlMaXN0KCk7XG4gICAgY29uc3Qga2V5c0xlbmd0aCA9IGtleXMubGVuZ3RoO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwga2V5c0xlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBrZXkgPSBrZXlzW2ldO1xuICAgICAgaWYgKGtleS5nZXRTdGF0dXMoKSA9PT0gUGJLZXlTdGF0dXNUeXBlLkVOQUJMRUQpIHtcbiAgICAgICAgY29uc3Qga2V5RGF0YSA9IGtleS5nZXRLZXlEYXRhKCk7XG4gICAgICAgIGlmICgha2V5RGF0YSkge1xuICAgICAgICAgIHRocm93IG5ldyBTZWN1cml0eUV4Y2VwdGlvbignS2V5IGRhdGEgaGFzIHRvIGJlIG5vbiBudWxsLicpO1xuICAgICAgICB9XG4gICAgICAgIGxldCBwcmltaXRpdmU7XG4gICAgICAgIGlmIChvcHRfY3VzdG9tS2V5TWFuYWdlciAmJlxuICAgICAgICAgICAgb3B0X2N1c3RvbUtleU1hbmFnZXIuZ2V0S2V5VHlwZSgpID09PSBrZXlEYXRhLmdldFR5cGVVcmwoKSkge1xuICAgICAgICAgIHByaW1pdGl2ZSA9XG4gICAgICAgICAgICAgIGF3YWl0IG9wdF9jdXN0b21LZXlNYW5hZ2VyLmdldFByaW1pdGl2ZShwcmltaXRpdmVUeXBlLCBrZXlEYXRhKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBwcmltaXRpdmUgPSBhd2FpdCBSZWdpc3RyeS5nZXRQcmltaXRpdmU8UD4ocHJpbWl0aXZlVHlwZSwga2V5RGF0YSk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgZW50cnkgPSBwcmltaXRpdmVTZXQuYWRkUHJpbWl0aXZlKHByaW1pdGl2ZSwga2V5KTtcbiAgICAgICAgaWYgKGtleS5nZXRLZXlJZCgpID09PSB0aGlzLmtleXNldF8uZ2V0UHJpbWFyeUtleUlkKCkpIHtcbiAgICAgICAgICBwcmltaXRpdmVTZXQuc2V0UHJpbWFyeShlbnRyeSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHByaW1pdGl2ZVNldDtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmNyeXB0cyB0aGUgdW5kZXJseWluZyBrZXlzZXQgd2l0aCB0aGUgcHJvdmlkZWQgbWFzdGVyS2V5QWVhZCB3bmQgd3JpdGVzXG4gICAqIHRoZSByZXN1bHRpbmcgZW5jcnlwdGVkS2V5c2V0IHRvIHRoZSBnaXZlbiB3cml0ZXIgd2hpY2ggbXVzdCBiZSBub24tbnVsbC5cbiAgICpcbiAgICpcbiAgICovXG4gIGFzeW5jIHdyaXRlKHdyaXRlcjogS2V5c2V0V3JpdGVyLCBtYXN0ZXJLZXlBZWFkOiBBZWFkKSB7XG4gICAgLy8gVE9ETyBpbXBsZW1lbnRcbiAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oJ0tleXNldEhhbmRsZSAtLSB3cml0ZTogTm90IGltcGxlbWVudGVkIHlldC4nKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXcml0ZXMgdGhpcyBrZXlzZXQgdXNpbmcgYHdyaXRlcmAgaWYgYW5kIG9ubHkgaWYgdGhlIGtleXNldCBkb2Vzbid0IGNvbnRhaW5cbiAgICogYW55IHNlY3JldCBrZXkgbWF0ZXJpYWwuXG4gICAqXG4gICAqIFRoaXMgY2FuIGJlIHVzZWQgdG8gcGVyc2lzdCBwdWJsaWMga2V5c2V0cyBvciBlbnZlbG9wZSBlbmNyeXB0aW9uIGtleXNldHMuXG4gICAqIFVzZSBgQ2xlYXJ0ZXh0S2V5c2V0SGFuZGxlYCB0byBwZXJzaXN0IGtleXNldHMgY29udGFpbmluZyBzZWNyZXQga2V5XG4gICAqIG1hdGVyaWFsLlxuICAgKi9cbiAgd3JpdGVOb1NlY3JldCh3cml0ZXI6IEtleXNldFdyaXRlcik6IFVpbnQ4QXJyYXkge1xuICAgIGFzc2VydE5vU2VjcmV0S2V5TWF0ZXJpYWwodGhpcy5rZXlzZXRfKTtcbiAgICByZXR1cm4gd3JpdGVyLmVuY29kZUJpbmFyeSh0aGlzLmtleXNldF8pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGtleXNldCBoZWxkIGJ5IHRoaXMgS2V5c2V0SGFuZGxlLlxuICAgKlxuICAgKi9cbiAgZ2V0S2V5c2V0KCk6IFBiS2V5c2V0IHtcbiAgICByZXR1cm4gdGhpcy5rZXlzZXRfO1xuICB9XG5cbiAgLyoqXG4gICAqIElmIHRoZSBtYW5hZ2VkIGtleXNldCBjb250YWlucyBwcml2YXRlIGtleXMsIHJldHVybnMgYSBgS2V5c2V0SGFuZGxlYCBvZlxuICAgKiB0aGUgcHVibGljIGtleXMuXG4gICAqL1xuICBnZXRQdWJsaWNLZXlzZXRIYW5kbGUoKTogS2V5c2V0SGFuZGxlIHtcbiAgICBjb25zdCBwdWJsaWNLZXlzZXQgPSBuZXcgUGJLZXlzZXQoKTtcbiAgICBmb3IgKGNvbnN0IGtleSBvZiB0aGlzLmtleXNldF8uZ2V0S2V5TGlzdCgpKSB7XG4gICAgICBwdWJsaWNLZXlzZXQuYWRkS2V5KGtleS5jbG9uZSgpLnNldEtleURhdGEoY3JlYXRlUHVibGljS2V5RGF0YShcbiAgICAgICAgICBub25OdWxsKCdLZXkgZGF0YScsIGtleS5nZXRLZXlEYXRhKCkpKSkpO1xuICAgIH1cbiAgICBwdWJsaWNLZXlzZXQuc2V0UHJpbWFyeUtleUlkKHRoaXMua2V5c2V0Xy5nZXRQcmltYXJ5S2V5SWQoKSk7XG4gICAgcmV0dXJuIG5ldyBLZXlzZXRIYW5kbGUocHVibGljS2V5c2V0KTtcbiAgfVxufVxuXG5mdW5jdGlvbiBub25OdWxsPFQ+KGRlc2M6IHN0cmluZywgdmFsdWU6IFR8bnVsbHx1bmRlZmluZWQpOiBUIHtcbiAgaWYgKHZhbHVlID09IG51bGwpIHtcbiAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oYCR7ZGVzY30gaGFzIHRvIGJlIG5vbiBudWxsLmApO1xuICB9XG4gIHJldHVybiB2YWx1ZTtcbn1cblxuZnVuY3Rpb24gY3JlYXRlUHVibGljS2V5RGF0YShwcml2YXRlS2V5RGF0YTogUGJLZXlEYXRhKTogUGJLZXlEYXRhIHtcbiAgaWYgKHByaXZhdGVLZXlEYXRhLmdldEtleU1hdGVyaWFsVHlwZSgpICE9PVxuICAgICAgUGJLZXlEYXRhLktleU1hdGVyaWFsVHlwZS5BU1lNTUVUUklDX1BSSVZBVEUpIHtcbiAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oJ1RoZSBrZXlzZXQgY29udGFpbnMgYSBub24tcHJpdmF0ZSBrZXknKTtcbiAgfVxuICByZXR1cm4gUmVnaXN0cnkuZ2V0UHVibGljS2V5RGF0YShcbiAgICAgIHByaXZhdGVLZXlEYXRhLmdldFR5cGVVcmwoKSwgYnl0ZXNBc1U4KHByaXZhdGVLZXlEYXRhLmdldFZhbHVlKCkpKTtcbn1cblxuLyoqXG4gKiBWYWxpZGF0ZXMgdGhhdCBga2V5c2V0YCBkb2Vzbid0IGNvbnRhaW4gYW55IHNlY3JldCBrZXkgbWF0ZXJpYWwuXG4gKlxuICogQHRocm93cyBTZWN1cml0eUV4Y2VwdGlvbiBpZiBga2V5c2V0YCBjb250YWlucyBzZWNyZXQga2V5IG1hdGVyaWFsLlxuICovXG5mdW5jdGlvbiBhc3NlcnROb1NlY3JldEtleU1hdGVyaWFsKGtleXNldDogUGJLZXlzZXQpIHtcbiAgZm9yIChjb25zdCBrZXkgb2Yga2V5c2V0LmdldEtleUxpc3QoKSkge1xuICAgIGNvbnN0IGtleURhdGEgPSBub25OdWxsKCdLZXkgZGF0YScsIGtleS5nZXRLZXlEYXRhKCkpO1xuICAgIGlmIChpc1NlY3JldEtleU1hdGVyaWFsVHlwZShrZXlEYXRhLmdldEtleU1hdGVyaWFsVHlwZSgpKSkge1xuICAgICAgdGhyb3cgbmV3IFNlY3VyaXR5RXhjZXB0aW9uKCdLZXlzZXQgY29udGFpbnMgc2VjcmV0IGtleSBtYXRlcmlhbC4nKTtcbiAgICB9XG4gIH1cbn1cblxuLyoqIFJldHVybnMgdHJ1ZSBpZiB0aGUga2V5IG1hdGVyaWFsIHR5cGUgaXMgc2VjcmV0LiAqL1xuZnVuY3Rpb24gaXNTZWNyZXRLZXlNYXRlcmlhbFR5cGUodHlwZTogUGJLZXlNYXRlcmlhbFR5cGUpIHtcbiAgcmV0dXJuIHR5cGUgPT09IFBiS2V5TWF0ZXJpYWxUeXBlLlVOS05PV05fS0VZTUFURVJJQUwgfHxcbiAgICAgIHR5cGUgPT09IFBiS2V5TWF0ZXJpYWxUeXBlLlNZTU1FVFJJQyB8fFxuICAgICAgdHlwZSA9PT0gUGJLZXlNYXRlcmlhbFR5cGUuQVNZTU1FVFJJQ19QUklWQVRFO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBLZXlzZXRIYW5kbGUgZnJvbSBhbiBlbmNyeXB0ZWQga2V5c2V0IG9idGFpbmVkIHZpYSByZWFkZXIsIHVzaW5nXG4gKiBtYXN0ZXJLZXlBZWFkIHRvIGRlY3J5cHQgdGhlIGtleXNldC5cbiAqXG4gKlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZChcbiAgICByZWFkZXI6IEtleXNldFJlYWRlciwgbWFzdGVyS2V5QWVhZDogQWVhZCk6IFByb21pc2U8S2V5c2V0SGFuZGxlPiB7XG4gIC8vIFRPRE8gaW1wbGVtZW50XG4gIHRocm93IG5ldyBTZWN1cml0eUV4Y2VwdGlvbignS2V5c2V0SGFuZGxlIC0tIHJlYWQ6IE5vdCBpbXBsZW1lbnRlZCB5ZXQuJyk7XG59XG5cbi8qKlxuICogUmV0dXJucyBhIG5ldyBLZXlzZXRIYW5kbGUgdGhhdCBjb250YWlucyBhIHNpbmdsZSBuZXcga2V5IGdlbmVyYXRlZFxuICogYWNjb3JkaW5nIHRvIGtleVRlbXBsYXRlLlxuICpcbiAqXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZW5lcmF0ZU5ldyhrZXlUZW1wbGF0ZTogUGJLZXlUZW1wbGF0ZSk6XG4gICAgUHJvbWlzZTxLZXlzZXRIYW5kbGU+IHtcbiAgLy8gVE9ETyh0aGFpZG4pOiBtb3ZlIHRoaXMgdG8gYSBrZXkgbWFuYWdlci5cbiAgY29uc3Qga2V5c2V0ID0gYXdhaXQgZ2VuZXJhdGVOZXdLZXlzZXRfKGtleVRlbXBsYXRlKTtcbiAgcmV0dXJuIG5ldyBLZXlzZXRIYW5kbGUoa2V5c2V0KTtcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZXMgYSBuZXcgS2V5c2V0IHRoYXQgY29udGFpbnMgYSBzaW5nbGUgbmV3IGtleSBnZW5lcmF0ZWRcbiAqIGFjY29yZGluZyB0byBrZXlUZW1wbGF0ZS5cbiAqXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdlbmVyYXRlTmV3S2V5c2V0XyhrZXlUZW1wbGF0ZTogUGJLZXlUZW1wbGF0ZSk6XG4gICAgUHJvbWlzZTxQYktleXNldD4ge1xuICBjb25zdCBrZXkgPSAobmV3IFBiS2V5c2V0S2V5KCkpXG4gICAgICAgICAgICAgICAgICAuc2V0U3RhdHVzKFBiS2V5U3RhdHVzVHlwZS5FTkFCTEVEKVxuICAgICAgICAgICAgICAgICAgLnNldE91dHB1dFByZWZpeFR5cGUoa2V5VGVtcGxhdGUuZ2V0T3V0cHV0UHJlZml4VHlwZSgpKTtcbiAgY29uc3Qga2V5SWQgPSBnZW5lcmF0ZU5ld0tleUlkXygpO1xuICBrZXkuc2V0S2V5SWQoa2V5SWQpO1xuICBjb25zdCBrZXlEYXRhID0gYXdhaXQgUmVnaXN0cnkubmV3S2V5RGF0YShrZXlUZW1wbGF0ZSk7XG4gIGtleS5zZXRLZXlEYXRhKGtleURhdGEpO1xuICBjb25zdCBrZXlzZXQgPSBuZXcgUGJLZXlzZXQoKTtcbiAga2V5c2V0LmFkZEtleShrZXkpO1xuICBrZXlzZXQuc2V0UHJpbWFyeUtleUlkKGtleUlkKTtcbiAgcmV0dXJuIGtleXNldDtcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZXMgYSBuZXcgcmFuZG9tIGtleSBJRC5cbiAqXG4gKiBAcmV0dXJuIFRoZSBrZXkgSUQuXG4gKi9cbmZ1bmN0aW9uIGdlbmVyYXRlTmV3S2V5SWRfKCk6IG51bWJlciB7XG4gIGNvbnN0IGJ5dGVzID0gUmFuZG9tLnJhbmRCeXRlcyg0KTtcbiAgbGV0IHZhbHVlID0gMDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBieXRlcy5sZW5ndGg7IGkrKykge1xuICAgIHZhbHVlICs9IChieXRlc1tpXSAmIDI1NSkgPDwgaSAqIDg7XG4gIH1cblxuICAvLyBNYWtlIHN1cmUgdGhlIGtleSBJRCBpcyBhIHBvc2l0aXZlIGludGVnZXIgc21hbGxlciB0aGFuIDJeMzIuXG4gIHJldHVybiBNYXRoLmFicyh2YWx1ZSkgJSAyICoqIDMyO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBLZXlzZXRIYW5kbGUgZnJvbSBhIGtleXNldCwgb2J0YWluZWQgdmlhIHJlYWRlciwgd2hpY2hcbiAqIG11c3QgY29udGFpbiBubyBzZWNyZXQga2V5IG1hdGVyaWFsLlxuICpcbiAqIFRoaXMgY2FuIGJlIHVzZWQgdG8gbG9hZCBwdWJsaWMga2V5c2V0cyBvciBlbnZlbG9wZSBlbmNyeXB0aW9uIGtleXNldHMuXG4gKiBVc2VycyB0aGF0IG5lZWQgdG8gbG9hZCBjbGVhcnRleHQga2V5c2V0cyBjYW4gdXNlIENsZWFydGV4dEtleXNldEhhbmRsZS5cbiAqXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWFkTm9TZWNyZXQocmVhZGVyOiBLZXlzZXRSZWFkZXIpOiBLZXlzZXRIYW5kbGUge1xuICBpZiAocmVhZGVyID09PSBudWxsKSB7XG4gICAgdGhyb3cgbmV3IFNlY3VyaXR5RXhjZXB0aW9uKCdSZWFkZXIgaGFzIHRvIGJlIG5vbi1udWxsLicpO1xuICB9XG4gIGNvbnN0IGtleXNldCA9IHJlYWRlci5yZWFkKCk7XG4gIGFzc2VydE5vU2VjcmV0S2V5TWF0ZXJpYWwoa2V5c2V0KTtcbiAgcmV0dXJuIG5ldyBLZXlzZXRIYW5kbGUoa2V5c2V0KTtcbn1cbiJdfQ==