refactor: root key manager (#2344)

This commit is contained in:
Mo
2023-07-04 07:31:50 -05:00
committed by GitHub
parent b4a90025c4
commit b06999d25b
56 changed files with 1400 additions and 1231 deletions

View File

@@ -63,7 +63,7 @@ export class SNActionsService extends AbstractService {
public deviceInterface: DeviceInterface,
private httpService: DeprecatedHttpService,
private payloadManager: PayloadManager,
private protocolService: EncryptionService,
private encryptionService: EncryptionService,
private syncService: SNSyncService,
private challengeService: ChallengeService,
private listedService: ListedService,
@@ -81,7 +81,7 @@ export class SNActionsService extends AbstractService {
;(this.payloadManager as unknown) = undefined
;(this.listedService as unknown) = undefined
;(this.challengeService as unknown) = undefined
;(this.protocolService as unknown) = undefined
;(this.encryptionService as unknown) = undefined
;(this.syncService as unknown) = undefined
this.payloadRequestHandlers.length = 0
this.previousPasswords.length = 0
@@ -207,7 +207,7 @@ export class SNActionsService extends AbstractService {
return
}
let decryptedPayload = await this.protocolService.decryptSplitSingle<ActionExtensionContent>({
let decryptedPayload = await this.encryptionService.decryptSplitSingle<ActionExtensionContent>({
usesItemsKeyWithKeyLookup: {
items: [payload],
},
@@ -218,7 +218,7 @@ export class SNActionsService extends AbstractService {
}
if (rootKey) {
decryptedPayload = await this.protocolService.decryptSplitSingle({
decryptedPayload = await this.encryptionService.decryptSplitSingle({
usesRootKey: {
items: [payload],
key: rootKey,
@@ -230,7 +230,7 @@ export class SNActionsService extends AbstractService {
}
for (const itemsKey of this.itemManager.getDisplayableItemsKeys()) {
const decryptedPayload = await this.protocolService.decryptSplitSingle<ActionExtensionContent>({
const decryptedPayload = await this.encryptionService.decryptSplitSingle<ActionExtensionContent>({
usesItemsKey: {
items: [payload],
key: itemsKey,
@@ -256,7 +256,7 @@ export class SNActionsService extends AbstractService {
)
return undefined
}
const keyParams = this.protocolService.createKeyParams(keyParamsData)
const keyParams = this.encryptionService.createKeyParams(keyParamsData)
/* Try previous passwords */
for (const passwordCandidate of this.previousPasswords) {
@@ -266,7 +266,7 @@ export class SNActionsService extends AbstractService {
triedPasswords.push(passwordCandidate)
const key = await this.protocolService.computeRootKey(passwordCandidate, keyParams)
const key = await this.encryptionService.computeRootKey(passwordCandidate, keyParams)
if (!key) {
continue
}
@@ -341,7 +341,7 @@ export class SNActionsService extends AbstractService {
return item.payload.ejected()
}
const encrypted = await this.protocolService.encryptSplitSingle({
const encrypted = await this.encryptionService.encryptSplitSingle({
usesItemsKeyWithKeyLookup: { items: [item.payload] },
})

View File

@@ -44,7 +44,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic
constructor(
private storageService: DiskStorageService,
private protocolService: EncryptionService,
private encryptionService: EncryptionService,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
@@ -52,7 +52,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic
public override deinit() {
;(this.storageService as unknown) = undefined
;(this.protocolService as unknown) = undefined
;(this.encryptionService as unknown) = undefined
;(this.sendChallenge as unknown) = undefined
;(this.challengeOperations as unknown) = undefined
;(this.challengeObservers as unknown) = undefined
@@ -79,9 +79,9 @@ export class ChallengeService extends AbstractService implements ChallengeServic
public async validateChallengeValue(value: ChallengeValue): Promise<ChallengeValidationResponse> {
switch (value.prompt.validation) {
case ChallengeValidation.LocalPasscode:
return this.protocolService.validatePasscode(value.value as string)
return this.encryptionService.validatePasscode(value.value as string)
case ChallengeValidation.AccountPassword:
return this.protocolService.validateAccountPassword(value.value as string)
return this.encryptionService.validateAccountPassword(value.value as string)
case ChallengeValidation.Biometric:
return { valid: value.value === true }
case ChallengeValidation.Authenticator:
@@ -104,7 +104,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic
}
async promptForAccountPassword(): Promise<string | null> {
if (!this.protocolService.hasAccount()) {
if (!this.encryptionService.hasAccount()) {
throw Error('Requiring account password for challenge with no account')
}
@@ -143,7 +143,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic
canceled?: undefined
}
> {
if (!this.protocolService.hasPasscode()) {
if (!this.encryptionService.hasPasscode()) {
return {}
}
@@ -154,12 +154,12 @@ export class ChallengeService extends AbstractService implements ChallengeServic
}
}
const wrappingKey = await this.protocolService.computeWrappingKey(passcode)
const wrappingKey = await this.encryptionService.computeWrappingKey(passcode)
return { wrappingKey }
}
public isPasscodeLocked() {
return this.protocolService.isPasscodeLocked()
return this.encryptionService.isPasscodeLocked()
}
public addChallengeObserver(challenge: Challenge, observer: ChallengeObserver): () => void {

View File

@@ -11,7 +11,7 @@ export class KeyRecoveryOperation {
constructor(
private queueItem: DecryptionQueueItem,
private itemManager: ItemManager,
private protocolService: EncryptionProviderInterface,
private encryptionService: EncryptionProviderInterface,
private challengeService: ChallengeServiceInterface,
private clientParams: SNRootKeyParams | undefined,
private serverParams: SNRootKeyParams | undefined,
@@ -43,7 +43,7 @@ export class KeyRecoveryOperation {
const decryptionResult = await DecryptItemsKeyByPromptingUser(
this.queueItem.encryptedKey,
this.protocolService,
this.encryptionService,
this.challengeService,
this.queueItem.keyParams,
)

View File

@@ -87,7 +87,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
private itemManager: ItemManager,
private payloadManager: PayloadManager,
private apiService: SNApiService,
private protocolService: EncryptionService,
private encryptionService: EncryptionService,
private challengeService: ChallengeService,
private alertService: AlertService,
private storageService: DiskStorageService,
@@ -121,7 +121,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
;(this.itemManager as unknown) = undefined
;(this.payloadManager as unknown) = undefined
;(this.apiService as unknown) = undefined
;(this.protocolService as unknown) = undefined
;(this.encryptionService as unknown) = undefined
;(this.challengeService as unknown) = undefined
;(this.alertService as unknown) = undefined
;(this.storageService as unknown) = undefined
@@ -251,7 +251,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
}
private getClientKeyParams() {
return this.protocolService.getAccountKeyParams()
return this.encryptionService.getAccountKeyParams()
}
private async performServerSignIn(): Promise<SNRootKey | undefined> {
@@ -279,7 +279,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
return
}
const rootKey = await this.protocolService.computeRootKey(password, serverParams)
const rootKey = await this.encryptionService.computeRootKey(password, serverParams)
const signInResponse = await this.userService.correctiveSignIn(rootKey)
@@ -295,7 +295,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
}
private async getWrappingKeyIfApplicable(): Promise<SNRootKey | undefined> {
if (!this.protocolService.hasPasscode()) {
if (!this.encryptionService.hasPasscode()) {
return undefined
}
const { wrappingKey, canceled } = await this.challengeService.getWrappingKeyIfApplicable()
@@ -312,7 +312,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
private addKeysToQueue(keys: EncryptedPayloadInterface[]) {
for (const key of keys) {
const keyParams = this.protocolService.getKeyEmbeddedKeyParamsFromItemsKey(key)
const keyParams = this.encryptionService.getKeyEmbeddedKeyParamsFromItemsKey(key)
if (!keyParams) {
continue
}
@@ -356,12 +356,12 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
serverParams = await this.getLatestKeyParamsFromServer(clientParams.identifier)
}
const deallocedAfterNetworkRequest = this.protocolService == undefined
const deallocedAfterNetworkRequest = this.encryptionService == undefined
if (deallocedAfterNetworkRequest) {
return
}
const credentialsMissing = !this.protocolService.hasAccount() && !this.protocolService.hasPasscode()
const credentialsMissing = !this.encryptionService.hasAccount() && !this.encryptionService.hasPasscode()
if (credentialsMissing) {
const rootKey = await this.performServerSignIn()
@@ -426,7 +426,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
const operation = new KeyRecoveryOperation(
queueItem,
this.itemManager,
this.protocolService,
this.encryptionService,
this.challengeService,
clientParams,
serverParams,
@@ -460,7 +460,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
if (replacesRootKey) {
const wrappingKey = await this.getWrappingKeyIfApplicable()
await this.protocolService.setRootKey(rootKey, wrappingKey)
await this.encryptionService.setRootKey(rootKey, wrappingKey)
}
const clientKeyParams = this.getClientKeyParams()
@@ -475,7 +475,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
: qItem.encryptedKey
})
const matchingResults = await this.protocolService.decryptSplit({
const matchingResults = await this.encryptionService.decryptSplit({
usesRootKey: {
items: matchingKeys,
key: rootKey,

View File

@@ -24,14 +24,14 @@ const setupRandomUuid = () => {
describe('protectionService', () => {
let mutator: MutatorClientInterface
let protocolService: EncryptionService
let encryptionService: EncryptionService
let challengeService: ChallengeService
let storageService: DiskStorageService
let internalEventBus: InternalEventBusInterface
let protectionService: SNProtectionService
const createService = () => {
return new SNProtectionService(protocolService, mutator, challengeService, storageService, internalEventBus)
return new SNProtectionService(encryptionService, mutator, challengeService, storageService, internalEventBus)
}
const createFile = (name: string, isProtected?: boolean) => {
@@ -59,9 +59,9 @@ describe('protectionService', () => {
storageService = {} as jest.Mocked<DiskStorageService>
storageService.getValue = jest.fn()
protocolService = {} as jest.Mocked<EncryptionService>
protocolService.hasAccount = jest.fn().mockReturnValue(true)
protocolService.hasPasscode = jest.fn().mockReturnValue(false)
encryptionService = {} as jest.Mocked<EncryptionService>
encryptionService.hasAccount = jest.fn().mockReturnValue(true)
encryptionService.hasPasscode = jest.fn().mockReturnValue(false)
mutator = {} as jest.Mocked<MutatorClientInterface>
})

View File

@@ -76,7 +76,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
private mobileBiometricsTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit
constructor(
private protocolService: EncryptionService,
private encryptionService: EncryptionService,
private mutator: MutatorClientInterface,
private challengeService: ChallengeService,
private storageService: DiskStorageService,
@@ -87,7 +87,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
public override deinit(): void {
clearTimeout(this.sessionExpiryTimeout)
;(this.protocolService as unknown) = undefined
;(this.encryptionService as unknown) = undefined
;(this.challengeService as unknown) = undefined
;(this.storageService as unknown) = undefined
super.deinit()
@@ -103,7 +103,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
}
public hasProtectionSources(): boolean {
return this.protocolService.hasAccount() || this.protocolService.hasPasscode() || this.hasBiometricsEnabled()
return this.encryptionService.hasAccount() || this.encryptionService.hasPasscode() || this.hasBiometricsEnabled()
}
public hasUnprotectedAccessSession(): boolean {
@@ -148,7 +148,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
if (this.hasBiometricsEnabled()) {
prompts.push(new ChallengePrompt(ChallengeValidation.Biometric))
}
if (this.protocolService.hasPasscode()) {
if (this.encryptionService.hasPasscode()) {
prompts.push(new ChallengePrompt(ChallengeValidation.LocalPasscode))
}
if (prompts.length > 0) {
@@ -354,19 +354,19 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
prompts.push(new ChallengePrompt(ChallengeValidation.Biometric))
}
if (this.protocolService.hasPasscode()) {
if (this.encryptionService.hasPasscode()) {
prompts.push(new ChallengePrompt(ChallengeValidation.LocalPasscode))
}
if (requireAccountPassword) {
if (!this.protocolService.hasAccount()) {
if (!this.encryptionService.hasAccount()) {
throw Error('Requiring account password for challenge with no account')
}
prompts.push(new ChallengePrompt(ChallengeValidation.AccountPassword))
}
if (prompts.length === 0) {
if (fallBackToAccountPassword && this.protocolService.hasAccount()) {
if (fallBackToAccountPassword && this.encryptionService.hasAccount()) {
prompts.push(new ChallengePrompt(ChallengeValidation.AccountPassword))
} else {
return true

View File

@@ -93,7 +93,7 @@ export class SNSessionManager
private apiService: SNApiService,
private userApiService: UserApiServiceInterface,
private alertService: AlertService,
private protocolService: EncryptionService,
private encryptionService: EncryptionService,
private challengeService: ChallengeService,
private webSocketsService: SNWebSocketsService,
private httpService: HttpServiceInterface,
@@ -119,7 +119,7 @@ export class SNSessionManager
}
override deinit(): void {
;(this.protocolService as unknown) = undefined
;(this.encryptionService as unknown) = undefined
;(this.diskStorageService as unknown) = undefined
;(this.apiService as unknown) = undefined
;(this.alertService as unknown) = undefined
@@ -205,11 +205,11 @@ export class SNSessionManager
}
public getPublicKey(): string {
return this.protocolService.getKeyPair().publicKey
return this.encryptionService.getKeyPair().publicKey
}
public getSigningPublicKey(): string {
return this.protocolService.getSigningKeyPair().publicKey
return this.encryptionService.getSigningKeyPair().publicKey
}
public get userUuid(): string {
@@ -285,7 +285,7 @@ export class SNSessionManager
onNonvalidatedSubmit: async (challengeResponse) => {
const email = challengeResponse.values[0].value as string
const password = challengeResponse.values[1].value as string
const currentKeyParams = this.protocolService.getAccountKeyParams()
const currentKeyParams = this.encryptionService.getAccountKeyParams()
const { response } = await this.signIn(
email,
password,
@@ -403,7 +403,7 @@ export class SNSessionManager
email = cleanedEmailString(email)
const rootKey = await this.protocolService.createRootKey<RootKeyWithKeyPairsInterface>(
const rootKey = await this.encryptionService.createRootKey<RootKeyWithKeyPairsInterface>(
email,
password,
Common.KeyParamsOrigination.Registration,
@@ -525,8 +525,8 @@ export class SNSessionManager
}
}
const keyParams = paramsResult.keyParams as SNRootKeyParams
if (!this.protocolService.supportedVersions().includes(keyParams.version)) {
if (this.protocolService.isVersionNewerThanLibraryVersion(keyParams.version)) {
if (!this.encryptionService.supportedVersions().includes(keyParams.version)) {
if (this.encryptionService.isVersionNewerThanLibraryVersion(keyParams.version)) {
return {
response: this.apiService.createErrorResponse(UNSUPPORTED_PROTOCOL_VERSION),
}
@@ -539,7 +539,7 @@ export class SNSessionManager
if (Common.isProtocolVersionExpired(keyParams.version)) {
/* Cost minimums only apply to now outdated versions (001 and 002) */
const minimum = this.protocolService.costMinimumForVersion(keyParams.version)
const minimum = this.encryptionService.costMinimumForVersion(keyParams.version)
if (keyParams.content002.pw_cost < minimum) {
return {
response: this.apiService.createErrorResponse(INVALID_PASSWORD_COST),
@@ -560,14 +560,14 @@ export class SNSessionManager
}
}
if (!this.protocolService.platformSupportsKeyDerivation(keyParams)) {
if (!this.encryptionService.platformSupportsKeyDerivation(keyParams)) {
return {
response: this.apiService.createErrorResponse(UNSUPPORTED_KEY_DERIVATION),
}
}
if (strict) {
minAllowedVersion = this.protocolService.getLatestVersion()
minAllowedVersion = this.encryptionService.getLatestVersion()
}
if (minAllowedVersion != undefined) {
@@ -577,7 +577,7 @@ export class SNSessionManager
}
}
}
const rootKey = await this.protocolService.computeRootKey(password, keyParams)
const rootKey = await this.encryptionService.computeRootKey(password, keyParams)
const signInResponse = await this.bypassChecksAndSignInWithRootKey(email, rootKey, ephemeral)
return {
@@ -641,8 +641,8 @@ export class SNSessionManager
let oldSigningKeyPair: PkcKeyPair | undefined
try {
oldKeyPair = this.protocolService.getKeyPair()
oldSigningKeyPair = this.protocolService.getSigningKeyPair()
oldKeyPair = this.encryptionService.getKeyPair()
oldSigningKeyPair = this.encryptionService.getSigningKeyPair()
} catch (error) {
void error
}
@@ -718,7 +718,7 @@ export class SNSessionManager
}
private decodeDemoShareToken(token: Base64String): ShareToken {
const jsonString = this.protocolService.crypto.base64Decode(token)
const jsonString = this.encryptionService.crypto.base64Decode(token)
return JSON.parse(jsonString)
}
@@ -735,7 +735,7 @@ export class SNSessionManager
host: string,
wrappingKey?: SNRootKey,
) {
await this.protocolService.setRootKey(rootKey, wrappingKey)
await this.encryptionService.setRootKey(rootKey, wrappingKey)
this.memoizeUser(user)
this.diskStorageService.setValue(StorageKey.User, user)

View File

@@ -153,7 +153,7 @@ export class SNSyncService
constructor(
private itemManager: ItemManager,
private sessionManager: SNSessionManager,
private protocolService: EncryptionService,
private encryptionService: EncryptionService,
private storageService: DiskStorageService,
private payloadManager: PayloadManager,
private apiService: SNApiService,
@@ -189,7 +189,7 @@ export class SNSyncService
this.dealloced = true
;(this.sessionManager as unknown) = undefined
;(this.itemManager as unknown) = undefined
;(this.protocolService as unknown) = undefined
;(this.encryptionService as unknown) = undefined
;(this.payloadManager as unknown) = undefined
;(this.storageService as unknown) = undefined
;(this.apiService as unknown) = undefined
@@ -250,7 +250,7 @@ export class SNSyncService
const encryptionSplit = SplitPayloadsByEncryptionType(encryptedPayloads)
const decryptionSplit = CreateDecryptionSplitWithKeyLookup(encryptionSplit)
const newlyDecryptedPayloads = await this.protocolService.decryptSplit(decryptionSplit)
const newlyDecryptedPayloads = await this.encryptionService.decryptSplit(decryptionSplit)
await this.payloadManager.emitPayloads(
[...alreadyDecryptedPayloads, ...newlyDecryptedPayloads],
@@ -369,7 +369,7 @@ export class SNSyncService
const encryptionSplit = SplitPayloadsByEncryptionType(encrypted)
const decryptionSplit = CreateDecryptionSplitWithKeyLookup(encryptionSplit)
const results = await this.protocolService.decryptSplit(decryptionSplit)
const results = await this.encryptionService.decryptSplit(decryptionSplit)
await this.payloadManager.emitPayloads([...nonencrypted, ...results], PayloadEmitSource.LocalDatabaseLoaded)
@@ -510,7 +510,7 @@ export class SNSyncService
const keyLookupSplit = CreateEncryptionSplitWithKeyLookup(encryptionSplit)
const encryptedResults = await this.protocolService.encryptSplit(keyLookupSplit)
const encryptedResults = await this.encryptionService.encryptSplit(keyLookupSplit)
const contextPayloads = [
...encryptedResults.map(CreateEncryptedServerSyncPushPayload),
@@ -1138,7 +1138,7 @@ export class SNSyncService
},
}
const results = await this.protocolService.decryptSplit<ItemsKeyContent>(rootKeySplit)
const results = await this.encryptionService.decryptSplit<ItemsKeyContent>(rootKeySplit)
results.forEach((result) => {
if (isDecryptedPayload<ItemsKeyContent>(result) && result.content_type === ContentType.ItemsKey) {
@@ -1168,7 +1168,7 @@ export class SNSyncService
},
}
const results = await this.protocolService.decryptSplit<KeySystemItemsKeyContent>(keySystemRootKeySplit)
const results = await this.encryptionService.decryptSplit<KeySystemItemsKeyContent>(keySystemRootKeySplit)
results.forEach((result) => {
if (
@@ -1213,7 +1213,7 @@ export class SNSyncService
}
}
return this.protocolService.decryptSplitSingle(keyedSplit)
return this.encryptionService.decryptSplitSingle(keyedSplit)
}),
)
}
@@ -1399,7 +1399,7 @@ export class SNSyncService
const keyedSplit = CreateDecryptionSplitWithKeyLookup(encryptionSplit)
const decryptionResults = await this.protocolService.decryptSplit(keyedSplit)
const decryptionResults = await this.encryptionService.decryptSplit(keyedSplit)
this.setInSync(false)