Skip to content

Commit 03a38d9

Browse files
authoredSep 3, 2021
fix: mfa settings encryption version (standardnotes#57)
1 parent 96dc4c7 commit 03a38d9

10 files changed

+20
-61
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm'
2+
3+
export class fixEncryptionVersionOnMfaSettings1630661830850 implements MigrationInterface {
4+
public async up(queryRunner: QueryRunner): Promise<void> {
5+
await queryRunner.query('UPDATE `settings` SET `server_encryption_version` = 1 WHERE `server_encryption_version` = 2')
6+
}
7+
8+
public async down(): Promise<void> {
9+
return
10+
}
11+
}

‎src/Controller/SettingsController.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ describe('SettingsController', () => {
136136
expect(result.statusCode).toEqual(400)
137137
})
138138

139-
it('should update user mfa setting with default encoded and encrypted setting', async () => {
139+
it('should update user mfa setting with default encrypted setting', async () => {
140140
request.params.userUuid = '1-2-3'
141141
request.body = {
142142
uuid: '2-3-4',
@@ -154,7 +154,7 @@ describe('SettingsController', () => {
154154
props: {
155155
createdAt: 123,
156156
name: 'MFA_SECRET',
157-
serverEncryptionVersion: Setting.ENCRYPTION_VERSION_CLIENT_ENCODED_AND_SERVER_ENCRYPTED,
157+
serverEncryptionVersion: Setting.ENCRYPTION_VERSION_DEFAULT,
158158
sensitive: true,
159159
updatedAt: 234,
160160
uuid: '2-3-4',
@@ -203,7 +203,7 @@ describe('SettingsController', () => {
203203
request.body = {
204204
uuid: '2-3-4',
205205
value: 'test',
206-
serverEncryptionVersion: Setting.ENCRYPTION_VERSION_CLIENT_ENCODED_AND_SERVER_ENCRYPTED,
206+
serverEncryptionVersion: Setting.ENCRYPTION_VERSION_DEFAULT,
207207
createdAt: 123,
208208
updatedAt: 234,
209209
}
@@ -217,7 +217,7 @@ describe('SettingsController', () => {
217217
props: {
218218
createdAt: 123,
219219
name: 'MFA_SECRET',
220-
serverEncryptionVersion: 2,
220+
serverEncryptionVersion: 1,
221221
updatedAt: 234,
222222
uuid: '2-3-4',
223223
value: 'test',

‎src/Controller/SettingsController.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export class SettingsController extends BaseHttpController {
8585
const {
8686
uuid,
8787
value,
88-
serverEncryptionVersion = Setting.ENCRYPTION_VERSION_CLIENT_ENCODED_AND_SERVER_ENCRYPTED,
88+
serverEncryptionVersion = Setting.ENCRYPTION_VERSION_DEFAULT,
8989
createdAt,
9090
updatedAt,
9191
} = request.body

‎src/Domain/Encryption/CrypterNode.spec.ts

+2-18
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@ import { Aes256GcmEncrypted } from '@standardnotes/sncrypto-common'
22
import { SnCryptoNode } from '@standardnotes/sncrypto-node'
33
import { Logger } from 'winston'
44
import { User } from '../User/User'
5-
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
65
import { CrypterNode } from './CrypterNode'
76

87
describe('CrypterNode', () => {
98
let crypto: SnCryptoNode
109
let user: User
11-
let userRepository: UserRepositoryInterface
1210
let logger: Logger
1311

1412
const iv = 'iv'
1513

16-
const createCrypter = () => new CrypterNode(serverKey, crypto, userRepository, logger)
14+
const createCrypter = () => new CrypterNode(serverKey, crypto, logger)
1715

1816
const makeEncrypted = (ciphertext: string): Aes256GcmEncrypted<string> => {
1917
return {
@@ -48,15 +46,12 @@ describe('CrypterNode', () => {
4846
user = {} as jest.Mocked<User>
4947
user.encryptedServerKey = version(encryptedUserKey)
5048

51-
userRepository = {} as jest.Mocked<UserRepositoryInterface>
52-
userRepository.save = jest.fn()
53-
5449
logger = {} as jest.Mocked<Logger>
5550
logger.debug = jest.fn()
5651
})
5752

5853
it('should fail to instantiate on non-32-byte key', async () => {
59-
expect(() => new CrypterNode('short-key', crypto, userRepository, logger)).toThrow()
54+
expect(() => new CrypterNode('short-key', crypto, logger)).toThrow()
6055
})
6156

6257
it('should encrypt a value for user', async () => {
@@ -79,17 +74,6 @@ describe('CrypterNode', () => {
7974
expect(crypto.aes256GcmDecrypt).toHaveBeenNthCalledWith(2, encrypted, decrypted)
8075
})
8176

82-
it('should generate an encrypted server key for user during decryption if one does not exist', async () => {
83-
user.encryptedServerKey = null
84-
user.serverEncryptionVersion = 0
85-
86-
expect(await createCrypter().decryptForUser(version(encrypted), user)).toEqual(decrypted)
87-
88-
expect(crypto.aes256GcmDecrypt).toHaveBeenCalledTimes(2)
89-
90-
expect(userRepository.save).toHaveBeenCalled()
91-
})
92-
9377
it('should generate an encrypted user server key', async () => {
9478
const anotherUserKey = 'anotherUserKey'
9579
crypto.generateRandomKey = jest.fn()

‎src/Domain/Encryption/CrypterNode.ts

-8
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ import { inject, injectable } from 'inversify'
44
import { Logger } from 'winston'
55
import TYPES from '../../Bootstrap/Types'
66
import { User } from '../User/User'
7-
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
87
import { CrypterInterface } from './CrypterInterface'
98

109
@injectable()
1110
export class CrypterNode implements CrypterInterface {
1211
constructor (
1312
@inject(TYPES.ENCRYPTION_SERVER_KEY) private encryptionServerKey: string,
1413
@inject(TYPES.SnCryptoNode) private cryptoNode: SnCryptoNode,
15-
@inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface,
1614
@inject(TYPES.Logger) private logger: Logger,
1715
) {
1816
const keyBuffer = Buffer.from(encryptionServerKey, 'hex')
@@ -64,12 +62,6 @@ export class CrypterNode implements CrypterInterface {
6462
}
6563

6664
async decryptUserServerKey(user: User): Promise<string> {
67-
if (!user.encryptedServerKey) {
68-
user.encryptedServerKey = await this.generateEncryptedUserServerKey()
69-
user.serverEncryptionVersion = User.DEFAULT_ENCRYPTION_VERSION
70-
await this.userRepository.save(user)
71-
}
72-
7365
const encrypted = this.parseVersionedEncrypted(user.encryptedServerKey as string)
7466

7567
return this.cryptoNode.aes256GcmDecrypt(encrypted, this.encryptionServerKey)

‎src/Domain/Setting/Setting.ts

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { User } from '../User/User'
66
export class Setting {
77
static readonly ENCRYPTION_VERSION_UNENCRYPTED = 0
88
static readonly ENCRYPTION_VERSION_DEFAULT = 1
9-
static readonly ENCRYPTION_VERSION_CLIENT_ENCODED_AND_SERVER_ENCRYPTED = 2
109

1110
@PrimaryGeneratedColumn('uuid')
1211
uuid: string

‎src/Domain/Setting/SettingFactory.ts

-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ export class SettingFactory {
6969
case Setting.ENCRYPTION_VERSION_UNENCRYPTED:
7070
return value
7171
case Setting.ENCRYPTION_VERSION_DEFAULT:
72-
case Setting.ENCRYPTION_VERSION_CLIENT_ENCODED_AND_SERVER_ENCRYPTED:
7372
return this.crypter.encryptForUser(value as string, user)
7473
default:
7574
throw Error(`Unrecognized encryption version: ${serverEncryptionVersion}!`)

‎src/Domain/Setting/SettingService.spec.ts

-16
Original file line numberDiff line numberDiff line change
@@ -132,20 +132,4 @@ describe('SettingService', () => {
132132

133133
expect(await createService().findSetting({ userUuid: '1-2-3', settingName: 'test' as SettingName })).toEqual(undefined)
134134
})
135-
136-
it('should decrypt a encoded value of a setting for user', async () => {
137-
setting = {
138-
value: 'encoded_and_encrypted',
139-
serverEncryptionVersion: Setting.ENCRYPTION_VERSION_CLIENT_ENCODED_AND_SERVER_ENCRYPTED,
140-
} as jest.Mocked<Setting>
141-
142-
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValue(setting)
143-
144-
crypter.decryptForUser = jest.fn().mockReturnValue('encoded_and_decrypted')
145-
146-
expect(await createService().findSetting({ userUuid: '1-2-3', settingName: 'test' as SettingName })).toEqual({
147-
serverEncryptionVersion: 2,
148-
value: 'encoded_and_decrypted',
149-
})
150-
})
151135
})

‎src/Domain/Setting/SettingService.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,7 @@ export class SettingService implements SettingServiceInterface {
3535
return undefined
3636
}
3737

38-
if (setting.value !== null &&
39-
(
40-
setting.serverEncryptionVersion === Setting.ENCRYPTION_VERSION_DEFAULT ||
41-
setting.serverEncryptionVersion === Setting.ENCRYPTION_VERSION_CLIENT_ENCODED_AND_SERVER_ENCRYPTED
42-
)
43-
) {
38+
if (setting.value !== null && setting.serverEncryptionVersion === Setting.ENCRYPTION_VERSION_DEFAULT) {
4439
const user = await this.userRepository.findOneByUuid(dto.userUuid)
4540

4641
if (user === undefined) {

‎src/Domain/UseCase/GetSettings/GetSettings.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,7 @@ export class GetSettings implements UseCaseInterface {
4848
}
4949

5050
for (const setting of settings) {
51-
if (setting.value !== null &&
52-
(
53-
setting.serverEncryptionVersion === Setting.ENCRYPTION_VERSION_DEFAULT ||
54-
setting.serverEncryptionVersion === Setting.ENCRYPTION_VERSION_CLIENT_ENCODED_AND_SERVER_ENCRYPTED
55-
)
56-
) {
51+
if (setting.value !== null && setting.serverEncryptionVersion === Setting.ENCRYPTION_VERSION_DEFAULT) {
5752
setting.value = await this.crypter.decryptForUser(setting.value, user)
5853
}
5954
}

0 commit comments

Comments
 (0)
Please sign in to comment.