ping-openmls-sdk-react-native-macos 0.3.0 → 0.6.1

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.
Binary file
@@ -814,11 +814,11 @@ open class MessagingClient:
814
814
  try! rustCall { uniffi_ping_ffi_fn_free_messagingclient(pointer, $0) }
815
815
  }
816
816
 
817
- public static func `init`(identityExport: Data, deviceLabel: String, storage: Storage, transport: Transport, nowMs: UInt64, sqlitePath: String?, sqliteEncryptionKey: Data?) async throws -> MessagingClient {
817
+ public static func `init`(identityExport: Data, deviceLabel: String, storage: Storage, transport: Transport, nowMs: UInt64, sqlitePath: String?, sqliteEncryptionKey: Data?, deviceSigningSecretKey: Data?) async throws -> MessagingClient {
818
818
  return
819
819
  try await uniffiRustCallAsync(
820
820
  rustFutureFunc: {
821
- uniffi_ping_ffi_fn_constructor_messagingclient_init(FfiConverterData.lower(identityExport), FfiConverterString.lower(deviceLabel), FfiConverterTypeStorage.lower(storage), FfiConverterTypeTransport.lower(transport), FfiConverterUInt64.lower(nowMs), FfiConverterOptionString.lower(sqlitePath), FfiConverterOptionData.lower(sqliteEncryptionKey))
821
+ uniffi_ping_ffi_fn_constructor_messagingclient_init(FfiConverterData.lower(identityExport), FfiConverterString.lower(deviceLabel), FfiConverterTypeStorage.lower(storage), FfiConverterTypeTransport.lower(transport), FfiConverterUInt64.lower(nowMs), FfiConverterOptionString.lower(sqlitePath), FfiConverterOptionData.lower(sqliteEncryptionKey), FfiConverterOptionData.lower(deviceSigningSecretKey))
822
822
  },
823
823
  pollFunc: ffi_ping_ffi_rust_future_poll_pointer,
824
824
  completeFunc: ffi_ping_ffi_rust_future_complete_pointer,
@@ -3610,7 +3610,7 @@ private var initializationResult: InitializationResult = {
3610
3610
  if uniffi_ping_ffi_checksum_method_transport_send() != 46493 {
3611
3611
  return InitializationResult.apiChecksumMismatch
3612
3612
  }
3613
- if uniffi_ping_ffi_checksum_constructor_messagingclient_init() != 34706 {
3613
+ if uniffi_ping_ffi_checksum_constructor_messagingclient_init() != 35443 {
3614
3614
  return InitializationResult.apiChecksumMismatch
3615
3615
  }
3616
3616
 
@@ -51,11 +51,15 @@ RCT_EXTERN_METHOD(generateIdentityExport: (RCTPromiseResolveBlock)resolve
51
51
  // Stage 4b: MessagingClient lifecycle.
52
52
  // [CR-4] sqlitePath + sqliteEncryptionKeyB64 are optional (nullable) — passing nil for
53
53
  // path falls back to the in-memory provider.
54
+ // `deviceSigningSecretKeyB64` is the optional 32-byte Ed25519 secret the SDK adopts
55
+ // as its device signing key on first init — used to align device_id with the host
56
+ // auth layer's JWT claim. Pass nil to keep the random-key default.
54
57
  RCT_EXTERN_METHOD(initClient: (NSString *)identityB64
55
58
  deviceLabel: (NSString *)deviceLabel
56
59
  nowMs: (double)nowMs
57
60
  sqlitePath: (NSString *)sqlitePath
58
61
  sqliteEncryptionKeyB64: (NSString *)sqliteEncryptionKeyB64
62
+ deviceSigningSecretKeyB64: (NSString *)deviceSigningSecretKeyB64
59
63
  resolver: (RCTPromiseResolveBlock)resolve
60
64
  rejecter: (RCTPromiseRejectBlock)reject)
61
65
 
@@ -211,13 +211,14 @@ public final class PingNative: RCTEventEmitter {
211
211
  /// `nowMs` is the wall-clock at the call site (Hermes can't pass UInt64 across the
212
212
  /// bridge precisely so we accept Double and truncate; valid for the next ~285,000
213
213
  /// years of Unix time).
214
- @objc(initClient:deviceLabel:nowMs:sqlitePath:sqliteEncryptionKeyB64:resolver:rejecter:)
214
+ @objc(initClient:deviceLabel:nowMs:sqlitePath:sqliteEncryptionKeyB64:deviceSigningSecretKeyB64:resolver:rejecter:)
215
215
  public func initClient(
216
216
  _ identityB64: String,
217
217
  deviceLabel: String,
218
218
  nowMs: Double,
219
219
  sqlitePath: String?,
220
220
  sqliteEncryptionKeyB64: String?,
221
+ deviceSigningSecretKeyB64: String?,
221
222
  resolver resolve: @escaping RCTPromiseResolveBlock,
222
223
  rejecter reject: @escaping RCTPromiseRejectBlock
223
224
  ) {
@@ -238,6 +239,24 @@ public final class PingNative: RCTEventEmitter {
238
239
  }
239
240
  sqliteKey = keyData
240
241
  }
242
+ // Optional Ed25519 device signing secret. Same 32-byte
243
+ // shape as the SQLCipher key; loud-reject on mismatched
244
+ // length so callers find the bug at init time rather
245
+ // than via a server-side `sender_device_mismatch`
246
+ // hours later.
247
+ var deviceSigningSecret: Data? = nil
248
+ if let secretB64 = deviceSigningSecretKeyB64,
249
+ let secretData = Data(base64Encoded: secretB64) {
250
+ guard secretData.count == 32 else {
251
+ reject(
252
+ "InvalidDeviceSigningKey",
253
+ "deviceSigningSecretKey must be 32 bytes, got \(secretData.count)",
254
+ nil
255
+ )
256
+ return
257
+ }
258
+ deviceSigningSecret = secretData
259
+ }
241
260
  // UniFFI's `[Name=init]` constructor generates a static factory named
242
261
  // `init` (backquoted because Swift reserves the bare identifier for
243
262
  // designated initializers).
@@ -248,7 +267,8 @@ public final class PingNative: RCTEventEmitter {
248
267
  transport: self.transportBridge,
249
268
  nowMs: UInt64(nowMs),
250
269
  sqlitePath: sqlitePath,
251
- sqliteEncryptionKey: sqliteKey
270
+ sqliteEncryptionKey: sqliteKey,
271
+ deviceSigningSecretKey: deviceSigningSecret
252
272
  )
253
273
  self.client = c
254
274
  resolve(true)
package/ios/pingFFI.h CHANGED
@@ -374,7 +374,7 @@ void uniffi_ping_ffi_fn_free_messagingclient(void*_Nonnull ptr, RustCallStatus *
374
374
  #endif
375
375
  #ifndef UNIFFI_FFIDEF_UNIFFI_PING_FFI_FN_CONSTRUCTOR_MESSAGINGCLIENT_INIT
376
376
  #define UNIFFI_FFIDEF_UNIFFI_PING_FFI_FN_CONSTRUCTOR_MESSAGINGCLIENT_INIT
377
- uint64_t uniffi_ping_ffi_fn_constructor_messagingclient_init(RustBuffer identity_export, RustBuffer device_label, void*_Nonnull storage, void*_Nonnull transport, uint64_t now_ms, RustBuffer sqlite_path, RustBuffer sqlite_encryption_key
377
+ uint64_t uniffi_ping_ffi_fn_constructor_messagingclient_init(RustBuffer identity_export, RustBuffer device_label, void*_Nonnull storage, void*_Nonnull transport, uint64_t now_ms, RustBuffer sqlite_path, RustBuffer sqlite_encryption_key, RustBuffer device_signing_secret_key
378
378
  );
379
379
  #endif
380
380
  #ifndef UNIFFI_FFIDEF_UNIFFI_PING_FFI_FN_METHOD_MESSAGINGCLIENT_ADD_MEMBERS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ping-openmls-sdk-react-native-macos",
3
- "version": "0.3.0",
3
+ "version": "0.6.1",
4
4
  "description": "Real MLS for React Native macOS apps — wraps the ping-openmls-sdk Rust core via UniFFI.",
5
5
  "homepage": "https://github.com/AMP-Media-Development/ping-openmls-sdk",
6
6
  "license": "Apache-2.0",
@@ -37,6 +37,25 @@ export interface ClientConfig {
37
37
  * from your OS keyring (Keychain on iOS, Keystore on Android, etc.).
38
38
  */
39
39
  sqliteEncryptionKey?: Uint8Array;
40
+ /**
41
+ * Optional 32-byte Ed25519 secret key the SDK adopts as its device
42
+ * signing key on FIRST init. When supplied, `deviceId()` returns
43
+ * `SHA-256(public_key_of(secret))` — deterministic from what the
44
+ * caller passes.
45
+ *
46
+ * Use case: align the SDK's `device_id` (which the SDK stamps into
47
+ * every envelope's `sender_device` field) with an externally-
48
+ * computed device id — typically `SHA-256(device_signing_pubkey)`
49
+ * in the host's auth layer, where the JWT carries that same value
50
+ * as its `device_id` claim. Without this alignment, a server that
51
+ * validates `envelope.sender_device == jwt.device_id` rejects every
52
+ * send with `sender_device_mismatch`.
53
+ *
54
+ * Ignored on re-init when a `LocalDevice` is already persisted in
55
+ * storage — the on-disk identity is authoritative for stability
56
+ * across restarts.
57
+ */
58
+ deviceSigningSecretKey?: Uint8Array;
40
59
  }
41
60
 
42
61
  /** [CR-2] One `(deviceId, keyPackage)` pair for `Conversation.addMembers`. */
@@ -85,12 +104,16 @@ export class MessagingClient {
85
104
  const sqliteKeyB64 = cfg.sqliteEncryptionKey
86
105
  ? bytesToBase64(cfg.sqliteEncryptionKey)
87
106
  : null;
107
+ const signingSecretB64 = cfg.deviceSigningSecretKey
108
+ ? bytesToBase64(cfg.deviceSigningSecretKey)
109
+ : null;
88
110
  await NativePing.initClient(
89
111
  identityB64,
90
112
  cfg.deviceLabel,
91
113
  Date.now(),
92
114
  cfg.sqlitePath ?? null,
93
115
  sqliteKeyB64,
116
+ signingSecretB64,
94
117
  );
95
118
  } catch (e) {
96
119
  // Init failed — disconnect the bridges so we don't leak event listeners.
package/src/NativePing.ts CHANGED
@@ -59,6 +59,10 @@ export interface Spec extends TurboModule {
59
59
  * (fine for tests / non-cold-start flows; **not** fine for NSE-style wake-ups).
60
60
  * - [CR-4] `sqliteEncryptionKeyB64` — 32-byte SQLCipher key, base64-encoded. Ignored
61
61
  * when `sqlitePath` is `null`. Pass `null` if you don't want at-rest encryption.
62
+ * - `deviceSigningSecretKeyB64` — optional 32-byte Ed25519 secret the SDK
63
+ * adopts as its device signing key on FIRST init. Pass `null` to keep
64
+ * the legacy random-key behaviour. See `ClientConfig.deviceSigningSecretKey`
65
+ * in MessagingClient.ts for the security rationale.
62
66
  *
63
67
  * Resolves to `true` on success, rejects with `InitFailed` (Rust error) or
64
68
  * `InvalidIdentity` (base64 decode) on failure.
@@ -69,6 +73,7 @@ export interface Spec extends TurboModule {
69
73
  nowMs: number,
70
74
  sqlitePath: string | null,
71
75
  sqliteEncryptionKeyB64: string | null,
76
+ deviceSigningSecretKeyB64: string | null,
72
77
  ): Promise<boolean>;
73
78
 
74
79
  /** Returns the active client's user_id as a hex string. Rejects if not initialised. */