1629223072059-flatten_mfa_setting_and_encrypt.ts 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import { MigrationInterface, QueryRunner } from 'typeorm'
  2. import { CryptoNode } from '@standardnotes/sncrypto-node'
  3. export class flattenMfaSettingAndEncrypt1629223072059 implements MigrationInterface {
  4. public async up(queryRunner: QueryRunner): Promise<void> {
  5. const encryptedAndEncodedMFASettings = await queryRunner.manager.query(
  6. 'SELECT s.uuid as uuid, s.value as value, u.encrypted_server_key as encrypted_server_key FROM settings s LEFT JOIN users u ON u.uuid = s.user_uuid WHERE s.name = "MFA_SECRET" AND s.server_encryption_version = 2',
  7. )
  8. for (const encryptedAndEncodedMFASetting of encryptedAndEncodedMFASettings) {
  9. if (!encryptedAndEncodedMFASetting['value']) {
  10. continue
  11. }
  12. const encodedMFASetting = await this.decryptMFASetting(
  13. encryptedAndEncodedMFASetting['value'],
  14. encryptedAndEncodedMFASetting['encrypted_server_key'],
  15. )
  16. const mfaSecret = this.getDecodedMFASecret(encodedMFASetting)
  17. if (!mfaSecret) {
  18. continue
  19. }
  20. const encryptedMFASecret = await this.encryptMFASecret(
  21. mfaSecret,
  22. encryptedAndEncodedMFASetting['encrypted_server_key'],
  23. )
  24. await queryRunner.manager.query(
  25. `UPDATE settings s SET s.value = '${encryptedMFASecret}', s.server_encryption_version = 1 WHERE s.uuid="${encryptedAndEncodedMFASetting['uuid']}"`,
  26. )
  27. }
  28. }
  29. public async down(): Promise<void> {
  30. return
  31. }
  32. private async decryptMFASetting(encryptedMFASetting: string, userEncryptedServerKey: string) {
  33. const crypto = new CryptoNode()
  34. const userServerKey = JSON.parse(userEncryptedServerKey)
  35. const decryptedUserServerKey = await crypto.aes256GcmDecrypt(
  36. userServerKey.encrypted,
  37. process.env.ENCRYPTION_SERVER_KEY as string,
  38. )
  39. const parsedVersionedEncrypted = JSON.parse(encryptedMFASetting)
  40. return crypto.aes256GcmDecrypt(parsedVersionedEncrypted.encrypted, decryptedUserServerKey)
  41. }
  42. private async encryptMFASecret(secret: string, userEncryptedServerKey: string): Promise<string> {
  43. const crypto = new CryptoNode()
  44. const userServerKey = JSON.parse(userEncryptedServerKey)
  45. const decryptedUserServerKey = await crypto.aes256GcmDecrypt(
  46. userServerKey.encrypted,
  47. process.env.ENCRYPTION_SERVER_KEY as string,
  48. )
  49. const iv = await crypto.generateRandomKey(128)
  50. const encrypted = await crypto.aes256GcmEncrypt({
  51. unencrypted: secret,
  52. iv,
  53. key: decryptedUserServerKey,
  54. })
  55. return JSON.stringify({ version: 1, encrypted })
  56. }
  57. private getDecodedMFASecret(encodedValue: string): string | undefined {
  58. const valueBuffer = Buffer.from(encodedValue.substring(3), 'base64')
  59. const decodedValue = valueBuffer.toString()
  60. const decodedMFASecretObject = JSON.parse(decodedValue)
  61. if ('secret' in decodedMFASecretObject && decodedMFASecretObject.secret) {
  62. return decodedMFASecretObject.secret
  63. }
  64. return undefined
  65. }
  66. }