@wireapp/core-crypto 0.5.2 → 0.6.0-pre.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -55,9 +55,10 @@ rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
55
55
 
56
56
  ### MacOS
57
57
 
58
- Install macOS rust targets (M1 macs are currently not supported)
58
+ Install macOS rust targets
59
59
  ```ignore
60
60
  rustup target add x86_64-apple-darwin
61
+ rustup target add aarch64-apple-darwin
61
62
  ```
62
63
 
63
64
  ### Linux
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wireapp/core-crypto",
3
- "version": "0.5.2",
3
+ "version": "0.6.0-pre.2",
4
4
  "description": "CoreCrypto bindings for the Web",
5
5
  "type": "module",
6
6
  "module": "platforms/web/corecrypto.js",
@@ -68,16 +68,6 @@ export declare type ClientId = Uint8Array;
68
68
  * Alias for proposal reference. It is a byte array of size 16.
69
69
  */
70
70
  export declare type ProposalRef = Uint8Array;
71
- /**
72
- * Alias for an MLS generic commit.
73
- * It contains:
74
- * * MLS Commit that needs to be fanned out to other (existing) members of the conversation
75
- * * (Optional) MLS Welcome message that needs to be fanned out to the clients newly added to the conversation (if any)
76
- * * TLS-serialized MLS PublicGroupState (GroupInfo in draft-15) which is required for joining a group by external commit + some metadatas for optimizations
77
- * (For final version. Requires to be implemented in the Delivery Service)
78
- * This is a freeform, uninspected buffer.
79
- */
80
- export declare type TlsCommitBundle = Uint8Array;
81
71
  /**
82
72
  * Data shape for the returned MLS commit & welcome message tuple upon adding clients to a conversation
83
73
  */
@@ -95,11 +85,11 @@ export interface MemberAddedMessages {
95
85
  */
96
86
  welcome: Uint8Array;
97
87
  /**
98
- * TLS-serialized MLS PublicGroupState (GroupInfo in draft-15) which is required for joining a group by external commit
88
+ * MLS PublicGroupState (GroupInfo in draft-15) which is required for joining a group by external commit
99
89
  *
100
90
  * @readonly
101
91
  */
102
- publicGroupState: Uint8Array;
92
+ publicGroupState: PublicGroupStateBundle;
103
93
  }
104
94
  /**
105
95
  * Data shape for a MLS generic commit + optional bundle (aka stapled commit & welcome)
@@ -118,11 +108,61 @@ export interface CommitBundle {
118
108
  */
119
109
  welcome?: Uint8Array;
120
110
  /**
121
- * TLS-serialized MLS PublicGroupState (GroupInfo in draft-15) which is required for joining a group by external commit
111
+ * MLS PublicGroupState (GroupInfo in draft-15) which is required for joining a group by external commit
122
112
  *
123
113
  * @readonly
124
114
  */
125
- publicGroupState: Uint8Array;
115
+ publicGroupState: PublicGroupStateBundle;
116
+ }
117
+ /**
118
+ * Wraps a PublicGroupState in order to efficiently upload it to the Delivery Service.
119
+ * This is not part of MLS protocol but parts might be standardized at some point.
120
+ */
121
+ export interface PublicGroupStateBundle {
122
+ /**
123
+ * see {@link PublicGroupStateEncryptionType}
124
+ */
125
+ encryptionType: PublicGroupStateEncryptionType;
126
+ /**
127
+ * see {@link RatchetTreeType}
128
+ */
129
+ ratchetTreeType: RatchetTreeType;
130
+ /**
131
+ * TLS-serialized PublicGroupState
132
+ */
133
+ payload: Uint8Array;
134
+ }
135
+ /**
136
+ * Informs whether the PublicGroupState is confidential
137
+ * see [core_crypto::mls::conversation::public_group_state::PublicGroupStateEncryptionType]
138
+ */
139
+ export declare enum PublicGroupStateEncryptionType {
140
+ /**
141
+ * Unencrypted
142
+ */
143
+ Plaintext = 1,
144
+ /**
145
+ * Encrypted in a JWE (not yet implemented)
146
+ */
147
+ JweEncrypted = 2
148
+ }
149
+ /**
150
+ * Represents different ways of carrying the Ratchet Tree with some optimizations to save some space
151
+ * see [core_crypto::mls::conversation::public_group_state::RatchetTreeType]
152
+ */
153
+ export declare enum RatchetTreeType {
154
+ /**
155
+ * Complete PublicGroupState
156
+ */
157
+ Full = 1,
158
+ /**
159
+ * Contains the difference since previous epoch (not yet implemented)
160
+ */
161
+ Delta = 2,
162
+ /**
163
+ * To define (not yet implemented)
164
+ */
165
+ ByRef = 3
126
166
  }
127
167
  /**
128
168
  * Params for CoreCrypto initialization
@@ -166,19 +206,26 @@ export interface Invitee {
166
206
  */
167
207
  kp: Uint8Array;
168
208
  }
169
- export interface MlsConversationInitMessage {
209
+ export interface ConversationInitBundle {
170
210
  /**
171
- * Group ID
211
+ * Conversation ID of the conversation created
172
212
  *
173
213
  * @readonly
174
214
  */
175
- group: Uint8Array;
215
+ conversationId: ConversationId;
176
216
  /**
177
- * TLS-serialized MLS Commit that needs to be fanned out
217
+ * TLS-serialized MLS External Commit that needs to be fanned out
178
218
  *
179
219
  * @readonly
180
220
  */
181
221
  commit: Uint8Array;
222
+ /**
223
+ * MLS Public Group State (aka Group Info) which becomes valid when the external commit is accepted by the Delivery Service
224
+ * with {@link CoreCrypto.mergePendingGroupFromExternalCommit}
225
+ *
226
+ * @readonly
227
+ */
228
+ publicGroupState: PublicGroupStateBundle;
182
229
  }
183
230
  /**
184
231
  * This is a wrapper for all the possible outcomes you can get after decrypting a message
@@ -207,6 +254,10 @@ export interface DecryptedMessage {
207
254
  * Client identifier of the sender of the message being decrypted. Only present for application messages.
208
255
  */
209
256
  senderClientId?: ClientId;
257
+ /**
258
+ * true when the decrypted message resulted in an epoch change i.e. it was a commit
259
+ */
260
+ hasEpochChanged: boolean;
210
261
  }
211
262
  /**
212
263
  * Returned by all methods creating proposals. Contains a proposal message and an identifier to roll back the proposal
@@ -305,7 +356,7 @@ export interface CoreCryptoCallbacks {
305
356
  * in the given conversationId. Think of it as a "isAdmin" callback conceptually
306
357
  *
307
358
  * This callback exists because there are many business cases where CoreCrypto doesn't have enough knowledge
308
- * (such as what can exists on a backend) to inform the decision
359
+ * (such as what can exist on a backend) to inform the decision
309
360
  *
310
361
  * @param conversationId - id of the group/conversation
311
362
  * @param clientId - id of the client performing an operation requiring authorization
@@ -313,10 +364,25 @@ export interface CoreCryptoCallbacks {
313
364
  */
314
365
  authorize: (conversationId: Uint8Array, clientId: Uint8Array) => boolean;
315
366
  /**
316
- * Callback to ensure that the given `clientId` belongs to one of the provided `otherClients`
367
+ * A mix between {@link authorize} and {@link clientIsExistingGroupUser}. We currently use this callback to verify
368
+ * external commits to join a group ; in such case, the client has to:
369
+ * * first, belong to a user which is already in the MLS group (similar to {@link clientIsExistingGroupUser})
370
+ * * then, this user should be authorized to "write" in the given conversation (similar to {@link authorize})
371
+ *
372
+ * @param conversationId - id of the group/conversation
373
+ * @param externalClientId - id of the client performing an operation requiring authorization
374
+ * @param existingClients - all the clients currently within the MLS group
375
+ * @returns true if the external client is authorized to write to the conversation
376
+ */
377
+ userAuthorize: (conversationId: Uint8Array, externalClientId: Uint8Array, existingClients: Uint8Array[]) => boolean;
378
+ /**
379
+ * Callback to ensure that the given `clientId` belongs to one of the provided `existingClients`
317
380
  * This basically allows to defer the client ID parsing logic to the caller - because CoreCrypto is oblivious to such things
381
+ *
382
+ * @param clientId - id of a client
383
+ * @param existingClients - all the clients currently within the MLS group
318
384
  */
319
- clientIdBelongsToOneOf: (clientId: Uint8Array, otherClients: Uint8Array[]) => boolean;
385
+ clientIsExistingGroupUser: (clientId: Uint8Array, existingClients: Uint8Array[]) => boolean;
320
386
  }
321
387
  /**
322
388
  * Wrapper for the WASM-compiled version of CoreCrypto
@@ -515,72 +581,6 @@ export declare class CoreCrypto {
515
581
  * @returns A {@link CommitBundle} or `undefined` when there was no pending proposal to commit
516
582
  */
517
583
  commitPendingProposals(conversationId: ConversationId): Promise<CommitBundle | undefined>;
518
- /**
519
- * Adds new clients to a conversation, assuming the current client has the right to add new clients to the conversation.
520
- * The returned {@link CommitBundle} is a TLS struct that needs to be fanned out to Delivery Service in order to validate the commit.
521
- * It also contains a Welcome message the Delivery Service will forward to invited clients and
522
- * an updated PublicGroupState required by clients willing to join the group by an external commit.
523
- *
524
- * **CAUTION**: {@link CoreCrypto.commitAccepted} **HAS TO** be called afterwards **ONLY IF** the Delivery Service responds
525
- * '200 OK' to the {@link CommitBundle} upload. It will "merge" the commit locally i.e. increment the local group
526
- * epoch, use new encryption secrets etc...
527
- *
528
- * @param conversationId - The ID of the conversation
529
- * @param clients - Array of {@link Invitee} (which are Client ID / KeyPackage pairs)
530
- *
531
- * @returns A {@link CommitBundle} byte array to fan out to the Delivery Service
532
- */
533
- finalAddClientsToConversation(conversationId: ConversationId, clients: Invitee[]): Promise<TlsCommitBundle>;
534
- /**
535
- * Removes the provided clients from a conversation; Assuming those clients exist and the current client is allowed
536
- * to do so, otherwise this operation does nothing.
537
- *
538
- * The returned {@link CommitBundle} is a TLS struct that needs to be fanned out to Delivery Service in order to validate the commit.
539
- * It also contains a Welcome message the Delivery Service will forward to invited clients and
540
- * an updated PublicGroupState required by clients willing to join the group by an external commit.
541
- *
542
- * **CAUTION**: {@link CoreCrypto.commitAccepted} **HAS TO** be called afterwards **ONLY IF** the Delivery Service responds
543
- * '200 OK' to the {@link CommitBundle} upload. It will "merge" the commit locally i.e. increment the local group
544
- * epoch, use new encryption secrets etc...
545
- *
546
- * @param conversationId - The ID of the conversation
547
- * @param clientIds - Array of Client IDs to remove.
548
- *
549
- * @returns A {@link CommitBundle} byte array to fan out to the Delivery Service, or `undefined` if for any reason, the operation would result in an empty commit
550
- */
551
- finalRemoveClientsFromConversation(conversationId: ConversationId, clientIds: ClientId[]): Promise<TlsCommitBundle>;
552
- /**
553
- * Creates an update commit which forces every client to update their keypackages in the conversation
554
- *
555
- * The returned {@link CommitBundle} is a TLS struct that needs to be fanned out to Delivery Service in order to validate the commit.
556
- * It also contains a Welcome message the Delivery Service will forward to invited clients and
557
- * an updated PublicGroupState required by clients willing to join the group by an external commit.
558
- *
559
- * **CAUTION**: {@link CoreCrypto.commitAccepted} **HAS TO** be called afterwards **ONLY IF** the Delivery Service responds
560
- * '200 OK' to the {@link CommitBundle} upload. It will "merge" the commit locally i.e. increment the local group
561
- * epoch, use new encryption secrets etc...
562
- *
563
- * @param conversationId - The ID of the conversation
564
- *
565
- * @returns A {@link CommitBundle} byte array to fan out to the Delivery Service
566
- */
567
- finalUpdateKeyingMaterial(conversationId: ConversationId): Promise<TlsCommitBundle>;
568
- /**
569
- * Commits the local pending proposals and returns the {@link CommitBundle} object containing what can result from this operation.
570
- *
571
- * The returned {@link CommitBundle} is a TLS struct that needs to be fanned out to Delivery Service in order to validate the commit.
572
- * It also contains a Welcome message the Delivery Service will forward to invited clients and
573
- * an updated PublicGroupState required by clients willing to join the group by an external commit.
574
- *
575
- * **CAUTION**: {@link CoreCrypto.commitAccepted} **HAS TO** be called afterwards **ONLY IF** the Delivery Service responds
576
- * '200 OK' to the {@link CommitBundle} upload. It will "merge" the commit locally i.e. increment the local group
577
- * epoch, use new encryption secrets etc...
578
- *
579
- * @param conversationId - The ID of the conversation
580
- *
581
- * @returns A {@link CommitBundle} byte array to fan out to the Delivery Service or `undefined` when there was no pending proposal to commit
582
- */
583
- finalCommitPendingProposals(conversationId: ConversationId): Promise<TlsCommitBundle | undefined>;
584
584
  /**
585
585
  * Creates a new proposal for the provided Conversation ID
586
586
  *
@@ -599,23 +599,35 @@ export declare class CoreCrypto {
599
599
  */
600
600
  exportGroupState(conversationId: ConversationId): Promise<Uint8Array>;
601
601
  /**
602
- * Allows to create an external commit to "apply" to join a group through its public group state
602
+ * Allows to create an external commit to "apply" to join a group through its public group state.
603
603
  *
604
- * Also see the second step of this process: {@link CoreCrypto.mergePendingGroupFromExternalCommit}
604
+ * If the Delivery Service accepts the external commit, you have to {@link CoreCrypto.mergePendingGroupFromExternalCommit}
605
+ * in order to get back a functional MLS group. On the opposite, if it rejects it, you can either retry by just
606
+ * calling again {@link CoreCrypto.joinByExternalCommit}, no need to {@link CoreCrypto.clearPendingGroupFromExternalCommit}.
607
+ * If you want to abort the operation (too many retries or the user decided to abort), you can use
608
+ * {@link CoreCrypto.clearPendingGroupFromExternalCommit} in order not to bloat the user's storage but nothing
609
+ * bad can happen if you forget to except some storage space wasted.
605
610
  *
606
- * @param publicGroupState - The public group state that can be fetched from the backend for a given conversation
607
- * @returns see {@link MlsConversationInitMessage}
611
+ * @param publicGroupState - a TLS encoded PublicGroupState fetched from the Delivery Service
612
+ * @returns see {@link ConversationInitBundle}
608
613
  */
609
- joinByExternalCommit(publicGroupState: Uint8Array): Promise<MlsConversationInitMessage>;
614
+ joinByExternalCommit(publicGroupState: Uint8Array): Promise<ConversationInitBundle>;
610
615
  /**
611
- * This is the second step of the process of joining a group through an external commit
612
- *
613
- * This step makes the group operational and ready to encrypt/decrypt message
616
+ * This merges the commit generated by {@link CoreCrypto.joinByExternalCommit}, persists the group permanently
617
+ * and deletes the temporary one. This step makes the group operational and ready to encrypt/decrypt message
614
618
  *
615
619
  * @param conversationId - The ID of the conversation
616
620
  * @param configuration - Configuration of the group, see {@link ConversationConfiguration}
617
621
  */
618
622
  mergePendingGroupFromExternalCommit(conversationId: ConversationId, configuration: ConversationConfiguration): Promise<void>;
623
+ /**
624
+ * In case the external commit generated by {@link CoreCrypto.joinByExternalCommit} is rejected by the Delivery Service, and we
625
+ * want to abort this external commit once for all, we can wipe out the pending group from the keystore in order
626
+ * not to waste space
627
+ *
628
+ * @param conversationId - The ID of the conversation
629
+ */
630
+ clearPendingGroupFromExternalCommit(conversationId: ConversationId): Promise<void>;
619
631
  /**
620
632
  * Allows to mark the latest commit produced as "accepted" and be able to safely merge it
621
633
  * into the local group state
@@ -646,6 +658,24 @@ export declare class CoreCrypto {
646
658
  * @param conversationId - The group's ID
647
659
  */
648
660
  clearPendingCommit(conversationId: ConversationId): Promise<void>;
661
+ /**
662
+ * Derives a new key from the group
663
+ *
664
+ * @param conversationId - The group's ID
665
+ * @param keyLength - the length of the key to be derived. If the value is higher than the
666
+ * bounds of `u16` or the context hash * 255, an error will be returned
667
+ *
668
+ * @returns A `Uint8Array` representing the derived key
669
+ */
670
+ exportSecretKey(conversationId: ConversationId, keyLength: number): Promise<Uint8Array>;
671
+ /**
672
+ * Returns all clients from group's members
673
+ *
674
+ * @param conversationId - The group's ID
675
+ *
676
+ * @returns A list of clients from the members of the group
677
+ */
678
+ getClientIds(conversationId: ConversationId): Promise<ClientId[]>;
649
679
  /**
650
680
  * Allows {@link CoreCrypto} to act as a CSPRNG provider
651
681
  * @note The underlying CSPRNG algorithm is ChaCha20 and takes in account the external seed provider either at init time or provided with {@link CoreCrypto.reseedRng}
@@ -661,6 +691,82 @@ export declare class CoreCrypto {
661
691
  * @param seed - **exactly 32** bytes buffer seed
662
692
  */
663
693
  reseedRng(seed: Uint8Array): Promise<void>;
694
+ /**
695
+ * Initiailizes the proteus client
696
+ */
697
+ proteusInit(): Promise<void>;
698
+ /**
699
+ * Create a Proteus session using a prekey
700
+ *
701
+ * @param sessionId - ID of the Proteus session
702
+ * @param prekey - CBOR-encoded Proteus prekey of the other client
703
+ */
704
+ proteusSessionFromPrekey(sessionId: string, prekey: Uint8Array): Promise<void>;
705
+ /**
706
+ * Create a Proteus session from a handshake message
707
+ *
708
+ * @param sessionId - ID of the Proteus session
709
+ * @param envelope - CBOR-encoded Proteus message
710
+ */
711
+ proteusSessionFromMessage(sessionId: string, envelope: Uint8Array): Promise<void>;
712
+ /**
713
+ * Locally persists a session to the keystore
714
+ *
715
+ * @param sessionId - ID of the Proteus session
716
+ */
717
+ proteusSessionSave(sessionId: string): Promise<void>;
718
+ /**
719
+ * Deletes a session
720
+ * Note: this also deletes the persisted data within the keystore
721
+ *
722
+ * @param sessionId - ID of the Proteus session
723
+ */
724
+ proteusSessionDelete(sessionId: string): Promise<void>;
725
+ /**
726
+ * Decrypt an incoming message for an existing Proteus session
727
+ *
728
+ * @param sessionId - ID of the Proteus session
729
+ * @param ciphertext - CBOR encoded, encrypted proteus message
730
+ * @returns The decrypted payload contained within the message
731
+ */
732
+ proteusDecrypt(sessionId: string, ciphertext: Uint8Array): Promise<Uint8Array>;
733
+ /**
734
+ * Encrypt a message for a given Proteus session
735
+ *
736
+ * @param sessionId - ID of the Proteus session
737
+ * @param plaintext - payload to encrypt
738
+ * @returns The CBOR-serialized encrypted message
739
+ */
740
+ proteusEncrypt(sessionId: string, plaintext: Uint8Array): Promise<Uint8Array>;
741
+ /**
742
+ * Batch encryption for proteus messages
743
+ * This is used to minimize FFI roundtrips when used in the context of a multi-client session (i.e. conversation)
744
+ *
745
+ * @param sessions - List of Proteus session IDs to encrypt the message for
746
+ * @param plaintext - payload to encrypt
747
+ * @returns A map indexed by each session ID and the corresponding CBOR-serialized encrypted message for this session
748
+ */
749
+ proteusEncryptBatched(sessions: string[], plaintext: Uint8Array): Promise<Map<string, Uint8Array>>;
750
+ /**
751
+ * Creates a new prekey with the requested ID.
752
+ *
753
+ * @param prekeyId - ID of the PreKey to generate. This cannot be bigger than a u16
754
+ * @returns: A CBOR-serialized version of the PreKeyBundle corresponding to the newly generated and stored PreKey
755
+ */
756
+ proteusNewPrekey(prekeyId: number): Promise<Uint8Array>;
757
+ /**
758
+ * Proteus public key fingerprint
759
+ * It's basically the public key encoded as an hex string
760
+ *
761
+ * @returns Hex-encoded public key string
762
+ */
763
+ proteusFingerprint(): Promise<string>;
764
+ /**
765
+ * Imports all the data stored by Cryptobox into the CoreCrypto keystore
766
+ *
767
+ * @param storeName - The name of the IndexedDB store where the data is stored
768
+ */
769
+ proteusCryptoboxMigrate(storeName: string): Promise<void>;
664
770
  /**
665
771
  * Returns the current version of {@link CoreCrypto}
666
772
  *