Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 72x 1272x 1272x 636x 555x 288x 288x 514x 5140x 5140x 5140x 1439x 27x 1x 8996x 8996x 8996x 8996x 8996x 1099x 1099x 1099x 1099x 514x 5x 5x 514x 291x 291x 2907x 2907x 5037x 5037x 2115x 2115x 2115x 2907x 792x 291x 72x 72x 719x 719x 719x 72x 151x 151x 1509x 1509x 1469x 1469x 754x 754x 1509x 755x 1510x 1510x 752x 752x 752x 1509x 3x 151x 27x 27x 27x 27x 261x 261x 261x 27x 27x | "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPreferredCandidate = getPreferredCandidate;
exports.orderPreferredFirst = orderPreferredFirst;
exports.requireCandidate = requireCandidate;
exports.getPresentPayloadFields = getPresentPayloadFields;
exports.isUnencryptedCompromisedANullString = isUnencryptedCompromisedANullString;
exports.rawDecryptJsonField = rawDecryptJsonField;
exports.emptyPayloadResult = emptyPayloadResult;
exports.decryptPayloadPerCandidate = decryptPayloadPerCandidate;
exports.decryptPayloadWithKey = decryptPayloadWithKey;
exports.decryptPayloadWithKeyThenCandidates = decryptPayloadWithKeyThenCandidates;
exports.decryptRecoverablePayload = decryptRecoverablePayload;
const PassmanCrypto_1 = require("../PassmanCrypto");
const types_1 = require("./types");
function getPreferredCandidate(candidates) {
return candidates.find((candidate) => candidate.preferred) ?? candidates[0];
}
function orderPreferredFirst(candidates, preferredKeyId) {
const preferred = candidates.filter((candidate) => candidate.id === preferredKeyId);
const rest = candidates.filter((candidate) => candidate.id !== preferredKeyId);
return [...preferred, ...rest];
}
function requireCandidate(candidates, id, description) {
const candidate = id !== undefined ? candidates.find((c) => c.id === id) : undefined;
Iif (!candidate) {
throw new Error(`No ${description} key candidate found (id: ${id}).`);
}
return candidate;
}
function getPresentPayloadFields(encrypted) {
return types_1.PAYLOAD_FIELDS.filter((field) => {
const value = encrypted[field];
Iif (value === undefined || value === null) {
return false;
}
// unencrypted `"null"` sentinel is handled separately; do not probe it as ciphertext
return !(field === 'compromised' && value === 'null');
});
}
/**
* Past bug: `compromised` was stored as the unencrypted sentinel string `"null"` (same shape as a missing `shared_key`) instead of ciphertext.
* Decrypt-side callers treat this as `false`; repair persists encrypted `false`.
*/
function isUnencryptedCompromisedANullString(encrypted) {
return encrypted.compromised === 'null';
}
function applyUnencryptedNullCompromisedDefault(encrypted, decryptedData) {
if (isUnencryptedCompromisedANullString(encrypted)) {
decryptedData.compromised = false;
}
}
function rawDecryptJsonField(encrypted, field, key) {
Iif (field === 'compromised' && isUnencryptedCompromisedANullString(encrypted)) {
return false;
}
const ciphertext = encrypted[field];
Iif (ciphertext === undefined || ciphertext === null) {
return null;
}
Iif (typeof ciphertext !== 'string') {
throw new Error('Ciphertext is not a string');
}
return JSON.parse(PassmanCrypto_1.PassmanCrypto.decryptString(ciphertext, key));
}
function emptyPayloadResult(label) {
const recoveredPreview = {};
Eif (label !== undefined) {
recoveredPreview.label = label;
}
return { fieldKeyIds: {}, recoveredPreview, brokenFields: [], coercedCompromised: false };
}
function finishPayloadDecrypt(encrypted, result) {
if (isUnencryptedCompromisedANullString(encrypted)) {
result.recoveredPreview.compromised = false;
result.coercedCompromised = true;
}
return result;
}
/**
* Tries each present payload field against the given candidates in order.
* Mixed keys are allowed: a field keeps the first candidate that decrypts it.
* Fields that decrypt with none are listed in `brokenFields`.
*/
function decryptPayloadPerCandidate(encrypted, candidates, label) {
const result = emptyPayloadResult(label);
for (const field of getPresentPayloadFields(encrypted)) {
let decrypted = false;
for (const candidate of candidates) {
try {
// @ts-ignore transitively gated by PAYLOAD_FIELDS / ENCRYPTED_FIELDS
result.recoveredPreview[field] = rawDecryptJsonField(encrypted, field, candidate.password);
result.fieldKeyIds[field] = candidate.id;
decrypted = true;
break;
}
catch (e) {
// try next candidate
}
}
if (!decrypted) {
result.brokenFields.push(field);
}
}
return finishPayloadDecrypt(encrypted, result);
}
/**
* Decrypts present payload fields with a single payload key (shared_key plaintext or ACL key).
*/
function decryptPayloadWithKey(encrypted, payloadKey, payloadKeyId, label) {
const result = emptyPayloadResult(label);
for (const field of getPresentPayloadFields(encrypted)) {
try {
// @ts-ignore gated by PAYLOAD_FIELDS / ENCRYPTED_FIELDS
result.recoveredPreview[field] = rawDecryptJsonField(encrypted, field, payloadKey);
result.fieldKeyIds[field] = payloadKeyId;
}
catch (e) {
result.brokenFields.push(field);
}
}
return finishPayloadDecrypt(encrypted, result);
}
/**
* Decrypts each present payload field with `payloadKey` first (the key a normal client would use),
* then with vault candidates for anything that key does not open.
* `payloadKeyId` is stored on fields the first key opened (for example `__shared_key__`).
*/
function decryptPayloadWithKeyThenCandidates(encrypted, payloadKey, payloadKeyId, candidates, label) {
const result = emptyPayloadResult(label);
for (const field of getPresentPayloadFields(encrypted)) {
let decrypted = false;
if (payloadKey !== undefined && payloadKey !== '') {
try {
// @ts-ignore gated by PAYLOAD_FIELDS / ENCRYPTED_FIELDS
result.recoveredPreview[field] = rawDecryptJsonField(encrypted, field, payloadKey);
result.fieldKeyIds[field] = payloadKeyId;
decrypted = true;
}
catch (e) {
// field is not under the client-preferred key; try vault candidates below
}
}
if (!decrypted) {
for (const candidate of candidates) {
try {
// @ts-ignore transitively gated by PAYLOAD_FIELDS / ENCRYPTED_FIELDS
result.recoveredPreview[field] = rawDecryptJsonField(encrypted, field, candidate.password);
result.fieldKeyIds[field] = candidate.id;
decrypted = true;
break;
}
catch (e) {
// try next candidate
}
}
}
if (!decrypted) {
result.brokenFields.push(field);
}
}
return finishPayloadDecrypt(encrypted, result);
}
/**
* Decrypts recoverable payload fields for repair.
*
* Uses {@link CredentialRecoveryHitInterface.fieldKeyIds} when present (partial / mixed-key);
* otherwise decrypts every payload field with `hit.workingKeyId` and throws on the first failure.
* `extraKeys` maps synthetic ids such as `__shared_key__` to the plaintext those fields were encrypted with.
*/
function decryptRecoverablePayload(encrypted, hit, candidates, extraKeys) {
const decryptedData = {};
const fieldIds = hit.fieldKeyIds;
Eif (fieldIds !== undefined && Object.keys(fieldIds).length > 0) {
for (const field of Object.keys(fieldIds)) {
const keyId = fieldIds[field];
const key = extraKeys?.[keyId] ?? requireCandidate(candidates, keyId, 'field').password;
// @ts-ignore gated by PAYLOAD_FIELDS
decryptedData[field] = rawDecryptJsonField(encrypted, field, key);
}
applyUnencryptedNullCompromisedDefault(encrypted, decryptedData);
return decryptedData;
}
const fromCandidate = requireCandidate(candidates, hit.workingKeyId, 'working');
for (const field of types_1.PAYLOAD_FIELDS) {
// @ts-ignore gated by PAYLOAD_FIELDS
decryptedData[field] = rawDecryptJsonField(encrypted, field, fromCandidate.password);
}
applyUnencryptedNullCompromisedDefault(encrypted, decryptedData);
return decryptedData;
}
|