Skip to content

Commit

Permalink
Split out cert setting for performance reasons
Browse files Browse the repository at this point in the history
  • Loading branch information
Half-Shot committed Aug 11, 2023
1 parent 578131d commit 9609692
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 14 deletions.
7 changes: 3 additions & 4 deletions src/bridge/AdminRoomHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,11 @@ export class AdminRoomHandler {


try {
const clientConfig = await this.getOrCreateClientConfig(sender, server);
clientConfig.setCertificate({
const keypair = {
cert: cert.toString(),
key: privateKey.export({type: 'pkcs8', format: 'pem'}).toString(),
});
await this.ircBridge.getStore().storeIrcClientConfig(clientConfig);
};
await this.ircBridge.getStore().storeClientCert(sender, server.domain, keypair);
}
catch (ex) {
req.log.error('Unable to store certificate for user', ex);
Expand Down
4 changes: 3 additions & 1 deletion src/datastore/DataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
} from "matrix-appservice-bridge";
import { MatrixDirectoryVisibility } from "../bridge/IrcHandler";
import { IrcRoom } from "../models/IrcRoom";
import { IrcClientConfig } from "../models/IrcClientConfig";
import { IrcClientCertKeypair, IrcClientConfig } from "../models/IrcClientConfig";
import { IrcServer, IrcServerConfig } from "../irc/IrcServer";

export type RoomOrigin = "config"|"provision"|"alias"|"join";
Expand Down Expand Up @@ -175,6 +175,8 @@ export interface DataStore extends ProvisioningStore {

removePass(userId: string, domain: string): Promise<void>;

storeClientCert(userId: string, domain: string, keypair: IrcClientCertKeypair): Promise<void>;

removeClientCert(userId: string, domain: string): Promise<void>;

getMatrixUserByUsername(domain: string, username: string): Promise<MatrixUser|undefined>;
Expand Down
11 changes: 10 additions & 1 deletion src/datastore/NedbDataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ limitations under the License.

import { MatrixDirectoryVisibility } from "../bridge/IrcHandler";
import { IrcRoom } from "../models/IrcRoom";
import { IrcClientConfig, IrcClientConfigSeralized } from "../models/IrcClientConfig"
import { IrcClientCertKeypair, IrcClientConfig, IrcClientConfigSeralized } from "../models/IrcClientConfig"
import { getLogger } from "../logging";

import {
Expand Down Expand Up @@ -701,6 +701,15 @@ export class NeDBDataStore implements DataStore {
}
}

public async storeClientCert(userId: string, domain: string, keypair: IrcClientCertKeypair): Promise<void> {
const config = await this.getIrcClientConfig(userId, domain);
if (!config) {
throw new Error(`${userId} does not have an IRC client configured for ${domain}`);
}
config.setCertificate(keypair);
await this.storeIrcClientConfig(config);
}

public async removeClientCert(userId: string, domain: string): Promise<void> {
const config = await this.getIrcClientConfig(userId, domain);
if (config) {
Expand Down
7 changes: 7 additions & 0 deletions src/datastore/StringCrypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,21 @@ export class StringCrypto {
const password = randomBytes(32).toString(ENCRYPTED_ENCODING);
const key = await scrypt(password, 'salt', 32) as Buffer;
const iv = randomBytes(16);

const cipher = createCipheriv(algorithm, key, iv);
cipher.setEncoding(ENCRYPTED_ENCODING);
let encrypted = '';

// Large strings are encrypted as 'lg:encrypt($key_$iv):$encrypted_block' where the key_iv is further
// encrypted by the root private key.
const secret = this.encrypt(`${key.toString(ENCRYPTED_ENCODING)}_${iv.toString(ENCRYPTED_ENCODING)}`);
const streamPromise = new Promise<string>((resolve, reject) => {
cipher.on('error', (err) => reject(err));
cipher.on('end', () => resolve(
`lg:${secret}:${encrypted}`
));
});

cipher.on('data', (chunk) => { encrypted += chunk });
cipher.write(plaintext);
cipher.end();
Expand All @@ -110,13 +115,15 @@ export class StringCrypto {
const [keyB64, ivB64] = this.decrypt(keyPlusIvEnc).split('_');
const iv = Buffer.from(ivB64, ENCRYPTED_ENCODING);
const key = Buffer.from(keyB64, ENCRYPTED_ENCODING);

const decipher = createDecipheriv(algorithm, key, iv);
let decrypted = '';
decipher.on('data', (chunk) => { decrypted += chunk });
const streamPromise = new Promise<string>((resolve, reject) => {
decipher.on('error', (err) => reject(err));
decipher.on('end', () => resolve(decrypted));
});

decipher.write(Buffer.from(data, ENCRYPTED_ENCODING));
decipher.end();
return streamPromise;
Expand Down
25 changes: 17 additions & 8 deletions src/datastore/postgres/PgDataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
import { DataStore, RoomOrigin, ChannelMappings, UserFeatures } from "../DataStore";
import { MatrixDirectoryVisibility } from "../../bridge/IrcHandler";
import { IrcRoom } from "../../models/IrcRoom";
import { IrcClientConfig, IrcClientConfigSeralized } from "../../models/IrcClientConfig";
import { IrcClientCertKeypair, IrcClientConfig, IrcClientConfigSeralized } from "../../models/IrcClientConfig";
import { IrcServer, IrcServerConfig } from "../../irc/IrcServer";

import { getLogger } from "../../logging";
Expand Down Expand Up @@ -553,24 +553,17 @@ export class PgDataStore implements DataStore, ProvisioningStore {
// We need to make sure we have a matrix user in the store.
await this.pgPool.query("INSERT INTO matrix_users VALUES ($1, NULL) ON CONFLICT DO NOTHING", [userId]);
let password = config.getPassword();
const keypair: {cert?: string, key?: string} = { };

// This implies without a cryptostore these will be stored plain.
if (password && this.cryptoStore) {
password = this.cryptoStore.encrypt(password);
}

if (config.certificate && this.cryptoStore) {
keypair.cert = config.certificate.cert;
keypair.key = await this.cryptoStore.encryptLargeString(config.certificate.key);
}
const parameters = {
user_id: userId,
domain: config.getDomain(),
// either use the decrypted password, or whatever is stored already.
password,
cert: keypair.cert,
key: keypair.key,
config: JSON.stringify(config.serialize(true)),
};
const statement = PgDataStore.BuildUpsertStatement(
Expand Down Expand Up @@ -667,6 +660,22 @@ export class PgDataStore implements DataStore, ProvisioningStore {
[userId, domain]);
}

public async storeClientCert(userId: string, domain: string, keypair: IrcClientCertKeypair): Promise<void> {
if (!this.cryptoStore) {
throw Error("Password encryption is not configured.")
}
const key = this.cryptoStore.encrypt(keypair.key);
const parameters = {
user_id: userId,
domain,
cert: keypair.cert,
key,
};
const statement = PgDataStore.BuildUpsertStatement("client_config",
"ON CONSTRAINT cons_client_config_unique", Object.keys(parameters));
await this.pgPool.query(statement, Object.values(parameters));
}

public async removeClientCert(userId: string, domain: string): Promise<void> {
await this.pgPool.query(
"UPDATE client_config SET cert = NULL AND key = NULL WHERE user_id = $1 AND domain = $2",
Expand Down

0 comments on commit 9609692

Please sign in to comment.