Skip to content

Commit

Permalink
fix(keystore): hash should actually be hashed o_O
Browse files Browse the repository at this point in the history
  • Loading branch information
TBonnin committed Sep 25, 2024
1 parent 587d9a2 commit f7f4934
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 15 deletions.
3 changes: 1 addition & 2 deletions packages/keystore/lib/models/privatekeys.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ describe('PrivateKey', async () => {

it('should be retrieved before it expires', async () => {
const entityType = 'connect_session';
const ttlInMs = 30;
const ttlInMs = 100;
const createKey = await createPrivateKey(db, {
displayName: 'this is my key',
entityType,
Expand All @@ -97,7 +97,6 @@ describe('PrivateKey', async () => {
environmentId: 1,
ttlInMs
});
await new Promise((resolve) => setTimeout(resolve, ttlInMs / 2));
const [keyValue] = createKey.unwrap();
const getKey = await getPrivateKey(db, keyValue);
expect(getKey.isOk()).toBe(true);
Expand Down
19 changes: 7 additions & 12 deletions packages/keystore/lib/models/privatekeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type knex from 'knex';
import type { Result } from '@nangohq/utils';
import { Ok, Err } from '@nangohq/utils';
import type { EntityType, PrivateKey } from '@nangohq/types';
import { getEncryption } from '../utils/encryption.js';
import { getEncryption, hashValue } from '../utils/encryption.js';

export const PRIVATE_KEYS_TABLE = 'private_keys';

Expand Down Expand Up @@ -81,12 +81,13 @@ export async function createPrivateKey(
const now = new Date();
const random = crypto.randomBytes(32).toString('hex');
const keyValue = `nango_${entityType}_${random}`;
const hash = await hashValue(keyValue);
const key: DbInsertPrivateKey = {
display_name: displayName,
account_id: accountId,
environment_id: environmentId,
encrypted: options.onlyStoreHash ? null : encryptValue(keyValue),
hash: hashValue(keyValue),
hash,
expires_at: ttlInMs ? new Date(now.getTime() + ttlInMs) : null,
entity_type: entityType,
entity_id: entityId
Expand All @@ -100,10 +101,11 @@ export async function createPrivateKey(

export async function getPrivateKey(db: knex.Knex, keyValue: string): Promise<Result<PrivateKey, PrivateKeyError>> {
const now = new Date();
const hash = await hashValue(keyValue);
const [key] = await db
.update({ last_access_at: now })
.from<DbPrivateKey>(PRIVATE_KEYS_TABLE)
.where({ hash: hashValue(keyValue) })
.where({ hash })
.andWhere((builder) => builder.whereNull('expires_at').orWhere('expires_at', '>', now))
.returning('*');
if (!key) {
Expand All @@ -124,11 +126,8 @@ export async function deletePrivateKey(
db: knex.Knex,
{ keyValue, entityType }: { keyValue: string; entityType: EntityType }
): Promise<Result<void, PrivateKeyError>> {
const [key] = await db
.delete()
.from<DbPrivateKey>(PRIVATE_KEYS_TABLE)
.where({ hash: hashValue(keyValue), entity_type: entityType })
.returning('*');
const hash = await hashValue(keyValue);
const [key] = await db.delete().from<DbPrivateKey>(PRIVATE_KEYS_TABLE).where({ hash, entity_type: entityType }).returning('*');
if (!key) {
return Err(new PrivateKeyError({ code: 'not_found', message: `Private key not found` }));
}
Expand All @@ -153,7 +152,3 @@ function decryptValue(encryptedValue: Buffer): Result<string, PrivateKeyError> {
}
return Ok(encryption.decrypt(encrypted, iv, tag));
}

function hashValue(keyValue: string): string {
return keyValue;
}
19 changes: 18 additions & 1 deletion packages/keystore/lib/utils/encryption.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
import utils from 'node:util';
import crypto from 'crypto';
import { Encryption } from '@nangohq/utils';
import { envs } from './env.js';

const pbkdf2 = utils.promisify(crypto.pbkdf2);

let encryption: Encryption | null = null;

function getEncryptionKey(): string | undefined {
return envs.NANGO_ENCRYPTION_KEY;
}

export function getEncryption(): Encryption {
if (!encryption) {
const encryptionKey = envs.NANGO_ENCRYPTION_KEY;
const encryptionKey = getEncryptionKey();
if (!encryptionKey) {
throw new Error('NANGO_ENCRYPTION_KEY is not set');
}
encryption = new Encryption(encryptionKey);
}
return encryption;
}

export async function hashValue(val: string): Promise<string> {
const encryptionKey = getEncryptionKey();
if (!encryptionKey) {
throw new Error('NANGO_ENCRYPTION_KEY is not set');
}

return (await pbkdf2(val, encryptionKey, 310000, 32, 'sha256')).toString('base64');
}

0 comments on commit f7f4934

Please sign in to comment.