feat: complete Ancestor quote bot with production-ready Docker support
This commit is contained in:
134
node_modules/matrix-js-sdk/lib/secret-storage.js
generated
vendored
134
node_modules/matrix-js-sdk/lib/secret-storage.js
generated
vendored
@@ -1,3 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ServerSideSecretStorageImpl = exports.SECRET_STORAGE_ALGORITHM_V1_AES = void 0;
|
||||
exports.trimTrailingEquals = trimTrailingEquals;
|
||||
var _client = require("./client");
|
||||
var _aes = require("./crypto/aes");
|
||||
var _randomstring = require("./randomstring");
|
||||
var _logger = require("./logger");
|
||||
/*
|
||||
Copyright 2021-2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
@@ -20,11 +31,7 @@ limitations under the License.
|
||||
* @see https://spec.matrix.org/v1.6/client-server-api/#storage
|
||||
*/
|
||||
|
||||
import { secureRandomString } from "./randomstring.js";
|
||||
import { logger } from "./logger.js";
|
||||
import encryptAESSecretStorageItem from "./utils/encryptAESSecretStorageItem.js";
|
||||
import decryptAESSecretStorageItem from "./utils/decryptAESSecretStorageItem.js";
|
||||
export const SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha2";
|
||||
const SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha2";
|
||||
|
||||
/**
|
||||
* Common base interface for Secret Storage Keys.
|
||||
@@ -75,23 +82,13 @@ export const SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha
|
||||
* Application callbacks for use with {@link SecretStorage.ServerSideSecretStorageImpl}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Account Data event types which can store secret-storage-encrypted information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Account Data event content type for storing secret-storage-encrypted information.
|
||||
*
|
||||
* See https://spec.matrix.org/v1.13/client-server-api/#msecret_storagev1aes-hmac-sha2-1
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface provided by SecretStorage implementations
|
||||
*
|
||||
* Normally this will just be an {@link ServerSideSecretStorageImpl}, but for backwards
|
||||
* compatibility some methods allow other implementations.
|
||||
*/
|
||||
|
||||
exports.SECRET_STORAGE_ALGORITHM_V1_AES = SECRET_STORAGE_ALGORITHM_V1_AES;
|
||||
/**
|
||||
* Implementation of Server-side secret storage.
|
||||
*
|
||||
@@ -100,7 +97,7 @@ export const SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha
|
||||
*
|
||||
* @see https://spec.matrix.org/v1.6/client-server-api/#storage
|
||||
*/
|
||||
export class ServerSideSecretStorageImpl {
|
||||
class ServerSideSecretStorageImpl {
|
||||
/**
|
||||
* Construct a new `SecretStorage`.
|
||||
*
|
||||
@@ -124,21 +121,30 @@ export class ServerSideSecretStorageImpl {
|
||||
async getDefaultKeyId() {
|
||||
const defaultKey = await this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.default_key");
|
||||
if (!defaultKey) return null;
|
||||
return defaultKey.key ?? null;
|
||||
return defaultKey.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link ServerSideSecretStorage#setDefaultKeyId}.
|
||||
* Set the default key ID for encrypting secrets.
|
||||
*
|
||||
* @param keyId - The new default key ID
|
||||
*/
|
||||
async setDefaultKeyId(keyId) {
|
||||
// The spec [1] says that the value of the account data entry should be an object with a `key` property.
|
||||
// It doesn't specify how to delete the default key; we do it by setting the account data to an empty object.
|
||||
//
|
||||
// [1]: https://spec.matrix.org/v1.13/client-server-api/#key-storage
|
||||
const newValue = keyId === null ? {} : {
|
||||
key: keyId
|
||||
};
|
||||
await this.accountDataAdapter.setAccountData("m.secret_storage.default_key", newValue);
|
||||
setDefaultKeyId(keyId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const listener = ev => {
|
||||
if (ev.getType() === "m.secret_storage.default_key" && ev.getContent().key === keyId) {
|
||||
this.accountDataAdapter.removeListener(_client.ClientEvent.AccountData, listener);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
this.accountDataAdapter.on(_client.ClientEvent.AccountData, listener);
|
||||
this.accountDataAdapter.setAccountData("m.secret_storage.default_key", {
|
||||
key: keyId
|
||||
}).catch(e => {
|
||||
this.accountDataAdapter.removeListener(_client.ClientEvent.AccountData, listener);
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +160,7 @@ export class ServerSideSecretStorageImpl {
|
||||
* keyId: the ID of the key
|
||||
* keyInfo: details about the key (iv, mac, passphrase)
|
||||
*/
|
||||
async addKey(algorithm, opts, keyId) {
|
||||
async addKey(algorithm, opts = {}, keyId) {
|
||||
if (algorithm !== SECRET_STORAGE_ALGORITHM_V1_AES) {
|
||||
throw new Error(`Unknown key algorithm ${algorithm}`);
|
||||
}
|
||||
@@ -167,17 +173,19 @@ export class ServerSideSecretStorageImpl {
|
||||
if (opts.passphrase) {
|
||||
keyInfo.passphrase = opts.passphrase;
|
||||
}
|
||||
const {
|
||||
iv,
|
||||
mac
|
||||
} = await calculateKeyCheck(opts.key);
|
||||
keyInfo.iv = iv;
|
||||
keyInfo.mac = mac;
|
||||
if (opts.key) {
|
||||
const {
|
||||
iv,
|
||||
mac
|
||||
} = await (0, _aes.calculateKeyCheck)(opts.key);
|
||||
keyInfo.iv = iv;
|
||||
keyInfo.mac = mac;
|
||||
}
|
||||
|
||||
// Create a unique key id. XXX: this is racey.
|
||||
if (!keyId) {
|
||||
do {
|
||||
keyId = secureRandomString(32);
|
||||
keyId = (0, _randomstring.randomString)(32);
|
||||
} while (await this.accountDataAdapter.getAccountDataFromServer(`m.secret_storage.key.${keyId}`));
|
||||
}
|
||||
await this.accountDataAdapter.setAccountData(`m.secret_storage.key.${keyId}`, keyInfo);
|
||||
@@ -203,7 +211,7 @@ export class ServerSideSecretStorageImpl {
|
||||
if (!keyId) {
|
||||
return null;
|
||||
}
|
||||
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer(`m.secret_storage.key.${keyId}`);
|
||||
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.key." + keyId);
|
||||
return keyInfo ? [keyId, keyInfo] : null;
|
||||
}
|
||||
|
||||
@@ -232,7 +240,7 @@ export class ServerSideSecretStorageImpl {
|
||||
if (info.mac) {
|
||||
const {
|
||||
mac
|
||||
} = await calculateKeyCheck(key, info.iv);
|
||||
} = await (0, _aes.calculateKeyCheck)(key, info.iv);
|
||||
return trimTrailingEquals(info.mac) === trimTrailingEquals(mac);
|
||||
} else {
|
||||
// if we have no information, we have to assume the key is right
|
||||
@@ -244,14 +252,17 @@ export class ServerSideSecretStorageImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link ServerSideSecretStorage#store}.
|
||||
* Store an encrypted secret on the server.
|
||||
*
|
||||
* Details of the encryption keys to be used must previously have been stored in account data
|
||||
* (for example, via {@link ServerSideSecretStorageImpl#addKey}. {@link SecretStorageCallbacks#getSecretStorageKey} will be called to obtain a secret storage
|
||||
* key to decrypt the secret.
|
||||
*
|
||||
* @param name - The name of the secret - i.e., the "event type" to be stored in the account data
|
||||
* @param secret - The secret contents.
|
||||
* @param keys - The IDs of the keys to use to encrypt the secret, or null/undefined to use the default key.
|
||||
*/
|
||||
async store(name, secret, keys) {
|
||||
if (secret === null) {
|
||||
// remove secret
|
||||
await this.accountDataAdapter.setAccountData(name, {});
|
||||
return;
|
||||
}
|
||||
const encrypted = {};
|
||||
if (!keys) {
|
||||
const defaultKeyId = await this.getDefaultKeyId();
|
||||
@@ -265,7 +276,7 @@ export class ServerSideSecretStorageImpl {
|
||||
}
|
||||
for (const keyId of keys) {
|
||||
// get key information from key storage
|
||||
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer(`m.secret_storage.key.${keyId}`);
|
||||
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.key." + keyId);
|
||||
if (!keyInfo) {
|
||||
throw new Error("Unknown key: " + keyId);
|
||||
}
|
||||
@@ -278,7 +289,7 @@ export class ServerSideSecretStorageImpl {
|
||||
const [, encryption] = await this.getSecretStorageKey(keys, name);
|
||||
encrypted[keyId] = await encryption.encrypt(secret);
|
||||
} else {
|
||||
logger.warn("unknown algorithm for secret storage key " + keyId + ": " + keyInfo.algorithm);
|
||||
_logger.logger.warn("unknown algorithm for secret storage key " + keyId + ": " + keyInfo.algorithm);
|
||||
// do nothing if we don't understand the encryption algorithm
|
||||
}
|
||||
}
|
||||
@@ -313,10 +324,10 @@ export class ServerSideSecretStorageImpl {
|
||||
const keys = {};
|
||||
for (const keyId of Object.keys(secretInfo.encrypted)) {
|
||||
// get key information from key storage
|
||||
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer(`m.secret_storage.key.${keyId}`);
|
||||
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.key." + keyId);
|
||||
const encInfo = secretInfo.encrypted[keyId];
|
||||
// only use keys we understand the encryption algorithm of
|
||||
if (keyInfo?.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
|
||||
if ((keyInfo === null || keyInfo === void 0 ? void 0 : keyInfo.algorithm) === SECRET_STORAGE_ALGORITHM_V1_AES) {
|
||||
if (encInfo.iv && encInfo.ciphertext && encInfo.mac) {
|
||||
keys[keyId] = keyInfo;
|
||||
}
|
||||
@@ -344,13 +355,13 @@ export class ServerSideSecretStorageImpl {
|
||||
async isStored(name) {
|
||||
// check if secret exists
|
||||
const secretInfo = await this.accountDataAdapter.getAccountDataFromServer(name);
|
||||
if (!secretInfo?.encrypted) return null;
|
||||
if (!(secretInfo !== null && secretInfo !== void 0 && secretInfo.encrypted)) return null;
|
||||
const ret = {};
|
||||
|
||||
// filter secret encryption keys with supported algorithm
|
||||
for (const keyId of Object.keys(secretInfo.encrypted)) {
|
||||
// get key information from key storage
|
||||
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer(`m.secret_storage.key.${keyId}`);
|
||||
const keyInfo = await this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.key." + keyId);
|
||||
if (!keyInfo) continue;
|
||||
const encInfo = secretInfo.encrypted[keyId];
|
||||
|
||||
@@ -383,10 +394,10 @@ export class ServerSideSecretStorageImpl {
|
||||
if (keys[keyId].algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
|
||||
const decryption = {
|
||||
encrypt: function (secret) {
|
||||
return encryptAESSecretStorageItem(secret, privateKey, name);
|
||||
return (0, _aes.encryptAES)(secret, privateKey, name);
|
||||
},
|
||||
decrypt: function (encInfo) {
|
||||
return decryptAESSecretStorageItem(encInfo, privateKey, name);
|
||||
return (0, _aes.decryptAES)(encInfo, privateKey, name);
|
||||
}
|
||||
};
|
||||
return [keyId, decryption];
|
||||
@@ -402,7 +413,8 @@ export class ServerSideSecretStorageImpl {
|
||||
*
|
||||
* @param input - input string
|
||||
*/
|
||||
export function trimTrailingEquals(input) {
|
||||
exports.ServerSideSecretStorageImpl = ServerSideSecretStorageImpl;
|
||||
function trimTrailingEquals(input) {
|
||||
// according to Sonar and CodeQL, a regex such as /=+$/ is superlinear.
|
||||
// Not sure I believe it, but it's easy enough to work around.
|
||||
|
||||
@@ -417,20 +429,4 @@ export function trimTrailingEquals(input) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
// string of zeroes, for calculating the key check
|
||||
const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
|
||||
/**
|
||||
* Calculate the MAC for checking the key.
|
||||
* See https://spec.matrix.org/v1.11/client-server-api/#msecret_storagev1aes-hmac-sha2, steps 3 and 4.
|
||||
*
|
||||
* @param key - the key to use
|
||||
* @param iv - The initialization vector as a base64-encoded string.
|
||||
* If omitted, a random initialization vector will be created.
|
||||
* @returns An object that contains, `mac` and `iv` properties.
|
||||
*/
|
||||
export function calculateKeyCheck(key, iv) {
|
||||
return encryptAESSecretStorageItem(ZERO_STR, key, "", iv);
|
||||
}
|
||||
//# sourceMappingURL=secret-storage.js.map
|
||||
Reference in New Issue
Block a user