Skip to content

Commit 8203b8d

Browse files
vaf-hubsarashub
authored andcommitted
sign public encryption keys
implement PublicKeySignatueFacade to encode public encryption keys and sign them sign public encryption keys where it is needed (e.g. key rotation, identity key pair creation) add ecc keys to current rsa key pairs when creating identity key pairs refactor GroupManagementFacade, extract loading keys via admin group key, extract creation of identity key pairs #tutadb2083
1 parent 4b7ba44 commit 8203b8d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+5044
-3646
lines changed

app-ios/tutanotaTests/CompatibilityTestData.json

Lines changed: 2515 additions & 2494 deletions
Large diffs are not rendered by default.

packages/tutanota-crypto/lib/encryption/KeyEncryption.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type AbstractEncryptedKeyPair = {
1818
symEncPrivEccKey: null | Uint8Array
1919
symEncPrivKyberKey: null | Uint8Array
2020
symEncPrivRsaKey: null | Uint8Array
21+
signature: null | unknown
2122
}
2223

2324
export type EncryptedPqKeyPairs = {
@@ -27,6 +28,7 @@ export type EncryptedPqKeyPairs = {
2728
symEncPrivEccKey: Uint8Array
2829
symEncPrivKyberKey: Uint8Array
2930
symEncPrivRsaKey: null
31+
signature: null | unknown
3032
}
3133

3234
export type EncryptedRsaKeyPairs = {
@@ -36,6 +38,7 @@ export type EncryptedRsaKeyPairs = {
3638
symEncPrivEccKey: null
3739
symEncPrivKyberKey: null
3840
symEncPrivRsaKey: Uint8Array
41+
signature: null
3942
}
4043

4144
export type EncryptedRsaX25519KeyPairs = {
@@ -45,6 +48,7 @@ export type EncryptedRsaX25519KeyPairs = {
4548
symEncPrivEccKey: Uint8Array
4649
symEncPrivKyberKey: null
4750
symEncPrivRsaKey: Uint8Array
51+
signature: null | unknown
4852
}
4953

5054
export function isEncryptedPqKeyPairs(keyPair: AbstractEncryptedKeyPair): keyPair is EncryptedPqKeyPairs {

packages/tutanota-crypto/lib/encryption/Rsa.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @ts-ignore[untyped-import]
22
import { BigInteger, parseBigInt, RSAKey } from "../internal/crypto-jsbn-2012-08-09_1.js"
33
import type { Base64, Hex } from "@tutao/tutanota-utils"
4-
import { base64ToHex, base64ToUint8Array, concat, int8ArrayToBase64, uint8ArrayToHex } from "@tutao/tutanota-utils"
4+
import { base64ToHex, base64ToUint8Array, concat, hexToUint8Array, int8ArrayToBase64, uint8ArrayToHex } from "@tutao/tutanota-utils"
55
import type { RawRsaPublicKey, RsaPrivateKey, RsaPublicKey } from "./RsaKeyPair.js"
66
import { CryptoError } from "../misc/CryptoError.js"
77
import { sha256Hash } from "../hashes/Sha256.js"
@@ -308,7 +308,7 @@ export function i2osp(i: number): Uint8Array {
308308
* @returns The public key in a persistable array format
309309
* @private
310310
*/
311-
function _publicKeyToArray(publicKey: RsaPublicKey): BigInteger[] {
311+
function _publicKeyToArray(publicKey: RawRsaPublicKey): BigInteger[] {
312312
return [_base64ToBigInt(publicKey.modulus)]
313313
}
314314

@@ -422,10 +422,14 @@ export function rsaPrivateKeyToHex(privateKey: RsaPrivateKey): Hex {
422422
return _keyArrayToHex(_privateKeyToArray(privateKey))
423423
}
424424

425-
export function rsaPublicKeyToHex(publicKey: RsaPublicKey): Hex {
425+
export function rsaPublicKeyToHex(publicKey: RawRsaPublicKey): Hex {
426426
return _keyArrayToHex(_publicKeyToArray(publicKey))
427427
}
428428

429+
export function rsaPublicKeyToBytes(rsaPublicKey: RawRsaPublicKey) {
430+
return hexToUint8Array(rsaPublicKeyToHex(rsaPublicKey))
431+
}
432+
429433
export function hexToRsaPrivateKey(privateKeyHex: Hex): RsaPrivateKey {
430434
return _arrayToPrivateKey(_hexToKeyArray(privateKeyHex))
431435
}

packages/tutanota-crypto/lib/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export {
9797
rsaPublicKeyToHex,
9898
rsaEncrypt,
9999
extractRawPublicRsaKeyFromPrivateRsaKey,
100+
rsaPublicKeyToBytes,
100101
} from "./encryption/Rsa.js"
101102
export { RsaKeyPair, RsaX25519KeyPair, RsaPrivateKey, RawRsaPublicKey, RsaPublicKey, RsaX25519PublicKey } from "./encryption/RsaKeyPair.js"
102103
export {
@@ -112,6 +113,7 @@ export {
112113
isVersionedRsaX25519PublicKey,
113114
isVersionedPqPublicKey,
114115
isVersionedRsaOrRsaX25519PublicKey,
116+
isRsaX25519PublicKey,
115117
} from "./encryption/AsymmetricKeyPair.js"
116118
export { PQKeyPairs, PQPublicKeys, pqKeyPairsToPublicKeys } from "./encryption/PQKeyPairs.js"
117119
export { sha1Hash } from "./hashes/Sha1.js"

packages/tutanota-utils/lib/Utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,17 @@ export function assertNotNull<T>(value: T | null | undefined, message: string =
143143
return value
144144
}
145145

146+
/**
147+
* throws if the value is not null.
148+
* @param value the value to check
149+
* @param message optional error message
150+
*/
151+
export function assertNull<T>(value: T | null | undefined, message: string = "not null") {
152+
if (value != null) {
153+
throw new Error("AssertNull failed : " + message)
154+
}
155+
}
156+
146157
/**
147158
* assertion function that only returns if the argument is non-null
148159
* (acts as a type guard)

src/calendar-app/calendarLocator.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ import { SyncTracker } from "../common/api/main/SyncTracker.js"
114114
import { KeyVerificationFacade } from "../common/api/worker/facades/lazy/KeyVerificationFacade"
115115
import { getEventWithDefaultTimes, setNextHalfHour } from "../common/api/common/utils/CommonCalendarUtils.js"
116116
import { PublicKeyProvider } from "../common/api/worker/facades/PublicKeyProvider"
117+
import { IdentityKeyCreator } from "../common/api/worker/facades/lazy/IdentityKeyCreator"
117118

118119
assertMainOrNode()
119120

@@ -170,6 +171,7 @@ class CalendarLocator {
170171
themeController!: ThemeController
171172
Const!: Record<string, any>
172173
syncTracker!: SyncTracker
174+
identityKeyCreator!: IdentityKeyCreator
173175

174176
private nativeInterfaces: NativeInterfaces | null = null
175177
private entropyFacade!: EntropyFacade
@@ -579,6 +581,7 @@ class CalendarLocator {
579581
workerFacade,
580582
sqlCipherFacade,
581583
contactFacade,
584+
identityKeyCreator,
582585
} = this.worker.getWorkerInterface()
583586
this.loginFacade = loginFacade
584587
this.customerFacade = customerFacade
@@ -599,6 +602,7 @@ class CalendarLocator {
599602
this.contactFacade = contactFacade
600603
this.serviceExecutor = serviceExecutor
601604
this.sqlCipherFacade = sqlCipherFacade
605+
this.identityKeyCreator = identityKeyCreator
602606
this.logins = new LoginController(
603607
this.loginFacade,
604608
this.customerFacade,

src/calendar-app/workerUtils/worker/CalendarWorkerImpl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ export class CalendarWorkerImpl implements NativeInterface {
7777
return locator.groupManagement()
7878
},
7979

80+
async identityKeyCreator() {
81+
return locator.identityKeyCreator()
82+
},
83+
8084
async configFacade() {
8185
return locator.configFacade()
8286
},

src/calendar-app/workerUtils/worker/CalendarWorkerLocator.ts

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ import { InstancePipeline } from "../../../common/api/worker/crypto/InstancePipe
8484
import { ApplicationTypesFacade } from "../../../common/api/worker/facades/ApplicationTypesFacade"
8585
import { Ed25519Facade } from "../../../common/api/worker/facades/Ed25519Facade"
8686
import { RolloutFacade } from "../../../common/api/worker/facades/RolloutFacade"
87+
import { PublicKeySignatureFacade } from "../../../common/api/worker/facades/PublicKeySignatureFacade"
88+
import { AdminKeyLoaderFacade } from "../../../common/api/worker/facades/AdminKeyLoaderFacade"
89+
import { IdentityKeyCreator } from "../../../common/api/worker/facades/lazy/IdentityKeyCreator"
8790

8891
assertWorkerOrNode()
8992

@@ -105,9 +108,11 @@ export type CalendarWorkerLocatorType = {
105108
blobAccessToken: BlobAccessTokenFacade
106109
keyCache: KeyCache
107110
keyLoader: KeyLoaderFacade
111+
adminKeyLoader: AdminKeyLoaderFacade
108112
publicKeyProvider: PublicKeyProvider
109113
keyRotation: KeyRotationFacade
110114
ed25519Facade: Ed25519Facade
115+
publicKeySignatureFacade: PublicKeySignatureFacade
111116
cryptoWrapper: CryptoWrapper
112117
rolloutFacade: RolloutFacade
113118

@@ -124,6 +129,7 @@ export type CalendarWorkerLocatorType = {
124129

125130
// management facades
126131
groupManagement: lazyAsync<GroupManagementFacade>
132+
identityKeyCreator: lazyAsync<IdentityKeyCreator>
127133
userManagement: lazyAsync<UserManagementFacade>
128134
recoverCode: lazyAsync<RecoverCodeFacade>
129135
customer: lazyAsync<CustomerFacade>
@@ -233,15 +239,10 @@ export async function initLocator(worker: CalendarWorkerImpl, browserData: Brows
233239

234240
locator.cryptoWrapper = new CryptoWrapper()
235241

236-
locator.keyLoader = new KeyLoaderFacade(locator.keyCache, locator.user, locator.cachingEntityClient, locator.cacheManagement)
237-
const keyAuthenticationFacade = new KeyAuthenticationFacade(cryptoWrapper)
238-
locator.publicKeyProvider = new PublicKeyProvider(locator.serviceExecutor, locator.cachingEntityClient, keyAuthenticationFacade, locator.keyLoader)
239-
240-
locator.keyVerification = lazyMemoized(async () => {
241-
const { KeyVerificationFacade } = await import("../../../common/api/worker/facades/lazy/KeyVerificationFacade.js")
242-
return new KeyVerificationFacade(locator.sqlCipherFacade, locator.ed25519Facade)
243-
})
242+
locator.publicKeySignatureFacade = new PublicKeySignatureFacade(locator.ed25519Facade, locator.cryptoWrapper)
244243

244+
const keyAuthenticationFacade = new KeyAuthenticationFacade(cryptoWrapper)
245+
locator.keyLoader = new KeyLoaderFacade(locator.keyCache, locator.user, locator.cachingEntityClient, locator.cacheManagement, locator.cryptoWrapper)
245246
const asymmetricCrypto = new AsymmetricCryptoFacade(
246247
locator.rsa,
247248
locator.pqFacade,
@@ -251,6 +252,21 @@ export async function initLocator(worker: CalendarWorkerImpl, browserData: Brows
251252
locator.keyVerification,
252253
locator.publicKeyProvider,
253254
)
255+
locator.adminKeyLoader = new AdminKeyLoaderFacade(
256+
locator.user,
257+
locator.cachingEntityClient,
258+
locator.keyLoader,
259+
locator.cacheManagement,
260+
asymmetricCrypto,
261+
locator.cryptoWrapper,
262+
keyAuthenticationFacade,
263+
)
264+
locator.publicKeyProvider = new PublicKeyProvider(locator.serviceExecutor, locator.cachingEntityClient, keyAuthenticationFacade, locator.keyLoader)
265+
266+
locator.keyVerification = lazyMemoized(async () => {
267+
const { KeyVerificationFacade } = await import("../../../common/api/worker/facades/lazy/KeyVerificationFacade.js")
268+
return new KeyVerificationFacade(locator.sqlCipherFacade, locator.ed25519Facade)
269+
})
254270

255271
locator.crypto = new CryptoFacade(
256272
locator.user,
@@ -280,6 +296,23 @@ export async function initLocator(worker: CalendarWorkerImpl, browserData: Brows
280296
return new CounterFacade(locator.serviceExecutor)
281297
})
282298

299+
locator.identityKeyCreator = lazyMemoized(async () => {
300+
const { IdentityKeyCreator } = await import("../../../common/api/worker/facades/lazy/IdentityKeyCreator.js")
301+
return new IdentityKeyCreator(
302+
locator.user,
303+
locator.cachingEntityClient,
304+
locator.serviceExecutor,
305+
locator.keyLoader,
306+
locator.adminKeyLoader,
307+
await locator.cacheManagement(),
308+
asymmetricCrypto,
309+
locator.cryptoWrapper,
310+
keyAuthenticationFacade,
311+
locator.ed25519Facade,
312+
locator.publicKeySignatureFacade,
313+
)
314+
})
315+
283316
locator.groupManagement = lazyMemoized(async () => {
284317
const { GroupManagementFacade } = await import("../../../common/api/worker/facades/lazy/GroupManagementFacade.js")
285318
return new GroupManagementFacade(
@@ -289,11 +322,10 @@ export async function initLocator(worker: CalendarWorkerImpl, browserData: Brows
289322
locator.serviceExecutor,
290323
locator.pqFacade,
291324
locator.keyLoader,
325+
locator.adminKeyLoader,
292326
await locator.cacheManagement(),
293-
asymmetricCrypto,
294327
cryptoWrapper,
295-
keyAuthenticationFacade,
296-
locator.ed25519Facade,
328+
await locator.identityKeyCreator(),
297329
)
298330
})
299331
locator.keyRotation = new KeyRotationFacade(
@@ -310,6 +342,8 @@ export async function initLocator(worker: CalendarWorkerImpl, browserData: Brows
310342
asymmetricCrypto,
311343
keyAuthenticationFacade,
312344
locator.publicKeyProvider,
345+
locator.publicKeySignatureFacade,
346+
locator.adminKeyLoader,
313347
)
314348
locator.rolloutFacade = new RolloutFacade(locator.serviceExecutor)
315349

@@ -374,6 +408,8 @@ export async function initLocator(worker: CalendarWorkerImpl, browserData: Brows
374408
locator.pqFacade,
375409
locator.keyLoader,
376410
await locator.recoverCode(),
411+
locator.adminKeyLoader,
412+
await locator.identityKeyCreator(),
377413
)
378414
})
379415
locator.customer = lazyMemoized(async () => {
@@ -438,7 +474,7 @@ export async function initLocator(worker: CalendarWorkerImpl, browserData: Brows
438474
const { MailAddressFacade } = await import("../../../common/api/worker/facades/lazy/MailAddressFacade.js")
439475
return new MailAddressFacade(
440476
locator.user,
441-
await locator.groupManagement(),
477+
locator.adminKeyLoader,
442478
locator.serviceExecutor,
443479
nonCachingEntityClient, // without cache
444480
)
@@ -465,6 +501,7 @@ export async function initLocator(worker: CalendarWorkerImpl, browserData: Brows
465501
(queuedBatch: QueuedBatch[]) => noOp,
466502
locator.rolloutFacade,
467503
locator.groupManagement,
504+
locator.identityKeyCreator,
468505
mainInterface.syncTracker,
469506
)
470507

src/common/api/common/TutanotaConstants.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,3 +1310,19 @@ export const enum RolloutType {
13101310
UserIdentityKeyCreation = "0",
13111311
SharedMailboxIdentityKeyCreation = "1",
13121312
}
1313+
1314+
/**
1315+
* The type of signature of a public encryption key, signed with an identity key pair.
1316+
*/
1317+
export enum PublicKeySignatureType {
1318+
RsaEcc = "0", // the signed public key is RSA ECC key
1319+
TutaCrypt = "1", // the signed public key is a TutaCrypt key
1320+
RsaFormerGroupKey = "2", // the signed public key is a former(!) group key Rsa only (only kept for decryption of existing data)
1321+
}
1322+
1323+
export function asPublicKeySignatureType(maybe: NumberString): PublicKeySignatureType {
1324+
if (Object.values(PublicKeySignatureType).includes(maybe as PublicKeySignatureType)) {
1325+
return maybe as PublicKeySignatureType
1326+
}
1327+
throw new Error("bad public key signature type")
1328+
}

0 commit comments

Comments
 (0)