From 8cb07e38b1bed6e93cf55a2c013bed4a4219437d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Mon, 26 Sep 2022 11:07:57 +0200 Subject: [PATCH 01/31] Add test for signing a multisig transaction --- tests/lib/Key.spec.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/lib/Key.spec.js b/tests/lib/Key.spec.js index 8402e021..7c7fc850 100644 --- a/tests/lib/Key.spec.js +++ b/tests/lib/Key.spec.js @@ -56,4 +56,47 @@ describe('Key', () => { expect(key.deriveAddress('m').toUserFriendlyAddress()).toEqual(address1); expect(key.deriveAddress('m/0\'').toUserFriendlyAddress()).toEqual(address2); }); + + it('can partially sign a multisig transaction (BIP39)', () => { + const keypairA = Nimiq.KeyPair.fromHex('14a3bc3b25c73b6ca3e829aef329a2a6dc69ae52b8d20a164831a021b6a9f9feec98d39d98a58c13d399673d6da7dc6c74f379eddd8c8628e40ffc6be7c2498300'); + const keypairB = Nimiq.KeyPair.fromHex('2da15ede9992fad834b73283dd1a24f5a7a52b067b09be132ddb5232df863125bb639b6bbf6db003a94a83ef9d12f12fcc5990f63954b7f6d88f5be58f8c411200'); + // const keypairC = Nimiq.KeyPair.fromHex('a3b3d799e7fca4baa3568d58e0c909af1f832926020163a1d48998621a15c9c6b81b12bcb1a6e9ba49a6dec268705c2cc2d70d1d7e22493a4128559eadacdbd400'); + + const signerPublicKeys = [ + keypairA.publicKey, + keypairB.publicKey, + ]; + signerPublicKeys.sort((a, b) => a.compare(b)); + + const secretA = Nimiq.RandomSecret.unserialize(Nimiq.BufferUtils.fromHex('79c97389d2670f76d2e55192bc7ed875d9941bbfaa7932f8f452d9a907f94903')); + + const aggregatedCommitment = Nimiq.Commitment.unserialize(Nimiq.BufferUtils.fromHex('1c3eebbd316a7c46c7fb8359fd06ffa54ed77ff076de88ff429ae126d935482e')); + + const transaction = Nimiq.Transaction.unserialize(Nimiq.BufferUtils.fromHex('010000f4e305f34ea1ccf00c0f7fcbc030d1347dc5eafe00000000000000000000000000000000000000000000000000000000000a00000000000000000000000001000000')); + + const partialSignatureA = Nimiq.PartialSignature.create( + keypairA.privateKey, + keypairA.publicKey, + signerPublicKeys, + secretA, + aggregatedCommitment, + transaction.serializeContent(), + ); + + expect(partialSignatureA.toHex()).toEqual('b3584f24b073410d9c6f8c092068a2d1b66e67387fa3319e57609f2b2425be02'); + + + const secretB = Nimiq.RandomSecret.unserialize(Nimiq.BufferUtils.fromHex('0e400561be5711fc7d39d24774233419fb99b7421a86e0520b98d5399a6a5801')); + + const partialSignatureB = Nimiq.PartialSignature.create( + keypairB.privateKey, + keypairB.publicKey, + signerPublicKeys, + secretB, + aggregatedCommitment, + transaction.serializeContent(), + ); + + expect(partialSignatureB.toHex()).toEqual('caa6353261d250e2f1f67499f526c47503015e08d2a69169322fecae83cdf607'); + }) }); From a520a0a0c56294981016af101d1aafad322e3247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Mon, 26 Sep 2022 11:45:45 +0200 Subject: [PATCH 02/31] Add `signPartially` method to Key and use in test --- src/lib/Key.js | 24 ++++++++++++++++++++++++ tests/lib/Key.spec.js | 23 ++++++++++++----------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/lib/Key.js b/src/lib/Key.js index 28a4a45a..3a6a29aa 100644 --- a/src/lib/Key.js +++ b/src/lib/Key.js @@ -50,6 +50,30 @@ class Key { return Nimiq.Signature.create(privateKey, publicKey, data); } + /** + * Partially sign a multisig transaction + * + * @param {string} path + * @param {Uint8Array} data + * @param {Nimiq.PublicKey[]} signerPublicKeys + * @param {Nimiq.RandomSecret} secret + * @param {Nimiq.Commitment} aggregatedCommitment + * @returns {Nimiq.PartialSignature} + */ + signPartially(path, data, signerPublicKeys, secret, aggregatedCommitment) { + const privateKey = this.derivePrivateKey(path); + const publicKey = Nimiq.PublicKey.derive(privateKey); + signerPublicKeys.sort((a, b) => a.compare(b)); + return Nimiq.PartialSignature.create( + privateKey, + publicKey, + signerPublicKeys, + secret, + aggregatedCommitment, + data, + ); + } + /** * @param {string} path * @param {Uint8Array} message - A byte array diff --git a/tests/lib/Key.spec.js b/tests/lib/Key.spec.js index 7c7fc850..5aabf68c 100644 --- a/tests/lib/Key.spec.js +++ b/tests/lib/Key.spec.js @@ -62,11 +62,14 @@ describe('Key', () => { const keypairB = Nimiq.KeyPair.fromHex('2da15ede9992fad834b73283dd1a24f5a7a52b067b09be132ddb5232df863125bb639b6bbf6db003a94a83ef9d12f12fcc5990f63954b7f6d88f5be58f8c411200'); // const keypairC = Nimiq.KeyPair.fromHex('a3b3d799e7fca4baa3568d58e0c909af1f832926020163a1d48998621a15c9c6b81b12bcb1a6e9ba49a6dec268705c2cc2d70d1d7e22493a4128559eadacdbd400'); + const keyA = new Key(keypairA.privateKey); + const keyB = new Key(keypairB.privateKey); + // const keyC = new Key(keypairC.privateKey); + const signerPublicKeys = [ - keypairA.publicKey, - keypairB.publicKey, + keyA.derivePublicKey('m'), + keyB.derivePublicKey('m'), ]; - signerPublicKeys.sort((a, b) => a.compare(b)); const secretA = Nimiq.RandomSecret.unserialize(Nimiq.BufferUtils.fromHex('79c97389d2670f76d2e55192bc7ed875d9941bbfaa7932f8f452d9a907f94903')); @@ -74,13 +77,12 @@ describe('Key', () => { const transaction = Nimiq.Transaction.unserialize(Nimiq.BufferUtils.fromHex('010000f4e305f34ea1ccf00c0f7fcbc030d1347dc5eafe00000000000000000000000000000000000000000000000000000000000a00000000000000000000000001000000')); - const partialSignatureA = Nimiq.PartialSignature.create( - keypairA.privateKey, - keypairA.publicKey, + const partialSignatureA = keyA.signPartially( + 'm', + transaction.serializeContent(), signerPublicKeys, secretA, aggregatedCommitment, - transaction.serializeContent(), ); expect(partialSignatureA.toHex()).toEqual('b3584f24b073410d9c6f8c092068a2d1b66e67387fa3319e57609f2b2425be02'); @@ -88,13 +90,12 @@ describe('Key', () => { const secretB = Nimiq.RandomSecret.unserialize(Nimiq.BufferUtils.fromHex('0e400561be5711fc7d39d24774233419fb99b7421a86e0520b98d5399a6a5801')); - const partialSignatureB = Nimiq.PartialSignature.create( - keypairB.privateKey, - keypairB.publicKey, + const partialSignatureB = keyB.signPartially( + 'm', + transaction.serializeContent(), signerPublicKeys, secretB, aggregatedCommitment, - transaction.serializeContent(), ); expect(partialSignatureB.toHex()).toEqual('caa6353261d250e2f1f67499f526c47503015e08d2a69169322fecae83cdf607'); From ee6a2bac3e378e9b89a7a9eee363bd1a9143094a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Mon, 26 Sep 2022 12:55:10 +0200 Subject: [PATCH 03/31] MonkeyPatch MerkleTree to verify multisig sender address from participants' public keys --- src/lib/multisig/MerkleTreePatch.js | 72 +++++++++++++++++++++++++++++ src/lib/multisig/MultisigUtils.js | 25 ++++++++++ tests/lib/Key.spec.js | 13 +++++- 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 src/lib/multisig/MerkleTreePatch.js create mode 100644 src/lib/multisig/MultisigUtils.js diff --git a/src/lib/multisig/MerkleTreePatch.js b/src/lib/multisig/MerkleTreePatch.js new file mode 100644 index 00000000..0aed3feb --- /dev/null +++ b/src/lib/multisig/MerkleTreePatch.js @@ -0,0 +1,72 @@ +/* global Nimiq */ +/* eslint-disable valid-jsdoc */ + +/** + * Monkey patch the Nimiq core MerkleTree class until it gets actually shipped with the Nimiq core web-offline package. + */ +class MerkleTreePatch { // eslint-disable-line no-unused-vars + static apply() { + if (typeof Nimiq.MerkleTree !== 'undefined') { + throw new Error('MerkleTree monkey patch not required anymore. Please remove it.'); + } else { + class MerkleTree { + /** + * @param {any[]} values + * @param {(o: any) => Nimiq.Hash} [fnHash] + * @returns {Nimiq.Hash} + */ + static computeRoot(values, fnHash = MerkleTree._hash) { + return MerkleTree._computeRoot(values, fnHash); + } + + /** + * @param {any[]} values + * @param {(o: any) => Nimiq.Hash} fnHash + * @returns {Nimiq.Hash} + * @private + */ + static _computeRoot(values, fnHash) { + const len = values.length; + if (len === 0) { + return Nimiq.Hash.light(new Uint8Array(0)); + } + if (len === 1) { + return fnHash(values[0]); + } + + const mid = Math.round(len / 2); + const left = values.slice(0, mid); + const right = values.slice(mid); + const leftHash = MerkleTree._computeRoot(left, fnHash); + const rightHash = MerkleTree._computeRoot(right, fnHash); + return Nimiq.Hash.light( + /** @type {Uint8Array} */ + (Nimiq.BufferUtils.concatTypedArrays(leftHash.serialize(), rightHash.serialize())), + ); + } + + /** + * @param {Nimiq.Hash | Uint8Array | { hash: () => Nimiq.Hash } | { serialize: () => Uint8Array }} o + * @returns {Nimiq.Hash} + * @private + */ + static _hash(o) { + if (o instanceof Nimiq.Hash) { + return o; + } + if ('hash' in o && typeof o.hash === 'function') { + return o.hash(); + } + if ('serialize' in o && typeof o.serialize === 'function') { + return Nimiq.Hash.light(o.serialize()); + } + if (o instanceof Uint8Array) { + return Nimiq.Hash.light(o); + } + throw new Error('MerkleTree objects must be Uint8Array or have a .hash()/.serialize() method'); + } + } + Nimiq.Class.register(MerkleTree); + } + } +} diff --git a/src/lib/multisig/MultisigUtils.js b/src/lib/multisig/MultisigUtils.js new file mode 100644 index 00000000..33dbf9f3 --- /dev/null +++ b/src/lib/multisig/MultisigUtils.js @@ -0,0 +1,25 @@ +/* global Nimiq */ +/* global MerkleTreePatch */ + +class MultisigUtils { // eslint-disable-line no-unused-vars + /** + * @param {Nimiq.PublicKey[]} publicKeys + * @param {number} signersRequired + * @returns {Nimiq.Address} + */ + static calculateAddress(publicKeys, signersRequired) { + publicKeys.sort((a, b) => a.compare(b)); + const combinations = [ + ...( + // @ts-ignore Generator is not generic in Keyguard's old TS version + /** @type {Generator} */ + (Nimiq.ArrayUtils.k_combinations(publicKeys, signersRequired)) + ), + ]; + const multiSigKeys = combinations.map(combination => Nimiq.PublicKey.sum(combination)); + multiSigKeys.sort((a, b) => a.compare(b)); + MerkleTreePatch.apply(); + const merkleRoot = Nimiq.MerkleTree.computeRoot(multiSigKeys); + return Nimiq.Address.fromHash(merkleRoot); + } +} diff --git a/tests/lib/Key.spec.js b/tests/lib/Key.spec.js index 5aabf68c..4f4bff73 100644 --- a/tests/lib/Key.spec.js +++ b/tests/lib/Key.spec.js @@ -60,11 +60,11 @@ describe('Key', () => { it('can partially sign a multisig transaction (BIP39)', () => { const keypairA = Nimiq.KeyPair.fromHex('14a3bc3b25c73b6ca3e829aef329a2a6dc69ae52b8d20a164831a021b6a9f9feec98d39d98a58c13d399673d6da7dc6c74f379eddd8c8628e40ffc6be7c2498300'); const keypairB = Nimiq.KeyPair.fromHex('2da15ede9992fad834b73283dd1a24f5a7a52b067b09be132ddb5232df863125bb639b6bbf6db003a94a83ef9d12f12fcc5990f63954b7f6d88f5be58f8c411200'); - // const keypairC = Nimiq.KeyPair.fromHex('a3b3d799e7fca4baa3568d58e0c909af1f832926020163a1d48998621a15c9c6b81b12bcb1a6e9ba49a6dec268705c2cc2d70d1d7e22493a4128559eadacdbd400'); + const keypairC = Nimiq.KeyPair.fromHex('a3b3d799e7fca4baa3568d58e0c909af1f832926020163a1d48998621a15c9c6b81b12bcb1a6e9ba49a6dec268705c2cc2d70d1d7e22493a4128559eadacdbd400'); const keyA = new Key(keypairA.privateKey); const keyB = new Key(keypairB.privateKey); - // const keyC = new Key(keypairC.privateKey); + const keyC = new Key(keypairC.privateKey); const signerPublicKeys = [ keyA.derivePublicKey('m'), @@ -77,6 +77,15 @@ describe('Key', () => { const transaction = Nimiq.Transaction.unserialize(Nimiq.BufferUtils.fromHex('010000f4e305f34ea1ccf00c0f7fcbc030d1347dc5eafe00000000000000000000000000000000000000000000000000000000000a00000000000000000000000001000000')); + // Test if participants' public keys create the transaction's sender address + const expectedAddress = MultisigUtils.calculateAddress([ + keyA.derivePublicKey('m'), + keyB.derivePublicKey('m'), + keyC.derivePublicKey('m'), + ], 2); + + expect(transaction.sender.equals(expectedAddress)).toBe(true); + const partialSignatureA = keyA.signPartially( 'm', transaction.serializeContent(), From 4b00fdff6d508de16efccce397fcd87073ca528a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Mon, 26 Sep 2022 15:46:50 +0200 Subject: [PATCH 04/31] [WIP] Add SignMultisigTransaction request Missing multisig-specific UI elements --- client/src/KeyguardClient.ts | 5 + client/src/KeyguardCommand.ts | 1 + client/src/PublicRequest.ts | 30 ++- src/lib/RequestParser.js | 7 +- .../SignMultisigTransaction.css | 156 +++++++++++ .../SignMultisigTransaction.js | 250 ++++++++++++++++++ .../SignMultisigTransactionApi.js | 211 +++++++++++++++ .../sign-multisig-transaction/index.html | 143 ++++++++++ .../sign-multisig-transaction/index.js | 4 + types/Keyguard.d.ts | 17 ++ 10 files changed, 819 insertions(+), 5 deletions(-) create mode 100644 src/request/sign-multisig-transaction/SignMultisigTransaction.css create mode 100644 src/request/sign-multisig-transaction/SignMultisigTransaction.js create mode 100644 src/request/sign-multisig-transaction/SignMultisigTransactionApi.js create mode 100644 src/request/sign-multisig-transaction/index.html create mode 100644 src/request/sign-multisig-transaction/index.js diff --git a/client/src/KeyguardClient.ts b/client/src/KeyguardClient.ts index 67d6cc7a..b0f4620e 100644 --- a/client/src/KeyguardClient.ts +++ b/client/src/KeyguardClient.ts @@ -20,6 +20,7 @@ import { ReleaseKeyRequest, ResetPasswordRequest, SignTransactionRequest, + SignMultisigTransactionRequest, SignStakingRequest, SignMessageRequest, SimpleRequest, @@ -121,6 +122,10 @@ export class KeyguardClient { this._redirectRequest(KeyguardCommand.SIGN_TRANSACTION, request); } + public signMultisigTransaction(request: SignMultisigTransactionRequest) { + this._redirectRequest(KeyguardCommand.SIGN_MULTISIG_TRANSACTION, request); + } + public signStaking(request: SignStakingRequest) { this._redirectRequest(KeyguardCommand.SIGN_STAKING, request); } diff --git a/client/src/KeyguardCommand.ts b/client/src/KeyguardCommand.ts index 5dca19ab..461985e0 100644 --- a/client/src/KeyguardCommand.ts +++ b/client/src/KeyguardCommand.ts @@ -5,6 +5,7 @@ export enum KeyguardCommand { EXPORT = 'export', CHANGE_PASSWORD = 'change-password', SIGN_TRANSACTION = 'sign-transaction', + SIGN_MULTISIG_TRANSACTION = 'sign-multisig-transaction', SIGN_STAKING = 'sign-staking', SIGN_MESSAGE = 'sign-message', DERIVE_ADDRESS = 'derive-address', diff --git a/client/src/PublicRequest.ts b/client/src/PublicRequest.ts index 91e8bae4..36d366d9 100644 --- a/client/src/PublicRequest.ts +++ b/client/src/PublicRequest.ts @@ -94,6 +94,7 @@ export type BitcoinTransactionInfo = { }; export type SignTransactionRequestLayout = 'standard' | 'checkout' | 'cashlink'; +export type SignMultisigTransactionRequestLayout = 'standard'; export type SignBtcTransactionRequestLayout = 'standard' | 'checkout'; // Specific Requests @@ -189,6 +190,24 @@ export type SignTransactionRequest | SignTransactionRequestCheckout | SignTransactionRequestCashlink; +export type MultisigInfo = { + publicKeys: Uint8Array[], + numberOfSigners: number, + signerPublicKeys: Uint8Array[], + secret: Uint8Array, + aggregatedCommitment: Uint8Array, +}; + +export type SignMultisigTransactionRequestCommon = SignTransactionRequestCommon & MultisigInfo; + +export type SignMultisigTransactionRequestStandard = SignMultisigTransactionRequestCommon & { + layout?: 'standard', + recipientLabel?: string, +}; + +export type SignMultisigTransactionRequest + = SignMultisigTransactionRequestStandard; + export type SignStakingRequest = SimpleRequest & { keyPath: string, transaction: Uint8Array | Uint8Array[], // An array is only allowed for retire_stake + remove_stake transactions @@ -545,6 +564,7 @@ export type ListLegacyResult = LegacyKeyInfoObject[]; export type SignTransactionResult = SignatureResult & { serializedTx: Uint8Array, }; +export type SignMultisigTransactionResult = SignatureResult; export type SignStakingResult = SignatureResult & { transaction: Uint8Array, }; @@ -579,7 +599,9 @@ export type RedirectResult = DerivedAddress[] | ExportResult | KeyResult + | SignatureResult | SignTransactionResult + | SignMultisigTransactionResult | SignStakingResult[] | SignedBitcoinTransaction | SignedPolygonTransaction @@ -593,7 +615,9 @@ export type Result = RedirectResult | IFrameResult; // Derived Result types export type ResultType = - T extends Is | Is ? SignatureResult : + T extends Is ? SignatureResult : + T extends Is ? SignTransactionResult : + T extends Is ? SignMultisigTransactionResult : T extends Is ? SignStakingResult[] : T extends Is ? DerivedAddress[] : T extends Is | Is | Is ? KeyResult : @@ -607,7 +631,9 @@ export type ResultType = never; export type ResultByCommand = - T extends KeyguardCommand.SIGN_MESSAGE | KeyguardCommand.SIGN_TRANSACTION ? SignatureResult : + T extends KeyguardCommand.SIGN_MESSAGE ? SignatureResult : + T extends KeyguardCommand.SIGN_TRANSACTION ? SignTransactionResult : + T extends KeyguardCommand.SIGN_MULTISIG_TRANSACTION ? SignMultisigTransactionResult : T extends KeyguardCommand.SIGN_STAKING ? SignStakingResult[] : T extends KeyguardCommand.DERIVE_ADDRESS ? DerivedAddress[] : T extends KeyguardCommand.CREATE | KeyguardCommand.IMPORT ? KeyResult : diff --git a/src/lib/RequestParser.js b/src/lib/RequestParser.js index cda3cbec..d050f506 100644 --- a/src/lib/RequestParser.js +++ b/src/lib/RequestParser.js @@ -128,15 +128,16 @@ class RequestParser { // eslint-disable-line no-unused-vars * @returns {Nimiq.Transaction} */ parseTransaction(object) { + if (!object || typeof object !== 'object' || object === null) { + throw new Errors.InvalidRequestError('Request must be an object'); + } + const accountTypes = new Set([ Nimiq.AccountType.Basic, Nimiq.AccountType.Vesting, Nimiq.AccountType.HTLC, Nimiq.AccountType.Staking, ]); - if (!object || typeof object !== 'object' || object === null) { - throw new Errors.InvalidRequestError('Request must be an object'); - } const sender = this.parseAddress(object.sender, 'sender', false); const senderType = object.senderType || Nimiq.AccountType.Basic; diff --git a/src/request/sign-multisig-transaction/SignMultisigTransaction.css b/src/request/sign-multisig-transaction/SignMultisigTransaction.css new file mode 100644 index 00000000..9441c597 --- /dev/null +++ b/src/request/sign-multisig-transaction/SignMultisigTransaction.css @@ -0,0 +1,156 @@ +#confirm-transaction { + position: relative; + overflow: hidden; +} + +#confirm-transaction.standard .hide-standard, +#confirm-transaction.checkout .hide-checkout, +#confirm-transaction.cashlink .hide-cashlink { + display: none; +} + +#confirm-transaction .payment-info-line + .nq-card-header { + padding-top: 2rem; +} + +#confirm-transaction .nq-card-body { + padding-bottom: 2rem; +} + +#confirm-transaction .transaction { + display: flex; + flex-direction: column; +} + +#confirm-transaction .value-fee-data { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding-top: 2rem; + flex-grow: 1; +} + +#confirm-transaction > #account-details { + position: absolute; + overflow: hidden; + top: 0; + left: 0; + height: 100%; + width: 100%; + border-radius: 1rem; + background-color: rgba(255,255,255, 0.75); + z-index: -1; + opacity: 0; + padding: 4rem; + display: flex; + flex-direction: column; + justify-content: center; + transition: opacity .4s 0s, z-index 0s .4s; +} + +#confirm-transaction.account-details-open > #account-details { + z-index: 3; + transition-delay: 0s; + opacity: 1; +} + +#confirm-transaction #details { + flex-direction: column; + align-items: center; +} + +#confirm-transaction #effect-container { + display: flex; + flex-direction: column; + overflow: hidden; + border-radius: 1rem; + flex-grow: 1; +} + +#confirm-transaction > #effect-container > * { + transition: filter .4s, opacity .4s; + opacity: 1; +} + +#confirm-transaction.account-details-open > #effect-container > .page-header, +#confirm-transaction.account-details-open > #effect-container > .payment-info-line, +#confirm-transaction.account-details-open > #effect-container > .page-body { + -webkit-filter: blur(20px); + -moz-filter: blur(20px); + -o-filter: blur(20px); + -ms-filter: blur(20px); + filter: blur(20px); + opacity: 0.5; +} + +#confirm-transaction.account-details-open > #effect-container > .page-footer { + -webkit-filter: blur(35px); + -moz-filter: blur(35px); + -o-filter: blur(35px); + -ms-filter: blur(35px); + filter: blur(35px); + opacity: 0.5; +} + +#confirm-transaction #account-details #close-details { + position: absolute; + right: 2rem; + top: 2rem; + border: 0; + padding: 0; + height: 3rem; + font-size: 3rem; + background-color: unset; +} + +#confirm-transaction #account-details #close-details .nq-icon { + opacity: .2; + transition: opacity .3s var(--nimiq-ease); +} + +#confirm-transaction #account-details #close-details:hover .nq-icon, +#confirm-transaction #account-details #close-details:active .nq-icon, +#confirm-transaction #account-details #close-details:focus .nq-icon { + opacity: .4; +} + +#confirm-transaction .accounts { + flex-direction: row; + align-items: flex-start; + padding-bottom: 2rem; + border-bottom: 1px solid rgba(31, 35, 72, 0.1); + flex-shrink: 0; +} + +#confirm-transaction .accounts .address-info { + width: calc(50% - 1.5rem); +} + +#confirm-transaction .arrow { + margin-top: 4rem; + height: 2.25rem; + width: 3rem; + color: var(--nimiq-light-blue); +} + +#confirm-transaction .total #value { + font-size: 5rem; +} + +#confirm-transaction .total .nim-symbol { + margin-left: 1rem; + font-weight: 700; +} + +#confirm-transaction .fee-section { + opacity: 0.5; + margin-bottom: 0.25rem; +} + +#confirm-transaction .data-section { + margin: 1rem 3rem; + text-align: center; + max-width: 100%; + overflow-wrap: break-word; +} diff --git a/src/request/sign-multisig-transaction/SignMultisigTransaction.js b/src/request/sign-multisig-transaction/SignMultisigTransaction.js new file mode 100644 index 00000000..2cd58f5d --- /dev/null +++ b/src/request/sign-multisig-transaction/SignMultisigTransaction.js @@ -0,0 +1,250 @@ +/* global Nimiq */ +/* global Key */ +/* global KeyStore */ +// /* global SignMultisigTransactionApi */ +/* global PasswordBox */ +/* global Errors */ +/* global Utf8Tools */ +/* global TopLevelApi */ +/* global AddressInfo */ +// /* global PaymentInfoLine */ +/* global Constants */ +/* global NumberFormatting */ +/* global I18n */ + +/** + * @callback SignMultisigTransaction.resolve + * @param {KeyguardRequest.SignMultisigTransactionResult} result + */ + +class SignMultisigTransaction { + /** + * @param {Parsed} request + * @param {SignMultisigTransaction.resolve} resolve + * @param {reject} reject + */ + constructor(request, resolve, reject) { + this._request = request; + /** @type {HTMLElement} */ + this.$el = (document.getElementById(SignMultisigTransaction.Pages.CONFIRM_TRANSACTION)); + this.$el.classList.add(request.layout); + + const transaction = request.transaction; + + /** @type {HTMLElement} */ + this.$accountDetails = (this.$el.querySelector('#account-details')); + + /** @type {HTMLLinkElement} */ + const $sender = (this.$el.querySelector('.accounts .sender')); + this._senderAddressInfo = new AddressInfo({ + userFriendlyAddress: transaction.sender.toUserFriendlyAddress(), + label: request.senderLabel || null, + imageUrl: null, + accountLabel: request.keyLabel || null, + }); + this._senderAddressInfo.renderTo($sender); + $sender.addEventListener('click', () => { + this._openDetails(this._senderAddressInfo); + }); + + /** @type {HTMLLinkElement} */ + const $recipient = (this.$el.querySelector('.accounts .recipient')); + const recipientAddress = transaction.recipient.toUserFriendlyAddress(); + /* eslint-disable no-nested-ternary */ + // eslint-disable-next-line operator-linebreak + const recipientLabel = /* 'shopOrigin' in request && !!request.shopOrigin + ? request.shopOrigin.split('://')[1] + : */ 'recipientLabel' in request && !!request.recipientLabel + ? request.recipientLabel + : null; + /** @type {URL | null} */ + /* eslint-enable no-nested-ternary */ + // eslint-disable-next-line operator-linebreak + const recipientImage = /* 'shopLogoUrl' in request && !!request.shopLogoUrl + ? request.shopLogoUrl + : */ null; + this._recipientAddressInfo = new AddressInfo({ + userFriendlyAddress: recipientAddress, + label: recipientLabel, + imageUrl: recipientImage, + accountLabel: null, + }/* , request.layout === SignMultisigTransactionApi.Layouts.CASHLINK */); + this._recipientAddressInfo.renderTo($recipient); + // if (request.layout !== SignMultisigTransactionApi.Layouts.CASHLINK) { + $recipient.addEventListener('click', () => { + this._openDetails(this._recipientAddressInfo); + }); + // } + + // /** @type {HTMLElement} */ + // const $paymentInfoLine = (this.$el.querySelector('.payment-info-line')); + // if (request.layout === SignMultisigTransactionApi.Layouts.CHECKOUT) { + // // eslint-disable-next-line no-new + // new PaymentInfoLine(Object.assign({}, request, { + // recipient: recipientAddress, + // label: recipientLabel || recipientAddress, + // imageUrl: request.shopLogoUrl, + // amount: request.transaction.value, + // currency: /** @type {'nim'} */ ('nim'), + // unitsToCoins: Nimiq.Policy.lunasToCoins, + // networkFee: request.transaction.fee, + // }), $paymentInfoLine); + // } else { + // $paymentInfoLine.remove(); + // } + + /** @type {HTMLButtonElement} */ + const $closeDetails = (this.$accountDetails.querySelector('#close-details')); + $closeDetails.addEventListener('click', this._closeDetails.bind(this)); + + /** @type {HTMLDivElement} */ + const $value = (this.$el.querySelector('#value')); + /** @type {HTMLDivElement} */ + const $fee = (this.$el.querySelector('#fee')); + /** @type {HTMLDivElement} */ + const $data = (this.$el.querySelector('#data')); + + // Set value and fee. + $value.textContent = NumberFormatting.formatNumber(Nimiq.Policy.lunasToCoins(transaction.value)); + if ($fee && transaction.fee > 0) { + $fee.textContent = NumberFormatting.formatNumber(Nimiq.Policy.lunasToCoins(transaction.fee)); + /** @type {HTMLDivElement} */ + const $feeSection = (this.$el.querySelector('.fee-section')); + $feeSection.classList.remove('display-none'); + } + + // if (request.layout === SignMultisigTransactionApi.Layouts.CASHLINK + // && Nimiq.BufferUtils.equals(transaction.data, Constants.CASHLINK_FUNDING_DATA)) { + // if (request.cashlinkMessage) { + // $data.textContent = request.cashlinkMessage; + // /** @type {HTMLDivElement} */ + // const $dataSection = (this.$el.querySelector('.data-section')); + // $dataSection.classList.remove('display-none'); + // } + /* } else */ if ($data && transaction.data.byteLength > 0) { + // Set transaction extra data. + $data.textContent = this._formatData(transaction); + /** @type {HTMLDivElement} */ + const $dataSection = (this.$el.querySelector('.data-section')); + $dataSection.classList.remove('display-none'); + } + + // Set up password box. + /** @type {HTMLFormElement} */ + const $passwordBox = (document.querySelector('#password-box')); + this._passwordBox = new PasswordBox($passwordBox, { + hideInput: !request.keyInfo.encrypted, + buttonI18nTag: /* request.layout === SignMultisigTransactionApi.Layouts.CASHLINK + ? 'passwordbox-create-cashlink' + : */ 'passwordbox-confirm-tx', + minLength: request.keyInfo.hasPin ? Key.PIN_LENGTH : undefined, + }); + + this._passwordBox.on( + PasswordBox.Events.SUBMIT, + /** @param {string} [password] */ password => { + this._onConfirm(request, resolve, reject, password); + }, + ); + + // if ('expires' in request && request.expires) { + // setTimeout(() => reject(new Errors.RequestExpired()), request.expires - Date.now()); + // } + } + + /** + * @param {AddressInfo} which + */ + _openDetails(which) { + which.renderTo( + /** @type {HTMLElement} */(this.$accountDetails.querySelector('#details')), + true, + ); + this.$el.classList.add('account-details-open'); + } + + _closeDetails() { + this.$el.classList.remove('account-details-open'); + } + + /** + * @param {Parsed} request + * @param {SignMultisigTransaction.resolve} resolve + * @param {reject} reject + * @param {string} [password] + * @returns {Promise} + * @private + */ + async _onConfirm(request, resolve, reject, password) { + TopLevelApi.setLoading(true); + const passwordBuf = password ? Utf8Tools.stringToUtf8ByteArray(password) : undefined; + /** @type {Key?} */ + let key = null; + try { + key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); + } catch (e) { + if (e.message === 'Invalid key') { + TopLevelApi.setLoading(false); + this._passwordBox.onPasswordIncorrect(); + return; + } + reject(new Errors.CoreError(e)); + return; + } + if (!key) { + reject(new Errors.KeyNotFoundError()); + return; + } + + const publicKey = key.derivePublicKey(request.keyPath); + + // Verify publicKey is part of the signing public keys + if (!request.multisig.signerPublicKeys.find(pubKey => pubKey.equals(publicKey))) { + reject(new Errors.InvalidRequestError('Selected key is not part of the multisig transaction signers')); + return; + } + + const signature = key.signPartially( + request.keyPath, + request.transaction.serializeContent(), + request.multisig.signerPublicKeys, + request.multisig.secret, + request.multisig.aggregatedCommitment, + ); + + /** @type {KeyguardRequest.SignMultisigTransactionResult} */ + const result = { + publicKey: publicKey.serialize(), + signature: signature.serialize(), + }; + resolve(result); + } + + run() { + // Go to start page + window.location.hash = SignMultisigTransaction.Pages.CONFIRM_TRANSACTION; + } + + /** + * @param {Nimiq.Transaction} transaction + * @returns {string} + */ + _formatData(transaction) { + if (Nimiq.BufferUtils.equals(transaction.data, Constants.CASHLINK_FUNDING_DATA)) { + return I18n.translatePhrase('funding-cashlink'); + } + + if (transaction.hasFlag(Nimiq.Transaction.Flag.CONTRACT_CREATION)) { + // TODO: Decode contract creation transactions + // return ... + } + + return Utf8Tools.isValidUtf8(transaction.data) + ? Utf8Tools.utf8ByteArrayToString(transaction.data) + : Nimiq.BufferUtils.toHex(transaction.data); + } +} + +SignMultisigTransaction.Pages = { + CONFIRM_TRANSACTION: 'confirm-transaction', +}; diff --git a/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js b/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js new file mode 100644 index 00000000..78979b05 --- /dev/null +++ b/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js @@ -0,0 +1,211 @@ +/* global Nimiq */ +/* global TopLevelApi */ +/* global SignMultisigTransaction */ +/* global MultisigUtils */ +/* global Errors */ + +/** @extends {TopLevelApi} */ +class SignMultisigTransactionApi extends TopLevelApi { + /** + * @param {KeyguardRequest.SignMultisigTransactionRequest} request + * @returns {Promise>} + */ + async parseRequest(request) { + if (!request) { + throw new Errors.InvalidRequestError('request is required'); + } + + /** @type {Parsed} */ + const parsedRequest = {}; + parsedRequest.appName = this.parseAppName(request.appName); + parsedRequest.keyInfo = await this.parseKeyId(request.keyId); + parsedRequest.keyLabel = this.parseLabel(request.keyLabel); + parsedRequest.keyPath = this.parsePath(request.keyPath, 'keyPath'); + parsedRequest.senderLabel = this.parseLabel(request.senderLabel); + parsedRequest.transaction = this.parseTransaction(request); + parsedRequest.multisig = this.parseMultisigConfig(request); + parsedRequest.layout = this.parseLayout(request.layout); + + this.verifyMultisigAddress(parsedRequest.transaction, parsedRequest.multisig); + + if ((!request.layout || request.layout === SignMultisigTransactionApi.Layouts.STANDARD) + && parsedRequest.layout === SignMultisigTransactionApi.Layouts.STANDARD) { + parsedRequest.recipientLabel = this.parseLabel(request.recipientLabel); + } // else if (request.layout === SignMultisigTransactionApi.Layouts.CHECKOUT + // && parsedRequest.layout === SignMultisigTransactionApi.Layouts.CHECKOUT) { + // parsedRequest.shopOrigin = this.parseShopOrigin(request.shopOrigin); + // parsedRequest.shopLogoUrl = this.parseShopLogoUrl(request.shopLogoUrl); + // if (parsedRequest.shopLogoUrl && parsedRequest.shopLogoUrl.origin !== parsedRequest.shopOrigin) { + // throw new Errors.InvalidRequestError('origin of shopLogoUrl must be same as shopOrigin'); + // } + + // parsedRequest.fiatAmount = this.parseNonNegativeFiniteNumber(request.fiatAmount); + // parsedRequest.fiatCurrency = this.parseFiatCurrency(request.fiatCurrency); + // if ((parsedRequest.fiatAmount === undefined) !== (parsedRequest.fiatCurrency === undefined)) { + // throw new Errors.InvalidRequestError( + // 'fiatAmount and fiatCurrency must be both defined or undefined.', + // ); + // } + + // parsedRequest.vendorMarkup = this.parseVendorMarkup(request.vendorMarkup); + + // parsedRequest.time = this.parseNonNegativeFiniteNumber(request.time); + // parsedRequest.expires = this.parseNonNegativeFiniteNumber(request.expires); + // if (parsedRequest.expires !== undefined) { + // if (parsedRequest.time === undefined) { + // throw new Errors.InvalidRequestError('If `expires` is given, `time` must be given too.'); + // } else if (parsedRequest.time >= parsedRequest.expires) { + // throw new Errors.InvalidRequestError('`expires` must be greater than `time`'); + // } + // } + // } else if (request.layout === SignMultisigTransactionApi.Layouts.CASHLINK + // && parsedRequest.layout === SignMultisigTransactionApi.Layouts.CASHLINK + // && request.cashlinkMessage) { + // parsedRequest.cashlinkMessage = /** @type {string} */(this.parseMessage(request.cashlinkMessage)); + // } + + return parsedRequest; + } + + /** + * @param {any} object + * @returns {MultisigConfig} + */ + parseMultisigConfig(object) { + if (!object || typeof object !== 'object' || object === null) { + throw new Errors.InvalidRequestError('Request must be an object'); + } + + /** @type {Nimiq.PublicKey[]} */ + const publicKeys = []; + try { + if (!('publicKeys' in object)) throw new Error('missing'); + if (!Array.isArray(object.publicKeys)) throw new Error('not an array'); + for (const key of object.publicKeys) { + if (!(key instanceof Uint8Array)) throw new Error('not an Uint8Array'); + publicKeys.push(new Nimiq.PublicKey(key)); + } + } catch (error) { + throw new Errors.InvalidRequestError(`Invalid public keys: ${(error).message}`); + } + + const numberOfSigners = this.parsePositiveInteger(object.numberOfSigners, false, 'numberOfSigners'); + if (numberOfSigners > publicKeys.length) { + throw new Errors.InvalidRequestError('Number of signers must be smaller or equal to number of public keys'); + } + + /** @type {Nimiq.PublicKey[]} */ + const signerPublicKeys = []; + try { + if (!('signerPublicKeys' in object)) throw new Error('missing'); + if (!Array.isArray(object.signerPublicKeys)) throw new Error('not an array'); + if (object.signerPublicKeys.length < numberOfSigners) throw new Error('missing keys'); + if (object.signerPublicKeys.length > numberOfSigners) throw new Error('too many keys'); + for (const key of object.signerPublicKeys) { + if (!(key instanceof Uint8Array)) throw new Error('not an Uint8Array'); + const signerPublicKey = new Nimiq.PublicKey(key); + // Verify key is included in publicKeys as well + if (!publicKeys.find(publicKey => publicKey.equals(signerPublicKey))) { + throw new Errors.InvalidRequestError('not in public keys'); + } + signerPublicKeys.push(signerPublicKey); + } + } catch (error) { + throw new Errors.InvalidRequestError(`Invalid signer public keys: ${error.message}`); + } + + /** @type {Nimiq.RandomSecret} */ + let secret; + try { + secret = new Nimiq.RandomSecret(object.secret); + } catch (error) { + throw new Errors.InvalidRequestError(`Invalid secret: ${error.message}`); + } + + /** @type {Nimiq.RandomSecret} */ + let aggregatedCommitment; + try { + aggregatedCommitment = new Nimiq.Commitment(object.aggregatedCommitment); + } catch (error) { + throw new Errors.InvalidRequestError( + `Invalid aggregated commitment: ${error.message}`, + ); + } + + return { + publicKeys, + numberOfSigners, + signerPublicKeys, + secret, + aggregatedCommitment, + }; + } + + /** + * @param {Nimiq.Transaction} transaction + * @param {MultisigConfig} multisig + */ + verifyMultisigAddress(transaction, multisig) { + const multisigAddress = MultisigUtils.calculateAddress( + multisig.publicKeys, + multisig.numberOfSigners, + ); + + if (transaction.senderType === Nimiq.Account.Type.BASIC) { + if (!transaction.sender.equals(multisigAddress)) { + throw new Errors.InvalidRequestError( + 'Transaction sender does not match calculated multisig address', + ); + } + } else if (transaction.recipientType === Nimiq.Account.Type.BASIC) { + if (!transaction.recipient.equals(multisigAddress)) { + throw new Errors.InvalidRequestError( + 'Transaction recipient does not match calculated multisig address', + ); + } + } else { + throw new Errors.InvalidRequestError( + 'The multisig account must either be the sender or the recipient of the transaction', + ); + } + } + + /** + * Checks that the given layout is valid + * @param {unknown} layout + * @returns {KeyguardRequest.SignMultisigTransactionRequestLayout} + */ + parseLayout(layout) { + if (!layout) { + return SignMultisigTransactionApi.Layouts.STANDARD; + } + // @ts-ignore (Property 'values' does not exist on type 'ObjectConstructor'.) + if (Object.values(SignMultisigTransactionApi.Layouts).indexOf(layout) === -1) { + throw new Errors.InvalidRequestError('Invalid selected layout'); + } + return /** @type KeyguardRequest.SignMultisigTransactionRequestLayout */ (layout); + } + + get Handler() { + return SignMultisigTransaction; + } + + // /** + // * @param {Parsed} parsedRequest + // */ + // async onBeforeRun(parsedRequest) { + // if (parsedRequest.layout === SignMultisigTransactionApi.Layouts.CHECKOUT) { + // this.enableGlobalCloseButton(I18n.translatePhrase('sign-tx-cancel-payment')); + // } + // } +} + +/** + * @enum {KeyguardRequest.SignMultisigTransactionRequestLayout} + * @readonly + */ +SignMultisigTransactionApi.Layouts = { + STANDARD: /** @type {'standard'} */ ('standard'), + // CHECKOUT: /** @type {'checkout'} */ ('checkout'), + // CASHLINK: /** @type {'cashlink'} */ ('cashlink'), +}; diff --git a/src/request/sign-multisig-transaction/index.html b/src/request/sign-multisig-transaction/index.html new file mode 100644 index 00000000..a162e13b --- /dev/null +++ b/src/request/sign-multisig-transaction/index.html @@ -0,0 +1,143 @@ + + + + + + + + + Nimiq Keyguard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+
+ +
+
+
+ + + +
+
+
+ +
+
+ +
+
+ +
+ + + + +
+
+ + +
+
+ + + + +
+ + + + + +
+
+ + diff --git a/src/request/sign-multisig-transaction/index.js b/src/request/sign-multisig-transaction/index.js new file mode 100644 index 00000000..37880b61 --- /dev/null +++ b/src/request/sign-multisig-transaction/index.js @@ -0,0 +1,4 @@ +/* global SignMultisigTransactionApi */ +/* global runKeyguard */ + +runKeyguard(SignMultisigTransactionApi); diff --git a/types/Keyguard.d.ts b/types/Keyguard.d.ts index 4b9cedab..1345dc58 100644 --- a/types/Keyguard.d.ts +++ b/types/Keyguard.d.ts @@ -40,6 +40,14 @@ type KeyRecord = { defaultAddress: Uint8Array } +type MultisigConfig = { + publicKeys: Nimiq.PublicKey[] + numberOfSigners: number + signerPublicKeys: Nimiq.PublicKey[] + secret: Nimiq.RandomSecret + aggregatedCommitment: Nimiq.Commitment +} + type ParsedBitcoinTransactionInput = { hash: string, index: number, @@ -206,6 +214,12 @@ type ConstructTransaction = Transform 'value' | 'fee' | 'validityStartHeight' | 'flags', { transaction: Nimiq.Transaction }> +type ConstructMultisigTransaction + = ConstructTransaction + >; + type ConstructSwap = Transform = > : T extends Is ? ConstructTransaction> : + T extends Is ? + ConstructMultisigTransaction> + & { layout: KeyguardRequest.SignMultisigTransactionRequestLayout } : T extends Is ? Transform & { plain: Nimiq.PlainTransaction[], From b80eabd9dffce7a6cd2a01fe3a3197173d86c6b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Tue, 27 Sep 2022 11:43:45 +0200 Subject: [PATCH 05/31] Add demo for multisig signing --- demos/SignMultisigTransaction.html | 241 ++++++++++++++++++++++++++++ demos/index.html | 1 + src/lib/multisig/MerkleTreePatch.js | 7 + 3 files changed, 249 insertions(+) create mode 100644 demos/SignMultisigTransaction.html diff --git a/demos/SignMultisigTransaction.html b/demos/SignMultisigTransaction.html new file mode 100644 index 00000000..ae8af113 --- /dev/null +++ b/demos/SignMultisigTransaction.html @@ -0,0 +1,241 @@ + + + + + SignTransaction | Keyguard Demo + + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + +
+ + + +
+
+ +

+
+ + + + + diff --git a/demos/index.html b/demos/index.html index 3e83c425..92670454 100644 --- a/demos/index.html +++ b/demos/index.html @@ -24,6 +24,7 @@ + diff --git a/src/lib/multisig/MerkleTreePatch.js b/src/lib/multisig/MerkleTreePatch.js index 0aed3feb..9326b8de 100644 --- a/src/lib/multisig/MerkleTreePatch.js +++ b/src/lib/multisig/MerkleTreePatch.js @@ -7,6 +7,11 @@ class MerkleTreePatch { // eslint-disable-line no-unused-vars static apply() { if (typeof Nimiq.MerkleTree !== 'undefined') { + // @ts-ignore + if (Nimiq.MerkleTree.__isMonkeyPatch) { + // Patch already applied + return; + } throw new Error('MerkleTree monkey patch not required anymore. Please remove it.'); } else { class MerkleTree { @@ -66,6 +71,8 @@ class MerkleTreePatch { // eslint-disable-line no-unused-vars throw new Error('MerkleTree objects must be Uint8Array or have a .hash()/.serialize() method'); } } + // @ts-ignore + MerkleTree.__isMonkeyPatch = true; Nimiq.Class.register(MerkleTree); } } From 7402611f95f9586dbd8fb0663211b5b3b1b16e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Tue, 27 Sep 2022 12:25:34 +0200 Subject: [PATCH 06/31] Add multisig config badge to AddressInfo component --- src/components/AddressInfo.css | 19 ++++++++++++++++++ src/components/AddressInfo.js | 20 ++++++++++++++++++- .../SignMultisigTransaction.js | 4 ++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/components/AddressInfo.css b/src/components/AddressInfo.css index f3800629..1d81ad7e 100644 --- a/src/components/AddressInfo.css +++ b/src/components/AddressInfo.css @@ -18,6 +18,7 @@ border-radius: 1rem; transition: transform 0.45s var(--nimiq-ease); flex-shrink: 0; + position: relative; } .address-info.cashlink .identicon { @@ -37,6 +38,24 @@ transform: scale(1.1); } +.address-info .identicon .multisig-badge { + position: absolute; + right: -0.875rem; + bottom: -0.25rem; + border-radius: 5rem; + font-weight: bold; + font-size: 1.75rem; + padding: 0.375rem 0.75rem; + line-height: 1; + border: 0.5rem solid white; + letter-spacing: 0.07em; // 1px / 14px +} + +.address-info.detailed-view .identicon .multisig-badge { + transform: scale(1.5); + transform-origin: 100% 100%; +} + .address-info:not(.detailed-view) .label { font-size: 2rem; margin: 1.25rem 0; diff --git a/src/components/AddressInfo.js b/src/components/AddressInfo.js index ac776d78..9ad798ef 100644 --- a/src/components/AddressInfo.js +++ b/src/components/AddressInfo.js @@ -3,8 +3,18 @@ /* global Identicon */ class AddressInfo { // eslint-disable-line no-unused-vars + // eslint-disable-next-line valid-jsdoc /** - * @param {{ userFriendlyAddress: string, label: string?, imageUrl: URL?, accountLabel: string?}} addressInfo + * @param {{ + * userFriendlyAddress: string, + * label: string?, + * imageUrl: URL?, + * accountLabel: string?, + * multisig?: { + * signers: number, + * participants: number, + * }, + * }} addressInfo * @param {boolean} [displayAsCashlink = false] */ constructor(addressInfo, displayAsCashlink = false) { @@ -58,6 +68,14 @@ class AddressInfo { // eslint-disable-line no-unused-vars // eslint-disable-next-line no-new new Identicon(this._addressInfo.userFriendlyAddress, $identicon); } + + if (this._addressInfo.multisig) { + const $badge = document.createElement('div'); + $badge.classList.add('multisig-badge', 'nq-blue-bg'); + $badge.textContent = `${this._addressInfo.multisig.signers}/${this._addressInfo.multisig.participants}`; + $identicon.appendChild($badge); + } + $el.appendChild($identicon); // label diff --git a/src/request/sign-multisig-transaction/SignMultisigTransaction.js b/src/request/sign-multisig-transaction/SignMultisigTransaction.js index 2cd58f5d..9b31a0b2 100644 --- a/src/request/sign-multisig-transaction/SignMultisigTransaction.js +++ b/src/request/sign-multisig-transaction/SignMultisigTransaction.js @@ -41,6 +41,10 @@ class SignMultisigTransaction { label: request.senderLabel || null, imageUrl: null, accountLabel: request.keyLabel || null, + multisig: { + signers: request.multisig.numberOfSigners, + participants: request.multisig.publicKeys.length, + }, }); this._senderAddressInfo.renderTo($sender); $sender.addEventListener('click', () => { From 07da9981e48576ece8525edcaa4524bb0859eb85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Thu, 29 Sep 2022 15:24:09 +0200 Subject: [PATCH 07/31] Add user and account name section --- client/src/PublicRequest.ts | 5 +- demos/SignMultisigTransaction.html | 8 +++ src/assets/login-file-icon.svg | 10 ++++ .../SignMultisigTransaction.css | 60 ++++++++++++++++++- .../SignMultisigTransaction.js | 26 ++++++++ .../SignMultisigTransactionApi.js | 5 +- .../sign-multisig-transaction/index.html | 10 ++++ src/translations/en.json | 4 ++ types/Keyguard.d.ts | 1 + 9 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 src/assets/login-file-icon.svg diff --git a/client/src/PublicRequest.ts b/client/src/PublicRequest.ts index 36d366d9..eadc39f2 100644 --- a/client/src/PublicRequest.ts +++ b/client/src/PublicRequest.ts @@ -196,9 +196,12 @@ export type MultisigInfo = { signerPublicKeys: Uint8Array[], secret: Uint8Array, aggregatedCommitment: Uint8Array, + userName?: string, }; -export type SignMultisigTransactionRequestCommon = SignTransactionRequestCommon & MultisigInfo; +export type SignMultisigTransactionRequestCommon = Transform & MultisigInfo; export type SignMultisigTransactionRequestStandard = SignMultisigTransactionRequestCommon & { layout?: 'standard', diff --git a/demos/SignMultisigTransaction.html b/demos/SignMultisigTransaction.html index ae8af113..7f24bb3e 100644 --- a/demos/SignMultisigTransaction.html +++ b/demos/SignMultisigTransaction.html @@ -66,6 +66,11 @@ +
+ + +
+
diff --git a/src/translations/de.json b/src/translations/de.json index 119bb3db..dc61d490 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -69,6 +69,11 @@ "sign-tx-fee": "Gebühr", "sign-tx-cancel-payment": "Zahlung abbrechen", + "sign-multisig-tx-heading-tx": "Approve Multisig Transaction", + "sign-multisig-tx-approving-as": "Approving as", + "sign-multisig-tx-with": "with", + "sign-multisig-tx-approving-with": "Approving with", + "sign-staking-heading-stake": "Stake NIM", "sign-staking-heading-unstake": "Unstake NIM", "sign-staking-heading-change": "Change Validator", diff --git a/src/translations/en.json b/src/translations/en.json index 166cb8cd..002288ed 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -69,6 +69,7 @@ "sign-tx-fee": "fee", "sign-tx-cancel-payment": "Cancel payment", + "sign-multisig-tx-heading-tx": "Approve Multisig Transaction", "sign-multisig-tx-approving-as": "Approving as", "sign-multisig-tx-with": "with", "sign-multisig-tx-approving-with": "Approving with", diff --git a/src/translations/es.json b/src/translations/es.json index b31fee59..c693b24c 100644 --- a/src/translations/es.json +++ b/src/translations/es.json @@ -69,6 +69,11 @@ "sign-tx-fee": "cuota", "sign-tx-cancel-payment": "Cancelar pago", + "sign-multisig-tx-heading-tx": "Approve Multisig Transaction", + "sign-multisig-tx-approving-as": "Approving as", + "sign-multisig-tx-with": "with", + "sign-multisig-tx-approving-with": "Approving with", + "sign-staking-heading-stake": "Stake NIM", "sign-staking-heading-unstake": "Unstake NIM", "sign-staking-heading-change": "Change Validator", diff --git a/src/translations/fr.json b/src/translations/fr.json index bf753c8b..ac44cc40 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -69,6 +69,11 @@ "sign-tx-fee": "frais", "sign-tx-cancel-payment": "Annuler le paiement", + "sign-multisig-tx-heading-tx": "Approve Multisig Transaction", + "sign-multisig-tx-approving-as": "Approving as", + "sign-multisig-tx-with": "with", + "sign-multisig-tx-approving-with": "Approving with", + "sign-staking-heading-stake": "Staker du NIM", "sign-staking-heading-unstake": "Dé-staker du NIM", "sign-staking-heading-change": "Changer de Validateur", diff --git a/src/translations/nl.json b/src/translations/nl.json index 6a97d543..eb608365 100644 --- a/src/translations/nl.json +++ b/src/translations/nl.json @@ -69,6 +69,11 @@ "sign-tx-fee": "kosten", "sign-tx-cancel-payment": "Annuleer betaling", + "sign-multisig-tx-heading-tx": "Approve Multisig Transaction", + "sign-multisig-tx-approving-as": "Approving as", + "sign-multisig-tx-with": "with", + "sign-multisig-tx-approving-with": "Approving with", + "sign-staking-heading-stake": "Stake NIM", "sign-staking-heading-unstake": "Unstake NIM", "sign-staking-heading-change": "Change Validator", diff --git a/src/translations/pt.json b/src/translations/pt.json index 99561704..b49126ae 100644 --- a/src/translations/pt.json +++ b/src/translations/pt.json @@ -69,6 +69,11 @@ "sign-tx-fee": "taxa", "sign-tx-cancel-payment": "Cancelar pagamento", + "sign-multisig-tx-heading-tx": "Approve Multisig Transaction", + "sign-multisig-tx-approving-as": "Approving as", + "sign-multisig-tx-with": "with", + "sign-multisig-tx-approving-with": "Approving with", + "sign-staking-heading-stake": "Stake NIM", "sign-staking-heading-unstake": "Unstake NIM", "sign-staking-heading-change": "Change Validator", diff --git a/src/translations/ru.json b/src/translations/ru.json index 11af42be..b42cd2a0 100644 --- a/src/translations/ru.json +++ b/src/translations/ru.json @@ -69,6 +69,11 @@ "sign-tx-fee": "комиссия", "sign-tx-cancel-payment": "Отменить платёж", + "sign-multisig-tx-heading-tx": "Approve Multisig Transaction", + "sign-multisig-tx-approving-as": "Approving as", + "sign-multisig-tx-with": "with", + "sign-multisig-tx-approving-with": "Approving with", + "sign-staking-heading-stake": "Stake NIM", "sign-staking-heading-unstake": "Unstake NIM", "sign-staking-heading-change": "Change Validator", diff --git a/src/translations/uk.json b/src/translations/uk.json index 4d6e7822..e16a85f6 100644 --- a/src/translations/uk.json +++ b/src/translations/uk.json @@ -69,6 +69,11 @@ "sign-tx-fee": "комісія", "sign-tx-cancel-payment": "Скасувати платіж", + "sign-multisig-tx-heading-tx": "Approve Multisig Transaction", + "sign-multisig-tx-approving-as": "Approving as", + "sign-multisig-tx-with": "with", + "sign-multisig-tx-approving-with": "Approving with", + "sign-staking-heading-stake": "Stake NIM", "sign-staking-heading-unstake": "Unstake NIM", "sign-staking-heading-change": "Change Validator", diff --git a/src/translations/zh.json b/src/translations/zh.json index a093c8c5..3e10dcdc 100644 --- a/src/translations/zh.json +++ b/src/translations/zh.json @@ -69,6 +69,11 @@ "sign-tx-fee": "手续费", "sign-tx-cancel-payment": "取消付款", + "sign-multisig-tx-heading-tx": "Approve Multisig Transaction", + "sign-multisig-tx-approving-as": "Approving as", + "sign-multisig-tx-with": "with", + "sign-multisig-tx-approving-with": "Approving with", + "sign-staking-heading-stake": "Stake NIM", "sign-staking-heading-unstake": "Unstake NIM", "sign-staking-heading-change": "Change Validator", From f60550449c992c2b96756fab5605d2c5fe8790e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Fri, 7 Oct 2022 14:14:03 +0200 Subject: [PATCH 09/31] Demo of generating random RSA keys in a sandboxed iframe --- demos/RSAKeys.html | 145 +++++++++++++++++++++++++++++++++++++++ demos/RSAKeysIframe.html | 57 +++++++++++++++ demos/index.html | 1 + 3 files changed, 203 insertions(+) create mode 100644 demos/RSAKeys.html create mode 100644 demos/RSAKeysIframe.html diff --git a/demos/RSAKeys.html b/demos/RSAKeys.html new file mode 100644 index 00000000..7e4ceef4 --- /dev/null +++ b/demos/RSAKeys.html @@ -0,0 +1,145 @@ + + + + + RSA Keys | Keyguard Demo + + + + + + + + + + + +

RSA Keys

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ +

+ +

+ + +

+ + + + diff --git a/demos/RSAKeysIframe.html b/demos/RSAKeysIframe.html new file mode 100644 index 00000000..6175ee3d --- /dev/null +++ b/demos/RSAKeysIframe.html @@ -0,0 +1,57 @@ + + + + + RSA Keys Iframe | Keyguard Demo + + + + + + + + + RSA Keys Iframe + + + + diff --git a/demos/index.html b/demos/index.html index 92670454..0e1c790a 100644 --- a/demos/index.html +++ b/demos/index.html @@ -25,6 +25,7 @@
+ From b10662fd1e4e9fa438fad9353f65c4541cabc568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Fri, 7 Oct 2022 22:30:49 +0200 Subject: [PATCH 10/31] Use node-forge in sandboxed iframe to generate deterministic RSA key --- demos/RSAKeys.html | 52 +++++++++++--- demos/RSAKeysIframe.html | 57 ---------------- demos/rsa-iframe/RSAKeysIframe.html | 72 ++++++++++++++++++++ demos/rsa-iframe/node-forge/forge.min.js | 2 + demos/rsa-iframe/node-forge/forge.min.js.map | 1 + 5 files changed, 116 insertions(+), 68 deletions(-) delete mode 100644 demos/RSAKeysIframe.html create mode 100644 demos/rsa-iframe/RSAKeysIframe.html create mode 100644 demos/rsa-iframe/node-forge/forge.min.js create mode 100644 demos/rsa-iframe/node-forge/forge.min.js.map diff --git a/demos/RSAKeys.html b/demos/RSAKeys.html index 7e4ceef4..25ff2342 100644 --- a/demos/RSAKeys.html +++ b/demos/RSAKeys.html @@ -31,6 +31,10 @@ height: 400px; } + textarea.key.same { + background: lightgreen; + } + iframe { height: 40px; } @@ -53,14 +57,14 @@

RSA Keys

- +

@@ -69,7 +73,7 @@

RSA Keys

- +

diff --git a/demos/RSAKeysIframe.html b/demos/RSAKeysIframe.html deleted file mode 100644 index 6175ee3d..00000000 --- a/demos/RSAKeysIframe.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - RSA Keys Iframe | Keyguard Demo - - - - - - - - - RSA Keys Iframe - - - - diff --git a/demos/rsa-iframe/RSAKeysIframe.html b/demos/rsa-iframe/RSAKeysIframe.html new file mode 100644 index 00000000..11bc59b9 --- /dev/null +++ b/demos/rsa-iframe/RSAKeysIframe.html @@ -0,0 +1,72 @@ + + + + + RSA Keys Iframe | Keyguard Demo + + + + + + + + + + RSA Keys Iframe + + + + diff --git a/demos/rsa-iframe/node-forge/forge.min.js b/demos/rsa-iframe/node-forge/forge.min.js new file mode 100644 index 00000000..17773f62 --- /dev/null +++ b/demos/rsa-iframe/node-forge/forge.min.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.forge=t():e.forge=t()}(window,(function(){return function(e){var t={};function r(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=e,r.c=t,r.d=function(e,t,a){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(a,n,function(t){return e[t]}.bind(null,n));return a},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=32)}([function(e,t){e.exports={options:{usePureJavaScript:!1}}},function(e,t,r){(function(t){var a=r(0),n=r(35),i=e.exports=a.util=a.util||{};function s(e){if(8!==e&&16!==e&&24!==e&&32!==e)throw new Error("Only 8, 16, 24, or 32 bits supported: "+e)}function o(e){if(this.data="",this.read=0,"string"==typeof e)this.data=e;else if(i.isArrayBuffer(e)||i.isArrayBufferView(e))if("undefined"!=typeof Buffer&&e instanceof Buffer)this.data=e.toString("binary");else{var t=new Uint8Array(e);try{this.data=String.fromCharCode.apply(null,t)}catch(e){for(var r=0;r15?(r=Date.now(),s(e)):(t.push(e),1===t.length&&n.setAttribute("a",a=!a))}}i.nextTick=i.setImmediate}(),i.isNodejs="undefined"!=typeof process&&process.versions&&process.versions.node,i.globalScope=i.isNodejs?t:"undefined"==typeof self?window:self,i.isArray=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i.isArrayBuffer=function(e){return"undefined"!=typeof ArrayBuffer&&e instanceof ArrayBuffer},i.isArrayBufferView=function(e){return e&&i.isArrayBuffer(e.buffer)&&void 0!==e.byteLength},i.ByteBuffer=o,i.ByteStringBuffer=o;i.ByteStringBuffer.prototype._optimizeConstructedString=function(e){this._constructedStringLength+=e,this._constructedStringLength>4096&&(this.data.substr(0,1),this._constructedStringLength=0)},i.ByteStringBuffer.prototype.length=function(){return this.data.length-this.read},i.ByteStringBuffer.prototype.isEmpty=function(){return this.length()<=0},i.ByteStringBuffer.prototype.putByte=function(e){return this.putBytes(String.fromCharCode(e))},i.ByteStringBuffer.prototype.fillWithByte=function(e,t){e=String.fromCharCode(e);for(var r=this.data;t>0;)1&t&&(r+=e),(t>>>=1)>0&&(e+=e);return this.data=r,this._optimizeConstructedString(t),this},i.ByteStringBuffer.prototype.putBytes=function(e){return this.data+=e,this._optimizeConstructedString(e.length),this},i.ByteStringBuffer.prototype.putString=function(e){return this.putBytes(i.encodeUtf8(e))},i.ByteStringBuffer.prototype.putInt16=function(e){return this.putBytes(String.fromCharCode(e>>8&255)+String.fromCharCode(255&e))},i.ByteStringBuffer.prototype.putInt24=function(e){return this.putBytes(String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e))},i.ByteStringBuffer.prototype.putInt32=function(e){return this.putBytes(String.fromCharCode(e>>24&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e))},i.ByteStringBuffer.prototype.putInt16Le=function(e){return this.putBytes(String.fromCharCode(255&e)+String.fromCharCode(e>>8&255))},i.ByteStringBuffer.prototype.putInt24Le=function(e){return this.putBytes(String.fromCharCode(255&e)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255))},i.ByteStringBuffer.prototype.putInt32Le=function(e){return this.putBytes(String.fromCharCode(255&e)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>24&255))},i.ByteStringBuffer.prototype.putInt=function(e,t){s(t);var r="";do{t-=8,r+=String.fromCharCode(e>>t&255)}while(t>0);return this.putBytes(r)},i.ByteStringBuffer.prototype.putSignedInt=function(e,t){return e<0&&(e+=2<0);return t},i.ByteStringBuffer.prototype.getSignedInt=function(e){var t=this.getInt(e),r=2<=r&&(t-=r<<1),t},i.ByteStringBuffer.prototype.getBytes=function(e){var t;return e?(e=Math.min(this.length(),e),t=this.data.slice(this.read,this.read+e),this.read+=e):0===e?t="":(t=0===this.read?this.data:this.data.slice(this.read),this.clear()),t},i.ByteStringBuffer.prototype.bytes=function(e){return void 0===e?this.data.slice(this.read):this.data.slice(this.read,this.read+e)},i.ByteStringBuffer.prototype.at=function(e){return this.data.charCodeAt(this.read+e)},i.ByteStringBuffer.prototype.setAt=function(e,t){return this.data=this.data.substr(0,this.read+e)+String.fromCharCode(t)+this.data.substr(this.read+e+1),this},i.ByteStringBuffer.prototype.last=function(){return this.data.charCodeAt(this.data.length-1)},i.ByteStringBuffer.prototype.copy=function(){var e=i.createBuffer(this.data);return e.read=this.read,e},i.ByteStringBuffer.prototype.compact=function(){return this.read>0&&(this.data=this.data.slice(this.read),this.read=0),this},i.ByteStringBuffer.prototype.clear=function(){return this.data="",this.read=0,this},i.ByteStringBuffer.prototype.truncate=function(e){var t=Math.max(0,this.length()-e);return this.data=this.data.substr(this.read,t),this.read=0,this},i.ByteStringBuffer.prototype.toHex=function(){for(var e="",t=this.read;t=e)return this;t=Math.max(t||this.growSize,e);var r=new Uint8Array(this.data.buffer,this.data.byteOffset,this.data.byteLength),a=new Uint8Array(this.length()+t);return a.set(r),this.data=new DataView(a.buffer),this},i.DataBuffer.prototype.putByte=function(e){return this.accommodate(1),this.data.setUint8(this.write++,e),this},i.DataBuffer.prototype.fillWithByte=function(e,t){this.accommodate(t);for(var r=0;r>8&65535),this.data.setInt8(this.write,e>>16&255),this.write+=3,this},i.DataBuffer.prototype.putInt32=function(e){return this.accommodate(4),this.data.setInt32(this.write,e),this.write+=4,this},i.DataBuffer.prototype.putInt16Le=function(e){return this.accommodate(2),this.data.setInt16(this.write,e,!0),this.write+=2,this},i.DataBuffer.prototype.putInt24Le=function(e){return this.accommodate(3),this.data.setInt8(this.write,e>>16&255),this.data.setInt16(this.write,e>>8&65535,!0),this.write+=3,this},i.DataBuffer.prototype.putInt32Le=function(e){return this.accommodate(4),this.data.setInt32(this.write,e,!0),this.write+=4,this},i.DataBuffer.prototype.putInt=function(e,t){s(t),this.accommodate(t/8);do{t-=8,this.data.setInt8(this.write++,e>>t&255)}while(t>0);return this},i.DataBuffer.prototype.putSignedInt=function(e,t){return s(t),this.accommodate(t/8),e<0&&(e+=2<0);return t},i.DataBuffer.prototype.getSignedInt=function(e){var t=this.getInt(e),r=2<=r&&(t-=r<<1),t},i.DataBuffer.prototype.getBytes=function(e){var t;return e?(e=Math.min(this.length(),e),t=this.data.slice(this.read,this.read+e),this.read+=e):0===e?t="":(t=0===this.read?this.data:this.data.slice(this.read),this.clear()),t},i.DataBuffer.prototype.bytes=function(e){return void 0===e?this.data.slice(this.read):this.data.slice(this.read,this.read+e)},i.DataBuffer.prototype.at=function(e){return this.data.getUint8(this.read+e)},i.DataBuffer.prototype.setAt=function(e,t){return this.data.setUint8(e,t),this},i.DataBuffer.prototype.last=function(){return this.data.getUint8(this.write-1)},i.DataBuffer.prototype.copy=function(){return new i.DataBuffer(this)},i.DataBuffer.prototype.compact=function(){if(this.read>0){var e=new Uint8Array(this.data.buffer,this.read),t=new Uint8Array(e.byteLength);t.set(e),this.data=new DataView(t),this.write-=this.read,this.read=0}return this},i.DataBuffer.prototype.clear=function(){return this.data=new DataView(new ArrayBuffer(0)),this.read=this.write=0,this},i.DataBuffer.prototype.truncate=function(e){return this.write=Math.max(0,this.length()-e),this.read=Math.min(this.read,this.write),this},i.DataBuffer.prototype.toHex=function(){for(var e="",t=this.read;t0;)1&t&&(r+=e),(t>>>=1)>0&&(e+=e);return r},i.xorBytes=function(e,t,r){for(var a="",n="",i="",s=0,o=0;r>0;--r,++s)n=e.charCodeAt(s)^t.charCodeAt(s),o>=10&&(a+=i,i="",o=0),i+=String.fromCharCode(n),++o;return a+=i},i.hexToBytes=function(e){var t="",r=0;for(!0&e.length&&(r=1,t+=String.fromCharCode(parseInt(e[0],16)));r>24&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e)};var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",u=[62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],l="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";i.encode64=function(e,t){for(var r,a,n,i="",s="",o=0;o>2),i+=c.charAt((3&r)<<4|a>>4),isNaN(a)?i+="==":(i+=c.charAt((15&a)<<2|n>>6),i+=isNaN(n)?"=":c.charAt(63&n)),t&&i.length>t&&(s+=i.substr(0,t)+"\r\n",i=i.substr(t));return s+=i},i.decode64=function(e){e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var t,r,a,n,i="",s=0;s>4),64!==a&&(i+=String.fromCharCode((15&r)<<4|a>>2),64!==n&&(i+=String.fromCharCode((3&a)<<6|n)));return i},i.encodeUtf8=function(e){return unescape(encodeURIComponent(e))},i.decodeUtf8=function(e){return decodeURIComponent(escape(e))},i.binary={raw:{},hex:{},base64:{},base58:{},baseN:{encode:n.encode,decode:n.decode}},i.binary.raw.encode=function(e){return String.fromCharCode.apply(null,e)},i.binary.raw.decode=function(e,t,r){var a=t;a||(a=new Uint8Array(e.length));for(var n=r=r||0,i=0;i>2),i+=c.charAt((3&r)<<4|a>>4),isNaN(a)?i+="==":(i+=c.charAt((15&a)<<2|n>>6),i+=isNaN(n)?"=":c.charAt(63&n)),t&&i.length>t&&(s+=i.substr(0,t)+"\r\n",i=i.substr(t));return s+=i},i.binary.base64.decode=function(e,t,r){var a,n,i,s,o=t;o||(o=new Uint8Array(3*Math.ceil(e.length/4))),e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var c=0,l=r=r||0;c>4,64!==i&&(o[l++]=(15&n)<<4|i>>2,64!==s&&(o[l++]=(3&i)<<6|s));return t?l-r:o.subarray(0,l)},i.binary.base58.encode=function(e,t){return i.binary.baseN.encode(e,l,t)},i.binary.base58.decode=function(e,t){return i.binary.baseN.decode(e,l,t)},i.text={utf8:{},utf16:{}},i.text.utf8.encode=function(e,t,r){e=i.encodeUtf8(e);var a=t;a||(a=new Uint8Array(e.length));for(var n=r=r||0,s=0;s0&&i.push(r),s=a.lastIndex;var o=t[0][1];switch(o){case"s":case"o":n");break;case"%":i.push("%");break;default:i.push("<%"+o+"?>")}}return i.push(e.substring(s)),i.join("")},i.formatNumber=function(e,t,r,a){var n=e,i=isNaN(t=Math.abs(t))?2:t,s=void 0===r?",":r,o=void 0===a?".":a,c=n<0?"-":"",u=parseInt(n=Math.abs(+n||0).toFixed(i),10)+"",l=u.length>3?u.length%3:0;return c+(l?u.substr(0,l)+o:"")+u.substr(l).replace(/(\d{3})(?=\d)/g,"$1"+o)+(i?s+Math.abs(n-u).toFixed(i).slice(2):"")},i.formatSize=function(e){return e=e>=1073741824?i.formatNumber(e/1073741824,2,".","")+" GiB":e>=1048576?i.formatNumber(e/1048576,2,".","")+" MiB":e>=1024?i.formatNumber(e/1024,0)+" KiB":i.formatNumber(e,0)+" bytes"},i.bytesFromIP=function(e){return-1!==e.indexOf(".")?i.bytesFromIPv4(e):-1!==e.indexOf(":")?i.bytesFromIPv6(e):null},i.bytesFromIPv4=function(e){if(4!==(e=e.split(".")).length)return null;for(var t=i.createBuffer(),r=0;rr[a].end-r[a].start&&(a=r.length-1)):r.push({start:c,end:c})}t.push(s)}if(r.length>0){var u=r[a];u.end-u.start>0&&(t.splice(u.start,u.end-u.start+1,""),0===u.start&&t.unshift(""),7===u.end&&t.push(""))}return t.join(":")},i.estimateCores=function(e,t){if("function"==typeof e&&(t=e,e={}),e=e||{},"cores"in i&&!e.update)return t(null,i.cores);if("undefined"!=typeof navigator&&"hardwareConcurrency"in navigator&&navigator.hardwareConcurrency>0)return i.cores=navigator.hardwareConcurrency,t(null,i.cores);if("undefined"==typeof Worker)return i.cores=1,t(null,i.cores);if("undefined"==typeof Blob)return i.cores=2,t(null,i.cores);var r=URL.createObjectURL(new Blob(["(",function(){self.addEventListener("message",(function(e){for(var t=Date.now(),r=t+4;Date.now()o.st&&n.stn.st&&o.stt){var a=new Error("Too few bytes to parse DER.");throw a.available=e.length(),a.remaining=t,a.requested=r,a}}n.Class={UNIVERSAL:0,APPLICATION:64,CONTEXT_SPECIFIC:128,PRIVATE:192},n.Type={NONE:0,BOOLEAN:1,INTEGER:2,BITSTRING:3,OCTETSTRING:4,NULL:5,OID:6,ODESC:7,EXTERNAL:8,REAL:9,ENUMERATED:10,EMBEDDED:11,UTF8:12,ROID:13,SEQUENCE:16,SET:17,PRINTABLESTRING:19,IA5STRING:22,UTCTIME:23,GENERALIZEDTIME:24,BMPSTRING:30},n.create=function(e,t,r,i,s){if(a.util.isArray(i)){for(var o=[],c=0;cr){if(s.strict){var d=new Error("Too few bytes to read ASN.1 value.");throw d.available=t.length(),d.remaining=r,d.requested=h,d}h=r}var y=32==(32&c);if(y)if(p=[],void 0===h)for(;;){if(i(t,r,2),t.bytes(2)===String.fromCharCode(0,0)){t.getBytes(2),r-=2;break}o=t.length(),p.push(e(t,r,a+1,s)),r-=o-t.length()}else for(;h>0;)o=t.length(),p.push(e(t,h,a+1,s)),r-=o-t.length(),h-=o-t.length();void 0===p&&u===n.Class.UNIVERSAL&&l===n.Type.BITSTRING&&(f=t.bytes(h));if(void 0===p&&s.decodeBitStrings&&u===n.Class.UNIVERSAL&&l===n.Type.BITSTRING&&h>1){var g=t.read,v=r,m=0;if(l===n.Type.BITSTRING&&(i(t,r,1),m=t.getByte(),r--),0===m)try{o=t.length();var C=e(t,r,a+1,{strict:!0,decodeBitStrings:!0}),E=o-t.length();r-=E,l==n.Type.BITSTRING&&E++;var S=C.tagClass;E!==h||S!==n.Class.UNIVERSAL&&S!==n.Class.CONTEXT_SPECIFIC||(p=[C])}catch(e){}void 0===p&&(t.read=g,r=v)}if(void 0===p){if(void 0===h){if(s.strict)throw new Error("Non-constructed ASN.1 object of indefinite length.");h=r}if(l===n.Type.BMPSTRING)for(p="";h>0;h-=2)i(t,r,2),p+=String.fromCharCode(t.getInt16()),r-=2;else p=t.getBytes(h),r-=h}var T=void 0===f?null:{bitStringContents:f};return n.create(u,l,y,p,T)}(e,e.length(),0,t);if(t.parseAllBytes&&0!==e.length()){var o=new Error("Unparsed DER bytes remain after ASN.1 parsing.");throw o.byteCount=r,o.remaining=e.length(),o}return s},n.toDer=function(e){var t=a.util.createBuffer(),r=e.tagClass|e.type,i=a.util.createBuffer(),s=!1;if("bitStringContents"in e&&(s=!0,e.original&&(s=n.equals(e,e.original))),s)i.putBytes(e.bitStringContents);else if(e.composed){e.constructed?r|=32:i.putByte(0);for(var o=0;o1&&(0===e.value.charCodeAt(0)&&0==(128&e.value.charCodeAt(1))||255===e.value.charCodeAt(0)&&128==(128&e.value.charCodeAt(1)))?i.putBytes(e.value.substr(1)):i.putBytes(e.value);if(t.putByte(r),i.length()<=127)t.putByte(127&i.length());else{var c=i.length(),u="";do{u+=String.fromCharCode(255&c),c>>>=8}while(c>0);t.putByte(128|u.length);for(o=u.length-1;o>=0;--o)t.putByte(u.charCodeAt(o))}return t.putBuffer(i),t},n.oidToDer=function(e){var t,r,n,i,s=e.split("."),o=a.util.createBuffer();o.putByte(40*parseInt(s[0],10)+parseInt(s[1],10));for(var c=2;c>>=7,t||(i|=128),r.push(i),t=!1}while(n>0);for(var u=r.length-1;u>=0;--u)o.putByte(r[u])}return o},n.derToOid=function(e){var t;"string"==typeof e&&(e=a.util.createBuffer(e));var r=e.getByte();t=Math.floor(r/40)+"."+r%40;for(var n=0;e.length()>0;)n<<=7,128&(r=e.getByte())?n+=127&r:(t+="."+(n+r),n=0);return t},n.utcTimeToDate=function(e){var t=new Date,r=parseInt(e.substr(0,2),10);r=r>=50?1900+r:2e3+r;var a=parseInt(e.substr(2,2),10)-1,n=parseInt(e.substr(4,2),10),i=parseInt(e.substr(6,2),10),s=parseInt(e.substr(8,2),10),o=0;if(e.length>11){var c=e.charAt(10),u=10;"+"!==c&&"-"!==c&&(o=parseInt(e.substr(10,2),10),u+=2)}if(t.setUTCFullYear(r,a,n),t.setUTCHours(i,s,o,0),u&&("+"===(c=e.charAt(u))||"-"===c)){var l=60*parseInt(e.substr(u+1,2),10)+parseInt(e.substr(u+4,2),10);l*=6e4,"+"===c?t.setTime(+t-l):t.setTime(+t+l)}return t},n.generalizedTimeToDate=function(e){var t=new Date,r=parseInt(e.substr(0,4),10),a=parseInt(e.substr(4,2),10)-1,n=parseInt(e.substr(6,2),10),i=parseInt(e.substr(8,2),10),s=parseInt(e.substr(10,2),10),o=parseInt(e.substr(12,2),10),c=0,u=0,l=!1;"Z"===e.charAt(e.length-1)&&(l=!0);var p=e.length-5,f=e.charAt(p);"+"!==f&&"-"!==f||(u=60*parseInt(e.substr(p+1,2),10)+parseInt(e.substr(p+4,2),10),u*=6e4,"+"===f&&(u*=-1),l=!0);return"."===e.charAt(14)&&(c=1e3*parseFloat(e.substr(14),10)),l?(t.setUTCFullYear(r,a,n),t.setUTCHours(i,s,o,c),t.setTime(+t+u)):(t.setFullYear(r,a,n),t.setHours(i,s,o,c)),t},n.dateToUtcTime=function(e){if("string"==typeof e)return e;var t="",r=[];r.push((""+e.getUTCFullYear()).substr(2)),r.push(""+(e.getUTCMonth()+1)),r.push(""+e.getUTCDate()),r.push(""+e.getUTCHours()),r.push(""+e.getUTCMinutes()),r.push(""+e.getUTCSeconds());for(var a=0;a=-128&&e<128)return t.putSignedInt(e,8);if(e>=-32768&&e<32768)return t.putSignedInt(e,16);if(e>=-8388608&&e<8388608)return t.putSignedInt(e,24);if(e>=-2147483648&&e<2147483648)return t.putSignedInt(e,32);var r=new Error("Integer too large; max is 32-bits.");throw r.integer=e,r},n.derToInteger=function(e){"string"==typeof e&&(e=a.util.createBuffer(e));var t=8*e.length();if(t>32)throw new Error("Integer too large; max is 32-bits.");return e.getSignedInt(t)},n.validate=function(e,t,r,i){var s=!1;if(e.tagClass!==t.tagClass&&void 0!==t.tagClass||e.type!==t.type&&void 0!==t.type)i&&(e.tagClass!==t.tagClass&&i.push("["+t.name+'] Expected tag class "'+t.tagClass+'", got "'+e.tagClass+'"'),e.type!==t.type&&i.push("["+t.name+'] Expected type "'+t.type+'", got "'+e.type+'"'));else if(e.constructed===t.constructed||void 0===t.constructed){if(s=!0,t.value&&a.util.isArray(t.value))for(var o=0,c=0;s&&c0&&(i+="\n");for(var o="",c=0;c1?i+="0x"+a.util.bytesToHex(e.value.slice(1)):i+="(none)",e.value.length>0){var f=e.value.charCodeAt(0);1==f?i+=" (1 unused bit shown)":f>1&&(i+=" ("+f+" unused bits shown)")}}else if(e.type===n.Type.OCTETSTRING)s.test(e.value)||(i+="("+e.value+") "),i+="0x"+a.util.bytesToHex(e.value);else if(e.type===n.Type.UTF8)try{i+=a.util.decodeUtf8(e.value)}catch(t){if("URI malformed"!==t.message)throw t;i+="0x"+a.util.bytesToHex(e.value)+" (malformed UTF8)"}else e.type===n.Type.PRINTABLESTRING||e.type===n.Type.IA5String?i+=e.value:s.test(e.value)?i+="0x"+a.util.bytesToHex(e.value):0===e.value.length?i+="[null]":i+=e.value}return i}},function(e,t,r){var a=r(0);e.exports=a.md=a.md||{},a.md.algorithms=a.md.algorithms||{}},function(e,t,r){var a=r(0);function n(e,t){a.cipher.registerAlgorithm(e,(function(){return new a.aes.Algorithm(e,t)}))}r(13),r(19),r(1),e.exports=a.aes=a.aes||{},a.aes.startEncrypting=function(e,t,r,a){var n=d({key:e,output:r,decrypt:!1,mode:a});return n.start(t),n},a.aes.createEncryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!1,mode:t})},a.aes.startDecrypting=function(e,t,r,a){var n=d({key:e,output:r,decrypt:!0,mode:a});return n.start(t),n},a.aes.createDecryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!0,mode:t})},a.aes.Algorithm=function(e,t){l||p();var r=this;r.name=e,r.mode=new t({blockSize:16,cipher:{encrypt:function(e,t){return h(r._w,e,t,!1)},decrypt:function(e,t){return h(r._w,e,t,!0)}}}),r._init=!1},a.aes.Algorithm.prototype.initialize=function(e){if(!this._init){var t,r=e.key;if("string"!=typeof r||16!==r.length&&24!==r.length&&32!==r.length){if(a.util.isArray(r)&&(16===r.length||24===r.length||32===r.length)){t=r,r=a.util.createBuffer();for(var n=0;n>>=2;for(n=0;n>8^255&p^99,i[y]=p,s[p]=y,h=(f=e[p])<<24^p<<16^p<<8^p^f,d=((r=e[y])^(a=e[r])^(n=e[a]))<<24^(y^n)<<16^(y^a^n)<<8^y^r^n;for(var v=0;v<4;++v)c[v][y]=h,u[v][p]=d,h=h<<24|h>>>8,d=d<<24|d>>>8;0===y?y=g=1:(y=r^e[e[e[r^n]]],g^=e[e[g]])}}function f(e,t){for(var r,a=e.slice(0),n=1,s=a.length,c=4*(s+6+1),l=s;l>>16&255]<<24^i[r>>>8&255]<<16^i[255&r]<<8^i[r>>>24]^o[n]<<24,n++):s>6&&l%s==4&&(r=i[r>>>24]<<24^i[r>>>16&255]<<16^i[r>>>8&255]<<8^i[255&r]),a[l]=a[l-s]^r;if(t){for(var p,f=u[0],h=u[1],d=u[2],y=u[3],g=a.slice(0),v=(l=0,(c=a.length)-4);l>>24]]^h[i[p>>>16&255]]^d[i[p>>>8&255]]^y[i[255&p]];a=g}return a}function h(e,t,r,a){var n,o,l,p,f,h,d,y,g,v,m,C,E=e.length/4-1;a?(n=u[0],o=u[1],l=u[2],p=u[3],f=s):(n=c[0],o=c[1],l=c[2],p=c[3],f=i),h=t[0]^e[0],d=t[a?3:1]^e[1],y=t[2]^e[2],g=t[a?1:3]^e[3];for(var S=3,T=1;T>>24]^o[d>>>16&255]^l[y>>>8&255]^p[255&g]^e[++S],m=n[d>>>24]^o[y>>>16&255]^l[g>>>8&255]^p[255&h]^e[++S],C=n[y>>>24]^o[g>>>16&255]^l[h>>>8&255]^p[255&d]^e[++S],g=n[g>>>24]^o[h>>>16&255]^l[d>>>8&255]^p[255&y]^e[++S],h=v,d=m,y=C;r[0]=f[h>>>24]<<24^f[d>>>16&255]<<16^f[y>>>8&255]<<8^f[255&g]^e[++S],r[a?3:1]=f[d>>>24]<<24^f[y>>>16&255]<<16^f[g>>>8&255]<<8^f[255&h]^e[++S],r[2]=f[y>>>24]<<24^f[g>>>16&255]<<16^f[h>>>8&255]<<8^f[255&d]^e[++S],r[a?1:3]=f[g>>>24]<<24^f[h>>>16&255]<<16^f[d>>>8&255]<<8^f[255&y]^e[++S]}function d(e){var t,r="AES-"+((e=e||{}).mode||"CBC").toUpperCase(),n=(t=e.decrypt?a.cipher.createDecipher(r,e.key):a.cipher.createCipher(r,e.key)).start;return t.start=function(e,r){var i=null;r instanceof a.util.ByteBuffer&&(i=r,r={}),(r=r||{}).output=i,r.iv=e,n.call(t,r)},t}},function(e,t,r){var a=r(0);a.pki=a.pki||{};var n=e.exports=a.pki.oids=a.oids=a.oids||{};function i(e,t){n[e]=t,n[t]=e}function s(e,t){n[e]=t}i("1.2.840.113549.1.1.1","rsaEncryption"),i("1.2.840.113549.1.1.4","md5WithRSAEncryption"),i("1.2.840.113549.1.1.5","sha1WithRSAEncryption"),i("1.2.840.113549.1.1.7","RSAES-OAEP"),i("1.2.840.113549.1.1.8","mgf1"),i("1.2.840.113549.1.1.9","pSpecified"),i("1.2.840.113549.1.1.10","RSASSA-PSS"),i("1.2.840.113549.1.1.11","sha256WithRSAEncryption"),i("1.2.840.113549.1.1.12","sha384WithRSAEncryption"),i("1.2.840.113549.1.1.13","sha512WithRSAEncryption"),i("1.3.101.112","EdDSA25519"),i("1.2.840.10040.4.3","dsa-with-sha1"),i("1.3.14.3.2.7","desCBC"),i("1.3.14.3.2.26","sha1"),i("1.3.14.3.2.29","sha1WithRSASignature"),i("2.16.840.1.101.3.4.2.1","sha256"),i("2.16.840.1.101.3.4.2.2","sha384"),i("2.16.840.1.101.3.4.2.3","sha512"),i("2.16.840.1.101.3.4.2.4","sha224"),i("2.16.840.1.101.3.4.2.5","sha512-224"),i("2.16.840.1.101.3.4.2.6","sha512-256"),i("1.2.840.113549.2.2","md2"),i("1.2.840.113549.2.5","md5"),i("1.2.840.113549.1.7.1","data"),i("1.2.840.113549.1.7.2","signedData"),i("1.2.840.113549.1.7.3","envelopedData"),i("1.2.840.113549.1.7.4","signedAndEnvelopedData"),i("1.2.840.113549.1.7.5","digestedData"),i("1.2.840.113549.1.7.6","encryptedData"),i("1.2.840.113549.1.9.1","emailAddress"),i("1.2.840.113549.1.9.2","unstructuredName"),i("1.2.840.113549.1.9.3","contentType"),i("1.2.840.113549.1.9.4","messageDigest"),i("1.2.840.113549.1.9.5","signingTime"),i("1.2.840.113549.1.9.6","counterSignature"),i("1.2.840.113549.1.9.7","challengePassword"),i("1.2.840.113549.1.9.8","unstructuredAddress"),i("1.2.840.113549.1.9.14","extensionRequest"),i("1.2.840.113549.1.9.20","friendlyName"),i("1.2.840.113549.1.9.21","localKeyId"),i("1.2.840.113549.1.9.22.1","x509Certificate"),i("1.2.840.113549.1.12.10.1.1","keyBag"),i("1.2.840.113549.1.12.10.1.2","pkcs8ShroudedKeyBag"),i("1.2.840.113549.1.12.10.1.3","certBag"),i("1.2.840.113549.1.12.10.1.4","crlBag"),i("1.2.840.113549.1.12.10.1.5","secretBag"),i("1.2.840.113549.1.12.10.1.6","safeContentsBag"),i("1.2.840.113549.1.5.13","pkcs5PBES2"),i("1.2.840.113549.1.5.12","pkcs5PBKDF2"),i("1.2.840.113549.1.12.1.1","pbeWithSHAAnd128BitRC4"),i("1.2.840.113549.1.12.1.2","pbeWithSHAAnd40BitRC4"),i("1.2.840.113549.1.12.1.3","pbeWithSHAAnd3-KeyTripleDES-CBC"),i("1.2.840.113549.1.12.1.4","pbeWithSHAAnd2-KeyTripleDES-CBC"),i("1.2.840.113549.1.12.1.5","pbeWithSHAAnd128BitRC2-CBC"),i("1.2.840.113549.1.12.1.6","pbewithSHAAnd40BitRC2-CBC"),i("1.2.840.113549.2.7","hmacWithSHA1"),i("1.2.840.113549.2.8","hmacWithSHA224"),i("1.2.840.113549.2.9","hmacWithSHA256"),i("1.2.840.113549.2.10","hmacWithSHA384"),i("1.2.840.113549.2.11","hmacWithSHA512"),i("1.2.840.113549.3.7","des-EDE3-CBC"),i("2.16.840.1.101.3.4.1.2","aes128-CBC"),i("2.16.840.1.101.3.4.1.22","aes192-CBC"),i("2.16.840.1.101.3.4.1.42","aes256-CBC"),i("2.5.4.3","commonName"),i("2.5.4.4","surname"),i("2.5.4.5","serialNumber"),i("2.5.4.6","countryName"),i("2.5.4.7","localityName"),i("2.5.4.8","stateOrProvinceName"),i("2.5.4.9","streetAddress"),i("2.5.4.10","organizationName"),i("2.5.4.11","organizationalUnitName"),i("2.5.4.12","title"),i("2.5.4.13","description"),i("2.5.4.15","businessCategory"),i("2.5.4.17","postalCode"),i("2.5.4.42","givenName"),i("1.3.6.1.4.1.311.60.2.1.2","jurisdictionOfIncorporationStateOrProvinceName"),i("1.3.6.1.4.1.311.60.2.1.3","jurisdictionOfIncorporationCountryName"),i("2.16.840.1.113730.1.1","nsCertType"),i("2.16.840.1.113730.1.13","nsComment"),s("2.5.29.1","authorityKeyIdentifier"),s("2.5.29.2","keyAttributes"),s("2.5.29.3","certificatePolicies"),s("2.5.29.4","keyUsageRestriction"),s("2.5.29.5","policyMapping"),s("2.5.29.6","subtreesConstraint"),s("2.5.29.7","subjectAltName"),s("2.5.29.8","issuerAltName"),s("2.5.29.9","subjectDirectoryAttributes"),s("2.5.29.10","basicConstraints"),s("2.5.29.11","nameConstraints"),s("2.5.29.12","policyConstraints"),s("2.5.29.13","basicConstraints"),i("2.5.29.14","subjectKeyIdentifier"),i("2.5.29.15","keyUsage"),s("2.5.29.16","privateKeyUsagePeriod"),i("2.5.29.17","subjectAltName"),i("2.5.29.18","issuerAltName"),i("2.5.29.19","basicConstraints"),s("2.5.29.20","cRLNumber"),s("2.5.29.21","cRLReason"),s("2.5.29.22","expirationDate"),s("2.5.29.23","instructionCode"),s("2.5.29.24","invalidityDate"),s("2.5.29.25","cRLDistributionPoints"),s("2.5.29.26","issuingDistributionPoint"),s("2.5.29.27","deltaCRLIndicator"),s("2.5.29.28","issuingDistributionPoint"),s("2.5.29.29","certificateIssuer"),s("2.5.29.30","nameConstraints"),i("2.5.29.31","cRLDistributionPoints"),i("2.5.29.32","certificatePolicies"),s("2.5.29.33","policyMappings"),s("2.5.29.34","policyConstraints"),i("2.5.29.35","authorityKeyIdentifier"),s("2.5.29.36","policyConstraints"),i("2.5.29.37","extKeyUsage"),s("2.5.29.46","freshestCRL"),s("2.5.29.54","inhibitAnyPolicy"),i("1.3.6.1.4.1.11129.2.4.2","timestampList"),i("1.3.6.1.5.5.7.1.1","authorityInfoAccess"),i("1.3.6.1.5.5.7.3.1","serverAuth"),i("1.3.6.1.5.5.7.3.2","clientAuth"),i("1.3.6.1.5.5.7.3.3","codeSigning"),i("1.3.6.1.5.5.7.3.4","emailProtection"),i("1.3.6.1.5.5.7.3.8","timeStamping")},function(e,t,r){var a=r(0);r(1);var n=e.exports=a.pem=a.pem||{};function i(e){for(var t=e.name+": ",r=[],a=function(e,t){return" "+t},n=0;n65&&-1!==s){var o=t[s];","===o?(++s,t=t.substr(0,s)+"\r\n "+t.substr(s)):t=t.substr(0,s)+"\r\n"+o+t.substr(s+1),i=n-s-1,s=-1,++n}else" "!==t[n]&&"\t"!==t[n]&&","!==t[n]||(s=n);return t}function s(e){return e.replace(/^\s+/,"")}n.encode=function(e,t){t=t||{};var r,n="-----BEGIN "+e.type+"-----\r\n";if(e.procType&&(n+=i(r={name:"Proc-Type",values:[String(e.procType.version),e.procType.type]})),e.contentDomain&&(n+=i(r={name:"Content-Domain",values:[e.contentDomain]})),e.dekInfo&&(r={name:"DEK-Info",values:[e.dekInfo.algorithm]},e.dekInfo.parameters&&r.values.push(e.dekInfo.parameters),n+=i(r)),e.headers)for(var s=0;st.blockLength&&(t.start(),t.update(s.bytes()),s=t.digest()),r=a.util.createBuffer(),n=a.util.createBuffer(),u=s.length();for(c=0;c>>0,c>>>0];for(var u=n.fullMessageLength.length-1;u>=0;--u)n.fullMessageLength[u]+=c[1],c[1]=c[0]+(n.fullMessageLength[u]/4294967296>>>0),n.fullMessageLength[u]=n.fullMessageLength[u]>>>0,c[0]=c[1]/4294967296>>>0;return t.putBytes(i),o(e,r,t),(t.read>2048||0===t.length())&&t.compact(),n},n.digest=function(){var s=a.util.createBuffer();s.putBytes(t.bytes());var c,u=n.fullMessageLength[n.fullMessageLength.length-1]+n.messageLengthSize&n.blockLength-1;s.putBytes(i.substr(0,n.blockLength-u));for(var l=8*n.fullMessageLength[0],p=0;p>>0,s.putInt32(l>>>0),l=c>>>0;s.putInt32(l);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3,h4:e.h4};o(f,r,s);var h=a.util.createBuffer();return h.putInt32(f.h0),h.putInt32(f.h1),h.putInt32(f.h2),h.putInt32(f.h3),h.putInt32(f.h4),h},n};var i=null,s=!1;function o(e,t,r){for(var a,n,i,s,o,c,u,l=r.length();l>=64;){for(n=e.h0,i=e.h1,s=e.h2,o=e.h3,c=e.h4,u=0;u<16;++u)a=r.getInt32(),t[u]=a,a=(n<<5|n>>>27)+(o^i&(s^o))+c+1518500249+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<20;++u)a=(a=t[u-3]^t[u-8]^t[u-14]^t[u-16])<<1|a>>>31,t[u]=a,a=(n<<5|n>>>27)+(o^i&(s^o))+c+1518500249+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<32;++u)a=(a=t[u-3]^t[u-8]^t[u-14]^t[u-16])<<1|a>>>31,t[u]=a,a=(n<<5|n>>>27)+(i^s^o)+c+1859775393+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<40;++u)a=(a=t[u-6]^t[u-16]^t[u-28]^t[u-32])<<2|a>>>30,t[u]=a,a=(n<<5|n>>>27)+(i^s^o)+c+1859775393+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<60;++u)a=(a=t[u-6]^t[u-16]^t[u-28]^t[u-32])<<2|a>>>30,t[u]=a,a=(n<<5|n>>>27)+(i&s|o&(i^s))+c+2400959708+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;for(;u<80;++u)a=(a=t[u-6]^t[u-16]^t[u-28]^t[u-32])<<2|a>>>30,t[u]=a,a=(n<<5|n>>>27)+(i^s^o)+c+3395469782+a,c=o,o=s,s=(i<<30|i>>>2)>>>0,i=n,n=a;e.h0=e.h0+n|0,e.h1=e.h1+i|0,e.h2=e.h2+s|0,e.h3=e.h3+o|0,e.h4=e.h4+c|0,l-=64}}},function(e,t,r){var a=r(0);function n(e,t){a.cipher.registerAlgorithm(e,(function(){return new a.des.Algorithm(e,t)}))}r(13),r(19),r(1),e.exports=a.des=a.des||{},a.des.startEncrypting=function(e,t,r,a){var n=d({key:e,output:r,decrypt:!1,mode:a||(null===t?"ECB":"CBC")});return n.start(t),n},a.des.createEncryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!1,mode:t})},a.des.startDecrypting=function(e,t,r,a){var n=d({key:e,output:r,decrypt:!0,mode:a||(null===t?"ECB":"CBC")});return n.start(t),n},a.des.createDecryptionCipher=function(e,t){return d({key:e,output:null,decrypt:!0,mode:t})},a.des.Algorithm=function(e,t){var r=this;r.name=e,r.mode=new t({blockSize:8,cipher:{encrypt:function(e,t){return h(r._keys,e,t,!1)},decrypt:function(e,t){return h(r._keys,e,t,!0)}}}),r._init=!1},a.des.Algorithm.prototype.initialize=function(e){if(!this._init){var t=a.util.createBuffer(e.key);if(0===this.name.indexOf("3DES")&&24!==t.length())throw new Error("Invalid Triple-DES key size: "+8*t.length());this._keys=function(e){for(var t,r=[0,4,536870912,536870916,65536,65540,536936448,536936452,512,516,536871424,536871428,66048,66052,536936960,536936964],a=[0,1,1048576,1048577,67108864,67108865,68157440,68157441,256,257,1048832,1048833,67109120,67109121,68157696,68157697],n=[0,8,2048,2056,16777216,16777224,16779264,16779272,0,8,2048,2056,16777216,16777224,16779264,16779272],i=[0,2097152,134217728,136314880,8192,2105344,134225920,136323072,131072,2228224,134348800,136445952,139264,2236416,134356992,136454144],s=[0,262144,16,262160,0,262144,16,262160,4096,266240,4112,266256,4096,266240,4112,266256],o=[0,1024,32,1056,0,1024,32,1056,33554432,33555456,33554464,33555488,33554432,33555456,33554464,33555488],c=[0,268435456,524288,268959744,2,268435458,524290,268959746,0,268435456,524288,268959744,2,268435458,524290,268959746],u=[0,65536,2048,67584,536870912,536936448,536872960,536938496,131072,196608,133120,198656,537001984,537067520,537004032,537069568],l=[0,262144,0,262144,2,262146,2,262146,33554432,33816576,33554432,33816576,33554434,33816578,33554434,33816578],p=[0,268435456,8,268435464,0,268435456,8,268435464,1024,268436480,1032,268436488,1024,268436480,1032,268436488],f=[0,32,0,32,1048576,1048608,1048576,1048608,8192,8224,8192,8224,1056768,1056800,1056768,1056800],h=[0,16777216,512,16777728,2097152,18874368,2097664,18874880,67108864,83886080,67109376,83886592,69206016,85983232,69206528,85983744],d=[0,4096,134217728,134221824,524288,528384,134742016,134746112,16,4112,134217744,134221840,524304,528400,134742032,134746128],y=[0,4,256,260,0,4,256,260,1,5,257,261,1,5,257,261],g=e.length()>8?3:1,v=[],m=[0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0],C=0,E=0;E>>4^T))<<4,S^=t=65535&((T^=t)>>>-16^S),S^=(t=858993459&(S>>>2^(T^=t<<-16)))<<2,S^=t=65535&((T^=t)>>>-16^S),S^=(t=1431655765&(S>>>1^(T^=t<<-16)))<<1,S^=t=16711935&((T^=t)>>>8^S),t=(S^=(t=1431655765&(S>>>1^(T^=t<<8)))<<1)<<8|(T^=t)>>>20&240,S=T<<24|T<<8&16711680|T>>>8&65280|T>>>24&240,T=t;for(var I=0;I>>26,T=T<<2|T>>>26):(S=S<<1|S>>>27,T=T<<1|T>>>27);var A=r[(S&=-15)>>>28]|a[S>>>24&15]|n[S>>>20&15]|i[S>>>16&15]|s[S>>>12&15]|o[S>>>8&15]|c[S>>>4&15],B=u[(T&=-15)>>>28]|l[T>>>24&15]|p[T>>>20&15]|f[T>>>16&15]|h[T>>>12&15]|d[T>>>8&15]|y[T>>>4&15];t=65535&(B>>>16^A),v[C++]=A^t,v[C++]=B^t<<16}}return v}(t),this._init=!0}},n("DES-ECB",a.cipher.modes.ecb),n("DES-CBC",a.cipher.modes.cbc),n("DES-CFB",a.cipher.modes.cfb),n("DES-OFB",a.cipher.modes.ofb),n("DES-CTR",a.cipher.modes.ctr),n("3DES-ECB",a.cipher.modes.ecb),n("3DES-CBC",a.cipher.modes.cbc),n("3DES-CFB",a.cipher.modes.cfb),n("3DES-OFB",a.cipher.modes.ofb),n("3DES-CTR",a.cipher.modes.ctr);var i=[16843776,0,65536,16843780,16842756,66564,4,65536,1024,16843776,16843780,1024,16778244,16842756,16777216,4,1028,16778240,16778240,66560,66560,16842752,16842752,16778244,65540,16777220,16777220,65540,0,1028,66564,16777216,65536,16843780,4,16842752,16843776,16777216,16777216,1024,16842756,65536,66560,16777220,1024,4,16778244,66564,16843780,65540,16842752,16778244,16777220,1028,66564,16843776,1028,16778240,16778240,0,65540,66560,0,16842756],s=[-2146402272,-2147450880,32768,1081376,1048576,32,-2146435040,-2147450848,-2147483616,-2146402272,-2146402304,-2147483648,-2147450880,1048576,32,-2146435040,1081344,1048608,-2147450848,0,-2147483648,32768,1081376,-2146435072,1048608,-2147483616,0,1081344,32800,-2146402304,-2146435072,32800,0,1081376,-2146435040,1048576,-2147450848,-2146435072,-2146402304,32768,-2146435072,-2147450880,32,-2146402272,1081376,32,32768,-2147483648,32800,-2146402304,1048576,-2147483616,1048608,-2147450848,-2147483616,1048608,1081344,0,-2147450880,32800,-2147483648,-2146435040,-2146402272,1081344],o=[520,134349312,0,134348808,134218240,0,131592,134218240,131080,134217736,134217736,131072,134349320,131080,134348800,520,134217728,8,134349312,512,131584,134348800,134348808,131592,134218248,131584,131072,134218248,8,134349320,512,134217728,134349312,134217728,131080,520,131072,134349312,134218240,0,512,131080,134349320,134218240,134217736,512,0,134348808,134218248,131072,134217728,134349320,8,131592,131584,134217736,134348800,134218248,520,134348800,131592,8,134348808,131584],c=[8396801,8321,8321,128,8396928,8388737,8388609,8193,0,8396800,8396800,8396929,129,0,8388736,8388609,1,8192,8388608,8396801,128,8388608,8193,8320,8388737,1,8320,8388736,8192,8396928,8396929,129,8388736,8388609,8396800,8396929,129,0,0,8396800,8320,8388736,8388737,1,8396801,8321,8321,128,8396929,129,1,8192,8388609,8193,8396928,8388737,8193,8320,8388608,8396801,128,8388608,8192,8396928],u=[256,34078976,34078720,1107296512,524288,256,1073741824,34078720,1074266368,524288,33554688,1074266368,1107296512,1107820544,524544,1073741824,33554432,1074266112,1074266112,0,1073742080,1107820800,1107820800,33554688,1107820544,1073742080,0,1107296256,34078976,33554432,1107296256,524544,524288,1107296512,256,33554432,1073741824,34078720,1107296512,1074266368,33554688,1073741824,1107820544,34078976,1074266368,256,33554432,1107820544,1107820800,524544,1107296256,1107820800,34078720,0,1074266112,1107296256,524544,33554688,1073742080,524288,0,1074266112,34078976,1073742080],l=[536870928,541065216,16384,541081616,541065216,16,541081616,4194304,536887296,4210704,4194304,536870928,4194320,536887296,536870912,16400,0,4194320,536887312,16384,4210688,536887312,16,541065232,541065232,0,4210704,541081600,16400,4210688,541081600,536870912,536887296,16,541065232,4210688,541081616,4194304,16400,536870928,4194304,536887296,536870912,16400,536870928,541081616,4210688,541065216,4210704,541081600,0,541065232,16,16384,541065216,4210704,16384,4194320,536887312,0,541081600,536870912,4194320,536887312],p=[2097152,69206018,67110914,0,2048,67110914,2099202,69208064,69208066,2097152,0,67108866,2,67108864,69206018,2050,67110912,2099202,2097154,67110912,67108866,69206016,69208064,2097154,69206016,2048,2050,69208066,2099200,2,67108864,2099200,67108864,2099200,2097152,67110914,67110914,69206018,69206018,2,2097154,67108864,67110912,2097152,69208064,2050,2099202,69208064,2050,67108866,69208066,69206016,2099200,0,2,69208066,0,2099202,69206016,2048,67108866,67110912,2048,2097154],f=[268439616,4096,262144,268701760,268435456,268439616,64,268435456,262208,268697600,268701760,266240,268701696,266304,4096,64,268697600,268435520,268439552,4160,266240,262208,268697664,268701696,4160,0,0,268697664,268435520,268439552,266304,262144,266304,262144,268701696,4096,64,268697664,4096,266304,268439552,64,268435520,268697600,268697664,268435456,262144,268439616,0,268701760,262208,268435520,268697600,268439552,268439616,0,268701760,266240,266240,4160,4160,262208,268435456,268701696];function h(e,t,r,a){var n,h,d=32===e.length?3:9;n=3===d?a?[30,-2,-2]:[0,32,2]:a?[94,62,-2,32,64,2,30,-2,-2]:[0,32,2,62,30,-2,64,96,2];var y=t[0],g=t[1];y^=(h=252645135&(y>>>4^g))<<4,y^=(h=65535&(y>>>16^(g^=h)))<<16,y^=h=858993459&((g^=h)>>>2^y),y^=h=16711935&((g^=h<<2)>>>8^y),y=(y^=(h=1431655765&(y>>>1^(g^=h<<8)))<<1)<<1|y>>>31,g=(g^=h)<<1|g>>>31;for(var v=0;v>>4|g<<28)^e[E+1];h=y,y=g,g=h^(s[S>>>24&63]|c[S>>>16&63]|l[S>>>8&63]|f[63&S]|i[T>>>24&63]|o[T>>>16&63]|u[T>>>8&63]|p[63&T])}h=y,y=g,g=h}g=g>>>1|g<<31,g^=h=1431655765&((y=y>>>1|y<<31)>>>1^g),g^=(h=16711935&(g>>>8^(y^=h<<1)))<<8,g^=(h=858993459&(g>>>2^(y^=h)))<<2,g^=h=65535&((y^=h)>>>16^g),g^=h=252645135&((y^=h<<16)>>>4^g),y^=h<<4,r[0]=y,r[1]=g}function d(e){var t,r="DES-"+((e=e||{}).mode||"CBC").toUpperCase(),n=(t=e.decrypt?a.cipher.createDecipher(r,e.key):a.cipher.createCipher(r,e.key)).start;return t.start=function(e,r){var i=null;r instanceof a.util.ByteBuffer&&(i=r,r={}),(r=r||{}).output=i,r.iv=e,n.call(t,r)},t}},function(e,t,r){var a=r(0);if(r(3),r(12),r(6),r(26),r(27),r(2),r(1),void 0===n)var n=a.jsbn.BigInteger;var i=a.util.isNodejs?r(16):null,s=a.asn1,o=a.util;a.pki=a.pki||{},e.exports=a.pki.rsa=a.rsa=a.rsa||{};var c=a.pki,u=[6,4,2,4,2,4,6,2],l={name:"PrivateKeyInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"PrivateKeyInfo.version",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"PrivateKeyInfo.privateKeyAlgorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.OID,constructed:!1,capture:"privateKeyOid"}]},{name:"PrivateKeyInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.OCTETSTRING,constructed:!1,capture:"privateKey"}]},p={name:"RSAPrivateKey",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"RSAPrivateKey.version",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyVersion"},{name:"RSAPrivateKey.modulus",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyModulus"},{name:"RSAPrivateKey.publicExponent",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPublicExponent"},{name:"RSAPrivateKey.privateExponent",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPrivateExponent"},{name:"RSAPrivateKey.prime1",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPrime1"},{name:"RSAPrivateKey.prime2",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyPrime2"},{name:"RSAPrivateKey.exponent1",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyExponent1"},{name:"RSAPrivateKey.exponent2",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyExponent2"},{name:"RSAPrivateKey.coefficient",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"privateKeyCoefficient"}]},f={name:"RSAPublicKey",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"RSAPublicKey.modulus",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"publicKeyModulus"},{name:"RSAPublicKey.exponent",tagClass:s.Class.UNIVERSAL,type:s.Type.INTEGER,constructed:!1,capture:"publicKeyExponent"}]},h=a.pki.rsa.publicKeyValidator={name:"SubjectPublicKeyInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,captureAsn1:"subjectPublicKeyInfo",value:[{name:"SubjectPublicKeyInfo.AlgorithmIdentifier",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.OID,constructed:!1,capture:"publicKeyOid"}]},{name:"SubjectPublicKeyInfo.subjectPublicKey",tagClass:s.Class.UNIVERSAL,type:s.Type.BITSTRING,constructed:!1,value:[{name:"SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,optional:!0,captureAsn1:"rsaPublicKey"}]}]},d={name:"DigestInfo",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"DigestInfo.DigestAlgorithm",tagClass:s.Class.UNIVERSAL,type:s.Type.SEQUENCE,constructed:!0,value:[{name:"DigestInfo.DigestAlgorithm.algorithmIdentifier",tagClass:s.Class.UNIVERSAL,type:s.Type.OID,constructed:!1,capture:"algorithmIdentifier"},{name:"DigestInfo.DigestAlgorithm.parameters",tagClass:s.Class.UNIVERSAL,type:s.Type.NULL,capture:"parameters",optional:!0,constructed:!1}]},{name:"DigestInfo.digest",tagClass:s.Class.UNIVERSAL,type:s.Type.OCTETSTRING,constructed:!1,capture:"digest"}]},y=function(e){var t;if(!(e.algorithm in c.oids)){var r=new Error("Unknown message digest algorithm.");throw r.algorithm=e.algorithm,r}t=c.oids[e.algorithm];var a=s.oidToDer(t).getBytes(),n=s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[]),i=s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[]);i.value.push(s.create(s.Class.UNIVERSAL,s.Type.OID,!1,a)),i.value.push(s.create(s.Class.UNIVERSAL,s.Type.NULL,!1,""));var o=s.create(s.Class.UNIVERSAL,s.Type.OCTETSTRING,!1,e.digest().getBytes());return n.value.push(i),n.value.push(o),s.toDer(n).getBytes()},g=function(e,t,r){if(r)return e.modPow(t.e,t.n);if(!t.p||!t.q)return e.modPow(t.d,t.n);var i;t.dP||(t.dP=t.d.mod(t.p.subtract(n.ONE))),t.dQ||(t.dQ=t.d.mod(t.q.subtract(n.ONE))),t.qInv||(t.qInv=t.q.modInverse(t.p));do{i=new n(a.util.bytesToHex(a.random.getBytes(t.n.bitLength()/8)),16)}while(i.compareTo(t.n)>=0||!i.gcd(t.n).equals(n.ONE));for(var s=(e=e.multiply(i.modPow(t.e,t.n)).mod(t.n)).mod(t.p).modPow(t.dP,t.p),o=e.mod(t.q).modPow(t.dQ,t.q);s.compareTo(o)<0;)s=s.add(t.p);var c=s.subtract(o).multiply(t.qInv).mod(t.p).multiply(t.q).add(o);return c=c.multiply(i.modInverse(t.n)).mod(t.n)};function v(e,t,r){var n=a.util.createBuffer(),i=Math.ceil(t.n.bitLength()/8);if(e.length>i-11){var s=new Error("Message is too long for PKCS#1 v1.5 padding.");throw s.length=e.length,s.max=i-11,s}n.putByte(0),n.putByte(r);var o,c=i-3-e.length;if(0===r||1===r){o=0===r?0:255;for(var u=0;u0;){var l=0,p=a.random.getBytes(c);for(u=0;u1;){if(255!==s.getByte()){--s.read;break}++u}else if(2===c)for(u=0;s.length()>1;){if(0===s.getByte()){--s.read;break}++u}if(0!==s.getByte()||u!==i-3-s.length())throw new Error("Encryption block is invalid.");return s.getBytes()}function C(e,t,r){"function"==typeof t&&(r=t,t={});var i={algorithm:{name:(t=t||{}).algorithm||"PRIMEINC",options:{workers:t.workers||2,workLoad:t.workLoad||100,workerScript:t.workerScript}}};function s(){o(e.pBits,(function(t,a){return t?r(t):(e.p=a,null!==e.q?u(t,e.q):void o(e.qBits,u))}))}function o(e,t){a.prime.generateProbablePrime(e,i,t)}function u(t,a){if(t)return r(t);if(e.q=a,e.p.compareTo(e.q)<0){var i=e.p;e.p=e.q,e.q=i}if(0!==e.p.subtract(n.ONE).gcd(e.e).compareTo(n.ONE))return e.p=null,void s();if(0!==e.q.subtract(n.ONE).gcd(e.e).compareTo(n.ONE))return e.q=null,void o(e.qBits,u);if(e.p1=e.p.subtract(n.ONE),e.q1=e.q.subtract(n.ONE),e.phi=e.p1.multiply(e.q1),0!==e.phi.gcd(e.e).compareTo(n.ONE))return e.p=e.q=null,void s();if(e.n=e.p.multiply(e.q),e.n.bitLength()!==e.bits)return e.q=null,void o(e.qBits,u);var l=e.e.modInverse(e.phi);e.keys={privateKey:c.rsa.setPrivateKey(e.n,e.e,l,e.p,e.q,l.mod(e.p1),l.mod(e.q1),e.q.modInverse(e.p)),publicKey:c.rsa.setPublicKey(e.n,e.e)},r(null,e.keys)}"prng"in t&&(i.prng=t.prng),s()}function E(e){var t=e.toString(16);t[0]>="8"&&(t="00"+t);var r=a.util.hexToBytes(t);return r.length>1&&(0===r.charCodeAt(0)&&0==(128&r.charCodeAt(1))||255===r.charCodeAt(0)&&128==(128&r.charCodeAt(1)))?r.substr(1):r}function S(e){return e<=100?27:e<=150?18:e<=200?15:e<=250?12:e<=300?9:e<=350?8:e<=400?7:e<=500?6:e<=600?5:e<=800?4:e<=1250?3:2}function T(e){return a.util.isNodejs&&"function"==typeof i[e]}function I(e){return void 0!==o.globalScope&&"object"==typeof o.globalScope.crypto&&"object"==typeof o.globalScope.crypto.subtle&&"function"==typeof o.globalScope.crypto.subtle[e]}function A(e){return void 0!==o.globalScope&&"object"==typeof o.globalScope.msCrypto&&"object"==typeof o.globalScope.msCrypto.subtle&&"function"==typeof o.globalScope.msCrypto.subtle[e]}function B(e){for(var t=a.util.hexToBytes(e.toString(16)),r=new Uint8Array(t.length),n=0;n0;)l.putByte(0),--p;return l.putBytes(a.util.hexToBytes(u)),l.getBytes()},c.rsa.decrypt=function(e,t,r,i){var s=Math.ceil(t.n.bitLength()/8);if(e.length!==s){var o=new Error("Encrypted message length is invalid.");throw o.length=e.length,o.expected=s,o}var c=new n(a.util.createBuffer(e).toHex(),16);if(c.compareTo(t.n)>=0)throw new Error("Encrypted message is invalid.");for(var u=g(c,t,r).toString(16),l=a.util.createBuffer(),p=s-Math.ceil(u.length/2);p>0;)l.putByte(0),--p;return l.putBytes(a.util.hexToBytes(u)),!1!==i?m(l.getBytes(),t,r):l.getBytes()},c.rsa.createKeyPairGenerationState=function(e,t,r){"string"==typeof e&&(e=parseInt(e,10)),e=e||2048;var i,s=(r=r||{}).prng||a.random,o={nextBytes:function(e){for(var t=s.getBytesSync(e.length),r=0;r>1,pBits:e-(e>>1),pqState:0,num:null,keys:null}).e.fromInt(i.eInt),i},c.rsa.stepKeyPairGenerationState=function(e,t){"algorithm"in e||(e.algorithm="PRIMEINC");var r=new n(null);r.fromInt(30);for(var a,i=0,s=function(e,t){return e|t},o=+new Date,l=0;null===e.keys&&(t<=0||lp?e.pqState=0:e.num.isProbablePrime(S(e.num.bitLength()))?++e.pqState:e.num.dAddOffset(u[i++%8],0):2===e.pqState?e.pqState=0===e.num.subtract(n.ONE).gcd(e.e).compareTo(n.ONE)?3:0:3===e.pqState&&(e.pqState=0,null===e.p?e.p=e.num:e.q=e.num,null!==e.p&&null!==e.q&&++e.state,e.num=null)}else if(1===e.state)e.p.compareTo(e.q)<0&&(e.num=e.p,e.p=e.q,e.q=e.num),++e.state;else if(2===e.state)e.p1=e.p.subtract(n.ONE),e.q1=e.q.subtract(n.ONE),e.phi=e.p1.multiply(e.q1),++e.state;else if(3===e.state)0===e.phi.gcd(e.e).compareTo(n.ONE)?++e.state:(e.p=null,e.q=null,e.state=0);else if(4===e.state)e.n=e.p.multiply(e.q),e.n.bitLength()===e.bits?++e.state:(e.q=null,e.state=0);else if(5===e.state){var h=e.e.modInverse(e.phi);e.keys={privateKey:c.rsa.setPrivateKey(e.n,e.e,h,e.p,e.q,h.mod(e.p1),h.mod(e.q1),e.q.modInverse(e.p)),publicKey:c.rsa.setPublicKey(e.n,e.e)}}l+=(a=+new Date)-o,o=a}return null!==e.keys},c.rsa.generateKeyPair=function(e,t,r,n){if(1===arguments.length?"object"==typeof e?(r=e,e=void 0):"function"==typeof e&&(n=e,e=void 0):2===arguments.length?"number"==typeof e?"function"==typeof t?(n=t,t=void 0):"number"!=typeof t&&(r=t,t=void 0):(r=e,n=t,e=void 0,t=void 0):3===arguments.length&&("number"==typeof t?"function"==typeof r&&(n=r,r=void 0):(n=r,r=t,t=void 0)),r=r||{},void 0===e&&(e=r.bits||2048),void 0===t&&(t=r.e||65537),!a.options.usePureJavaScript&&!r.prng&&e>=256&&e<=16384&&(65537===t||3===t))if(n){if(T("generateKeyPair"))return i.generateKeyPair("rsa",{modulusLength:e,publicExponent:t,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}},(function(e,t,r){if(e)return n(e);n(null,{privateKey:c.privateKeyFromPem(r),publicKey:c.publicKeyFromPem(t)})}));if(I("generateKey")&&I("exportKey"))return o.globalScope.crypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:e,publicExponent:B(t),hash:{name:"SHA-256"}},!0,["sign","verify"]).then((function(e){return o.globalScope.crypto.subtle.exportKey("pkcs8",e.privateKey)})).then(void 0,(function(e){n(e)})).then((function(e){if(e){var t=c.privateKeyFromAsn1(s.fromDer(a.util.createBuffer(e)));n(null,{privateKey:t,publicKey:c.setRsaPublicKey(t.n,t.e)})}}));if(A("generateKey")&&A("exportKey")){var u=o.globalScope.msCrypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:e,publicExponent:B(t),hash:{name:"SHA-256"}},!0,["sign","verify"]);return u.oncomplete=function(e){var t=e.target.result,r=o.globalScope.msCrypto.subtle.exportKey("pkcs8",t.privateKey);r.oncomplete=function(e){var t=e.target.result,r=c.privateKeyFromAsn1(s.fromDer(a.util.createBuffer(t)));n(null,{privateKey:r,publicKey:c.setRsaPublicKey(r.n,r.e)})},r.onerror=function(e){n(e)}},void(u.onerror=function(e){n(e)})}}else if(T("generateKeyPairSync")){var l=i.generateKeyPairSync("rsa",{modulusLength:e,publicExponent:t,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}});return{privateKey:c.privateKeyFromPem(l.privateKey),publicKey:c.publicKeyFromPem(l.publicKey)}}var p=c.rsa.createKeyPairGenerationState(e,t,r);if(!n)return c.rsa.stepKeyPairGenerationState(p,0),p.keys;C(p,r,n)},c.setRsaPublicKey=c.rsa.setPublicKey=function(e,t){var r={n:e,e:t,encrypt:function(e,t,n){if("string"==typeof t?t=t.toUpperCase():void 0===t&&(t="RSAES-PKCS1-V1_5"),"RSAES-PKCS1-V1_5"===t)t={encode:function(e,t,r){return v(e,t,2).getBytes()}};else if("RSA-OAEP"===t||"RSAES-OAEP"===t)t={encode:function(e,t){return a.pkcs1.encode_rsa_oaep(t,e,n)}};else if(-1!==["RAW","NONE","NULL",null].indexOf(t))t={encode:function(e){return e}};else if("string"==typeof t)throw new Error('Unsupported encryption scheme: "'+t+'".');var i=t.encode(e,r,!0);return c.rsa.encrypt(i,r,!0)},verify:function(e,t,n,i){"string"==typeof n?n=n.toUpperCase():void 0===n&&(n="RSASSA-PKCS1-V1_5"),void 0===i&&(i={_parseAllDigestBytes:!0}),"_parseAllDigestBytes"in i||(i._parseAllDigestBytes=!0),"RSASSA-PKCS1-V1_5"===n?n={verify:function(e,t){t=m(t,r,!0);var n=s.fromDer(t,{parseAllBytes:i._parseAllDigestBytes}),o={},c=[];if(!s.validate(n,d,o,c))throw(u=new Error("ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value.")).errors=c,u;var u,l=s.derToOid(o.algorithmIdentifier);if(l!==a.oids.md2&&l!==a.oids.md5&&l!==a.oids.sha1&&l!==a.oids.sha224&&l!==a.oids.sha256&&l!==a.oids.sha384&&l!==a.oids.sha512&&l!==a.oids["sha512-224"]&&l!==a.oids["sha512-256"])throw(u=new Error("Unknown RSASSA-PKCS1-v1_5 DigestAlgorithm identifier.")).oid=l,u;if((l===a.oids.md2||l===a.oids.md5)&&!("parameters"in o))throw new Error("ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value. Missing algorithm identifer NULL parameters.");return e===o.digest}}:"NONE"!==n&&"NULL"!==n&&null!==n||(n={verify:function(e,t){return e===(t=m(t,r,!0))}});var o=c.rsa.decrypt(t,r,!0,!1);return n.verify(e,o,r.n.bitLength())}};return r},c.setRsaPrivateKey=c.rsa.setPrivateKey=function(e,t,r,n,i,s,o,u){var l={n:e,e:t,d:r,p:n,q:i,dP:s,dQ:o,qInv:u,decrypt:function(e,t,r){"string"==typeof t?t=t.toUpperCase():void 0===t&&(t="RSAES-PKCS1-V1_5");var n=c.rsa.decrypt(e,l,!1,!1);if("RSAES-PKCS1-V1_5"===t)t={decode:m};else if("RSA-OAEP"===t||"RSAES-OAEP"===t)t={decode:function(e,t){return a.pkcs1.decode_rsa_oaep(t,e,r)}};else{if(-1===["RAW","NONE","NULL",null].indexOf(t))throw new Error('Unsupported encryption scheme: "'+t+'".');t={decode:function(e){return e}}}return t.decode(n,l,!1)},sign:function(e,t){var r=!1;"string"==typeof t&&(t=t.toUpperCase()),void 0===t||"RSASSA-PKCS1-V1_5"===t?(t={encode:y},r=1):"NONE"!==t&&"NULL"!==t&&null!==t||(t={encode:function(){return e}},r=1);var a=t.encode(e,l.n.bitLength());return c.rsa.encrypt(a,l,r)}};return l},c.wrapRsaPrivateKey=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,s.integerToDer(0).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.OID,!1,s.oidToDer(c.oids.rsaEncryption).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.NULL,!1,"")]),s.create(s.Class.UNIVERSAL,s.Type.OCTETSTRING,!1,s.toDer(e).getBytes())])},c.privateKeyFromAsn1=function(e){var t,r,i,o,u,f,h,d,y={},g=[];if(s.validate(e,l,y,g)&&(e=s.fromDer(a.util.createBuffer(y.privateKey))),y={},g=[],!s.validate(e,p,y,g)){var v=new Error("Cannot read private key. ASN.1 object does not contain an RSAPrivateKey.");throw v.errors=g,v}return t=a.util.createBuffer(y.privateKeyModulus).toHex(),r=a.util.createBuffer(y.privateKeyPublicExponent).toHex(),i=a.util.createBuffer(y.privateKeyPrivateExponent).toHex(),o=a.util.createBuffer(y.privateKeyPrime1).toHex(),u=a.util.createBuffer(y.privateKeyPrime2).toHex(),f=a.util.createBuffer(y.privateKeyExponent1).toHex(),h=a.util.createBuffer(y.privateKeyExponent2).toHex(),d=a.util.createBuffer(y.privateKeyCoefficient).toHex(),c.setRsaPrivateKey(new n(t,16),new n(r,16),new n(i,16),new n(o,16),new n(u,16),new n(f,16),new n(h,16),new n(d,16))},c.privateKeyToAsn1=c.privateKeyToRSAPrivateKey=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,s.integerToDer(0).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.n)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.e)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.d)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.p)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.q)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.dP)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.dQ)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.qInv))])},c.publicKeyFromAsn1=function(e){var t={},r=[];if(s.validate(e,h,t,r)){var i,o=s.derToOid(t.publicKeyOid);if(o!==c.oids.rsaEncryption)throw(i=new Error("Cannot read public key. Unknown OID.")).oid=o,i;e=t.rsaPublicKey}if(r=[],!s.validate(e,f,t,r))throw(i=new Error("Cannot read public key. ASN.1 object does not contain an RSAPublicKey.")).errors=r,i;var u=a.util.createBuffer(t.publicKeyModulus).toHex(),l=a.util.createBuffer(t.publicKeyExponent).toHex();return c.setRsaPublicKey(new n(u,16),new n(l,16))},c.publicKeyToAsn1=c.publicKeyToSubjectPublicKeyInfo=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.OID,!1,s.oidToDer(c.oids.rsaEncryption).getBytes()),s.create(s.Class.UNIVERSAL,s.Type.NULL,!1,"")]),s.create(s.Class.UNIVERSAL,s.Type.BITSTRING,!1,[c.publicKeyToRSAPublicKey(e)])])},c.publicKeyToRSAPublicKey=function(e){return s.create(s.Class.UNIVERSAL,s.Type.SEQUENCE,!0,[s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.n)),s.create(s.Class.UNIVERSAL,s.Type.INTEGER,!1,E(e.e))])}},function(e,t,r){var a,n=r(0);e.exports=n.jsbn=n.jsbn||{};function i(e,t,r){this.data=[],null!=e&&("number"==typeof e?this.fromNumber(e,t,r):null==t&&"string"!=typeof e?this.fromString(e,256):this.fromString(e,t))}function s(){return new i(null)}function o(e,t,r,a,n,i){for(var s=16383&t,o=t>>14;--i>=0;){var c=16383&this.data[e],u=this.data[e++]>>14,l=o*c+u*s;n=((c=s*c+((16383&l)<<14)+r.data[a]+n)>>28)+(l>>14)+o*u,r.data[a++]=268435455&c}return n}n.jsbn.BigInteger=i,"undefined"==typeof navigator?(i.prototype.am=o,a=28):"Microsoft Internet Explorer"==navigator.appName?(i.prototype.am=function(e,t,r,a,n,i){for(var s=32767&t,o=t>>15;--i>=0;){var c=32767&this.data[e],u=this.data[e++]>>15,l=o*c+u*s;n=((c=s*c+((32767&l)<<15)+r.data[a]+(1073741823&n))>>>30)+(l>>>15)+o*u+(n>>>30),r.data[a++]=1073741823&c}return n},a=30):"Netscape"!=navigator.appName?(i.prototype.am=function(e,t,r,a,n,i){for(;--i>=0;){var s=t*this.data[e++]+r.data[a]+n;n=Math.floor(s/67108864),r.data[a++]=67108863&s}return n},a=26):(i.prototype.am=o,a=28),i.prototype.DB=a,i.prototype.DM=(1<>>16)&&(e=t,r+=16),0!=(t=e>>8)&&(e=t,r+=8),0!=(t=e>>4)&&(e=t,r+=4),0!=(t=e>>2)&&(e=t,r+=2),0!=(t=e>>1)&&(e=t,r+=1),r}function y(e){this.m=e}function g(e){this.m=e,this.mp=e.invDigit(),this.mpl=32767&this.mp,this.mph=this.mp>>15,this.um=(1<>=16,t+=16),0==(255&e)&&(e>>=8,t+=8),0==(15&e)&&(e>>=4,t+=4),0==(3&e)&&(e>>=2,t+=2),0==(1&e)&&++t,t}function T(e){for(var t=0;0!=e;)e&=e-1,++t;return t}function I(){}function A(e){return e}function B(e){this.r2=s(),this.q3=s(),i.ONE.dlShiftTo(2*e.t,this.r2),this.mu=this.r2.divide(e),this.m=e}y.prototype.convert=function(e){return e.s<0||e.compareTo(this.m)>=0?e.mod(this.m):e},y.prototype.revert=function(e){return e},y.prototype.reduce=function(e){e.divRemTo(this.m,null,e)},y.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r),this.reduce(r)},y.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)},g.prototype.convert=function(e){var t=s();return e.abs().dlShiftTo(this.m.t,t),t.divRemTo(this.m,null,t),e.s<0&&t.compareTo(i.ZERO)>0&&this.m.subTo(t,t),t},g.prototype.revert=function(e){var t=s();return e.copyTo(t),this.reduce(t),t},g.prototype.reduce=function(e){for(;e.t<=this.mt2;)e.data[e.t++]=0;for(var t=0;t>15)*this.mpl&this.um)<<15)&e.DM;for(r=t+this.m.t,e.data[r]+=this.m.am(0,a,e,t,0,this.m.t);e.data[r]>=e.DV;)e.data[r]-=e.DV,e.data[++r]++}e.clamp(),e.drShiftTo(this.m.t,e),e.compareTo(this.m)>=0&&e.subTo(this.m,e)},g.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r),this.reduce(r)},g.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)},i.prototype.copyTo=function(e){for(var t=this.t-1;t>=0;--t)e.data[t]=this.data[t];e.t=this.t,e.s=this.s},i.prototype.fromInt=function(e){this.t=1,this.s=e<0?-1:0,e>0?this.data[0]=e:e<-1?this.data[0]=e+this.DV:this.t=0},i.prototype.fromString=function(e,t){var r;if(16==t)r=4;else if(8==t)r=3;else if(256==t)r=8;else if(2==t)r=1;else if(32==t)r=5;else{if(4!=t)return void this.fromRadix(e,t);r=2}this.t=0,this.s=0;for(var a=e.length,n=!1,s=0;--a>=0;){var o=8==r?255&e[a]:f(e,a);o<0?"-"==e.charAt(a)&&(n=!0):(n=!1,0==s?this.data[this.t++]=o:s+r>this.DB?(this.data[this.t-1]|=(o&(1<>this.DB-s):this.data[this.t-1]|=o<=this.DB&&(s-=this.DB))}8==r&&0!=(128&e[0])&&(this.s=-1,s>0&&(this.data[this.t-1]|=(1<0&&this.data[this.t-1]==e;)--this.t},i.prototype.dlShiftTo=function(e,t){var r;for(r=this.t-1;r>=0;--r)t.data[r+e]=this.data[r];for(r=e-1;r>=0;--r)t.data[r]=0;t.t=this.t+e,t.s=this.s},i.prototype.drShiftTo=function(e,t){for(var r=e;r=0;--r)t.data[r+s+1]=this.data[r]>>n|o,o=(this.data[r]&i)<=0;--r)t.data[r]=0;t.data[s]=o,t.t=this.t+s+1,t.s=this.s,t.clamp()},i.prototype.rShiftTo=function(e,t){t.s=this.s;var r=Math.floor(e/this.DB);if(r>=this.t)t.t=0;else{var a=e%this.DB,n=this.DB-a,i=(1<>a;for(var s=r+1;s>a;a>0&&(t.data[this.t-r-1]|=(this.s&i)<>=this.DB;if(e.t>=this.DB;a+=this.s}else{for(a+=this.s;r>=this.DB;a-=e.s}t.s=a<0?-1:0,a<-1?t.data[r++]=this.DV+a:a>0&&(t.data[r++]=a),t.t=r,t.clamp()},i.prototype.multiplyTo=function(e,t){var r=this.abs(),a=e.abs(),n=r.t;for(t.t=n+a.t;--n>=0;)t.data[n]=0;for(n=0;n=0;)e.data[r]=0;for(r=0;r=t.DV&&(e.data[r+t.t]-=t.DV,e.data[r+t.t+1]=1)}e.t>0&&(e.data[e.t-1]+=t.am(r,t.data[r],e,2*r,0,1)),e.s=0,e.clamp()},i.prototype.divRemTo=function(e,t,r){var a=e.abs();if(!(a.t<=0)){var n=this.abs();if(n.t0?(a.lShiftTo(l,o),n.lShiftTo(l,r)):(a.copyTo(o),n.copyTo(r));var p=o.t,f=o.data[p-1];if(0!=f){var h=f*(1<1?o.data[p-2]>>this.F2:0),y=this.FV/h,g=(1<=0&&(r.data[r.t++]=1,r.subTo(E,r)),i.ONE.dlShiftTo(p,E),E.subTo(o,o);o.t=0;){var S=r.data[--m]==f?this.DM:Math.floor(r.data[m]*y+(r.data[m-1]+v)*g);if((r.data[m]+=o.am(0,S,r,C,0,p))0&&r.rShiftTo(l,r),c<0&&i.ZERO.subTo(r,r)}}},i.prototype.invDigit=function(){if(this.t<1)return 0;var e=this.data[0];if(0==(1&e))return 0;var t=3&e;return(t=(t=(t=(t=t*(2-(15&e)*t)&15)*(2-(255&e)*t)&255)*(2-((65535&e)*t&65535))&65535)*(2-e*t%this.DV)%this.DV)>0?this.DV-t:-t},i.prototype.isEven=function(){return 0==(this.t>0?1&this.data[0]:this.s)},i.prototype.exp=function(e,t){if(e>4294967295||e<1)return i.ONE;var r=s(),a=s(),n=t.convert(this),o=d(e)-1;for(n.copyTo(r);--o>=0;)if(t.sqrTo(r,a),(e&1<0)t.mulTo(a,n,r);else{var c=r;r=a,a=c}return t.revert(r)},i.prototype.toString=function(e){if(this.s<0)return"-"+this.negate().toString(e);var t;if(16==e)t=4;else if(8==e)t=3;else if(2==e)t=1;else if(32==e)t=5;else{if(4!=e)return this.toRadix(e);t=2}var r,a=(1<0)for(o>o)>0&&(n=!0,i=p(r));s>=0;)o>(o+=this.DB-t)):(r=this.data[s]>>(o-=t)&a,o<=0&&(o+=this.DB,--s)),r>0&&(n=!0),n&&(i+=p(r));return n?i:"0"},i.prototype.negate=function(){var e=s();return i.ZERO.subTo(this,e),e},i.prototype.abs=function(){return this.s<0?this.negate():this},i.prototype.compareTo=function(e){var t=this.s-e.s;if(0!=t)return t;var r=this.t;if(0!=(t=r-e.t))return this.s<0?-t:t;for(;--r>=0;)if(0!=(t=this.data[r]-e.data[r]))return t;return 0},i.prototype.bitLength=function(){return this.t<=0?0:this.DB*(this.t-1)+d(this.data[this.t-1]^this.s&this.DM)},i.prototype.mod=function(e){var t=s();return this.abs().divRemTo(e,null,t),this.s<0&&t.compareTo(i.ZERO)>0&&e.subTo(t,t),t},i.prototype.modPowInt=function(e,t){var r;return r=e<256||t.isEven()?new y(t):new g(t),this.exp(e,r)},i.ZERO=h(0),i.ONE=h(1),I.prototype.convert=A,I.prototype.revert=A,I.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r)},I.prototype.sqrTo=function(e,t){e.squareTo(t)},B.prototype.convert=function(e){if(e.s<0||e.t>2*this.m.t)return e.mod(this.m);if(e.compareTo(this.m)<0)return e;var t=s();return e.copyTo(t),this.reduce(t),t},B.prototype.revert=function(e){return e},B.prototype.reduce=function(e){for(e.drShiftTo(this.m.t-1,this.r2),e.t>this.m.t+1&&(e.t=this.m.t+1,e.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);e.compareTo(this.r2)<0;)e.dAddOffset(1,this.m.t+1);for(e.subTo(this.r2,e);e.compareTo(this.m)>=0;)e.subTo(this.m,e)},B.prototype.mulTo=function(e,t,r){e.multiplyTo(t,r),this.reduce(r)},B.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)};var b=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509],N=(1<<26)/b[b.length-1];i.prototype.chunkSize=function(e){return Math.floor(Math.LN2*this.DB/Math.log(e))},i.prototype.toRadix=function(e){if(null==e&&(e=10),0==this.signum()||e<2||e>36)return"0";var t=this.chunkSize(e),r=Math.pow(e,t),a=h(r),n=s(),i=s(),o="";for(this.divRemTo(a,n,i);n.signum()>0;)o=(r+i.intValue()).toString(e).substr(1)+o,n.divRemTo(a,n,i);return i.intValue().toString(e)+o},i.prototype.fromRadix=function(e,t){this.fromInt(0),null==t&&(t=10);for(var r=this.chunkSize(t),a=Math.pow(t,r),n=!1,s=0,o=0,c=0;c=r&&(this.dMultiply(a),this.dAddOffset(o,0),s=0,o=0))}s>0&&(this.dMultiply(Math.pow(t,s)),this.dAddOffset(o,0)),n&&i.ZERO.subTo(this,this)},i.prototype.fromNumber=function(e,t,r){if("number"==typeof t)if(e<2)this.fromInt(1);else for(this.fromNumber(e,r),this.testBit(e-1)||this.bitwiseTo(i.ONE.shiftLeft(e-1),m,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(t);)this.dAddOffset(2,0),this.bitLength()>e&&this.subTo(i.ONE.shiftLeft(e-1),this);else{var a=new Array,n=7&e;a.length=1+(e>>3),t.nextBytes(a),n>0?a[0]&=(1<>=this.DB;if(e.t>=this.DB;a+=this.s}else{for(a+=this.s;r>=this.DB;a+=e.s}t.s=a<0?-1:0,a>0?t.data[r++]=a:a<-1&&(t.data[r++]=this.DV+a),t.t=r,t.clamp()},i.prototype.dMultiply=function(e){this.data[this.t]=this.am(0,e-1,this,0,0,this.t),++this.t,this.clamp()},i.prototype.dAddOffset=function(e,t){if(0!=e){for(;this.t<=t;)this.data[this.t++]=0;for(this.data[t]+=e;this.data[t]>=this.DV;)this.data[t]-=this.DV,++t>=this.t&&(this.data[this.t++]=0),++this.data[t]}},i.prototype.multiplyLowerTo=function(e,t,r){var a,n=Math.min(this.t+e.t,t);for(r.s=0,r.t=n;n>0;)r.data[--n]=0;for(a=r.t-this.t;n=0;)r.data[a]=0;for(a=Math.max(t-this.t,0);a0)if(0==t)r=this.data[0]%e;else for(var a=this.t-1;a>=0;--a)r=(t*r+this.data[a])%e;return r},i.prototype.millerRabin=function(e){var t=this.subtract(i.ONE),r=t.getLowestSetBit();if(r<=0)return!1;for(var a,n=t.shiftRight(r),s={nextBytes:function(e){for(var t=0;t=0);var c=a.modPow(n,this);if(0!=c.compareTo(i.ONE)&&0!=c.compareTo(t)){for(var u=1;u++>24},i.prototype.shortValue=function(){return 0==this.t?this.s:this.data[0]<<16>>16},i.prototype.signum=function(){return this.s<0?-1:this.t<=0||1==this.t&&this.data[0]<=0?0:1},i.prototype.toByteArray=function(){var e=this.t,t=new Array;t[0]=this.s;var r,a=this.DB-e*this.DB%8,n=0;if(e-- >0)for(a>a)!=(this.s&this.DM)>>a&&(t[n++]=r|this.s<=0;)a<8?(r=(this.data[e]&(1<>(a+=this.DB-8)):(r=this.data[e]>>(a-=8)&255,a<=0&&(a+=this.DB,--e)),0!=(128&r)&&(r|=-256),0==n&&(128&this.s)!=(128&r)&&++n,(n>0||r!=this.s)&&(t[n++]=r);return t},i.prototype.equals=function(e){return 0==this.compareTo(e)},i.prototype.min=function(e){return this.compareTo(e)<0?this:e},i.prototype.max=function(e){return this.compareTo(e)>0?this:e},i.prototype.and=function(e){var t=s();return this.bitwiseTo(e,v,t),t},i.prototype.or=function(e){var t=s();return this.bitwiseTo(e,m,t),t},i.prototype.xor=function(e){var t=s();return this.bitwiseTo(e,C,t),t},i.prototype.andNot=function(e){var t=s();return this.bitwiseTo(e,E,t),t},i.prototype.not=function(){for(var e=s(),t=0;t=this.t?0!=this.s:0!=(this.data[t]&1<1){var p=s();for(a.sqrTo(o[1],p);c<=l;)o[c]=s(),a.mulTo(p,o[c-2],o[c]),c+=2}var f,v,m=e.t-1,C=!0,E=s();for(n=d(e.data[m])-1;m>=0;){for(n>=u?f=e.data[m]>>n-u&l:(f=(e.data[m]&(1<0&&(f|=e.data[m-1]>>this.DB+n-u)),c=r;0==(1&f);)f>>=1,--c;if((n-=c)<0&&(n+=this.DB,--m),C)o[f].copyTo(i),C=!1;else{for(;c>1;)a.sqrTo(i,E),a.sqrTo(E,i),c-=2;c>0?a.sqrTo(i,E):(v=i,i=E,E=v),a.mulTo(E,o[f],i)}for(;m>=0&&0==(e.data[m]&1<=0?(r.subTo(a,r),t&&n.subTo(o,n),s.subTo(c,s)):(a.subTo(r,a),t&&o.subTo(n,o),c.subTo(s,c))}return 0!=a.compareTo(i.ONE)?i.ZERO:c.compareTo(e)>=0?c.subtract(e):c.signum()<0?(c.addTo(e,c),c.signum()<0?c.add(e):c):c},i.prototype.pow=function(e){return this.exp(e,new I)},i.prototype.gcd=function(e){var t=this.s<0?this.negate():this.clone(),r=e.s<0?e.negate():e.clone();if(t.compareTo(r)<0){var a=t;t=r,r=a}var n=t.getLowestSetBit(),i=r.getLowestSetBit();if(i<0)return t;for(n0&&(t.rShiftTo(i,t),r.rShiftTo(i,r));t.signum()>0;)(n=t.getLowestSetBit())>0&&t.rShiftTo(n,t),(n=r.getLowestSetBit())>0&&r.rShiftTo(n,r),t.compareTo(r)>=0?(t.subTo(r,t),t.rShiftTo(1,t)):(r.subTo(t,r),r.rShiftTo(1,r));return i>0&&r.lShiftTo(i,r),r},i.prototype.isProbablePrime=function(e){var t,r=this.abs();if(1==r.t&&r.data[0]<=b[b.length-1]){for(t=0;t>>0,o>>>0];for(var c=n.fullMessageLength.length-1;c>=0;--c)n.fullMessageLength[c]+=o[1],o[1]=o[0]+(n.fullMessageLength[c]/4294967296>>>0),n.fullMessageLength[c]=n.fullMessageLength[c]>>>0,o[0]=o[1]/4294967296>>>0;return t.putBytes(i),l(e,r,t),(t.read>2048||0===t.length())&&t.compact(),n},n.digest=function(){var s=a.util.createBuffer();s.putBytes(t.bytes());var o=n.fullMessageLength[n.fullMessageLength.length-1]+n.messageLengthSize&n.blockLength-1;s.putBytes(i.substr(0,n.blockLength-o));for(var c,u=0,p=n.fullMessageLength.length-1;p>=0;--p)u=(c=8*n.fullMessageLength[p]+u)/4294967296>>>0,s.putInt32Le(c>>>0);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3};l(f,r,s);var h=a.util.createBuffer();return h.putInt32Le(f.h0),h.putInt32Le(f.h1),h.putInt32Le(f.h2),h.putInt32Le(f.h3),h},n};var i=null,s=null,o=null,c=null,u=!1;function l(e,t,r){for(var a,n,i,u,l,p,f,h=r.length();h>=64;){for(n=e.h0,i=e.h1,u=e.h2,l=e.h3,f=0;f<16;++f)t[f]=r.getInt32Le(),a=n+(l^i&(u^l))+c[f]+t[f],n=l,l=u,u=i,i+=a<<(p=o[f])|a>>>32-p;for(;f<32;++f)a=n+(u^l&(i^u))+c[f]+t[s[f]],n=l,l=u,u=i,i+=a<<(p=o[f])|a>>>32-p;for(;f<48;++f)a=n+(i^u^l)+c[f]+t[s[f]],n=l,l=u,u=i,i+=a<<(p=o[f])|a>>>32-p;for(;f<64;++f)a=n+(u^(i|~l))+c[f]+t[s[f]],n=l,l=u,u=i,i+=a<<(p=o[f])|a>>>32-p;e.h0=e.h0+n|0,e.h1=e.h1+i|0,e.h2=e.h2+u|0,e.h3=e.h3+l|0,h-=64}}},function(e,t,r){var a=r(0);r(8),r(4),r(1);var n,i=a.pkcs5=a.pkcs5||{};a.util.isNodejs&&!a.options.usePureJavaScript&&(n=r(16)),e.exports=a.pbkdf2=i.pbkdf2=function(e,t,r,i,s,o){if("function"==typeof s&&(o=s,s=null),a.util.isNodejs&&!a.options.usePureJavaScript&&n.pbkdf2&&(null===s||"object"!=typeof s)&&(n.pbkdf2Sync.length>4||!s||"sha1"===s))return"string"!=typeof s&&(s="sha1"),e=Buffer.from(e,"binary"),t=Buffer.from(t,"binary"),o?4===n.pbkdf2Sync.length?n.pbkdf2(e,t,r,i,(function(e,t){if(e)return o(e);o(null,t.toString("binary"))})):n.pbkdf2(e,t,r,i,s,(function(e,t){if(e)return o(e);o(null,t.toString("binary"))})):4===n.pbkdf2Sync.length?n.pbkdf2Sync(e,t,r,i).toString("binary"):n.pbkdf2Sync(e,t,r,i,s).toString("binary");if(null==s&&(s="sha1"),"string"==typeof s){if(!(s in a.md.algorithms))throw new Error("Unknown hash algorithm: "+s);s=a.md[s].create()}var c=s.digestLength;if(i>4294967295*c){var u=new Error("Derived key is too long.");if(o)return o(u);throw u}var l=Math.ceil(i/c),p=i-(l-1)*c,f=a.hmac.create();f.start(s,e);var h,d,y,g="";if(!o){for(var v=1;v<=l;++v){f.start(null,null),f.update(t),f.update(a.util.int32ToBytes(v)),h=y=f.digest().getBytes();for(var m=2;m<=r;++m)f.start(null,null),f.update(y),d=f.digest().getBytes(),h=a.util.xorBytes(h,d,c),y=d;g+=vl)return o(null,g);f.start(null,null),f.update(t),f.update(a.util.int32ToBytes(v)),h=y=f.digest().getBytes(),m=2,E()}function E(){if(m<=r)return f.start(null,null),f.update(y),d=f.digest().getBytes(),h=a.util.xorBytes(h,d,c),y=d,++m,a.util.setImmediate(E);g+=v128)throw new Error('Invalid "nsComment" content.');e.value=n.create(n.Class.UNIVERSAL,n.Type.IA5STRING,!1,e.comment)}else if("subjectKeyIdentifier"===e.name&&t.cert){var h=t.cert.generateSubjectKeyIdentifier();e.subjectKeyIdentifier=h.toHex(),e.value=n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,h.getBytes())}else if("authorityKeyIdentifier"===e.name&&t.cert){e.value=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]);l=e.value.value;if(e.keyIdentifier){var d=!0===e.keyIdentifier?t.cert.generateSubjectKeyIdentifier().getBytes():e.keyIdentifier;l.push(n.create(n.Class.CONTEXT_SPECIFIC,0,!1,d))}if(e.authorityCertIssuer){var y=[n.create(n.Class.CONTEXT_SPECIFIC,4,!0,[v(!0===e.authorityCertIssuer?t.cert.issuer:e.authorityCertIssuer)])];l.push(n.create(n.Class.CONTEXT_SPECIFIC,1,!0,y))}if(e.serialNumber){var g=a.util.hexToBytes(!0===e.serialNumber?t.cert.serialNumber:e.serialNumber);l.push(n.create(n.Class.CONTEXT_SPECIFIC,2,!1,g))}}else if("cRLDistributionPoints"===e.name){e.value=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]);l=e.value.value;var m,C=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]),E=n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[]);for(f=0;f2)throw new Error("Cannot read notBefore/notAfter validity times; more than two times were provided in the certificate.");if(p.length<2)throw new Error("Cannot read notBefore/notAfter validity times; they were not provided as either UTCTime or GeneralizedTime.");if(c.validity.notBefore=p[0],c.validity.notAfter=p[1],c.tbsCertificate=r.tbsCertificate,t){c.md=y({signatureOid:c.signatureOid,type:"certificate"});var f=n.toDer(c.tbsCertificate);c.md.update(f.getBytes())}var g=a.md.sha1.create(),v=n.toDer(r.certIssuer);g.update(v.getBytes()),c.issuer.getField=function(e){return h(c.issuer,e)},c.issuer.addField=function(e){m([e]),c.issuer.attributes.push(e)},c.issuer.attributes=i.RDNAttributesAsArray(r.certIssuer),r.certIssuerUniqueId&&(c.issuer.uniqueId=r.certIssuerUniqueId),c.issuer.hash=g.digest().toHex();var C=a.md.sha1.create(),E=n.toDer(r.certSubject);return C.update(E.getBytes()),c.subject.getField=function(e){return h(c.subject,e)},c.subject.addField=function(e){m([e]),c.subject.attributes.push(e)},c.subject.attributes=i.RDNAttributesAsArray(r.certSubject),r.certSubjectUniqueId&&(c.subject.uniqueId=r.certSubjectUniqueId),c.subject.hash=C.digest().toHex(),r.certExtensions?c.extensions=i.certificateExtensionsFromAsn1(r.certExtensions):c.extensions=[],c.publicKey=i.publicKeyFromAsn1(r.subjectPublicKeyInfo),c},i.certificateExtensionsFromAsn1=function(e){for(var t=[],r=0;r1&&(r=c.value.charCodeAt(1),i=c.value.length>2?c.value.charCodeAt(2):0),t.digitalSignature=128==(128&r),t.nonRepudiation=64==(64&r),t.keyEncipherment=32==(32&r),t.dataEncipherment=16==(16&r),t.keyAgreement=8==(8&r),t.keyCertSign=4==(4&r),t.cRLSign=2==(2&r),t.encipherOnly=1==(1&r),t.decipherOnly=128==(128&i)}else if("basicConstraints"===t.name){(c=n.fromDer(t.value)).value.length>0&&c.value[0].type===n.Type.BOOLEAN?t.cA=0!==c.value[0].value.charCodeAt(0):t.cA=!1;var o=null;c.value.length>0&&c.value[0].type===n.Type.INTEGER?o=c.value[0].value:c.value.length>1&&(o=c.value[1].value),null!==o&&(t.pathLenConstraint=n.derToInteger(o))}else if("extKeyUsage"===t.name)for(var c=n.fromDer(t.value),u=0;u1&&(r=c.value.charCodeAt(1)),t.client=128==(128&r),t.server=64==(64&r),t.email=32==(32&r),t.objsign=16==(16&r),t.reserved=8==(8&r),t.sslCA=4==(4&r),t.emailCA=2==(2&r),t.objCA=1==(1&r)}else if("subjectAltName"===t.name||"issuerAltName"===t.name){var p;t.altNames=[];c=n.fromDer(t.value);for(var f=0;f=T&&e0&&s.value.push(i.certificateExtensionsToAsn1(e.extensions)),s},i.getCertificationRequestInfo=function(e){return n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(e.version).getBytes()),v(e.subject),i.publicKeyToAsn1(e.publicKey),S(e)])},i.distinguishedNameToAsn1=function(e){return v(e)},i.certificateToAsn1=function(e){var t=e.tbsCertificate||i.getTBSCertificate(e);return n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[t,n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.signatureOid).getBytes()),E(e.signatureOid,e.signatureParameters)]),n.create(n.Class.UNIVERSAL,n.Type.BITSTRING,!1,String.fromCharCode(0)+e.signature)])},i.certificateExtensionsToAsn1=function(e){var t=n.create(n.Class.CONTEXT_SPECIFIC,3,!0,[]),r=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[]);t.value.push(r);for(var a=0;al.validity.notAfter)&&(c={message:"Certificate is not valid yet or has expired.",error:i.certificateError.certificate_expired,notBefore:l.validity.notBefore,notAfter:l.validity.notAfter,now:s}),null===c){if(null===(p=t[0]||e.getIssuer(l))&&l.isIssuer(l)&&(f=!0,p=l),p){var h=p;a.util.isArray(h)||(h=[h]);for(var d=!1;!d&&h.length>0;){p=h.shift();try{d=p.verify(l)}catch(e){}}d||(c={message:"Certificate signature is invalid.",error:i.certificateError.bad_certificate})}null!==c||p&&!f||e.hasCertificate(l)||(c={message:"Certificate is not trusted.",error:i.certificateError.unknown_ca})}if(null===c&&p&&!l.isIssuer(p)&&(c={message:"Certificate issuer is invalid.",error:i.certificateError.bad_certificate}),null===c)for(var y={keyUsage:!0,basicConstraints:!0},g=0;null===c&&gm.pathLenConstraint&&(c={message:"Certificate basicConstraints pathLenConstraint violated.",error:i.certificateError.bad_certificate})}var E=null===c||c.error,S=r.verify?r.verify(E,u,n):E;if(!0!==S)throw!0===E&&(c={message:"The application rejected the certificate.",error:i.certificateError.bad_certificate}),(S||0===S)&&("object"!=typeof S||a.util.isArray(S)?"string"==typeof S&&(c.error=S):(S.message&&(c.message=S.message),S.error&&(c.error=S.error))),c;c=null,o=!1,++u}while(t.length>0);return!0}},function(e,t,r){var a=r(0);r(2),r(1),(e.exports=a.pss=a.pss||{}).create=function(e){3===arguments.length&&(e={md:arguments[0],mgf:arguments[1],saltLength:arguments[2]});var t,r=e.md,n=e.mgf,i=r.digestLength,s=e.salt||null;if("string"==typeof s&&(s=a.util.createBuffer(s)),"saltLength"in e)t=e.saltLength;else{if(null===s)throw new Error("Salt length not specified or specific salt not given.");t=s.length()}if(null!==s&&s.length()!==t)throw new Error("Given salt length does not match length of given salt.");var o=e.prng||a.random,c={encode:function(e,c){var u,l,p=c-1,f=Math.ceil(p/8),h=e.digest().getBytes();if(f>8*f-p&255;return(E=String.fromCharCode(E.charCodeAt(0)&~S)+E.substr(1))+y+String.fromCharCode(188)},verify:function(e,s,o){var c,u=o-1,l=Math.ceil(u/8);if(s=s.substr(-l),l>8*l-u&255;if(0!=(f.charCodeAt(0)&d))throw new Error("Bits beyond keysize not zero as expected.");var y=n.generate(h,p),g="";for(c=0;c4){var r=e;e=a.util.createBuffer();for(var n=0;n0))return!0;for(var a=0;a0))return!0;for(var a=0;a0)return!1;var r=e.length(),a=e.at(r-1);return!(a>this.blockSize<<2)&&(e.truncate(a),!0)},n.cbc=function(e){e=e||{},this.name="CBC",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints)},n.cbc.prototype.start=function(e){if(null===e.iv){if(!this._prev)throw new Error("Invalid IV parameter.");this._iv=this._prev.slice(0)}else{if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._prev=this._iv.slice(0)}},n.cbc.prototype.encrypt=function(e,t,r){if(e.length()0))return!0;for(var a=0;a0))return!0;for(var a=0;a0)return!1;var r=e.length(),a=e.at(r-1);return!(a>this.blockSize<<2)&&(e.truncate(a),!0)},n.cfb=function(e){e=e||{},this.name="CFB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialBlock=new Array(this._ints),this._partialOutput=a.util.createBuffer(),this._partialBytes=0},n.cfb.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0},n.cfb.prototype.encrypt=function(e,t,r){var a=e.length();if(0===a)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize)for(var n=0;n0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n0)e.read-=this.blockSize;else for(n=0;n0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}},n.cfb.prototype.decrypt=function(e,t,r){var a=e.length();if(0===a)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize)for(var n=0;n0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n0)e.read-=this.blockSize;else for(n=0;n0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}},n.ofb=function(e){e=e||{},this.name="OFB",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=a.util.createBuffer(),this._partialBytes=0},n.ofb.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0},n.ofb.prototype.encrypt=function(e,t,r){var a=e.length();if(0===e.length())return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize)for(var n=0;n0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n0)e.read-=this.blockSize;else for(n=0;n0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}},n.ofb.prototype.decrypt=n.ofb.prototype.encrypt,n.ctr=function(e){e=e||{},this.name="CTR",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=a.util.createBuffer(),this._partialBytes=0},n.ctr.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");this._iv=i(e.iv,this.blockSize),this._inBlock=this._iv.slice(0),this._partialBytes=0},n.ctr.prototype.encrypt=function(e,t,r){var a=e.length();if(0===a)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize)for(var n=0;n0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n0&&(e.read-=this.blockSize),this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}s(this._inBlock)},n.ctr.prototype.decrypt=n.ctr.prototype.encrypt,n.gcm=function(e){e=e||{},this.name="GCM",this.cipher=e.cipher,this.blockSize=e.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints),this._partialOutput=a.util.createBuffer(),this._partialBytes=0,this._R=3774873600},n.gcm.prototype.start=function(e){if(!("iv"in e))throw new Error("Invalid IV parameter.");var t,r=a.util.createBuffer(e.iv);if(this._cipherLength=0,t="additionalData"in e?a.util.createBuffer(e.additionalData):a.util.createBuffer(),this._tagLength="tagLength"in e?e.tagLength:128,this._tag=null,e.decrypt&&(this._tag=a.util.createBuffer(e.tag).getBytes(),this._tag.length!==this._tagLength/8))throw new Error("Authentication tag does not match tag length.");this._hashBlock=new Array(this._ints),this.tag=null,this._hashSubkey=new Array(this._ints),this.cipher.encrypt([0,0,0,0],this._hashSubkey),this.componentBits=4,this._m=this.generateHashTable(this._hashSubkey,this.componentBits);var n=r.length();if(12===n)this._j0=[r.getInt32(),r.getInt32(),r.getInt32(),1];else{for(this._j0=[0,0,0,0];r.length()>0;)this._j0=this.ghash(this._hashSubkey,this._j0,[r.getInt32(),r.getInt32(),r.getInt32(),r.getInt32()]);this._j0=this.ghash(this._hashSubkey,this._j0,[0,0].concat(o(8*n)))}this._inBlock=this._j0.slice(0),s(this._inBlock),this._partialBytes=0,t=a.util.createBuffer(t),this._aDataLength=o(8*t.length());var i=t.length()%this.blockSize;for(i&&t.fillWithByte(0,this.blockSize-i),this._s=[0,0,0,0];t.length()>0;)this._s=this.ghash(this._hashSubkey,this._s,[t.getInt32(),t.getInt32(),t.getInt32(),t.getInt32()])},n.gcm.prototype.encrypt=function(e,t,r){var a=e.length();if(0===a)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&a>=this.blockSize){for(var n=0;n0&&(i=this.blockSize-i),this._partialOutput.clear();for(n=0;n0&&this._partialOutput.getBytes(this._partialBytes),i>0&&!r)return e.read-=this.blockSize,t.putBytes(this._partialOutput.getBytes(i-this._partialBytes)),this._partialBytes=i,!0;t.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=0}this._s=this.ghash(this._hashSubkey,this._s,this._outBlock),s(this._inBlock)},n.gcm.prototype.decrypt=function(e,t,r){var a=e.length();if(a0))return!0;this.cipher.encrypt(this._inBlock,this._outBlock),s(this._inBlock),this._hashBlock[0]=e.getInt32(),this._hashBlock[1]=e.getInt32(),this._hashBlock[2]=e.getInt32(),this._hashBlock[3]=e.getInt32(),this._s=this.ghash(this._hashSubkey,this._s,this._hashBlock);for(var n=0;n0;--a)t[a]=e[a]>>>1|(1&e[a-1])<<31;t[0]=e[0]>>>1,r&&(t[0]^=this._R)},n.gcm.prototype.tableMultiply=function(e){for(var t=[0,0,0,0],r=0;r<32;++r){var a=e[r/8|0]>>>4*(7-r%8)&15,n=this._m[r][a];t[0]^=n[0],t[1]^=n[1],t[2]^=n[2],t[3]^=n[3]}return t},n.gcm.prototype.ghash=function(e,t,r){return t[0]^=r[0],t[1]^=r[1],t[2]^=r[2],t[3]^=r[3],this.tableMultiply(t)},n.gcm.prototype.generateHashTable=function(e,t){for(var r=8/t,a=4*r,n=16*r,i=new Array(n),s=0;s>>1,n=new Array(r);n[a]=e.slice(0);for(var i=a>>>1;i>0;)this.pow(n[2*i],n[i]=[]),i>>=1;for(i=2;i>1,o=s+(1&e.length),c=e.substr(0,o),u=e.substr(s,o),l=a.util.createBuffer(),p=a.hmac.create();r=t+r;var f=Math.ceil(n/16),h=Math.ceil(n/20);p.start("MD5",c);var d=a.util.createBuffer();l.putBytes(r);for(var y=0;y0&&(u.queue(e,u.createAlert(e,{level:u.Alert.Level.warning,description:u.Alert.Description.no_renegotiation})),u.flush(e)),e.process()},u.parseHelloMessage=function(e,t,r){var n=null,i=e.entity===u.ConnectionEnd.client;if(r<38)e.error(e,{message:i?"Invalid ServerHello message. Message too short.":"Invalid ClientHello message. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});else{var s=t.fragment,c=s.length();if(n={version:{major:s.getByte(),minor:s.getByte()},random:a.util.createBuffer(s.getBytes(32)),session_id:o(s,1),extensions:[]},i?(n.cipher_suite=s.getBytes(2),n.compression_method=s.getByte()):(n.cipher_suites=o(s,2),n.compression_methods=o(s,1)),(c=r-(c-s.length()))>0){for(var l=o(s,2);l.length()>0;)n.extensions.push({type:[l.getByte(),l.getByte()],data:o(l,2)});if(!i)for(var p=0;p0;){if(0!==h.getByte())break;e.session.extensions.server_name.serverNameList.push(o(h,2).getBytes())}}}if(e.session.version&&(n.version.major!==e.session.version.major||n.version.minor!==e.session.version.minor))return e.error(e,{message:"TLS version change is disallowed during renegotiation.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.protocol_version}});if(i)e.session.cipherSuite=u.getCipherSuite(n.cipher_suite);else for(var d=a.util.createBuffer(n.cipher_suites.bytes());d.length()>0&&(e.session.cipherSuite=u.getCipherSuite(d.getBytes(2)),null===e.session.cipherSuite););if(null===e.session.cipherSuite)return e.error(e,{message:"No cipher suites in common.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.handshake_failure},cipherSuite:a.util.bytesToHex(n.cipher_suite)});e.session.compressionMethod=i?n.compression_method:u.CompressionMethod.none}return n},u.createSecurityParameters=function(e,t){var r=e.entity===u.ConnectionEnd.client,a=t.random.bytes(),n=r?e.session.sp.client_random:a,i=r?a:u.createRandom().getBytes();e.session.sp={entity:e.entity,prf_algorithm:u.PRFAlgorithm.tls_prf_sha256,bulk_cipher_algorithm:null,cipher_type:null,enc_key_length:null,block_length:null,fixed_iv_length:null,record_iv_length:null,mac_algorithm:null,mac_length:null,mac_key_length:null,compression_algorithm:e.session.compressionMethod,pre_master_secret:null,master_secret:null,client_random:n,server_random:i}},u.handleServerHello=function(e,t,r){var a=u.parseHelloMessage(e,t,r);if(!e.fail){if(!(a.version.minor<=e.version.minor))return e.error(e,{message:"Incompatible TLS version.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.protocol_version}});e.version.minor=a.version.minor,e.session.version=e.version;var n=a.session_id.bytes();n.length>0&&n===e.session.id?(e.expect=d,e.session.resuming=!0,e.session.sp.server_random=a.random.bytes()):(e.expect=l,e.session.resuming=!1,u.createSecurityParameters(e,a)),e.session.id=n,e.process()}},u.handleClientHello=function(e,t,r){var n=u.parseHelloMessage(e,t,r);if(!e.fail){var i=n.session_id.bytes(),s=null;if(e.sessionCache&&(null===(s=e.sessionCache.getSession(i))?i="":(s.version.major!==n.version.major||s.version.minor>n.version.minor)&&(s=null,i="")),0===i.length&&(i=a.random.getBytes(32)),e.session.id=i,e.session.clientHelloVersion=n.version,e.session.sp={},s)e.version=e.session.version=s.version,e.session.sp=s.sp;else{for(var o,c=1;c0;)n=o(c.certificate_list,3),i=a.asn1.fromDer(n),n=a.pki.certificateFromAsn1(i,!0),l.push(n)}catch(t){return e.error(e,{message:"Could not parse certificate list.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.bad_certificate}})}var f=e.entity===u.ConnectionEnd.client;!f&&!0!==e.verifyClient||0!==l.length?0===l.length?e.expect=f?p:C:(f?e.session.serverCertificate=l[0]:e.session.clientCertificate=l[0],u.verifyCertificateChain(e,l)&&(e.expect=f?p:C)):e.error(e,{message:f?"No server certificate provided.":"No client certificate provided.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}}),e.process()},u.handleServerKeyExchange=function(e,t,r){if(r>0)return e.error(e,{message:"Invalid key parameters. Only RSA is supported.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unsupported_certificate}});e.expect=f,e.process()},u.handleClientKeyExchange=function(e,t,r){if(r<48)return e.error(e,{message:"Invalid key parameters. Only RSA is supported.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.unsupported_certificate}});var n=t.fragment,i={enc_pre_master_secret:o(n,2).getBytes()},s=null;if(e.getPrivateKey)try{s=e.getPrivateKey(e,e.session.serverCertificate),s=a.pki.privateKeyFromPem(s)}catch(t){e.error(e,{message:"Could not get private key.",cause:t,send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}})}if(null===s)return e.error(e,{message:"No private key set.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}});try{var c=e.session.sp;c.pre_master_secret=s.decrypt(i.enc_pre_master_secret);var l=e.session.clientHelloVersion;if(l.major!==c.pre_master_secret.charCodeAt(0)||l.minor!==c.pre_master_secret.charCodeAt(1))throw new Error("TLS version rollback attack detected.")}catch(e){c.pre_master_secret=a.random.getBytes(48)}e.expect=S,null!==e.session.clientCertificate&&(e.expect=E),e.process()},u.handleCertificateRequest=function(e,t,r){if(r<3)return e.error(e,{message:"Invalid CertificateRequest. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var a=t.fragment,n={certificate_types:o(a,1),certificate_authorities:o(a,2)};e.session.certificateRequest=n,e.expect=h,e.process()},u.handleCertificateVerify=function(e,t,r){if(r<2)return e.error(e,{message:"Invalid CertificateVerify. Message too short.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var n=t.fragment;n.read-=4;var i=n.bytes();n.read+=4;var s={signature:o(n,2).getBytes()},c=a.util.createBuffer();c.putBuffer(e.session.md5.digest()),c.putBuffer(e.session.sha1.digest()),c=c.getBytes();try{if(!e.session.clientCertificate.publicKey.verify(c,s.signature,"NONE"))throw new Error("CertificateVerify signature does not match.");e.session.md5.update(i),e.session.sha1.update(i)}catch(t){return e.error(e,{message:"Bad signature in CertificateVerify.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.handshake_failure}})}e.expect=S,e.process()},u.handleServerHelloDone=function(e,t,r){if(r>0)return e.error(e,{message:"Invalid ServerHelloDone message. Invalid length.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.record_overflow}});if(null===e.serverCertificate){var n={message:"No server certificate provided. Not enough security.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.insufficient_security}},i=e.verify(e,n.alert.description,0,[]);if(!0!==i)return(i||0===i)&&("object"!=typeof i||a.util.isArray(i)?"number"==typeof i&&(n.alert.description=i):(i.message&&(n.message=i.message),i.alert&&(n.alert.description=i.alert))),e.error(e,n)}null!==e.session.certificateRequest&&(t=u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificate(e)}),u.queue(e,t)),t=u.createRecord(e,{type:u.ContentType.handshake,data:u.createClientKeyExchange(e)}),u.queue(e,t),e.expect=v;var s=function(e,t){null!==e.session.certificateRequest&&null!==e.session.clientCertificate&&u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createCertificateVerify(e,t)})),u.queue(e,u.createRecord(e,{type:u.ContentType.change_cipher_spec,data:u.createChangeCipherSpec()})),e.state.pending=u.createConnectionState(e),e.state.current.write=e.state.pending.write,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createFinished(e)})),e.expect=d,u.flush(e),e.process()};if(null===e.session.certificateRequest||null===e.session.clientCertificate)return s(e,null);u.getClientSignature(e,s)},u.handleChangeCipherSpec=function(e,t){if(1!==t.fragment.getByte())return e.error(e,{message:"Invalid ChangeCipherSpec message received.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.illegal_parameter}});var r=e.entity===u.ConnectionEnd.client;(e.session.resuming&&r||!e.session.resuming&&!r)&&(e.state.pending=u.createConnectionState(e)),e.state.current.read=e.state.pending.read,(!e.session.resuming&&r||e.session.resuming&&!r)&&(e.state.pending=null),e.expect=r?y:T,e.process()},u.handleFinished=function(e,t,r){var i=t.fragment;i.read-=4;var s=i.bytes();i.read+=4;var o=t.fragment.getBytes();(i=a.util.createBuffer()).putBuffer(e.session.md5.digest()),i.putBuffer(e.session.sha1.digest());var c=e.entity===u.ConnectionEnd.client,l=c?"server finished":"client finished",p=e.session.sp;if((i=n(p.master_secret,l,i.getBytes(),12)).getBytes()!==o)return e.error(e,{message:"Invalid verify_data in Finished message.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.decrypt_error}});e.session.md5.update(s),e.session.sha1.update(s),(e.session.resuming&&c||!e.session.resuming&&!c)&&(u.queue(e,u.createRecord(e,{type:u.ContentType.change_cipher_spec,data:u.createChangeCipherSpec()})),e.state.current.write=e.state.pending.write,e.state.pending=null,u.queue(e,u.createRecord(e,{type:u.ContentType.handshake,data:u.createFinished(e)}))),e.expect=c?g:I,e.handshaking=!1,++e.handshakes,e.peerCertificate=c?e.session.serverCertificate:e.session.clientCertificate,u.flush(e),e.isConnected=!0,e.connected(e),e.process()},u.handleAlert=function(e,t){var r,a=t.fragment,n={level:a.getByte(),description:a.getByte()};switch(n.description){case u.Alert.Description.close_notify:r="Connection closed.";break;case u.Alert.Description.unexpected_message:r="Unexpected message.";break;case u.Alert.Description.bad_record_mac:r="Bad record MAC.";break;case u.Alert.Description.decryption_failed:r="Decryption failed.";break;case u.Alert.Description.record_overflow:r="Record overflow.";break;case u.Alert.Description.decompression_failure:r="Decompression failed.";break;case u.Alert.Description.handshake_failure:r="Handshake failure.";break;case u.Alert.Description.bad_certificate:r="Bad certificate.";break;case u.Alert.Description.unsupported_certificate:r="Unsupported certificate.";break;case u.Alert.Description.certificate_revoked:r="Certificate revoked.";break;case u.Alert.Description.certificate_expired:r="Certificate expired.";break;case u.Alert.Description.certificate_unknown:r="Certificate unknown.";break;case u.Alert.Description.illegal_parameter:r="Illegal parameter.";break;case u.Alert.Description.unknown_ca:r="Unknown certificate authority.";break;case u.Alert.Description.access_denied:r="Access denied.";break;case u.Alert.Description.decode_error:r="Decode error.";break;case u.Alert.Description.decrypt_error:r="Decrypt error.";break;case u.Alert.Description.export_restriction:r="Export restriction.";break;case u.Alert.Description.protocol_version:r="Unsupported protocol version.";break;case u.Alert.Description.insufficient_security:r="Insufficient security.";break;case u.Alert.Description.internal_error:r="Internal error.";break;case u.Alert.Description.user_canceled:r="User canceled.";break;case u.Alert.Description.no_renegotiation:r="Renegotiation not supported.";break;default:r="Unknown error."}if(n.description===u.Alert.Description.close_notify)return e.close();e.error(e,{message:r,send:!1,origin:e.entity===u.ConnectionEnd.client?"server":"client",alert:n}),e.process()},u.handleHandshake=function(e,t){var r=t.fragment,n=r.getByte(),i=r.getInt24();if(i>r.length())return e.fragmented=t,t.fragment=a.util.createBuffer(),r.read-=4,e.process();e.fragmented=null,r.read-=4;var s=r.bytes(i+4);r.read+=4,n in K[e.entity][e.expect]?(e.entity!==u.ConnectionEnd.server||e.open||e.fail||(e.handshaking=!0,e.session={version:null,extensions:{server_name:{serverNameList:[]}},cipherSuite:null,compressionMethod:null,serverCertificate:null,clientCertificate:null,md5:a.md.md5.create(),sha1:a.md.sha1.create()}),n!==u.HandshakeType.hello_request&&n!==u.HandshakeType.certificate_verify&&n!==u.HandshakeType.finished&&(e.session.md5.update(s),e.session.sha1.update(s)),K[e.entity][e.expect][n](e,t,i)):u.handleUnexpected(e,t)},u.handleApplicationData=function(e,t){e.data.putBuffer(t.fragment),e.dataReady(e),e.process()},u.handleHeartbeat=function(e,t){var r=t.fragment,n=r.getByte(),i=r.getInt16(),s=r.getBytes(i);if(n===u.HeartbeatMessageType.heartbeat_request){if(e.handshaking||i>s.length)return e.process();u.queue(e,u.createRecord(e,{type:u.ContentType.heartbeat,data:u.createHeartbeat(u.HeartbeatMessageType.heartbeat_response,s)})),u.flush(e)}else if(n===u.HeartbeatMessageType.heartbeat_response){if(s!==e.expectedHeartbeatPayload)return e.process();e.heartbeatReceived&&e.heartbeatReceived(e,a.util.createBuffer(s))}e.process()};var l=1,p=2,f=3,h=4,d=5,y=6,g=7,v=8,m=1,C=2,E=3,S=4,T=5,I=6,A=u.handleUnexpected,B=u.handleChangeCipherSpec,b=u.handleAlert,N=u.handleHandshake,R=u.handleApplicationData,w=u.handleHeartbeat,_=[];_[u.ConnectionEnd.client]=[[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[B,b,A,A,w],[A,b,N,A,w],[A,b,N,R,w],[A,b,N,A,w]],_[u.ConnectionEnd.server]=[[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[A,b,N,A,w],[B,b,A,A,w],[A,b,N,A,w],[A,b,N,R,w],[A,b,N,A,w]];var L=u.handleHelloRequest,k=u.handleServerHello,U=u.handleCertificate,D=u.handleServerKeyExchange,P=u.handleCertificateRequest,V=u.handleServerHelloDone,O=u.handleFinished,K=[];K[u.ConnectionEnd.client]=[[A,A,k,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[L,A,A,A,A,A,A,A,A,A,A,U,D,P,V,A,A,A,A,A,A],[L,A,A,A,A,A,A,A,A,A,A,A,D,P,V,A,A,A,A,A,A],[L,A,A,A,A,A,A,A,A,A,A,A,A,P,V,A,A,A,A,A,A],[L,A,A,A,A,A,A,A,A,A,A,A,A,A,V,A,A,A,A,A,A],[L,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[L,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,O],[L,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[L,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A]];var x=u.handleClientHello,M=u.handleClientKeyExchange,F=u.handleCertificateVerify;K[u.ConnectionEnd.server]=[[A,x,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,U,A,A,A,A,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,M,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,F,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,O],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A],[A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A]],u.generateKeys=function(e,t){var r=n,a=t.client_random+t.server_random;e.session.resuming||(t.master_secret=r(t.pre_master_secret,"master secret",a,48).bytes(),t.pre_master_secret=null),a=t.server_random+t.client_random;var i=2*t.mac_key_length+2*t.enc_key_length,s=e.version.major===u.Versions.TLS_1_0.major&&e.version.minor===u.Versions.TLS_1_0.minor;s&&(i+=2*t.fixed_iv_length);var o=r(t.master_secret,"key expansion",a,i),c={client_write_MAC_key:o.getBytes(t.mac_key_length),server_write_MAC_key:o.getBytes(t.mac_key_length),client_write_key:o.getBytes(t.enc_key_length),server_write_key:o.getBytes(t.enc_key_length)};return s&&(c.client_write_IV=o.getBytes(t.fixed_iv_length),c.server_write_IV=o.getBytes(t.fixed_iv_length)),c},u.createConnectionState=function(e){var t=e.entity===u.ConnectionEnd.client,r=function(){var e={sequenceNumber:[0,0],macKey:null,macLength:0,macFunction:null,cipherState:null,cipherFunction:function(e){return!0},compressionState:null,compressFunction:function(e){return!0},updateSequenceNumber:function(){4294967295===e.sequenceNumber[1]?(e.sequenceNumber[1]=0,++e.sequenceNumber[0]):++e.sequenceNumber[1]}};return e},a={read:r(),write:r()};if(a.read.update=function(e,t){return a.read.cipherFunction(t,a.read)?a.read.compressFunction(e,t,a.read)||e.error(e,{message:"Could not decompress record.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.decompression_failure}}):e.error(e,{message:"Could not decrypt record or bad MAC.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.bad_record_mac}}),!e.fail},a.write.update=function(e,t){return a.write.compressFunction(e,t,a.write)?a.write.cipherFunction(t,a.write)||e.error(e,{message:"Could not encrypt record.",send:!1,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}}):e.error(e,{message:"Could not compress record.",send:!1,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.internal_error}}),!e.fail},e.session){var n=e.session.sp;switch(e.session.cipherSuite.initSecurityParameters(n),n.keys=u.generateKeys(e,n),a.read.macKey=t?n.keys.server_write_MAC_key:n.keys.client_write_MAC_key,a.write.macKey=t?n.keys.client_write_MAC_key:n.keys.server_write_MAC_key,e.session.cipherSuite.initConnectionState(a,e,n),n.compression_algorithm){case u.CompressionMethod.none:break;case u.CompressionMethod.deflate:a.read.compressFunction=s,a.write.compressFunction=i;break;default:throw new Error("Unsupported compression algorithm.")}}return a},u.createRandom=function(){var e=new Date,t=+e+6e4*e.getTimezoneOffset(),r=a.util.createBuffer();return r.putInt32(t),r.putBytes(a.random.getBytes(28)),r},u.createRecord=function(e,t){return t.data?{type:t.type,version:{major:e.version.major,minor:e.version.minor},length:t.data.length(),fragment:t.data}:null},u.createAlert=function(e,t){var r=a.util.createBuffer();return r.putByte(t.level),r.putByte(t.description),u.createRecord(e,{type:u.ContentType.alert,data:r})},u.createClientHello=function(e){e.session.clientHelloVersion={major:e.version.major,minor:e.version.minor};for(var t=a.util.createBuffer(),r=0;r0&&(d+=2);var y=e.session.id,g=y.length+1+2+4+28+2+i+1+o+d,v=a.util.createBuffer();return v.putByte(u.HandshakeType.client_hello),v.putInt24(g),v.putByte(e.version.major),v.putByte(e.version.minor),v.putBytes(e.session.sp.client_random),c(v,1,a.util.createBuffer(y)),c(v,2,t),c(v,1,s),d>0&&c(v,2,l),v},u.createServerHello=function(e){var t=e.session.id,r=t.length+1+2+4+28+2+1,n=a.util.createBuffer();return n.putByte(u.HandshakeType.server_hello),n.putInt24(r),n.putByte(e.version.major),n.putByte(e.version.minor),n.putBytes(e.session.sp.server_random),c(n,1,a.util.createBuffer(t)),n.putByte(e.session.cipherSuite.id[0]),n.putByte(e.session.cipherSuite.id[1]),n.putByte(e.session.compressionMethod),n},u.createCertificate=function(e){var t,r=e.entity===u.ConnectionEnd.client,n=null;e.getCertificate&&(t=r?e.session.certificateRequest:e.session.extensions.server_name.serverNameList,n=e.getCertificate(e,t));var i=a.util.createBuffer();if(null!==n)try{a.util.isArray(n)||(n=[n]);for(var s=null,o=0;ou.MaxFragment;)n.push(u.createRecord(e,{type:t.type,data:a.util.createBuffer(i.slice(0,u.MaxFragment))})),i=i.slice(u.MaxFragment);i.length>0&&n.push(u.createRecord(e,{type:t.type,data:a.util.createBuffer(i)}))}for(var s=0;s0&&(n=r.order[0]),null!==n&&n in r.cache)for(var i in t=r.cache[n],delete r.cache[n],r.order)if(r.order[i]===n){r.order.splice(i,1);break}return t},r.setSession=function(e,t){if(r.order.length===r.capacity){var n=r.order.shift();delete r.cache[n]}n=a.util.bytesToHex(e);r.order.push(n),r.cache[n]=t}}return r},u.createConnection=function(e){var t=null;t=e.caStore?a.util.isArray(e.caStore)?a.pki.createCaStore(e.caStore):e.caStore:a.pki.createCaStore();var r=e.cipherSuites||null;if(null===r)for(var n in r=[],u.CipherSuites)r.push(u.CipherSuites[n]);var i=e.server?u.ConnectionEnd.server:u.ConnectionEnd.client,s=e.sessionCache?u.createSessionCache(e.sessionCache):null,o={version:{major:u.Version.major,minor:u.Version.minor},entity:i,sessionId:e.sessionId,caStore:t,sessionCache:s,cipherSuites:r,connected:e.connected,virtualHost:e.virtualHost||null,verifyClient:e.verifyClient||!1,verify:e.verify||function(e,t,r,a){return t},verifyOptions:e.verifyOptions||{},getCertificate:e.getCertificate||null,getPrivateKey:e.getPrivateKey||null,getSignature:e.getSignature||null,input:a.util.createBuffer(),tlsData:a.util.createBuffer(),data:a.util.createBuffer(),tlsDataReady:e.tlsDataReady,dataReady:e.dataReady,heartbeatReceived:e.heartbeatReceived,closed:e.closed,error:function(t,r){r.origin=r.origin||(t.entity===u.ConnectionEnd.client?"client":"server"),r.send&&(u.queue(t,u.createAlert(t,r.alert)),u.flush(t));var a=!1!==r.fatal;a&&(t.fail=!0),e.error(t,r),a&&t.close(!1)},deflate:e.deflate||null,inflate:e.inflate||null,reset:function(e){o.version={major:u.Version.major,minor:u.Version.minor},o.record=null,o.session=null,o.peerCertificate=null,o.state={pending:null,current:null},o.expect=(o.entity,u.ConnectionEnd.client,0),o.fragmented=null,o.records=[],o.open=!1,o.handshakes=0,o.handshaking=!1,o.isConnected=!1,o.fail=!(e||void 0===e),o.input.clear(),o.tlsData.clear(),o.data.clear(),o.state.current=u.createConnectionState(o)}};o.reset();return o.handshake=function(e){if(o.entity!==u.ConnectionEnd.client)o.error(o,{message:"Cannot initiate handshake as a server.",fatal:!1});else if(o.handshaking)o.error(o,{message:"Handshake already in progress.",fatal:!1});else{o.fail&&!o.open&&0===o.handshakes&&(o.fail=!1),o.handshaking=!0;var t=null;(e=e||"").length>0&&(o.sessionCache&&(t=o.sessionCache.getSession(e)),null===t&&(e="")),0===e.length&&o.sessionCache&&null!==(t=o.sessionCache.getSession())&&(e=t.id),o.session={id:e,version:null,cipherSuite:null,compressionMethod:null,serverCertificate:null,certificateRequest:null,clientCertificate:null,sp:{},md5:a.md.md5.create(),sha1:a.md.sha1.create()},t&&(o.version=t.version,o.session.sp=t.sp),o.session.sp.client_random=u.createRandom().getBytes(),o.open=!0,u.queue(o,u.createRecord(o,{type:u.ContentType.handshake,data:u.createClientHello(o)})),u.flush(o)}},o.process=function(e){var t=0;return e&&o.input.putBytes(e),o.fail||(null!==o.record&&o.record.ready&&o.record.fragment.isEmpty()&&(o.record=null),null===o.record&&(t=function(e){var t=0,r=e.input,n=r.length();if(n<5)t=5-n;else{e.record={type:r.getByte(),version:{major:r.getByte(),minor:r.getByte()},length:r.getInt16(),fragment:a.util.createBuffer(),ready:!1};var i=e.record.version.major===e.version.major;i&&e.session&&e.session.version&&(i=e.record.version.minor===e.version.minor),i||e.error(e,{message:"Incompatible TLS version.",send:!0,alert:{level:u.Alert.Level.fatal,description:u.Alert.Description.protocol_version}})}return t}(o)),o.fail||null===o.record||o.record.ready||(t=function(e){var t=0,r=e.input,a=r.length();a=0;c--)w>>=8,w+=B.at(c)+R.at(c),R.setAt(c,255&w);N.putBuffer(R)}E=N,p.putBuffer(I)}return p.truncate(p.length()-i),p},s.pbe.getCipher=function(e,t,r){switch(e){case s.oids.pkcs5PBES2:return s.pbe.getCipherForPBES2(e,t,r);case s.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:case s.oids["pbewithSHAAnd40BitRC2-CBC"]:return s.pbe.getCipherForPKCS12PBE(e,t,r);default:var a=new Error("Cannot read encrypted PBE data block. Unsupported OID.");throw a.oid=e,a.supportedOids=["pkcs5PBES2","pbeWithSHAAnd3-KeyTripleDES-CBC","pbewithSHAAnd40BitRC2-CBC"],a}},s.pbe.getCipherForPBES2=function(e,t,r){var n,o={},c=[];if(!i.validate(t,u,o,c))throw(n=new Error("Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.")).errors=c,n;if((e=i.derToOid(o.kdfOid))!==s.oids.pkcs5PBKDF2)throw(n=new Error("Cannot read encrypted private key. Unsupported key derivation function OID.")).oid=e,n.supportedOids=["pkcs5PBKDF2"],n;if((e=i.derToOid(o.encOid))!==s.oids["aes128-CBC"]&&e!==s.oids["aes192-CBC"]&&e!==s.oids["aes256-CBC"]&&e!==s.oids["des-EDE3-CBC"]&&e!==s.oids.desCBC)throw(n=new Error("Cannot read encrypted private key. Unsupported encryption scheme OID.")).oid=e,n.supportedOids=["aes128-CBC","aes192-CBC","aes256-CBC","des-EDE3-CBC","desCBC"],n;var l,p,h=o.kdfSalt,d=a.util.createBuffer(o.kdfIterationCount);switch(d=d.getInt(d.length()<<3),s.oids[e]){case"aes128-CBC":l=16,p=a.aes.createDecryptionCipher;break;case"aes192-CBC":l=24,p=a.aes.createDecryptionCipher;break;case"aes256-CBC":l=32,p=a.aes.createDecryptionCipher;break;case"des-EDE3-CBC":l=24,p=a.des.createDecryptionCipher;break;case"desCBC":l=8,p=a.des.createDecryptionCipher}var y=f(o.prfOid),g=a.pkcs5.pbkdf2(r,h,d,l,y),v=o.encIv,m=p(g);return m.start(v),m},s.pbe.getCipherForPKCS12PBE=function(e,t,r){var n={},o=[];if(!i.validate(t,l,n,o))throw(y=new Error("Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.")).errors=o,y;var c,u,p,h=a.util.createBuffer(n.salt),d=a.util.createBuffer(n.iterations);switch(d=d.getInt(d.length()<<3),e){case s.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:c=24,u=8,p=a.des.startDecrypting;break;case s.oids["pbewithSHAAnd40BitRC2-CBC"]:c=5,u=8,p=function(e,t){var r=a.rc2.createDecryptionCipher(e,40);return r.start(t,null),r};break;default:var y;throw(y=new Error("Cannot read PKCS #12 PBE data block. Unsupported OID.")).oid=e,y}var g=f(n.prfOid),v=s.pbe.generatePkcs12Key(r,h,1,d,c,g);return g.start(),p(v,s.pbe.generatePkcs12Key(r,h,2,d,u,g))},s.pbe.opensslDeriveBytes=function(e,t,r,n){if(null==n){if(!("md5"in a.md))throw new Error('"md5" hash algorithm unavailable.');n=a.md.md5.create()}null===t&&(t="");for(var i=[p(n,e+t)],s=16,o=1;s>>0,o>>>0];for(var u=n.fullMessageLength.length-1;u>=0;--u)n.fullMessageLength[u]+=o[1],o[1]=o[0]+(n.fullMessageLength[u]/4294967296>>>0),n.fullMessageLength[u]=n.fullMessageLength[u]>>>0,o[0]=o[1]/4294967296>>>0;return t.putBytes(i),c(e,r,t),(t.read>2048||0===t.length())&&t.compact(),n},n.digest=function(){var s=a.util.createBuffer();s.putBytes(t.bytes());var o,u=n.fullMessageLength[n.fullMessageLength.length-1]+n.messageLengthSize&n.blockLength-1;s.putBytes(i.substr(0,n.blockLength-u));for(var l=8*n.fullMessageLength[0],p=0;p>>0,s.putInt32(l>>>0),l=o>>>0;s.putInt32(l);var f={h0:e.h0,h1:e.h1,h2:e.h2,h3:e.h3,h4:e.h4,h5:e.h5,h6:e.h6,h7:e.h7};c(f,r,s);var h=a.util.createBuffer();return h.putInt32(f.h0),h.putInt32(f.h1),h.putInt32(f.h2),h.putInt32(f.h3),h.putInt32(f.h4),h.putInt32(f.h5),h.putInt32(f.h6),h.putInt32(f.h7),h},n};var i=null,s=!1,o=null;function c(e,t,r){for(var a,n,i,s,c,u,l,p,f,h,d,y,g,v=r.length();v>=64;){for(c=0;c<16;++c)t[c]=r.getInt32();for(;c<64;++c)a=((a=t[c-2])>>>17|a<<15)^(a>>>19|a<<13)^a>>>10,n=((n=t[c-15])>>>7|n<<25)^(n>>>18|n<<14)^n>>>3,t[c]=a+t[c-7]+n+t[c-16]|0;for(u=e.h0,l=e.h1,p=e.h2,f=e.h3,h=e.h4,d=e.h5,y=e.h6,g=e.h7,c=0;c<64;++c)i=(u>>>2|u<<30)^(u>>>13|u<<19)^(u>>>22|u<<10),s=u&l|p&(u^l),a=g+((h>>>6|h<<26)^(h>>>11|h<<21)^(h>>>25|h<<7))+(y^h&(d^y))+o[c]+t[c],g=y,y=d,d=h,h=f+a>>>0,f=p,p=l,l=u,u=a+(n=i+s)>>>0;e.h0=e.h0+u|0,e.h1=e.h1+l|0,e.h2=e.h2+p|0,e.h3=e.h3+f|0,e.h4=e.h4+h|0,e.h5=e.h5+d|0,e.h6=e.h6+y|0,e.h7=e.h7+g|0,v-=64}}},function(e,t,r){var a=r(0);r(1);var n=null;!a.util.isNodejs||a.options.usePureJavaScript||process.versions["node-webkit"]||(n=r(16)),(e.exports=a.prng=a.prng||{}).create=function(e){for(var t={plugin:e,key:null,seed:null,time:null,reseeds:0,generated:0,keyBytes:""},r=e.md,i=new Array(32),s=0;s<32;++s)i[s]=r.create();function o(){if(t.pools[0].messageLength>=32)return c();var e=32-t.pools[0].messageLength<<5;t.collect(t.seedFileSync(e)),c()}function c(){t.reseeds=4294967295===t.reseeds?0:t.reseeds+1;var e=t.plugin.md.create();e.update(t.keyBytes);for(var r=1,a=0;a<32;++a)t.reseeds%r==0&&(e.update(t.pools[a].digest().getBytes()),t.pools[a].start()),r<<=1;t.keyBytes=e.digest().getBytes(),e.start(),e.update(t.keyBytes);var n=e.digest().getBytes();t.key=t.plugin.formatKey(t.keyBytes),t.seed=t.plugin.formatSeed(n),t.generated=0}function u(e){var t=null,r=a.util.globalScope,n=r.crypto||r.msCrypto;n&&n.getRandomValues&&(t=function(e){return n.getRandomValues(e)});var i=a.util.createBuffer();if(t)for(;i.length()>16)))<<16,f=4294967295&(l=(2147483647&(l+=u>>15))+(l>>31));for(c=0;c<3;++c)p=f>>>(c<<3),p^=Math.floor(256*Math.random()),i.putByte(255&p)}return i.getBytes(e)}return t.pools=i,t.pool=0,t.generate=function(e,r){if(!r)return t.generateSync(e);var n=t.plugin.cipher,i=t.plugin.increment,s=t.plugin.formatKey,o=t.plugin.formatSeed,u=a.util.createBuffer();t.key=null,function l(p){if(p)return r(p);if(u.length()>=e)return r(null,u.getBytes(e));t.generated>1048575&&(t.key=null);if(null===t.key)return a.util.nextTick((function(){!function(e){if(t.pools[0].messageLength>=32)return c(),e();var r=32-t.pools[0].messageLength<<5;t.seedFile(r,(function(r,a){if(r)return e(r);t.collect(a),c(),e()}))}(l)}));var f=n(t.key,t.seed);t.generated+=f.length,u.putBytes(f),t.key=s(n(t.key,i(t.seed))),t.seed=o(n(t.key,t.seed)),a.util.setImmediate(l)}()},t.generateSync=function(e){var r=t.plugin.cipher,n=t.plugin.increment,i=t.plugin.formatKey,s=t.plugin.formatSeed;t.key=null;for(var c=a.util.createBuffer();c.length()1048575&&(t.key=null),null===t.key&&o();var u=r(t.key,t.seed);t.generated+=u.length,c.putBytes(u),t.key=i(r(t.key,n(t.seed))),t.seed=s(r(t.key,t.seed))}return c.getBytes(e)},n?(t.seedFile=function(e,t){n.randomBytes(e,(function(e,r){if(e)return t(e);t(null,r.toString())}))},t.seedFileSync=function(e){return n.randomBytes(e).toString()}):(t.seedFile=function(e,t){try{t(null,u(e))}catch(e){t(e)}},t.seedFileSync=u),t.collect=function(e){for(var r=e.length,a=0;a>n&255);t.collect(a)},t.registerWorker=function(e){if(e===self)t.seedFile=function(e,t){self.addEventListener("message",(function e(r){var a=r.data;a.forge&&a.forge.prng&&(self.removeEventListener("message",e),t(a.forge.prng.err,a.forge.prng.bytes))})),self.postMessage({forge:{prng:{needed:e}}})};else{e.addEventListener("message",(function(r){var a=r.data;a.forge&&a.forge.prng&&t.seedFile(a.forge.prng.needed,(function(t,r){e.postMessage({forge:{prng:{err:t,bytes:r}}})}))}))}},t}},function(e,t,r){var a=r(0);r(1);var n=[217,120,249,196,25,221,181,237,40,233,253,121,74,160,216,157,198,126,55,131,43,118,83,142,98,76,100,136,68,139,251,162,23,154,89,245,135,179,79,19,97,69,109,141,9,129,125,50,189,143,64,235,134,183,123,11,240,149,33,34,92,107,78,130,84,214,101,147,206,96,178,28,115,86,192,20,167,140,241,220,18,117,202,31,59,190,228,209,66,61,212,48,163,60,182,38,111,191,14,218,70,105,7,87,39,242,29,155,188,148,67,3,248,17,199,246,144,239,62,231,6,195,213,47,200,102,30,215,8,232,234,222,128,82,238,247,132,170,114,172,53,77,106,42,150,26,210,113,90,21,73,116,75,159,208,94,4,24,164,236,194,224,65,110,15,81,203,204,36,145,175,80,161,244,112,57,153,124,58,133,35,184,180,122,252,2,54,91,37,85,151,49,45,93,250,152,227,138,146,174,5,223,41,16,103,108,186,201,211,0,230,207,225,158,168,44,99,22,1,63,88,226,137,169,13,56,52,27,171,51,255,176,187,72,12,95,185,177,205,46,197,243,219,71,229,165,156,119,10,166,32,104,254,127,193,173],i=[1,2,3,5],s=function(e,t){return e<>16-t},o=function(e,t){return(65535&e)>>t|e<<16-t&65535};e.exports=a.rc2=a.rc2||{},a.rc2.expandKey=function(e,t){"string"==typeof e&&(e=a.util.createBuffer(e)),t=t||128;var r,i=e,s=e.length(),o=t,c=Math.ceil(o/8),u=255>>(7&o);for(r=s;r<128;r++)i.putByte(n[i.at(r-1)+i.at(r-s)&255]);for(i.setAt(128-c,n[i.at(128-c)&u]),r=127-c;r>=0;r--)i.setAt(r,n[i.at(r+1)^i.at(r+c)]);return i};var c=function(e,t,r){var n,c,u,l,p=!1,f=null,h=null,d=null,y=[];for(e=a.rc2.expandKey(e,t),u=0;u<64;u++)y.push(e.getInt16Le());r?(n=function(e){for(u=0;u<4;u++)e[u]+=y[l]+(e[(u+3)%4]&e[(u+2)%4])+(~e[(u+3)%4]&e[(u+1)%4]),e[u]=s(e[u],i[u]),l++},c=function(e){for(u=0;u<4;u++)e[u]+=y[63&e[(u+3)%4]]}):(n=function(e){for(u=3;u>=0;u--)e[u]=o(e[u],i[u]),e[u]-=y[l]+(e[(u+3)%4]&e[(u+2)%4])+(~e[(u+3)%4]&e[(u+1)%4]),l--},c=function(e){for(u=3;u>=0;u--)e[u]-=y[63&e[(u+3)%4]]});var g=function(e){var t=[];for(u=0;u<4;u++){var a=f.getInt16Le();null!==d&&(r?a^=d.getInt16Le():d.putInt16Le(a)),t.push(65535&a)}l=r?0:63;for(var n=0;n=8;)g([[5,n],[1,c],[6,n],[1,c],[5,n]])},finish:function(e){var t=!0;if(r)if(e)t=e(8,f,!r);else{var a=8===f.length()?8:8-f.length();f.fillWithByte(a,a)}if(t&&(p=!0,v.update()),!r&&(t=0===f.length()))if(e)t=e(8,h,!r);else{var n=h.length(),i=h.at(n-1);i>n?t=!1:h.truncate(i)}return t}}};a.rc2.startEncrypting=function(e,t,r){var n=a.rc2.createEncryptionCipher(e,128);return n.start(t,r),n},a.rc2.createEncryptionCipher=function(e,t){return c(e,t,!0)},a.rc2.startDecrypting=function(e,t,r){var n=a.rc2.createDecryptionCipher(e,128);return n.start(t,r),n},a.rc2.createDecryptionCipher=function(e,t){return c(e,t,!1)}},function(e,t,r){var a=r(0);r(1),r(2),r(9);var n=e.exports=a.pkcs1=a.pkcs1||{};function i(e,t,r){r||(r=a.md.sha1.create());for(var n="",i=Math.ceil(t/r.digestLength),s=0;s>24&255,s>>16&255,s>>8&255,255&s);r.start(),r.update(e+o),n+=r.digest().getBytes()}return n.substring(0,t)}n.encode_rsa_oaep=function(e,t,r){var n,s,o,c;"string"==typeof r?(n=r,s=arguments[3]||void 0,o=arguments[4]||void 0):r&&(n=r.label||void 0,s=r.seed||void 0,o=r.md||void 0,r.mgf1&&r.mgf1.md&&(c=r.mgf1.md)),o?o.start():o=a.md.sha1.create(),c||(c=o);var u=Math.ceil(e.n.bitLength()/8),l=u-2*o.digestLength-2;if(t.length>l)throw(g=new Error("RSAES-OAEP input message length is too long.")).length=t.length,g.maxLength=l,g;n||(n=""),o.update(n,"raw");for(var p=o.digest(),f="",h=l-t.length,d=0;de&&(s=c(e,t));var h=s.toString(16);n.target.postMessage({hex:h,workLoad:l}),s.dAddOffset(p,0)}}}h()}(e,t,n,i);return o(e,t,n,i)}(e,u,i.options,n);throw new Error("Invalid prime generation algorithm: "+i.name)}}function o(e,t,r,i){var s=c(e,t),o=function(e){return e<=100?27:e<=150?18:e<=200?15:e<=250?12:e<=300?9:e<=350?8:e<=400?7:e<=500?6:e<=600?5:e<=800?4:e<=1250?3:2}(s.bitLength());"millerRabinTests"in r&&(o=r.millerRabinTests);var u=10;"maxBlockTime"in r&&(u=r.maxBlockTime),function e(t,r,i,s,o,u,l){var p=+new Date;do{if(t.bitLength()>r&&(t=c(r,i)),t.isProbablePrime(o))return l(null,t);t.dAddOffset(n[s++%8],0)}while(u<0||+new Date-p=0&&n.push(o):n.push(o))}return n}function h(e){if(e.composed||e.constructed){for(var t=a.util.createBuffer(),r=0;r0&&(c=n.create(n.Class.UNIVERSAL,n.Type.SET,!0,p));var f=[],h=[];null!==t&&(h=a.util.isArray(t)?t:[t]);for(var d=[],y=0;y0){var C=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,d),E=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.data).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,n.toDer(C).getBytes())])]);f.push(E)}var S=null;if(null!==e){var T=i.wrapRsaPrivateKey(i.privateKeyToAsn1(e));S=null===r?n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.keyBag).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[T]),c]):n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.pkcs8ShroudedKeyBag).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[i.encryptPrivateKeyInfo(T,r,o)]),c]);var I=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[S]),A=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.data).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,n.toDer(I).getBytes())])]);f.push(A)}var B,b=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,f);if(o.useMac){var N=a.md.sha1.create(),R=new a.util.ByteBuffer(a.random.getBytes(o.saltSize)),w=o.count,_=(e=s.generateKey(r,R,3,w,20),a.hmac.create());_.start(N,e),_.update(n.toDer(b).getBytes());var L=_.getMac();B=n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.sha1).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.NULL,!1,"")]),n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,L.getBytes())]),n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,R.getBytes()),n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(w).getBytes())])}return n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.INTEGER,!1,n.integerToDer(3).getBytes()),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(i.oids.data).getBytes()),n.create(n.Class.CONTEXT_SPECIFIC,0,!0,[n.create(n.Class.UNIVERSAL,n.Type.OCTETSTRING,!1,n.toDer(b).getBytes())])]),B])},s.generateKey=a.pbe.generatePkcs12Key},function(e,t,r){var a=r(0);r(3),r(1);var n=a.asn1,i=e.exports=a.pkcs7asn1=a.pkcs7asn1||{};a.pkcs7=a.pkcs7||{},a.pkcs7.asn1=i;var s={name:"ContentInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"ContentInfo.ContentType",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"contentType"},{name:"ContentInfo.content",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,captureAsn1:"content"}]};i.contentInfoValidator=s;var o={name:"EncryptedContentInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedContentInfo.contentType",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"contentType"},{name:"EncryptedContentInfo.contentEncryptionAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedContentInfo.contentEncryptionAlgorithm.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"encAlgorithm"},{name:"EncryptedContentInfo.contentEncryptionAlgorithm.parameter",tagClass:n.Class.UNIVERSAL,captureAsn1:"encParameter"}]},{name:"EncryptedContentInfo.encryptedContent",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,capture:"encryptedContent",captureAsn1:"encryptedContentAsn1"}]};i.envelopedDataValidator={name:"EnvelopedData",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"EnvelopedData.Version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"version"},{name:"EnvelopedData.RecipientInfos",tagClass:n.Class.UNIVERSAL,type:n.Type.SET,constructed:!0,captureAsn1:"recipientInfos"}].concat(o)},i.encryptedDataValidator={name:"EncryptedData",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"EncryptedData.Version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"version"}].concat(o)};var c={name:"SignerInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"SignerInfo.version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1},{name:"SignerInfo.issuerAndSerialNumber",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"SignerInfo.issuerAndSerialNumber.issuer",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"issuer"},{name:"SignerInfo.issuerAndSerialNumber.serialNumber",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"serial"}]},{name:"SignerInfo.digestAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"SignerInfo.digestAlgorithm.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"digestAlgorithm"},{name:"SignerInfo.digestAlgorithm.parameter",tagClass:n.Class.UNIVERSAL,constructed:!1,captureAsn1:"digestParameter",optional:!0}]},{name:"SignerInfo.authenticatedAttributes",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,constructed:!0,optional:!0,capture:"authenticatedAttributes"},{name:"SignerInfo.digestEncryptionAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,capture:"signatureAlgorithm"},{name:"SignerInfo.encryptedDigest",tagClass:n.Class.UNIVERSAL,type:n.Type.OCTETSTRING,constructed:!1,capture:"signature"},{name:"SignerInfo.unauthenticatedAttributes",tagClass:n.Class.CONTEXT_SPECIFIC,type:1,constructed:!0,optional:!0,capture:"unauthenticatedAttributes"}]};i.signedDataValidator={name:"SignedData",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"SignedData.Version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"version"},{name:"SignedData.DigestAlgorithms",tagClass:n.Class.UNIVERSAL,type:n.Type.SET,constructed:!0,captureAsn1:"digestAlgorithms"},s,{name:"SignedData.Certificates",tagClass:n.Class.CONTEXT_SPECIFIC,type:0,optional:!0,captureAsn1:"certificates"},{name:"SignedData.CertificateRevocationLists",tagClass:n.Class.CONTEXT_SPECIFIC,type:1,optional:!0,captureAsn1:"crls"},{name:"SignedData.SignerInfos",tagClass:n.Class.UNIVERSAL,type:n.Type.SET,capture:"signerInfos",optional:!0,value:[c]}]},i.recipientInfoValidator={name:"RecipientInfo",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"RecipientInfo.version",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"version"},{name:"RecipientInfo.issuerAndSerial",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"RecipientInfo.issuerAndSerial.issuer",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,captureAsn1:"issuer"},{name:"RecipientInfo.issuerAndSerial.serialNumber",tagClass:n.Class.UNIVERSAL,type:n.Type.INTEGER,constructed:!1,capture:"serial"}]},{name:"RecipientInfo.keyEncryptionAlgorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.SEQUENCE,constructed:!0,value:[{name:"RecipientInfo.keyEncryptionAlgorithm.algorithm",tagClass:n.Class.UNIVERSAL,type:n.Type.OID,constructed:!1,capture:"encAlgorithm"},{name:"RecipientInfo.keyEncryptionAlgorithm.parameter",tagClass:n.Class.UNIVERSAL,constructed:!1,captureAsn1:"encParameter",optional:!0}]},{name:"RecipientInfo.encryptedKey",tagClass:n.Class.UNIVERSAL,type:n.Type.OCTETSTRING,constructed:!1,capture:"encKey"}]}},function(e,t,r){var a=r(0);r(1),a.mgf=a.mgf||{},(e.exports=a.mgf.mgf1=a.mgf1=a.mgf1||{}).create=function(e){return{generate:function(t,r){for(var n=new a.util.ByteBuffer,i=Math.ceil(r/e.digestLength),s=0;s>>0,s>>>0];for(var o=h.fullMessageLength.length-1;o>=0;--o)h.fullMessageLength[o]+=s[1],s[1]=s[0]+(h.fullMessageLength[o]/4294967296>>>0),h.fullMessageLength[o]=h.fullMessageLength[o]>>>0,s[0]=s[1]/4294967296>>>0;return n.putBytes(e),l(r,i,n),(n.read>2048||0===n.length())&&n.compact(),h},h.digest=function(){var t=a.util.createBuffer();t.putBytes(n.bytes());var o,c=h.fullMessageLength[h.fullMessageLength.length-1]+h.messageLengthSize&h.blockLength-1;t.putBytes(s.substr(0,h.blockLength-c));for(var u=8*h.fullMessageLength[0],p=0;p>>0,t.putInt32(u>>>0),u=o>>>0;t.putInt32(u);var f=new Array(r.length);for(p=0;p=128;){for(_=0;_<16;++_)t[_][0]=r.getInt32()>>>0,t[_][1]=r.getInt32()>>>0;for(;_<80;++_)a=(((L=(U=t[_-2])[0])>>>19|(k=U[1])<<13)^(k>>>29|L<<3)^L>>>6)>>>0,n=((L<<13|k>>>19)^(k<<3|L>>>29)^(L<<26|k>>>6))>>>0,i=(((L=(P=t[_-15])[0])>>>1|(k=P[1])<<31)^(L>>>8|k<<24)^L>>>7)>>>0,s=((L<<31|k>>>1)^(L<<24|k>>>8)^(L<<25|k>>>7))>>>0,D=t[_-7],V=t[_-16],k=n+D[1]+s+V[1],t[_][0]=a+D[0]+i+V[0]+(k/4294967296>>>0)>>>0,t[_][1]=k>>>0;for(d=e[0][0],y=e[0][1],g=e[1][0],v=e[1][1],m=e[2][0],C=e[2][1],E=e[3][0],S=e[3][1],T=e[4][0],I=e[4][1],A=e[5][0],B=e[5][1],b=e[6][0],N=e[6][1],R=e[7][0],w=e[7][1],_=0;_<80;++_)l=((T>>>14|I<<18)^(T>>>18|I<<14)^(I>>>9|T<<23))>>>0,p=(b^T&(A^b))>>>0,o=((d>>>28|y<<4)^(y>>>2|d<<30)^(y>>>7|d<<25))>>>0,u=((d<<4|y>>>28)^(y<<30|d>>>2)^(y<<25|d>>>7))>>>0,f=(d&g|m&(d^g))>>>0,h=(y&v|C&(y^v))>>>0,k=w+(((T<<18|I>>>14)^(T<<14|I>>>18)^(I<<23|T>>>9))>>>0)+((N^I&(B^N))>>>0)+c[_][1]+t[_][1],a=R+l+p+c[_][0]+t[_][0]+(k/4294967296>>>0)>>>0,n=k>>>0,i=o+f+((k=u+h)/4294967296>>>0)>>>0,s=k>>>0,R=b,w=N,b=A,N=B,A=T,B=I,T=E+a+((k=S+n)/4294967296>>>0)>>>0,I=k>>>0,E=m,S=C,m=g,C=v,g=d,v=y,d=a+i+((k=n+s)/4294967296>>>0)>>>0,y=k>>>0;k=e[0][1]+y,e[0][0]=e[0][0]+d+(k/4294967296>>>0)>>>0,e[0][1]=k>>>0,k=e[1][1]+v,e[1][0]=e[1][0]+g+(k/4294967296>>>0)>>>0,e[1][1]=k>>>0,k=e[2][1]+C,e[2][0]=e[2][0]+m+(k/4294967296>>>0)>>>0,e[2][1]=k>>>0,k=e[3][1]+S,e[3][0]=e[3][0]+E+(k/4294967296>>>0)>>>0,e[3][1]=k>>>0,k=e[4][1]+I,e[4][0]=e[4][0]+T+(k/4294967296>>>0)>>>0,e[4][1]=k>>>0,k=e[5][1]+B,e[5][0]=e[5][0]+A+(k/4294967296>>>0)>>>0,e[5][1]=k>>>0,k=e[6][1]+N,e[6][0]=e[6][0]+b+(k/4294967296>>>0)>>>0,e[6][1]=k>>>0,k=e[7][1]+w,e[7][0]=e[7][0]+R+(k/4294967296>>>0)>>>0,e[7][1]=k>>>0,O-=128}}},function(e,t,r){e.exports=r(33)},function(e,t,r){e.exports=r(0),r(5),r(36),r(3),r(13),r(10),r(38),r(8),r(40),r(41),r(42),r(30),r(15),r(7),r(26),r(28),r(43),r(21),r(27),r(24),r(18),r(2),r(25),r(44),r(20),r(1)},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t){var r={};e.exports=r;var a={};r.encode=function(e,t,r){if("string"!=typeof t)throw new TypeError('"alphabet" must be a string.');if(void 0!==r&&"number"!=typeof r)throw new TypeError('"maxline" must be a number.');var a="";if(e instanceof Uint8Array){var n=0,i=t.length,s=t.charAt(0),o=[0];for(n=0;n0;)o.push(u%i),u=u/i|0}for(n=0;0===e[n]&&n=0;--n)a+=t[o[n]]}else a=function(e,t){var r=0,a=t.length,n=t.charAt(0),i=[0];for(r=0;r0;)i.push(o%a),o=o/a|0}var c="";for(r=0;0===e.at(r)&&r=0;--r)c+=t[i[r]];return c}(e,t);if(r){var l=new RegExp(".{1,"+r+"}","g");a=a.match(l).join("\r\n")}return a},r.decode=function(e,t){if("string"!=typeof e)throw new TypeError('"input" must be a string.');if("string"!=typeof t)throw new TypeError('"alphabet" must be a string.');var r=a[t];if(!r){r=a[t]=[];for(var n=0;n>=8;for(;l>0;)o.push(255&l),l>>=8}for(var p=0;e[p]===s&&p=n.Versions.TLS_1_1.minor&&c.output.putBytes(r),c.update(e.fragment),c.finish(o)&&(e.fragment=c.output,e.length=e.fragment.length(),i=!0),i}function o(e,t,r){if(!r){var a=e-t.length()%e;t.fillWithByte(a-1,a)}return!0}function c(e,t,r){var a=!0;if(r){for(var n=t.length(),i=t.last(),s=n-1-i;s=o?(e.fragment=s.output.getBytes(l-o),u=s.output.getBytes(o)):e.fragment=s.output.getBytes(),e.fragment=a.util.createBuffer(e.fragment),e.length=e.fragment.length();var p=t.macFunction(t.macKey,t.sequenceNumber,e);return t.updateSequenceNumber(),i=function(e,t,r){var n=a.hmac.create();return n.start("SHA1",e),n.update(t),t=n.digest().getBytes(),n.start(null,null),n.update(r),r=n.digest().getBytes(),t===r}(t.macKey,u,p)&&i}n.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA={id:[0,47],name:"TLS_RSA_WITH_AES_128_CBC_SHA",initSecurityParameters:function(e){e.bulk_cipher_algorithm=n.BulkCipherAlgorithm.aes,e.cipher_type=n.CipherType.block,e.enc_key_length=16,e.block_length=16,e.fixed_iv_length=16,e.record_iv_length=16,e.mac_algorithm=n.MACAlgorithm.hmac_sha1,e.mac_length=20,e.mac_key_length=20},initConnectionState:i},n.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA={id:[0,53],name:"TLS_RSA_WITH_AES_256_CBC_SHA",initSecurityParameters:function(e){e.bulk_cipher_algorithm=n.BulkCipherAlgorithm.aes,e.cipher_type=n.CipherType.block,e.enc_key_length=32,e.block_length=16,e.fixed_iv_length=16,e.record_iv_length=16,e.mac_algorithm=n.MACAlgorithm.hmac_sha1,e.mac_length=20,e.mac_key_length=20},initConnectionState:i}},function(e,t,r){var a=r(0);r(30),e.exports=a.mgf=a.mgf||{},a.mgf.mgf1=a.mgf1},function(e,t,r){var a=r(0);r(12),r(2),r(31),r(1);var n=r(39),i=n.publicKeyValidator,s=n.privateKeyValidator;if(void 0===o)var o=a.jsbn.BigInteger;var c=a.util.ByteBuffer,u="undefined"==typeof Buffer?Uint8Array:Buffer;a.pki=a.pki||{},e.exports=a.pki.ed25519=a.ed25519=a.ed25519||{};var l=a.ed25519;function p(e){var t=e.message;if(t instanceof Uint8Array||t instanceof u)return t;var r=e.encoding;if(void 0===t){if(!e.md)throw new TypeError('"options.message" or "options.md" not specified.');t=e.md.digest().getBytes(),r="binary"}if("string"==typeof t&&!r)throw new TypeError('"options.encoding" must be "binary" or "utf8".');if("string"==typeof t){if("undefined"!=typeof Buffer)return Buffer.from(t,r);t=new c(t,r)}else if(!(t instanceof c))throw new TypeError('"options.message" must be a node.js Buffer, a Uint8Array, a forge ByteBuffer, or a string with "options.encoding" specifying its encoding.');for(var a=new u(t.length()),n=0;n=0;--r)K(a,a),1!==r&&x(a,a,t);for(r=0;r<16;++r)e[r]=a[r]}(r,r),x(r,r,n),x(r,r,i),x(r,r,i),x(e[0],r,i),K(a,e[0]),x(a,a,i),N(a,n)&&x(e[0],e[0],C);if(K(a,e[0]),x(a,a,i),N(a,n))return-1;w(e[0])===t[31]>>7&&O(e[0],f,e[0]);return x(e[3],e[0],e[1]),0}(o,a))return-1;for(n=0;n=0};var f=P(),h=P([1]),d=P([30883,4953,19914,30187,55467,16705,2637,112,59544,30585,16505,36039,65139,11119,27886,20995]),y=P([61785,9906,39828,60374,45398,33411,5274,224,53552,61171,33010,6542,64743,22239,55772,9222]),g=P([54554,36645,11616,51542,42930,38181,51040,26924,56412,64982,57905,49316,21502,52590,14035,8553]),v=P([26200,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214]),m=new Float64Array([237,211,245,92,26,99,18,88,214,156,247,162,222,249,222,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16]),C=P([41136,18958,6951,50414,58488,44335,6150,12099,55207,15867,153,11085,57099,20417,9344,11139]);function E(e,t){var r=a.md.sha512.create(),n=new c(e);r.update(n.getBytes(t),"binary");var i=r.digest().getBytes();if("undefined"!=typeof Buffer)return Buffer.from(i,"binary");for(var s=new u(l.constants.HASH_BYTE_LENGTH),o=0;o<64;++o)s[o]=i.charCodeAt(o);return s}function S(e,t){var r,a,n,i;for(a=63;a>=32;--a){for(r=0,n=a-32,i=a-12;n>8,t[n]-=256*r;t[n]+=r,t[a]=0}for(r=0,n=0;n<32;++n)t[n]+=r-(t[31]>>4)*m[n],r=t[n]>>8,t[n]&=255;for(n=0;n<32;++n)t[n]-=r*m[n];for(a=0;a<32;++a)t[a+1]+=t[a]>>8,e[a]=255&t[a]}function T(e){for(var t=new Float64Array(64),r=0;r<64;++r)t[r]=e[r],e[r]=0;S(e,t)}function I(e,t){var r=P(),a=P(),n=P(),i=P(),s=P(),o=P(),c=P(),u=P(),l=P();O(r,e[1],e[0]),O(l,t[1],t[0]),x(r,r,l),V(a,e[0],e[1]),V(l,t[0],t[1]),x(a,a,l),x(n,e[3],t[3]),x(n,n,y),x(i,e[2],t[2]),V(i,i,i),O(s,a,r),O(o,i,n),V(c,i,n),V(u,a,r),x(e[0],s,o),x(e[1],u,c),x(e[2],c,o),x(e[3],s,u)}function A(e,t,r){for(var a=0;a<4;++a)D(e[a],t[a],r)}function B(e,t){var r=P(),a=P(),n=P();!function(e,t){var r,a=P();for(r=0;r<16;++r)a[r]=t[r];for(r=253;r>=0;--r)K(a,a),2!==r&&4!==r&&x(a,a,t);for(r=0;r<16;++r)e[r]=a[r]}(n,t[2]),x(r,t[0],n),x(a,t[1],n),b(e,a),e[31]^=w(r)<<7}function b(e,t){var r,a,n,i=P(),s=P();for(r=0;r<16;++r)s[r]=t[r];for(U(s),U(s),U(s),a=0;a<2;++a){for(i[0]=s[0]-65517,r=1;r<15;++r)i[r]=s[r]-65535-(i[r-1]>>16&1),i[r-1]&=65535;i[15]=s[15]-32767-(i[14]>>16&1),n=i[15]>>16&1,i[14]&=65535,D(s,i,1-n)}for(r=0;r<16;r++)e[2*r]=255&s[r],e[2*r+1]=s[r]>>8}function N(e,t){var r=new u(32),a=new u(32);return b(r,e),b(a,t),R(r,0,a,0)}function R(e,t,r,a){return function(e,t,r,a,n){var i,s=0;for(i=0;i>>8)-1}(e,t,r,a,32)}function w(e){var t=new u(32);return b(t,e),1&t[0]}function _(e,t,r){var a,n;for(k(e[0],f),k(e[1],h),k(e[2],h),k(e[3],f),n=255;n>=0;--n)A(e,t,a=r[n/8|0]>>(7&n)&1),I(t,e),I(e,e),A(e,t,a)}function L(e,t){var r=[P(),P(),P(),P()];k(r[0],g),k(r[1],v),k(r[2],h),x(r[3],g,v),_(e,r,t)}function k(e,t){var r;for(r=0;r<16;r++)e[r]=0|t[r]}function U(e){var t,r,a=1;for(t=0;t<16;++t)r=e[t]+a+65535,a=Math.floor(r/65536),e[t]=r-65536*a;e[0]+=a-1+37*(a-1)}function D(e,t,r){for(var a,n=~(r-1),i=0;i<16;++i)a=n&(e[i]^t[i]),e[i]^=a,t[i]^=a}function P(e){var t,r=new Float64Array(16);if(e)for(t=0;t0&&(s=a.util.fillString(String.fromCharCode(0),c)+s),{encapsulation:t.encrypt(s,"NONE"),key:e.generate(s,i)}},decrypt:function(t,r,a){var n=t.decrypt(r,"NONE");return e.generate(n,a)}};return i},a.kem.kdf1=function(e,t){i(this,e,0,t||e.digestLength)},a.kem.kdf2=function(e,t){i(this,e,1,t||e.digestLength)}},function(e,t,r){var a=r(0);r(1),e.exports=a.log=a.log||{},a.log.levels=["none","error","warning","info","debug","verbose","max"];var n={},i=[],s=null;a.log.LEVEL_LOCKED=2,a.log.NO_LEVEL_CHECK=4,a.log.INTERPOLATE=8;for(var o=0;o0){for(var r=n.create(n.Class.CONTEXT_SPECIFIC,1,!0,[]),i=0;i=r&&s0&&s.value[0].value.push(n.create(n.Class.CONTEXT_SPECIFIC,0,!0,t)),i.length>0&&s.value[0].value.push(n.create(n.Class.CONTEXT_SPECIFIC,1,!0,i)),s.value[0].value.push(n.create(n.Class.UNIVERSAL,n.Type.SET,!0,e.signerInfos)),n.create(n.Class.UNIVERSAL,n.Type.SEQUENCE,!0,[n.create(n.Class.UNIVERSAL,n.Type.OID,!1,n.oidToDer(e.type).getBytes()),s])},addSigner:function(t){var r=t.issuer,n=t.serialNumber;if(t.certificate){var i=t.certificate;"string"==typeof i&&(i=a.pki.certificateFromPem(i)),r=i.issuer.attributes,n=i.serialNumber}var s=t.key;if(!s)throw new Error("Could not add PKCS#7 signer; no private key specified.");"string"==typeof s&&(s=a.pki.privateKeyFromPem(s));var o=t.digestAlgorithm||a.pki.oids.sha1;switch(o){case a.pki.oids.sha1:case a.pki.oids.sha256:case a.pki.oids.sha384:case a.pki.oids.sha512:case a.pki.oids.md5:break;default:throw new Error("Could not add PKCS#7 signer; unknown message digest algorithm: "+o)}var c=t.authenticatedAttributes||[];if(c.length>0){for(var u=!1,l=!1,p=0;p="8"&&(r="00"+r);var n=a.util.hexToBytes(r);e.putInt32(n.length),e.putBytes(n)}function s(e,t){e.putInt32(t.length),e.putString(t)}function o(){for(var e=a.md.sha1.create(),t=arguments.length,r=0;r Date: Mon, 10 Oct 2022 11:18:05 +0200 Subject: [PATCH 11/31] Use ArrayBuffers to exchange key material --- demos/RSAKeys.html | 8 ++-- demos/rsa-iframe/RSAKeysIframe.html | 64 +++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/demos/RSAKeys.html b/demos/RSAKeys.html index 25ff2342..892468f1 100644 --- a/demos/RSAKeys.html +++ b/demos/RSAKeys.html @@ -122,7 +122,7 @@

RSA Keys

return; } - /** @type {{privateKey: string, publicKey: string}} */ + /** @type {{privateKey: ArrayBuffer, publicKey: ArrayBuffer}} */ const data = event.data; if (!('privateKey' in data) || !('publicKey' in data)) return; console.log("MSG:", data); @@ -130,7 +130,7 @@

RSA Keys

const keyPair = { privateKey: await window.crypto.subtle.importKey( 'pkcs8', - pemToArrayBuffer(data.privateKey), + data.privateKey, { name: 'RSA-OAEP', hash: 'SHA-256', @@ -140,7 +140,7 @@

RSA Keys

), publicKey: await window.crypto.subtle.importKey( 'spki', - pemToArrayBuffer(data.publicKey), + data.publicKey, { name: 'RSA-OAEP', hash: 'SHA-256', @@ -157,7 +157,7 @@

RSA Keys

const publicB64 = Nimiq.BufferUtils.toBase64(new Nimiq.SerialBuffer(publicKeyExport)); const privatePEM = `-----BEGIN RSA PRIVATE KEY-----\n${privateB64}\n-----END RSA PRIVATE KEY-----`; - const publicPEM = `-----BEGIN RSA PUBLIC KEY-----\n${publicB64}\n-----END RSA PUBLIC KEY-----`; + const publicPEM = `-----BEGIN PUBLIC KEY-----\n${publicB64}\n-----END PUBLIC KEY-----`; $privateKey.value = privatePEM; $publicKey.value = publicPEM; diff --git a/demos/rsa-iframe/RSAKeysIframe.html b/demos/rsa-iframe/RSAKeysIframe.html index 11bc59b9..dde13056 100644 --- a/demos/rsa-iframe/RSAKeysIframe.html +++ b/demos/rsa-iframe/RSAKeysIframe.html @@ -3,34 +3,58 @@ RSA Keys Iframe | Keyguard Demo - + + + - RSA Keys Iframe + RSA Keys Iframe From 35425a4a0780f2c3c28c600ad40e5c35f1a42d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Mon, 10 Oct 2022 11:18:35 +0200 Subject: [PATCH 12/31] Extend 32-byte entropy to 1024-byte seed with PBKDF2 --- demos/RSAKeys.html | 55 +++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/demos/RSAKeys.html b/demos/RSAKeys.html index 892468f1..c6ff6234 100644 --- a/demos/RSAKeys.html +++ b/demos/RSAKeys.html @@ -49,7 +49,7 @@

RSA Keys

- +

@@ -68,7 +68,7 @@

RSA Keys

- +

@@ -77,6 +77,8 @@

RSA Keys

From 42590303d7e0d3cb762d5cfe389c7314676fcd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Tue, 11 Oct 2022 14:12:28 +0200 Subject: [PATCH 13/31] Refactor Key config into an object and extend with rsaKeyPair option --- src/lib/AccountStore.js | 2 +- src/lib/Key.js | 14 +++++++++++--- src/lib/KeyStore.js | 14 +++++++++++--- src/request/import/ImportFile.js | 2 +- src/request/import/ImportWords.js | 8 ++++---- tests/DummyData.spec.js | 2 +- tests/lib/KeyStore.spec.js | 4 ++-- types/Keyguard.d.ts | 6 ++++++ 8 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/lib/AccountStore.js b/src/lib/AccountStore.js index 73bf3731..9492b35f 100644 --- a/src/lib/AccountStore.js +++ b/src/lib/AccountStore.js @@ -132,7 +132,7 @@ class AccountStore { if (!keyRecord) return null; const secret = await Nimiq.Secret.fromEncrypted(new Nimiq.SerialBuffer(keyRecord.secret), password); - return new Key(secret, keyRecord.hasPin); + return new Key(secret, { hasPin: keyRecord.hasPin }); } /** diff --git a/src/lib/Key.js b/src/lib/Key.js index 3a6a29aa..2b843ee6 100644 --- a/src/lib/Key.js +++ b/src/lib/Key.js @@ -1,6 +1,12 @@ /* global Constants */ /* global Nimiq */ /* global SignMessageConstants */ +/* global KeyStore */ +/* global CONFIG */ + +/** + * @typedef {{hasPin?: boolean, rsaKeyPair?: RsaKeyPairExport}} KeyConfig + */ class Key { /** @@ -13,14 +19,16 @@ class Key { /** * @param {Nimiq.Entropy|Nimiq.PrivateKey} secret - * @param {boolean} [hasPin] + * @param {KeyConfig} [config] */ - constructor(secret, hasPin = false) { + constructor(secret, config = {}) { this._secret = secret; - this._hasPin = hasPin; + this._hasPin = Boolean(config.hasPin); /** @type {string?} */ this._id = null; this._defaultAddress = this.deriveAddress(Constants.DEFAULT_DERIVATION_PATH); + + this.rsaKeyPair = config.rsaKeyPair; } /** diff --git a/src/lib/KeyStore.js b/src/lib/KeyStore.js index 1f7ddd17..08ab9b29 100644 --- a/src/lib/KeyStore.js +++ b/src/lib/KeyStore.js @@ -90,7 +90,10 @@ class KeyStore { ? new Nimiq.PrivateKey(keyRecord.secret.subarray(4)) // The first 4 bytes are the purposeId : new Nimiq.Entropy(keyRecord.secret.subarray(4)); - return new Key(secret, keyRecord.hasPin); + return new Key(secret, { + hasPin: keyRecord.hasPin, + rsaKeyPair: keyRecord.rsaKeyPair, + }); } if (!password) { @@ -98,7 +101,10 @@ class KeyStore { } const secret = await Nimiq.Secret.fromEncrypted(new Nimiq.SerialBuffer(keyRecord.secret), password); - return new Key(secret, keyRecord.hasPin); + return new Key(secret, { + hasPin: keyRecord.hasPin, + rsaKeyPair: keyRecord.rsaKeyPair, + }); } /** @@ -147,12 +153,14 @@ class KeyStore { key.secret.serialize(buffer); } - const keyRecord = /** @type {KeyRecord} */ { + /** @type {KeyRecord} */ + const keyRecord = { id: key.id, type: key.type, hasPin: key.hasPin, secret: buffer.subarray(0, buffer.byteLength), defaultAddress: key.defaultAddress.serialize(), + rsaKeyPair: key.rsaKeyPair, }; return this.putPlain(keyRecord); diff --git a/src/request/import/ImportFile.js b/src/request/import/ImportFile.js index c941dc81..86dfd6ab 100644 --- a/src/request/import/ImportFile.js +++ b/src/request/import/ImportFile.js @@ -288,7 +288,7 @@ class ImportFile { (this.$unlockAccountPage.querySelector('.lock')).classList.add('unlocked'); // TODO expectedKeyId check if it gets used in the future. - const key = new Key(secret, this._flags.hasPin); + const key = new Key(secret, { hasPin: this._flags.hasPin }); await KeyStore.instance.put(key, encryptionKey); return key; } catch (event) { diff --git a/src/request/import/ImportWords.js b/src/request/import/ImportWords.js index 1b54f4ed..a61cf50d 100644 --- a/src/request/import/ImportWords.js +++ b/src/request/import/ImportWords.js @@ -98,7 +98,7 @@ class ImportWords { this._passwordSetter.on(PasswordSetterBox.Events.ENTERED, () => { let colorClass = ''; if (this._secrets.entropy) { - const key = new Key(this._secrets.entropy, false); + const key = new Key(this._secrets.entropy, { hasPin: false }); const color = IqonHash.getBackgroundColorIndex( key.defaultAddress.toUserFriendlyAddress(), ); @@ -117,7 +117,7 @@ class ImportWords { // Prepare LoginFile for download - const key = new Key(/** @type {Nimiq.Entropy} */(this._secrets.entropy), false); + const key = new Key(/** @type {Nimiq.Entropy} */(this._secrets.entropy), { hasPin: false }); downloadLoginFile.setEncryptedEntropy( /** @type {Nimiq.SerialBuffer} */ (this._encryptedSecret), @@ -171,7 +171,7 @@ class ImportWords { } try { if (this._secrets.entropy) { - const key = new Key(this._secrets.entropy, false); + const key = new Key(this._secrets.entropy, { hasPin: false }); /** @type {{keyPath: string, address: Uint8Array}[]} */ const addresses = this._request.requestedKeyPaths.map(keyPath => ({ @@ -228,7 +228,7 @@ class ImportWords { // Is it possible to encrypt once and use KeyStore.putPlain() instead? } if (this._secrets.privateKey) { - const key = new Key(this._secrets.privateKey, false); + const key = new Key(this._secrets.privateKey, { hasPin: false }); /** @type {KeyguardRequest.SingleKeyResult} */ const result = { diff --git a/tests/DummyData.spec.js b/tests/DummyData.spec.js index d51760a5..4b272587 100644 --- a/tests/DummyData.spec.js +++ b/tests/DummyData.spec.js @@ -150,7 +150,7 @@ function DummyData() { }; const deprecatedAccount2Keys = () => [ - new Key(secrets[1], false) + new Key(secrets[1], { hasPin: false }) ]; const keyInfoCookieEncoded = '2071U/NKd5KzeimvyflGLYku6JfI9Mms2wGxWoCLmx9+0=#10LsYVUikGJA5z8p37+LqkH3EZ5opDz2zQRT1r8cGJ8dE='; diff --git a/tests/lib/KeyStore.spec.js b/tests/lib/KeyStore.spec.js index 7ee81c0e..f385dd53 100644 --- a/tests/lib/KeyStore.spec.js +++ b/tests/lib/KeyStore.spec.js @@ -93,7 +93,7 @@ describe('KeyStore', () => { const password = Nimiq.BufferUtils.fromUtf8(Dummy.encryptionPassword); await KeyStore.instance.put(new Key( Dummy.secrets[1], - Dummy.keyInfos()[1].hasPin, + { hasPin: Dummy.keyInfos()[1].hasPin }, ), password); currentKeys = await KeyStore.instance.list(); expect(currentKeys.length).toBe(1); @@ -101,7 +101,7 @@ describe('KeyStore', () => { // add a plain key await KeyStore.instance.put(new Key( Dummy.secrets[0], - Dummy.keyInfos()[0].hasPin, + { hasPin: Dummy.keyInfos()[0].hasPin }, )); currentKeys = await KeyStore.instance.list(); const expected = Dummy.keyInfos(); diff --git a/types/Keyguard.d.ts b/types/Keyguard.d.ts index ec90ee8c..c8840bee 100644 --- a/types/Keyguard.d.ts +++ b/types/Keyguard.d.ts @@ -32,12 +32,18 @@ type AccountRecord = AccountInfo & { encryptedKeyPair: Uint8Array } +type RsaKeyPairExport = { + privateKey: Uint8Array + publicKey: Uint8Array +} + type KeyRecord = { id: string type: Nimiq.Secret.Type hasPin: boolean secret: Uint8Array defaultAddress: Uint8Array + rsaKeyPair?: RsaKeyPairExport } type MultisigConfig = { From ff780c7d61fe89eba33e4133866ef1bb9424e666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Tue, 11 Oct 2022 15:15:09 +0200 Subject: [PATCH 14/31] Add RSA key computation to Key class --- demos/RSAKeys.html | 2 +- src/common.css | 7 ++ src/config/config.local.js | 2 + src/config/config.mainnet.js | 2 + src/config/config.testnet.js | 2 + src/lib/Key.js | 102 ++++++++++++++++++ src/lib/KeyStore.js | 13 +++ .../lib/rsa/sandboxed}/RSAKeysIframe.html | 12 +-- .../lib/rsa/sandboxed}/forge.min.js | 0 .../lib/rsa/sandboxed}/forge.min.js.map | 0 10 files changed, 132 insertions(+), 10 deletions(-) rename {demos/rsa-iframe => src/lib/rsa/sandboxed}/RSAKeysIframe.html (86%) rename {demos/rsa-iframe/node-forge => src/lib/rsa/sandboxed}/forge.min.js (100%) rename {demos/rsa-iframe/node-forge => src/lib/rsa/sandboxed}/forge.min.js.map (100%) diff --git a/demos/RSAKeys.html b/demos/RSAKeys.html index c6ff6234..f65189c4 100644 --- a/demos/RSAKeys.html +++ b/demos/RSAKeys.html @@ -64,7 +64,7 @@

RSA Keys

- +

diff --git a/src/common.css b/src/common.css index 657c5c78..731f0474 100644 --- a/src/common.css +++ b/src/common.css @@ -475,6 +475,13 @@ body.loading .page:target .page-footer > .loading-spinner { margin-bottom: 1rem; } +.rsa-sandboxed-iframe { + width: 0; + height: 0; + opacity: 0; + border: none; +} + /* Mobile Layout */ @media (max-width: 450px) { diff --git a/src/config/config.local.js b/src/config/config.local.js index dec77b1f..1545e304 100644 --- a/src/config/config.local.js +++ b/src/config/config.local.js @@ -31,4 +31,6 @@ const CONFIG = { // eslint-disable-line no-unused-vars BRIDGED_USDT_CONTRACT_ADDRESS: '0x1616d425Cd540B256475cBfb604586C8598eC0FB', BRIDGED_USDT_TRANSFER_CONTRACT_ADDRESS: '', BRIDGED_USDT_HTLC_CONTRACT_ADDRESS: '', + + RSA_KEY_BITS: 2048, // Possible values are 1024 (fast, but unsafe), 2048 (good compromise), 4096 (slow, but safe) }; diff --git a/src/config/config.mainnet.js b/src/config/config.mainnet.js index 76d6b320..4897f472 100644 --- a/src/config/config.mainnet.js +++ b/src/config/config.mainnet.js @@ -21,4 +21,6 @@ const CONFIG = { // eslint-disable-line no-unused-vars BRIDGED_USDT_CONTRACT_ADDRESS: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', BRIDGED_USDT_TRANSFER_CONTRACT_ADDRESS: '0x98E69a6927747339d5E543586FC0262112eBe4BD', BRIDGED_USDT_HTLC_CONTRACT_ADDRESS: '0xF615bD7EA00C4Cc7F39Faad0895dB5f40891359f', + + RSA_KEY_BITS: 2048, // Possible values are 1024 (fast, but unsafe), 2048 (good compromise), 4096 (slow, but safe) }; diff --git a/src/config/config.testnet.js b/src/config/config.testnet.js index 2d187aed..6973b6ef 100644 --- a/src/config/config.testnet.js +++ b/src/config/config.testnet.js @@ -21,4 +21,6 @@ const CONFIG = { // eslint-disable-line no-unused-vars BRIDGED_USDT_CONTRACT_ADDRESS: '0x1616d425Cd540B256475cBfb604586C8598eC0FB', BRIDGED_USDT_TRANSFER_CONTRACT_ADDRESS: '', BRIDGED_USDT_HTLC_CONTRACT_ADDRESS: '', + + RSA_KEY_BITS: 2048, // Possible values are 1024 (fast, but unsafe), 2048 (good compromise), 4096 (slow, but safe) }; diff --git a/src/lib/Key.js b/src/lib/Key.js index 2b843ee6..c5b6b268 100644 --- a/src/lib/Key.js +++ b/src/lib/Key.js @@ -123,6 +123,108 @@ class Key { : this._secret; } + /** + * @returns {Promise} + */ + async getRsaPrivateKey() { + if (!this.rsaKeyPair) { + this.rsaKeyPair = await this._computeRsaKeyPair(); + await KeyStore.instance.addRsaKeypair(this.id, this.rsaKeyPair); + } + + return window.crypto.subtle.importKey( + 'pkcs8', + this.rsaKeyPair.privateKey, + { name: 'RSA-OAEP', hash: 'SHA-256' }, + false, // Prevent extraction + ['decrypt'], + ); + } + + // /** + // * @returns {Promise} + // */ + // async getRsaPublicKey() { + // if (!this.rsaKeyPair) { + // this.rsaKeyPair = await this._computeRsaKeyPair(); + // await KeyStore.instance.addRsaKeypair(this.id, this.rsaKeyPair); + // } + + // return window.crypto.subtle.importKey( + // 'spki', + // this.rsaKeyPair.publicKey, + // { name: 'RSA-OAEP', hash: 'SHA-256' }, + // true, // Allow extraction + // ['encrypt'], + // ); + // } + + /** + * @returns {Promise} + */ + async _computeRsaKeyPair() { + const iframe = document.createElement('iframe'); + iframe.classList.add('rsa-sandboxed-iframe'); // Styles in common.css hide this class + iframe.setAttribute('sandbox', 'allow-scripts'); + iframe.src = '../../lib/rsa/sandboxed/RSAKeysIframe.html'; // Relative path from a request URL + /** @type {Promise} */ + const loadPromise = new Promise(resolve => iframe.addEventListener('load', () => resolve())); + document.body.appendChild(iframe); + await loadPromise; + + if (!iframe.contentWindow) { + throw new Error('Could not load sandboxed RSA iframe'); + } + + // Extend 32-byte secret into 1024-byte seed as bytestring + const seed = Nimiq.CryptoUtils.computePBKDF2sha512( + this.secret.serialize(), + this._defaultAddress.serialize(), + 1024, // Iterations + 1024, // Output size (required) + ); + + // Send computation command to iframe + iframe.contentWindow.postMessage({ + command: 'generateKey', + seed: Nimiq.BufferUtils.toAscii(seed), // seed is a bytestring + keySize: CONFIG.RSA_KEY_BITS, + }, '*'); + + /** @type {(keyPair: RsaKeyPairExport) => void} */ + let resolver; + /** @type {Promise} */ + const resultPromise = new Promise(resolve => { + resolver = resolve; + }); + + /** + * @param {MessageEvent} event + */ + function onMessage(event) { + if (event.source === event.target) { + // console.log("Ignored same-window event:", event); + return; + } + + /** @type {{privateKey: ArrayBuffer, publicKey: ArrayBuffer}} */ + const data = event.data; + if (!('privateKey' in data) || !('publicKey' in data)) return; + + window.removeEventListener('message', onMessage); + + resolver({ + privateKey: new Uint8Array(data.privateKey), + publicKey: new Uint8Array(data.publicKey), + }); + } + + // Listen for result from iframe + window.addEventListener('message', onMessage); + + return resultPromise; + } + /** * @type {string} */ diff --git a/src/lib/KeyStore.js b/src/lib/KeyStore.js index 08ab9b29..38133161 100644 --- a/src/lib/KeyStore.js +++ b/src/lib/KeyStore.js @@ -188,6 +188,19 @@ class KeyStore { return newId; } + /** + * + * @param {string} id + * @param {RsaKeyPairExport} rsaKeyPair + * @returns {Promise} + */ + async addRsaKeypair(id, rsaKeyPair) { + const record = await this._get(id); + if (!record) throw new Error('Key does not exist'); + record.rsaKeyPair = rsaKeyPair; + return this.putPlain(record); + } + /** * @param {string} id * @returns {Promise} diff --git a/demos/rsa-iframe/RSAKeysIframe.html b/src/lib/rsa/sandboxed/RSAKeysIframe.html similarity index 86% rename from demos/rsa-iframe/RSAKeysIframe.html rename to src/lib/rsa/sandboxed/RSAKeysIframe.html index dde13056..f62d8e3c 100644 --- a/demos/rsa-iframe/RSAKeysIframe.html +++ b/src/lib/rsa/sandboxed/RSAKeysIframe.html @@ -3,13 +3,7 @@ RSA Keys Iframe | Keyguard Demo - - - - - - - + + + + +

+
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +

+
+ + + + + diff --git a/demos/index.html b/demos/index.html index 0e1c790a..9af587de 100644 --- a/demos/index.html +++ b/demos/index.html @@ -26,6 +26,7 @@ + diff --git a/src/components/PasswordBox.js b/src/components/PasswordBox.js index ea99da11..843845a7 100644 --- a/src/components/PasswordBox.js +++ b/src/components/PasswordBox.js @@ -83,6 +83,7 @@ class PasswordBox extends Observable { 'passwordbox-show-words': '', 'passwordbox-sign-msg': '', 'passwordbox-confirm-swap': '', + 'passwordbox-connect-account': '', }; if (!buttonVersions[options.buttonI18nTag]) throw new Error('PasswordBox button i18n tag not defined'); diff --git a/src/lib/Key.js b/src/lib/Key.js index c5b6b268..018586fd 100644 --- a/src/lib/Key.js +++ b/src/lib/Key.js @@ -141,23 +141,23 @@ class Key { ); } - // /** - // * @returns {Promise} - // */ - // async getRsaPublicKey() { - // if (!this.rsaKeyPair) { - // this.rsaKeyPair = await this._computeRsaKeyPair(); - // await KeyStore.instance.addRsaKeypair(this.id, this.rsaKeyPair); - // } - - // return window.crypto.subtle.importKey( - // 'spki', - // this.rsaKeyPair.publicKey, - // { name: 'RSA-OAEP', hash: 'SHA-256' }, - // true, // Allow extraction - // ['encrypt'], - // ); - // } + /** + * @returns {Promise} + */ + async getRsaPublicKey() { + if (!this.rsaKeyPair) { + this.rsaKeyPair = await this._computeRsaKeyPair(); + await KeyStore.instance.addRsaKeypair(this.id, this.rsaKeyPair); + } + + return window.crypto.subtle.importKey( + 'spki', + this.rsaKeyPair.publicKey, + { name: 'RSA-OAEP', hash: 'SHA-256' }, + true, // Allow extraction + ['encrypt'], + ); + } /** * @returns {Promise} diff --git a/src/lib/RequestParser.js b/src/lib/RequestParser.js index d050f506..1ae8aaff 100644 --- a/src/lib/RequestParser.js +++ b/src/lib/RequestParser.js @@ -253,14 +253,17 @@ class RequestParser { // eslint-disable-line no-unused-vars * binary data and only ever displayed as HEX. * * @param {any} message + * @param {boolean} [forceString] * @returns {string | Uint8Array} */ - parseMessage(message) { + parseMessage(message, forceString = false) { if (typeof message === 'string') { const messageBytes = Utf8Tools.stringToUtf8ByteArray(message); if (!Utf8Tools.isValidUtf8(messageBytes)) { throw new Errors.InvalidRequestError('message cannot include control characters'); } + } else if (forceString) { + throw new Errors.InvalidRequestError('message must be a string'); } else if (!(message instanceof Uint8Array)) { throw new Errors.InvalidRequestError('message must be a string or Uint8Array'); } @@ -285,15 +288,17 @@ class RequestParser { // eslint-disable-line no-unused-vars /** * @param {any} url + * @param {boolean} allowEmpty + * @param {string} name * @returns {URL | undefined} */ - parseShopLogoUrl(url) { - if (!url) return undefined; + parseLogoUrl(url, allowEmpty, name) { + if (!url && allowEmpty) return undefined; if (typeof url !== 'string') { - throw new Errors.InvalidRequestError('shopLogoUrl must be of type string'); + throw new Errors.InvalidRequestError(`${name} must be of type string`); } try { - return this._parseUrl(url, 'shopLogoUrl'); + return this._parseUrl(url, name); } catch (error) { throw new Errors.InvalidRequestError(error instanceof Error ? error : String(error)); } diff --git a/src/request/connect/Connect.css b/src/request/connect/Connect.css new file mode 100644 index 00000000..0ea07e08 --- /dev/null +++ b/src/request/connect/Connect.css @@ -0,0 +1,60 @@ +.page#connect-account .page-header .more { + font-size: 2rem; + opacity: 0.6; + text-align: center; + margin-top: 1rem; + display: block; + line-height: 1.375; +} + +.page#connect-account .page-body { + display: flex; + flex-direction: column; + align-items: center; + + padding-bottom: 2rem; +} + +.app-icon-account-icon { + width: 15rem; + height: 14.625rem; + position: relative; +} + +.app-icon-account-icon .app-icon { + width: 13.25rem; + height: 13.25rem; +} + +.app-icon-account-icon .login-file-icon { + width: 6.5rem; + height: 10rem; + border: 0.625rem solid white; + position: absolute; + right: -0.625rem; + bottom: -0.625rem; + border-radius: 1.25rem; +} + +.app-icon-account-icon .login-file-icon img { + width: 100%; + height: 100%; +} + +.app-icon-account-icon .login-file-icon.identicon { + border: none; + width: 8rem; + height: 8rem; + right: 0; + bottom: -0.5rem; +} + +.bottom-explainer { + text-align: center; + font-size: 2rem; + line-height: 1.375; +} + +.password-box { + margin-top: 1rem; +} diff --git a/src/request/connect/Connect.js b/src/request/connect/Connect.js new file mode 100644 index 00000000..02aa687b --- /dev/null +++ b/src/request/connect/Connect.js @@ -0,0 +1,144 @@ +/* global Nimiq */ +/* global Key */ +/* global KeyStore */ +/* global IqonHash */ +/* global LoginFileConfig */ +/* global Identicon */ +/* global PasswordBox */ +/* global Utf8Tools */ +/* global KeyStore */ +/* global Errors */ +/* global TopLevelApi */ + +/** + * @callback Connect.resolve + * @param {KeyguardRequest.ConnectResult} result + */ + +class Connect { + /** + * @param {Parsed} request + * @param {Connect.resolve} resolve + * @param {reject} reject + */ + constructor(request, resolve, reject) { + /** @type {HTMLDivElement} */ + const $page = (document.getElementById(Connect.Pages.CONNECT_ACCOUNT)); + + /** @type {HTMLSpanElement} */ + const $appName = ($page.querySelector('.app-name')); + $appName.textContent = request.appName; + + /** @type {HTMLImageElement} */ + const $appIcon = ($page.querySelector('.app-icon')); + $appIcon.src = request.appLogoUrl.href; + $appIcon.alt = `${request.appName} logo`; + + /** @type {HTMLDivElement} */ + const $loginFileIcon = ($page.querySelector('.login-file-icon')); + if (request.keyInfo.type === Nimiq.Secret.Type.ENTROPY) { + const bgColorClassName = LoginFileConfig[ + IqonHash.getBackgroundColorIndex(request.keyInfo.defaultAddress.toUserFriendlyAddress()) + ].className; + $loginFileIcon.classList.add(bgColorClassName); + } else { + // Show identicon for legacy accounts (which must be supported to support Team Nimiq Multisig) + $loginFileIcon.innerHTML = ''; // Remove LoginFile icon + // eslint-disable-next-line no-new + new Identicon(request.keyInfo.defaultAddress.toUserFriendlyAddress(), $loginFileIcon); + } + + /** @type {HTMLButtonElement} */ + const $button = ($page.querySelector('.nq-button.continue')); + + // Set up password box + /** @type {HTMLFormElement} */ + const $passwordBox = (document.querySelector('#password-box')); + this._passwordBox = new PasswordBox($passwordBox, { + hideInput: !request.keyInfo.encrypted, + buttonI18nTag: 'passwordbox-connect-account', + minLength: request.keyInfo.hasPin ? Key.PIN_LENGTH : undefined, + }); + + $button.addEventListener('click', () => { + $button.classList.add('display-none'); + $passwordBox.classList.remove('display-none'); + }); + + this._passwordBox.on( + PasswordBox.Events.SUBMIT, + password => this._onConfirm(request, resolve, reject, password), + ); + } + + /** + * @param {Parsed} request + * @param {Connect.resolve} resolve + * @param {reject} reject + * @param {string} [password] + * @returns {Promise} + * @private + */ + async _onConfirm(request, resolve, reject, password) { + TopLevelApi.setLoading(true); + + const passwordBuf = password ? Utf8Tools.stringToUtf8ByteArray(password) : undefined; + + /** @type {Key?} */ + let key = null; + try { + key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); + } catch (e) { + if (e.message === 'Invalid key') { + TopLevelApi.setLoading(false); + this._passwordBox.onPasswordIncorrect(); + return; + } + reject(new Errors.CoreError(e)); + return; + } + + if (!key) { + reject(new Errors.KeyNotFoundError()); + return; + } + + /** @type {KeyguardRequest.SignatureResult[]} */ + const signatures = []; + + for (const keyPath of request.requestedKeyPaths) { + const publicKey = key.derivePublicKey(keyPath); + + /** @type {Uint8Array} */ + const messageBytes = Utf8Tools.stringToUtf8ByteArray(request.challenge); + const signature = key.signMessage(keyPath, messageBytes); + + signatures.push({ + publicKey: publicKey.serialize(), + signature: signature.serialize(), + }); + } + + /** @type {KeyguardRequest.ConnectResult} */ + const result = { + signatures, + encryptionKey: { + format: 'spki', + keyData: new Uint8Array(await window.crypto.subtle.exportKey('spki', await key.getRsaPublicKey())), + algorithm: { name: 'RSA-OAEP', hash: 'SHA-256' }, + keyUsages: ['encrypt'], + }, + }; + + resolve(result); + } + + run() { + // Go to start page + window.location.hash = Connect.Pages.CONNECT_ACCOUNT; + } +} + +Connect.Pages = { + CONNECT_ACCOUNT: 'connect-account', +}; diff --git a/src/request/connect/ConnectApi.js b/src/request/connect/ConnectApi.js new file mode 100644 index 00000000..b44a7125 --- /dev/null +++ b/src/request/connect/ConnectApi.js @@ -0,0 +1,73 @@ +/* global TopLevelApi */ +/* global Connect */ +/* global Errors */ + +const PermissionKeyguardCommands = [ + /** @type {KeyguardRequest.KeyguardCommand} */ ('remove-key'), + /** @type {KeyguardRequest.KeyguardCommand} */ ('export'), + /** @type {KeyguardRequest.KeyguardCommand} */ ('change-password'), + // /** @type {KeyguardRequest.KeyguardCommand} */ ('sign-transaction'), // Already third-party whitelisted in Hub + /** @type {KeyguardRequest.KeyguardCommand} */ ('sign-multisig-transaction'), + // /** @type {KeyguardRequest.KeyguardCommand} */ ('sign-message'), // Already third-party whitelisted in Hub + /** @type {KeyguardRequest.KeyguardCommand} */ ('derive-address'), + // Bitcoin + /** @type {KeyguardRequest.KeyguardCommand} */ ('sign-btc-transaction'), + /** @type {KeyguardRequest.KeyguardCommand} */ ('derive-btc-xpub'), + // Swap + /** @type {KeyguardRequest.KeyguardCommand} */ ('sign-swap'), +]; + +/** @extends {TopLevelApi} */ +class ConnectApi extends TopLevelApi { // eslint-disable-line no-unused-vars + /** + * @param {KeyguardRequest.ConnectRequest} request + * @returns {Promise>} + */ + async parseRequest(request) { + if (!request) { + throw new Errors.InvalidRequestError('request is required'); + } + + const parsedRequest = {}; + parsedRequest.appName = this.parseAppName(request.appName); + parsedRequest.keyInfo = await this.parseKeyId(request.keyId); + parsedRequest.keyLabel = this.parseLabel(request.keyLabel); + parsedRequest.requestedKeyPaths = request.requestedKeyPaths.map( + path => this.parsePath(path, 'requestedKeyPaths'), + ); + parsedRequest.appLogoUrl = /** @type {URL} */ (this.parseLogoUrl(request.appLogoUrl, false, 'appLogoUrl')); + parsedRequest.permissions = this.parsePermissions(request.permissions); + parsedRequest.challenge = /** @type {string} */ (this.parseMessage(request.challenge, true)); + + // Temporary limitation of permissions until UI can represent other permissions + if (parsedRequest.permissions.length !== 1 || parsedRequest.permissions[0] !== 'sign-multisig-transaction') { + throw new Errors.InvalidRequestError( + 'Only the sign-multisig-transaction permission is supported currently', + ); + } + + return parsedRequest; + } + + /** + * @param {unknown} permissions + * @returns {KeyguardRequest.KeyguardCommand[]} + */ + parsePermissions(permissions) { + if (!Array.isArray(permissions)) { + throw new Errors.InvalidRequestError('permissions must be an array'); + } + + for (const permission of permissions) { + if (!PermissionKeyguardCommands.includes(permission)) { + throw new Errors.InvalidRequestError(`invalid permission requested: ${permission}`); + } + } + + return permissions; + } + + get Handler() { + return Connect; + } +} diff --git a/src/request/connect/index.html b/src/request/connect/index.html new file mode 100644 index 00000000..d01f3586 --- /dev/null +++ b/src/request/connect/index.html @@ -0,0 +1,115 @@ + + + + + + + + + Nimiq Keyguard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + +
+ + +
+ +

+ The connection is for approving only.
+ Your funds remain unaffected. +

+
+ + +
+ + + + +
+ + + + + +
+
+ + diff --git a/src/request/connect/index.js b/src/request/connect/index.js new file mode 100644 index 00000000..e2258374 --- /dev/null +++ b/src/request/connect/index.js @@ -0,0 +1,4 @@ +/* global ConnectApi */ +/* global runKeyguard */ + +runKeyguard(ConnectApi); diff --git a/src/request/connect/logo.svg b/src/request/connect/logo.svg new file mode 100644 index 00000000..3ffcaeb7 --- /dev/null +++ b/src/request/connect/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/request/sign-btc-transaction/SignBtcTransactionApi.js b/src/request/sign-btc-transaction/SignBtcTransactionApi.js index 9bd31e21..f34289b8 100644 --- a/src/request/sign-btc-transaction/SignBtcTransactionApi.js +++ b/src/request/sign-btc-transaction/SignBtcTransactionApi.js @@ -38,7 +38,7 @@ class SignBtcTransactionApi extends BitcoinRequestParserMixin(TopLevelApi) { if (request.layout === SignBtcTransactionApi.Layouts.CHECKOUT && parsedRequest.layout === SignBtcTransactionApi.Layouts.CHECKOUT) { parsedRequest.shopOrigin = this.parseShopOrigin(request.shopOrigin); - parsedRequest.shopLogoUrl = this.parseShopLogoUrl(request.shopLogoUrl); + parsedRequest.shopLogoUrl = this.parseLogoUrl(request.shopLogoUrl, true, 'shopLogoUrl'); if (parsedRequest.shopLogoUrl && parsedRequest.shopLogoUrl.origin !== parsedRequest.shopOrigin) { throw new Errors.InvalidRequestError('origin of shopLogoUrl must be same as shopOrigin'); } diff --git a/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js b/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js index 2d03b507..cdbb973d 100644 --- a/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js +++ b/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js @@ -35,7 +35,7 @@ class SignMultisigTransactionApi extends TopLevelApi { } // else if (request.layout === SignMultisigTransactionApi.Layouts.CHECKOUT // && parsedRequest.layout === SignMultisigTransactionApi.Layouts.CHECKOUT) { // parsedRequest.shopOrigin = this.parseShopOrigin(request.shopOrigin); - // parsedRequest.shopLogoUrl = this.parseShopLogoUrl(request.shopLogoUrl); + // parsedRequest.shopLogoUrl = this.parseLogoUrl(request.shopLogoUrl, true, 'shopLogoUrl); // if (parsedRequest.shopLogoUrl && parsedRequest.shopLogoUrl.origin !== parsedRequest.shopOrigin) { // throw new Errors.InvalidRequestError('origin of shopLogoUrl must be same as shopOrigin'); // } diff --git a/src/request/sign-transaction/SignTransactionApi.js b/src/request/sign-transaction/SignTransactionApi.js index 34c72ec3..46378a87 100644 --- a/src/request/sign-transaction/SignTransactionApi.js +++ b/src/request/sign-transaction/SignTransactionApi.js @@ -29,7 +29,7 @@ class SignTransactionApi extends TopLevelApi { } else if (request.layout === SignTransactionApi.Layouts.CHECKOUT && parsedRequest.layout === SignTransactionApi.Layouts.CHECKOUT) { parsedRequest.shopOrigin = this.parseShopOrigin(request.shopOrigin); - parsedRequest.shopLogoUrl = this.parseShopLogoUrl(request.shopLogoUrl); + parsedRequest.shopLogoUrl = this.parseLogoUrl(request.shopLogoUrl, true, 'shopLogoUrl'); if (parsedRequest.shopLogoUrl && parsedRequest.shopLogoUrl.origin !== parsedRequest.shopOrigin) { throw new Errors.InvalidRequestError('origin of shopLogoUrl must be same as shopOrigin'); } diff --git a/src/translations/de.json b/src/translations/de.json index dc61d490..0a765606 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -113,6 +113,7 @@ "passwordbox-repeat-password-short": "Das Passwort ist zu kurz", "passwordbox-password-skip": "Erstmal überspringen", "passwordbox-reset-password": "Mit Wiederherstellungswörtern zurücksetzen", + "passwordbox-connect-account": "Connect your account", "payment-info-line-order-amount": "Kaufbetrag", "payment-info-line-vendor-markup": "Krypto-Aufschlag des Verkäufers", @@ -252,5 +253,7 @@ "sign-swap-of-exchange-value": "des Tauschwerts.", "sign-swap-total-fees": "Gebühren gesamt", "sign-swap-exchange-rate": "Wechselkurs", - "sign-swap-your-bank": "Deine Bank" + "sign-swap-your-bank": "Deine Bank", + + "connect-heading-prefix": "Log in to" } diff --git a/src/translations/en.json b/src/translations/en.json index 002288ed..7fde2358 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -113,6 +113,7 @@ "passwordbox-repeat-password-short": "Password is too short", "passwordbox-password-skip": "Skip for now", "passwordbox-reset-password": "Reset with Recovery Words", + "passwordbox-connect-account": "Connect your account", "payment-info-line-order-amount": "Order amount", "payment-info-line-vendor-markup": "Vendor crypto markup", @@ -252,5 +253,7 @@ "sign-swap-of-exchange-value": "of swap value.", "sign-swap-total-fees": "Total fees", "sign-swap-exchange-rate": "Exchange rate", - "sign-swap-your-bank": "Your bank" + "sign-swap-your-bank": "Your bank", + + "connect-heading-prefix": "Log in to" } diff --git a/src/translations/es.json b/src/translations/es.json index c693b24c..79955571 100644 --- a/src/translations/es.json +++ b/src/translations/es.json @@ -113,6 +113,7 @@ "passwordbox-repeat-password-short": "La contraseña es muy corta", "passwordbox-password-skip": "Salar por ahora", "passwordbox-reset-password": "Reiniciar con Palabras de Recuperación", + "passwordbox-connect-account": "Connect your account", "payment-info-line-order-amount": "Monto de orden", "payment-info-line-vendor-markup": "Margen del vendedor en cripto", @@ -252,5 +253,7 @@ "sign-swap-of-exchange-value": "del valor del intercambio.", "sign-swap-total-fees": "Cuotas totales", "sign-swap-exchange-rate": "Tasa de cambio", - "sign-swap-your-bank": "Su banco" + "sign-swap-your-bank": "Su banco", + + "connect-heading-prefix": "Log in to" } diff --git a/src/translations/fr.json b/src/translations/fr.json index ac44cc40..c5e9ef92 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -113,6 +113,7 @@ "passwordbox-repeat-password-short": "Le mot de passe est trop court", "passwordbox-password-skip": "Ignorer pour l'instant", "passwordbox-reset-password": "Réinitialiser avec les mots de récupération", + "passwordbox-connect-account": "Connect your account", "payment-info-line-order-amount": "Montant de la commande", "payment-info-line-vendor-markup": "Crypto-premium", @@ -252,5 +253,7 @@ "sign-swap-of-exchange-value": "de la valeur du swap.", "sign-swap-total-fees": "Total des frais", "sign-swap-exchange-rate": "Taux de change", - "sign-swap-your-bank": "Votre banque" + "sign-swap-your-bank": "Votre banque", + + "connect-heading-prefix": "Log in to" } diff --git a/src/translations/nl.json b/src/translations/nl.json index eb608365..3e053d40 100644 --- a/src/translations/nl.json +++ b/src/translations/nl.json @@ -113,6 +113,7 @@ "passwordbox-repeat-password-short": "Wachtwoord is te kort", "passwordbox-password-skip": "Voor nu overslaan", "passwordbox-reset-password": "Resetten met herstelwoorden", + "passwordbox-connect-account": "Connect your account", "payment-info-line-order-amount": "Orderbedrag", "payment-info-line-vendor-markup": "Crypto leverancier opmaak", @@ -252,5 +253,7 @@ "sign-swap-of-exchange-value": "van ruilwaarde.", "sign-swap-total-fees": "Totale kosten", "sign-swap-exchange-rate": "Wisselkoers", - "sign-swap-your-bank": "Jouw bank" + "sign-swap-your-bank": "Jouw bank", + + "connect-heading-prefix": "Log in to" } diff --git a/src/translations/pt.json b/src/translations/pt.json index b49126ae..79a055ba 100644 --- a/src/translations/pt.json +++ b/src/translations/pt.json @@ -113,6 +113,7 @@ "passwordbox-repeat-password-short": "Palavra passe demasiado curta", "passwordbox-password-skip": "Passar por enquanto", "passwordbox-reset-password": "Redefinir com as Palavras de Recuperação", + "passwordbox-connect-account": "Connect your account", "payment-info-line-order-amount": "Valor do pedido", "payment-info-line-vendor-markup": "Margem de lucro do vendedor da criptomoeda", @@ -252,5 +253,7 @@ "sign-swap-of-exchange-value": "de troca de valor", "sign-swap-total-fees": "Taxas totais", "sign-swap-exchange-rate": "Taxa de câmbio", - "sign-swap-your-bank": "O teu banco" + "sign-swap-your-bank": "O teu banco", + + "connect-heading-prefix": "Log in to" } diff --git a/src/translations/ru.json b/src/translations/ru.json index b42cd2a0..a5928d8d 100644 --- a/src/translations/ru.json +++ b/src/translations/ru.json @@ -113,6 +113,7 @@ "passwordbox-repeat-password-short": "Пароль слишком короткий", "passwordbox-password-skip": "Пока отложить", "passwordbox-reset-password": "Сбросить, используя Защитную Фразу", + "passwordbox-connect-account": "Connect your account", "payment-info-line-order-amount": "Сумма заказа", "payment-info-line-vendor-markup": "Наценка вендора криптовалюты", @@ -252,5 +253,7 @@ "sign-swap-of-exchange-value": "от суммы обмена.", "sign-swap-total-fees": "Итого", "sign-swap-exchange-rate": "Exchange rate", - "sign-swap-your-bank": "Ваш банк" + "sign-swap-your-bank": "Ваш банк", + + "connect-heading-prefix": "Log in to" } diff --git a/src/translations/uk.json b/src/translations/uk.json index e16a85f6..acd07afe 100644 --- a/src/translations/uk.json +++ b/src/translations/uk.json @@ -113,6 +113,7 @@ "passwordbox-repeat-password-short": "Пароль занадто короткий", "passwordbox-password-skip": "Наразі пропустити", "passwordbox-reset-password": "Скинути за допомогою секретних слів", + "passwordbox-connect-account": "Connect your account", "payment-info-line-order-amount": "Сума замовлення", "payment-info-line-vendor-markup": "Націнка від постачальника", @@ -252,5 +253,7 @@ "sign-swap-of-exchange-value": "суми обміну.", "sign-swap-total-fees": "Сумарна комісія", "sign-swap-exchange-rate": "Курс обміну", - "sign-swap-your-bank": "Ваш банк" + "sign-swap-your-bank": "Ваш банк", + + "connect-heading-prefix": "Log in to" } diff --git a/src/translations/zh.json b/src/translations/zh.json index 3e10dcdc..6493f1f8 100644 --- a/src/translations/zh.json +++ b/src/translations/zh.json @@ -113,6 +113,7 @@ "passwordbox-repeat-password-short": "密码太短", "passwordbox-password-skip": "现在跳过", "passwordbox-reset-password": "使用助记词重置", + "passwordbox-connect-account": "Connect your account", "payment-info-line-order-amount": "订单数量", "payment-info-line-vendor-markup": "供应商加密货币加价", @@ -252,5 +253,7 @@ "sign-swap-of-exchange-value": "交换价值", "sign-swap-total-fees": "总费用", "sign-swap-exchange-rate": "Exchange rate", - "sign-swap-your-bank": "你的银行" + "sign-swap-your-bank": "你的银行", + + "connect-heading-prefix": "Log in to" } diff --git a/tests/lib/RequestParser.spec.js b/tests/lib/RequestParser.spec.js index 48ffa27f..310130d4 100644 --- a/tests/lib/RequestParser.spec.js +++ b/tests/lib/RequestParser.spec.js @@ -189,12 +189,12 @@ describe('RequestParser', () => { it('can parse shopLogoUrl', () => { const requestParser = new RequestParser(); - expect(() => requestParser.parseShopLogoUrl(5)).toThrow(); - expect(() => requestParser.parseShopLogoUrl({})).toThrow(); - expect(() => requestParser.parseShopLogoUrl('isThisAUrl?')).toThrow(); - expect(() => requestParser.parseShopLogoUrl('file:///home/maxmustermann/image.jpg')).toThrow(); + expect(() => requestParser.parseLogoUrl(5, true, 'shopLogoUrl')).toThrow(); + expect(() => requestParser.parseLogoUrl({}, true, 'shopLogoUrl')).toThrow(); + expect(() => requestParser.parseLogoUrl('isThisAUrl?', true, 'shopLogoUrl')).toThrow(); + expect(() => requestParser.parseLogoUrl('file:///home/maxmustermann/image.jpg', true, 'shopLogoUrl')).toThrow(); - expect(requestParser.parseShopLogoUrl(undefined)).toBe(undefined); + expect(requestParser.parseLogoUrl(undefined, true, 'shopLogoUrl')).toBe(undefined); const vectors = [ ['http://exampleshop.com/image.png', 'http://exampleshop.com/image.png'], @@ -203,7 +203,7 @@ describe('RequestParser', () => { ['moz-extension://cjpalhdlnbpafiamejdnhcphjbkeiagm/logo.jpg', 'moz-extension://cjpalhdlnbpafiamejdnhcphjbkeiagm/logo.jpg'], ] for (const vector of vectors) { - const parsedUrl = /** @type {URL} */ (requestParser.parseShopLogoUrl(vector[0])); + const parsedUrl = /** @type {URL} */ (requestParser.parseLogoUrl(vector[0], true, 'shopLogoUrl')); expect(parsedUrl.href).toEqual(vector[1]); } }); diff --git a/types/Keyguard.d.ts b/types/Keyguard.d.ts index fb005cea..739c0cd3 100644 --- a/types/Keyguard.d.ts +++ b/types/Keyguard.d.ts @@ -328,6 +328,12 @@ type Parsed = 'signer' | 'message', { signer: Nimiq.Address, message: Uint8Array | string } > : + T extends Is ? + Transform< + KeyId2KeyInfo, + 'appLogoUrl', + { appLogoUrl: URL } + > : T extends Is | Is | Is From 293763a9a7d5cae5aa8ad57d3f280eb94bbc2492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Tue, 18 Oct 2022 14:15:53 +0200 Subject: [PATCH 19/31] Extract LoginFileAccountIcon into a component --- src/assets/login-file-icon.svg | 10 ---- src/components/LoginFileAccountIcon.css | 10 ++++ src/components/LoginFileAccountIcon.js | 57 +++++++++++++++++++ src/request/connect/Connect.css | 13 +---- src/request/connect/Connect.js | 12 ++-- src/request/connect/index.html | 4 +- .../SignMultisigTransaction.css | 9 +-- .../SignMultisigTransaction.js | 11 ++-- .../sign-multisig-transaction/index.html | 4 +- 9 files changed, 86 insertions(+), 44 deletions(-) delete mode 100644 src/assets/login-file-icon.svg create mode 100644 src/components/LoginFileAccountIcon.css create mode 100644 src/components/LoginFileAccountIcon.js diff --git a/src/assets/login-file-icon.svg b/src/assets/login-file-icon.svg deleted file mode 100644 index 3612f480..00000000 --- a/src/assets/login-file-icon.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/components/LoginFileAccountIcon.css b/src/components/LoginFileAccountIcon.css new file mode 100644 index 00000000..f8067c45 --- /dev/null +++ b/src/components/LoginFileAccountIcon.css @@ -0,0 +1,10 @@ +.login-file-account-icon { + width: 6.25rem; + height: 10rem; + border-radius: 0.625rem; +} + +.login-file-account-icon img { + width: 100%; + height: 100%; +} diff --git a/src/components/LoginFileAccountIcon.js b/src/components/LoginFileAccountIcon.js new file mode 100644 index 00000000..f804b89d --- /dev/null +++ b/src/components/LoginFileAccountIcon.js @@ -0,0 +1,57 @@ +/* global LoginFileConfig */ +/* global IqonHash */ + +class LoginFileAccountIcon { // eslint-disable-line no-unused-vars + /** + * @param {string} [address] + * @param {HTMLDivElement} [$el] + */ + constructor(address, $el) { + this._address = address; + + this.$el = LoginFileAccountIcon._createElement($el); + + this._update(); + } + + /** + * @returns {HTMLDivElement} + */ + getElement() { + return this.$el; + } + + /** + * @param {string} address + */ + set address(address) { + this._address = address; + this._update(); + } + + /** + * @param {HTMLDivElement} [$el] + * @returns {HTMLDivElement} + */ + static _createElement($el) { + const $element = $el || document.createElement('div'); + $element.classList.add('login-file-account-icon'); + const $img = new Image(); + // Converted to data-URL with https://yoksel.github.io/url-encoder/ + // eslint-disable-next-line max-len + $img.src = "data:image/svg+xml,%3Csvg width='16' height='28' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg opacity='.5' fill='%23fff'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.36 18.85h-2a.34.34 0 0 1-.34-.34v-2.04c0-.18.15-.34.33-.34h2.01c.19 0 .34.16.34.34v2.04c0 .19-.15.34-.34.34Zm-1.59-2.04a.08.08 0 0 0-.08.09v1.19c0 .04.04.08.08.08h1.18c.04 0 .08-.04.08-.08v-1.2a.08.08 0 0 0-.08-.08H4.77ZM4.35 21.57h2.01c.19 0 .34.15.34.34v2.04c0 .18-.15.34-.34.34h-2a.34.34 0 0 1-.34-.34V21.9c0-.19.15-.34.33-.34Zm1.6 2.04c.04 0 .08-.04.08-.09v-1.19a.08.08 0 0 0-.08-.08H4.77a.08.08 0 0 0-.08.08v1.2c0 .04.04.08.08.08h1.18ZM9.71 16.13h2.01c.19 0 .34.16.34.34v2.04c0 .19-.15.34-.34.34h-2a.34.34 0 0 1-.34-.34v-2.04c0-.18.15-.34.33-.34Zm1.6 2.04c.04 0 .08-.04.08-.08v-1.2a.08.08 0 0 0-.08-.08h-1.18a.08.08 0 0 0-.08.09v1.19c0 .04.04.08.08.08h1.18Z'/%3E%3Cpath d='M7.37 17.58h.33c.14 0 .26-.12.26-.26a.25.25 0 0 0-.26-.25.08.08 0 0 1-.08-.09v-.5a.25.25 0 0 0-.25-.26.25.25 0 0 0-.25.25v.85c0 .14.11.26.25.26ZM8.37 16.73c.05 0 .09.04.09.08v1.87c0 .14.11.26.25.26s.25-.12.25-.26v-2.2a.25.25 0 0 0-.25-.26h-.34a.25.25 0 0 0-.25.25c0 .15.12.26.25.26ZM5.95 19.7c0 .14.1.26.25.26h1.17c.14 0 .25-.12.25-.26v-1.36a.25.25 0 0 0-.25-.25.25.25 0 0 0-.25.25v1.02c0 .05-.04.09-.09.09H6.2a.25.25 0 0 0-.25.25Z'/%3E%3Cpath d='M5.02 19.45a.25.25 0 0 0-.25.25v.85c0 .05-.03.09-.08.09h-.34a.25.25 0 0 0-.25.25c0 .14.12.25.25.25h4.36c.14 0 .25-.1.25-.25v-1.02a.25.25 0 0 0-.25-.25.25.25 0 0 0-.25.25v.68c0 .05-.04.09-.09.09H5.36a.08.08 0 0 1-.08-.09v-.85a.25.25 0 0 0-.26-.25ZM8.63 21.9a.25.25 0 0 0-.26-.25h-1a.25.25 0 0 0-.25.26v1.36c0 .14.11.25.25.25s.25-.11.25-.25v-1.02c0-.05.04-.09.08-.09h.67c.14 0 .26-.11.26-.25ZM11.72 23.7H8.88a.08.08 0 0 1-.09-.1v-.67a.25.25 0 0 0-.25-.26.25.25 0 0 0-.25.26v1.02c0 .14.11.25.25.25h3.18c.14 0 .26-.11.26-.25a.25.25 0 0 0-.26-.26Z'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.55 23.01h-1a.25.25 0 0 1-.25-.25v-1.02c0-.14.1-.26.25-.26h1c.14 0 .25.12.25.26v1.02c0 .14-.11.25-.25.25ZM9.88 22a.08.08 0 0 0-.08.09v.34c0 .05.04.08.08.08h.34c.04 0 .08-.03.08-.08v-.34a.08.08 0 0 0-.08-.09h-.34Z'/%3E%3Cpath d='M11.56 20.47a.25.25 0 0 0-.25.25v2.2c0 .15.1.26.25.26.14 0 .25-.11.25-.25v-2.21a.25.25 0 0 0-.25-.25ZM11.8 19.53a.25.25 0 0 0-.24-.25H9.7a.25.25 0 0 0-.25.25v1.02c0 .14.12.26.25.26.14 0 .26-.12.26-.26v-.68c0-.05.03-.08.08-.08h1.5c.15 0 .26-.12.26-.26Z'/%3E%3C/g%3E%3Ccircle opacity='.4' cx='7.97' cy='7.95' r='3.26' fill='%23fff'/%3E%3C/svg%3E%0A"; + while ($element.firstChild) { $element.removeChild($element.firstChild); } + $element.appendChild($img); + + return $element; + } + + _update() { + if (!this._address) { + return; + } + + const bgColorClassName = LoginFileConfig[IqonHash.getBackgroundColorIndex(this._address)].className; + this.$el.classList.add(bgColorClassName); + } +} diff --git a/src/request/connect/Connect.css b/src/request/connect/Connect.css index 0ea07e08..ac742f9b 100644 --- a/src/request/connect/Connect.css +++ b/src/request/connect/Connect.css @@ -26,22 +26,15 @@ height: 13.25rem; } -.app-icon-account-icon .login-file-icon { - width: 6.5rem; - height: 10rem; +.app-icon-account-icon .login-file-account-icon { border: 0.625rem solid white; + border-radius: 1.25rem; position: absolute; right: -0.625rem; bottom: -0.625rem; - border-radius: 1.25rem; -} - -.app-icon-account-icon .login-file-icon img { - width: 100%; - height: 100%; } -.app-icon-account-icon .login-file-icon.identicon { +.app-icon-account-icon .login-file-account-icon.identicon { border: none; width: 8rem; height: 8rem; diff --git a/src/request/connect/Connect.js b/src/request/connect/Connect.js index 02aa687b..7a732488 100644 --- a/src/request/connect/Connect.js +++ b/src/request/connect/Connect.js @@ -1,8 +1,7 @@ /* global Nimiq */ /* global Key */ /* global KeyStore */ -/* global IqonHash */ -/* global LoginFileConfig */ +/* global LoginFileAccountIcon */ /* global Identicon */ /* global PasswordBox */ /* global Utf8Tools */ @@ -35,15 +34,12 @@ class Connect { $appIcon.alt = `${request.appName} logo`; /** @type {HTMLDivElement} */ - const $loginFileIcon = ($page.querySelector('.login-file-icon')); + const $loginFileIcon = ($page.querySelector('.login-file-account-icon')); if (request.keyInfo.type === Nimiq.Secret.Type.ENTROPY) { - const bgColorClassName = LoginFileConfig[ - IqonHash.getBackgroundColorIndex(request.keyInfo.defaultAddress.toUserFriendlyAddress()) - ].className; - $loginFileIcon.classList.add(bgColorClassName); + // eslint-disable-next-line no-new + new LoginFileAccountIcon(request.keyInfo.defaultAddress.toUserFriendlyAddress(), $loginFileIcon); } else { // Show identicon for legacy accounts (which must be supported to support Team Nimiq Multisig) - $loginFileIcon.innerHTML = ''; // Remove LoginFile icon // eslint-disable-next-line no-new new Identicon(request.keyInfo.defaultAddress.toUserFriendlyAddress(), $loginFileIcon); } diff --git a/src/request/connect/index.html b/src/request/connect/index.html index d01f3586..cf74f43e 100644 --- a/src/request/connect/index.html +++ b/src/request/connect/index.html @@ -44,6 +44,7 @@ + @@ -55,6 +56,7 @@ + @@ -80,7 +82,7 @@

diff --git a/src/request/sign-multisig-transaction/SignMultisigTransaction.css b/src/request/sign-multisig-transaction/SignMultisigTransaction.css index 0340bdcd..31d4ae5a 100644 --- a/src/request/sign-multisig-transaction/SignMultisigTransaction.css +++ b/src/request/sign-multisig-transaction/SignMultisigTransaction.css @@ -177,7 +177,7 @@ /* white-space: nowrap; */ } -#confirm-transaction .user-and-account-names .login-file-icon { +#confirm-transaction .user-and-account-names .login-file-account-icon { flex-shrink: 0; width: 2rem; height: 3.5rem; @@ -185,16 +185,11 @@ margin: 0 0.5rem; } -#confirm-transaction .user-and-account-names .login-file-icon.identicon { +#confirm-transaction .user-and-account-names .login-file-account-icon.identicon { width: 3rem; margin: 0; } -#confirm-transaction .user-and-account-names .login-file-icon img { - width: 100%; - height: 100%; -} - #confirm-transaction .user-and-account-names .user-name, #confirm-transaction .user-and-account-names .account-name { flex-shrink: 0.5; diff --git a/src/request/sign-multisig-transaction/SignMultisigTransaction.js b/src/request/sign-multisig-transaction/SignMultisigTransaction.js index ca2f2237..9c2065a4 100644 --- a/src/request/sign-multisig-transaction/SignMultisigTransaction.js +++ b/src/request/sign-multisig-transaction/SignMultisigTransaction.js @@ -11,9 +11,8 @@ /* global Constants */ /* global NumberFormatting */ /* global I18n */ -/* global LoginFileConfig */ -/* global IqonHash */ /* global MultisigUtils */ +/* global LoginFileAccountIcon */ /* global Identicon */ /** @@ -154,12 +153,10 @@ class SignMultisigTransaction { // Set up account icon /** @type {HTMLDivElement} */ - const $loginFileIcon = ($nameSection.querySelector('.login-file-icon')); + const $loginFileIcon = ($nameSection.querySelector('.login-file-account-icon')); if (request.keyInfo.type === Nimiq.Secret.Type.ENTROPY) { - const bgColorClassName = LoginFileConfig[ - IqonHash.getBackgroundColorIndex(request.keyInfo.defaultAddress.toUserFriendlyAddress()) - ].className; - $loginFileIcon.classList.add(bgColorClassName); + // eslint-disable-next-line no-new + new LoginFileAccountIcon(request.keyInfo.defaultAddress.toUserFriendlyAddress(), $loginFileIcon); } else { // Show identicon for legacy accounts (which must be supported to support Team Nimiq Multisig) $loginFileIcon.innerHTML = ''; // Remove LoginFile icon diff --git a/src/request/sign-multisig-transaction/index.html b/src/request/sign-multisig-transaction/index.html index fde6828b..094a9a19 100644 --- a/src/request/sign-multisig-transaction/index.html +++ b/src/request/sign-multisig-transaction/index.html @@ -52,6 +52,7 @@ + @@ -70,6 +71,7 @@ + @@ -124,7 +126,7 @@

with Approving with - +

From 36d898e1539d1a5ec3d090e39094d42ba804e98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Wed, 19 Oct 2022 13:14:31 +0200 Subject: [PATCH 20/31] Refactor multisig request type structure --- client/src/PublicRequest.ts | 11 +++++--- demos/SignMultisigTransaction.html | 23 ++++++++-------- .../SignMultisigTransaction.js | 26 +++++++++---------- .../SignMultisigTransactionApi.js | 6 ++--- types/Keyguard.d.ts | 8 +++--- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/client/src/PublicRequest.ts b/client/src/PublicRequest.ts index a8fba8f2..a42dacff 100644 --- a/client/src/PublicRequest.ts +++ b/client/src/PublicRequest.ts @@ -191,7 +191,7 @@ export type SignTransactionRequest | SignTransactionRequestCheckout | SignTransactionRequestCashlink; -export type MultisigInfo = { +export type MultisigConfig = { publicKeys: Uint8Array[], numberOfSigners: number, signerPublicKeys: Uint8Array[], @@ -205,9 +205,12 @@ export type MultisigInfo = { userName?: string, }; -export type SignMultisigTransactionRequestCommon = Transform & MultisigInfo; +export type SignMultisigTransactionRequestCommon = Transform & { + multisigConfig: MultisigConfig, +}; export type SignMultisigTransactionRequestStandard = SignMultisigTransactionRequestCommon & { layout?: 'standard', diff --git a/demos/SignMultisigTransaction.html b/demos/SignMultisigTransaction.html index 08dc01d7..6e77ac7c 100644 --- a/demos/SignMultisigTransaction.html +++ b/demos/SignMultisigTransaction.html @@ -62,7 +62,7 @@
- + of
@@ -176,7 +176,7 @@ // Transaction information sender: MultisigUtils.calculateAddress(publicKeys, numberOfSigners).serialize(), - senderLabel: 'Multisig Account', + senderLabel: 'Our Multisig Wallet', recipient: Nimiq.Address.fromUserFriendlyAddress('NQ82 HP54 C9D4 2FAG 69QD 6Q71 LURR 5187 0V3X').serialize(), recipientLabel: 'Best Friend', value: txAmount, @@ -184,15 +184,16 @@ data: Nimiq.BufferUtils.fromUtf8(txData), validityStartHeight: 0, - // Multisig Config - publicKeys: publicKeys.map(key => key.serialize()), - numberOfSigners, - signerPublicKeys: publicKeys.slice(0, numberOfSigners).map(key => key.serialize()), - secret: { - aggregatedSecret: Nimiq.PrivateKey.generate().serialize(), // Using random data - }, - aggregatedCommitment: Nimiq.PrivateKey.generate().serialize(), // Using random data - userName, + multisigConfig: { + publicKeys: publicKeys.map(key => key.serialize()), + numberOfSigners, + signerPublicKeys: publicKeys.slice(0, numberOfSigners).map(key => key.serialize()), + secret: { + aggregatedSecret: Nimiq.PrivateKey.generate().serialize(), // Using random data + }, + aggregatedCommitment: Nimiq.PrivateKey.generate().serialize(), // Using random data + userName, + } }; // if (layout === 'checkout') { diff --git a/src/request/sign-multisig-transaction/SignMultisigTransaction.js b/src/request/sign-multisig-transaction/SignMultisigTransaction.js index 9c2065a4..dcb1df70 100644 --- a/src/request/sign-multisig-transaction/SignMultisigTransaction.js +++ b/src/request/sign-multisig-transaction/SignMultisigTransaction.js @@ -41,12 +41,12 @@ class SignMultisigTransaction { const $sender = (this.$el.querySelector('.accounts .sender')); this._senderAddressInfo = new AddressInfo({ userFriendlyAddress: transaction.sender.toUserFriendlyAddress(), - label: request.senderLabel || null, + label: request.senderLabel, imageUrl: null, - accountLabel: request.keyLabel || null, + accountLabel: null, multisig: { - signers: request.multisig.numberOfSigners, - participants: request.multisig.publicKeys.length, + signers: request.multisigConfig.numberOfSigners, + participants: request.multisigConfig.publicKeys.length, }, }); this._senderAddressInfo.renderTo($sender); @@ -139,11 +139,11 @@ class SignMultisigTransaction { // Set up user and account names /** @type {HTMLDivElement} */ const $nameSection = (this.$el.querySelector('.user-and-account-names')); - if (request.multisig.userName) { + if (request.multisigConfig.userName) { $nameSection.classList.add('approving-as'); /** @type {HTMLDivElement} */ const $userName = ($nameSection.querySelector('.user-name')); - $userName.textContent = request.multisig.userName; + $userName.textContent = request.multisigConfig.userName; } else { $nameSection.classList.add('approving-with'); } @@ -235,33 +235,33 @@ class SignMultisigTransaction { const publicKey = key.derivePublicKey(request.keyPath); // Verify publicKey is part of the signing public keys - if (!request.multisig.signerPublicKeys.find(pubKey => pubKey.equals(publicKey))) { + if (!request.multisigConfig.signerPublicKeys.find(pubKey => pubKey.equals(publicKey))) { reject(new Errors.InvalidRequestError('Selected key is not part of the multisig transaction signers')); return; } /** @type {Nimiq.RandomSecret} */ let aggregatedSecret; - if ('aggregatedSecret' in request.multisig.secret) { - aggregatedSecret = request.multisig.secret.aggregatedSecret; + if ('aggregatedSecret' in request.multisigConfig.secret) { + aggregatedSecret = request.multisigConfig.secret.aggregatedSecret; } else { // If we only have encrypted secrets, decrypt them and aggregate them with the bScalar const rsaKey = await key.getRsaPrivateKey(); - const secrets = await Promise.all(request.multisig.secret.encryptedSecrets.map( + const secrets = await Promise.all(request.multisigConfig.secret.encryptedSecrets.map( async encrypted => new Uint8Array( await window.crypto.subtle.decrypt({ name: 'RSA-OAEP' }, rsaKey, encrypted), ), )); - aggregatedSecret = await MultisigUtils.aggregateSecrets(secrets, request.multisig.secret.bScalar); + aggregatedSecret = await MultisigUtils.aggregateSecrets(secrets, request.multisigConfig.secret.bScalar); } const signature = key.signPartially( request.keyPath, request.transaction.serializeContent(), - request.multisig.signerPublicKeys, + request.multisigConfig.signerPublicKeys, aggregatedSecret, - request.multisig.aggregatedCommitment, + request.multisigConfig.aggregatedCommitment, ); /** @type {KeyguardRequest.SignMultisigTransactionResult} */ diff --git a/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js b/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js index cdbb973d..87652f26 100644 --- a/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js +++ b/src/request/sign-multisig-transaction/SignMultisigTransactionApi.js @@ -22,12 +22,12 @@ class SignMultisigTransactionApi extends TopLevelApi { parsedRequest.keyInfo = await this.parseKeyId(request.keyId); parsedRequest.keyLabel = /** @type {string} */ (this.parseLabel(request.keyLabel, false, 'keyLabel')); parsedRequest.keyPath = this.parsePath(request.keyPath, 'keyPath'); - parsedRequest.senderLabel = this.parseLabel(request.senderLabel); + parsedRequest.senderLabel = /** @type {string} */ (this.parseLabel(request.senderLabel, false, 'senderLabel')); parsedRequest.transaction = this.parseTransaction(request); - parsedRequest.multisig = this.parseMultisigConfig(request); + parsedRequest.multisigConfig = this.parseMultisigConfig(request.multisigConfig); parsedRequest.layout = this.parseLayout(request.layout); - this.verifyMultisigAddress(parsedRequest.transaction, parsedRequest.multisig); + this.verifyMultisigAddress(parsedRequest.transaction, parsedRequest.multisigConfig); if ((!request.layout || request.layout === SignMultisigTransactionApi.Layouts.STANDARD) && parsedRequest.layout === SignMultisigTransactionApi.Layouts.STANDARD) { diff --git a/types/Keyguard.d.ts b/types/Keyguard.d.ts index 739c0cd3..302fd16b 100644 --- a/types/Keyguard.d.ts +++ b/types/Keyguard.d.ts @@ -226,11 +226,9 @@ type ConstructTransaction = Transform 'value' | 'fee' | 'validityStartHeight' | 'flags', { transaction: Nimiq.Transaction }> -type ConstructMultisigTransaction - = ConstructTransaction - >; +type ConstructMultisigTransaction = ConstructTransaction>; type ConstructSwap = Transform Date: Fri, 21 Oct 2022 08:38:59 +0200 Subject: [PATCH 21/31] Add comment about permission additions --- src/request/connect/ConnectApi.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/request/connect/ConnectApi.js b/src/request/connect/ConnectApi.js index b44a7125..c68b8f22 100644 --- a/src/request/connect/ConnectApi.js +++ b/src/request/connect/ConnectApi.js @@ -40,6 +40,8 @@ class ConnectApi extends TopLevelApi { // eslint-disable-line no-unused-vars parsedRequest.challenge = /** @type {string} */ (this.parseMessage(request.challenge, true)); // Temporary limitation of permissions until UI can represent other permissions + // When adding new permissioned request types here, the Connect UI must be updated to be able + // to display these permissions to the user. if (parsedRequest.permissions.length !== 1 || parsedRequest.permissions[0] !== 'sign-multisig-transaction') { throw new Errors.InvalidRequestError( 'Only the sign-multisig-transaction permission is supported currently', From 05932208a905e2061d393d978880fed619a72412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Fri, 21 Oct 2022 11:01:06 +0200 Subject: [PATCH 22/31] Narrow encryptionKey algorithm type --- client/src/PublicRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/PublicRequest.ts b/client/src/PublicRequest.ts index a42dacff..81ea43d3 100644 --- a/client/src/PublicRequest.ts +++ b/client/src/PublicRequest.ts @@ -535,7 +535,7 @@ export type ConnectResult = { encryptionKey: { format: 'spki', keyData: Uint8Array, - algorithm: RsaHashedImportParams, + algorithm: { name: string, hash: string }, keyUsages: ['encrypt'], }, }; From 63bf825c002a7a224925c8578cf08fea161271ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Mon, 24 Oct 2022 13:39:17 +0200 Subject: [PATCH 23/31] Add sandboxed iframe files to dist during build --- tools/build.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/build.sh b/tools/build.sh index a611161b..818d766c 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -148,6 +148,7 @@ mkdir -p dist/request mkdir -p dist/assets/nimiq mkdir -p dist/assets/albatross mkdir -p dist/lib +mkdir -p dist/lib/rsa/sandboxed # bundle names JS_BUNDLE="index.HASH.js" @@ -365,6 +366,7 @@ cp -v favicon.ico dist cp -rv src/assets/* dist/assets/ cp -v src/lib/QrScannerWorker.js dist/lib/ cp -v node_modules/@nimiq/style/nimiq-style.icons.svg dist/assets/ +cp -v src/lib/rsa/sandboxed/* dist/lib/rsa/sandboxed/ # copy service worker (which has to be in root to work) cp -v src/service-worker/ServiceWorker.js dist From edb4c2d9b06511a79f79701f0d9b90233c2fb621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Mon, 24 Oct 2022 14:16:14 +0200 Subject: [PATCH 24/31] Extract inline-script from RSAKeysIframe Because CSP doesn't allow inline-scripts. This required getting the types for node-forge correct, too. --- package.json | 1 + src/lib/rsa/sandboxed/RSAKeysIframe.html | 95 +----------------------- src/lib/rsa/sandboxed/RSAKeysIframe.js | 86 +++++++++++++++++++++ types/node-forge.d.ts | 4 + yarn.lock | 7 ++ 5 files changed, 100 insertions(+), 93 deletions(-) create mode 100644 src/lib/rsa/sandboxed/RSAKeysIframe.js create mode 100644 types/node-forge.d.ts diff --git a/package.json b/package.json index 8a3a5b3d..5d58dc15 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@opengsn/common": "^2.2.5", "@types/jasmine": "^2.8.8", "@types/node": "^10.5.2", + "@types/node-forge": "^1.3.0", "bitcoinjs-lib": "5.2.0", "browserify": "^16.5.0", "buffer": "5.6.0", diff --git a/src/lib/rsa/sandboxed/RSAKeysIframe.html b/src/lib/rsa/sandboxed/RSAKeysIframe.html index f62d8e3c..495f347f 100644 --- a/src/lib/rsa/sandboxed/RSAKeysIframe.html +++ b/src/lib/rsa/sandboxed/RSAKeysIframe.html @@ -4,101 +4,10 @@ RSA Keys Iframe | Keyguard Demo - - - RSA Keys Iframe - - + diff --git a/src/lib/rsa/sandboxed/RSAKeysIframe.js b/src/lib/rsa/sandboxed/RSAKeysIframe.js new file mode 100644 index 00000000..b3f46f34 --- /dev/null +++ b/src/lib/rsa/sandboxed/RSAKeysIframe.js @@ -0,0 +1,86 @@ +/* global forge */ + +/** + * @typedef {{command: 'generateKey', seed: string, keySize: number}} GenerateKeyCommand + * ...add new command types here and add them to the `Command` union type below + * + * @typedef {GenerateKeyCommand} Command + */ + +/** @type {HTMLSpanElement} */ +const $loading = (document.querySelector('#loading')); + +/** + * @param {any} key + * @returns {ArrayBuffer} + */ +function key2ArrayBuffer(key) { + const bytes = /** @type {string} */ (forge.asn1.toDer(key).getBytes()); + const buffer = new ArrayBuffer(bytes.length); + const view = new Uint8Array(buffer); + for (let i = 0; i < view.length; i++) { + view[i] = bytes.charCodeAt(i); + } + return buffer; +} + +window.addEventListener('message', async event => { + if (event.source === event.target) { + // console.log("Ignored same-window event:", event); + return; + } + + /** @type {Command} */ + const data = event.data; + if (!('command' in data) || data.command !== 'generateKey') return; + console.debug('RSA iframe MSG:', data); + + $loading.textContent = '(generating...)'; + await new Promise(res => window.setTimeout(res, 50)); // Give DOM time to update + + // PRNG-seeded RSA key generation + const prng = forge.random.createInstance(); + prng.seedFileSync = needed => { + // console.log('Need', needed, 'bytes of randomness, have', data.seed.length); + let seed = ''; + while (seed.length < needed) { + seed += data.seed; + } + return seed.substring(0, needed); + }; + + + /** + * The async version of Forge's pki.rsa.generateKeyPair uses WebWorkers. Sadly, those cannot be + * created in sandboxed iframes without `allow-same-origin` (https://stackoverflow.com/a/30567964/4204380). + */ + const keyPair = forge.pki.rsa.generateKeyPair({ bits: data.keySize, prng }); + // const keyPair = await new Promise((resolve, reject) => forge.pki.rsa.generateKeyPair( + // { bits: data.keySize, prng, workers: -1, workerScript: './node-forge/prime.worker.min.js' }, + // (error, keyPair) => { + // if (error) { + // reject(error); + // return; + // } + + // resolve(keyPair); + // }, + // )); + + // console.log({ keyPair }); + + // Export to ASN1 notation + const rsaPrivateKey = forge.pki.privateKeyToAsn1(keyPair.privateKey); + // Wrap into PKCS#8 format (to be understandable by `window.crypto.subtle.importKey`) + const privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey); + + // Export to ASN1 notation (already in SPKI format) + const publicKeyInfo = forge.pki.publicKeyToAsn1(keyPair.publicKey); + + /** @type {Window} */ (event.source).postMessage({ + privateKey: key2ArrayBuffer(privateKeyInfo), + publicKey: key2ArrayBuffer(publicKeyInfo), + }, '*'); + + $loading.textContent = ''; +}); diff --git a/types/node-forge.d.ts b/types/node-forge.d.ts new file mode 100644 index 00000000..dd160c0f --- /dev/null +++ b/types/node-forge.d.ts @@ -0,0 +1,4 @@ +import _forge from 'node-forge'; + +export as namespace forge; +export = _forge; diff --git a/yarn.lock b/yarn.lock index 7dda8d71..f52b8fd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -576,6 +576,13 @@ dependencies: "@types/node" "*" +"@types/node-forge@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.0.tgz#c655e951e0fb5c4b53c9f4746c2128d4f93002fd" + integrity sha512-yUsIEHG3d81E2c+akGjZAMdVcjbfqMzpMjvpebnTO7pEGfqxCtBzpRV52kR1RETtwJ9fHkLdEjtaM+uMKWpwFA== + dependencies: + "@types/node" "*" + "@types/node@*", "@types/node@>=10.0.0": version "18.11.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" From b1ea4a9dd140624455e93ebb8ed62a62dba40074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Mon, 22 May 2023 12:08:38 +0200 Subject: [PATCH 25/31] Add explainer tooltip to connect UI --- src/request/connect/Connect.css | 26 ++++++++++++++++++++++++++ src/request/connect/index.html | 19 +++++++++++++++++-- src/translations/de.json | 5 ++++- src/translations/en.json | 5 ++++- src/translations/es.json | 5 ++++- src/translations/fr.json | 5 ++++- src/translations/nl.json | 5 ++++- src/translations/pt.json | 5 ++++- src/translations/ru.json | 5 ++++- src/translations/uk.json | 5 ++++- src/translations/zh.json | 5 ++++- 11 files changed, 79 insertions(+), 11 deletions(-) diff --git a/src/request/connect/Connect.css b/src/request/connect/Connect.css index ac742f9b..78de9bbc 100644 --- a/src/request/connect/Connect.css +++ b/src/request/connect/Connect.css @@ -48,6 +48,32 @@ line-height: 1.375; } +.tooltip { + display: inline-block; + vertical-align: middle; +} + +.tooltip .nq-icon { + display: block; + opacity: 0.3; +} + +.tooltip-box { + width: 28rem; + text-align: left; + right: -2.5rem; +} + +.tooltip-box p { + margin: 0; + font-size: 2rem; + line-height: 1.3; +} + +.tooltip-box p + p { + margin-top: 1rem; +} + .password-box { margin-top: 1rem; } diff --git a/src/request/connect/index.html b/src/request/connect/index.html index cf74f43e..2e673077 100644 --- a/src/request/connect/index.html +++ b/src/request/connect/index.html @@ -56,6 +56,7 @@ + @@ -87,10 +88,24 @@

-

+

The connection is for approving only.
Your funds remain unaffected. -

+
+ +
+

+ Your account will be used to verify your identity only. +

+

+ The shared wallet has its own address. It can receive transactions from anywhere just like a regular wallet. +

+

+ The first address of your account will be visible to all participants in your shared wallet. +

+
+
+