Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS: Can't accept encrypted shared notebooks #10409

Closed
personalizedrefrigerator opened this issue May 7, 2024 · 4 comments
Closed

iOS: Can't accept encrypted shared notebooks #10409

personalizedrefrigerator opened this issue May 7, 2024 · 4 comments
Labels
bug It's a bug high High priority issues iOS v3.0

Comments

@personalizedrefrigerator
Copy link
Collaborator

Operating system

iOS

Joplin version

13.0.1

Desktop version info

Joplin 13.0.1 (dev, ios)

Client ID: 6480c522f64f41169da1440f54671dce
Sync Version: 3
Profile Version: 47
Keychain Supported: No

Note Tabs: 1.4.0
Quick Links: 1.3.2

FTS enabled: 1
Hermes enabled: 1

Current behaviour

To reproduce:

  1. Set up Joplin Server with two users (user A and user B).
  2. Sync with user A from a desktop client and user B from a mobile client.
  3. Enable encryption for both.
  4. Share a notebook from user A to user B.
  5. Attempt to accept the shared notebook on client B (the mobile device).
  6. Sync a desktop client with the account for user B.
  7. Accept the shared notebook from the desktop client.
  8. Sync the mobile client.
  9. Wait for decryption.
  10. Observe that a "press to set the decryption password" warning is visible.
  11. Click on the warning.
  12. Observe that there is no place to enter a decryption password and the notebook remains encrypted:
screenshot: encryption config screen on an iOS simulator: Has two master keys, no place to set the password

Expected behaviour

It should be possible to accept and sync shared notebooks

Logs

Error when failing to accept a share:

05-07T13:38:56: invitationRespond: Error: Cannot read property 'slice' of undefined
TypeError: Cannot read property 'slice' of undefined
    at encrypt (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:407212:20)
    at ja (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:407723:182)
    at apply (native)
    at encrypt (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:407728:23)
    at anonymous (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:236772:39)
    at ?anon_0_ (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:236786:34)
    at next (native)
    at anonymous (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:236442:73)
    at tryCallTwo (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:61:9)
    at doResolve (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:216:25)
    at Promise (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:82:14)
    at anonymous (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:236424:36)
    at encrypt (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:236699:25)
    at ?anon_0_ (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:236625:42)
    at next (native)
    at anonymous (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:236442:73)
    at tryCallTwo (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:61:9)
    at doResolve (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:216:25)
    at Promise (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:82:14)
    at anonymous (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:236424:36)
    at encryptMasterKeyContent (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:236612:25)
    at ?anon_0_ (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:334111:61)
    at next (native)
    at fulfilled (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=net.cozic.joplin:333957:30)
    at tryCallOne (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:53:16)
    at anonymous (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:139:27)
    at apply (native)
    ...

By adding console.log statements, the error seems to be caused by rsa().decrypt returning a promise that resolves to undefined on this line:

return rsa().decrypt(parsed.ciphertext, context.rsaKeyPair);

The .slice-related error comes from an attempt to use the undefined result.

@laurent22 laurent22 added the v3.0 label May 7, 2024
@personalizedrefrigerator
Copy link
Collaborator Author

personalizedrefrigerator commented May 9, 2024

Android seems to also have trouble with encrypted shared notebooks — notes can be downloaded, but uploading a new note fails with "master key is not loaded".

Edit: The Android issue might be new ­— it should be verified that this isn't an issue introduced by React Native 0.74. It's also possible that this issue is caused by an error in the mobile share-accepting logic. As such, I should test:

  • Android: RN RSA on-device tests.
    • The iOS RSA on-device tests pass.
  • Android: Retry with React Native v0.71.
    • The Android issue was observed while testing the RN 0.74 upgrade pull request. It should be verified that this isn't a regression introduced by upgrading to React Native 0.74.
    • The iOS issue can be reproduced with React Native 0.71.
  • Android: Accept a shared notebook on desktop, then sync.
    • Accepting shared notebooks on mobile is still a new feature. It's possible that the Android issue is related to an edge case in how shared notebooks are accepted on mobile.

Edit 2: Other react-native-rsa integration tests should run automatically on startup. The tests mentioned above are different and were run by uncommenting lines in root.tsx.
Edit 3: The Android issue seems to be different from the issue on iOS. Its cause seems to be that encryption was disabled on the client that was sharing the notebook. The Android issue can also be observed on desktop.

@personalizedrefrigerator
Copy link
Collaborator Author

Debugging the iOS issue:

  • When attempting to accept a share, react-native-rsa's decrypt fails with an NSOSStatusErrorDomain code 18446744073709551566.
  • During the on-device tests, the cyphertext consistently has a byte length of 256. During a failing "accept share", the cyphertext has a length of 768 bytes.
    • According to sample code available here,
       *  \details The exact definition of "small" depends on the key size and the padding in 
       *      use.  The key size represents the maximum size, and from that you subtract 
       *      the padding overhead (11 bytes for PKCS#1, 42 bytes for OAEP).  For example, a 
       *      2048-bit key with PKCS#1 padding can encrypt 245 bytes (2048 bits -> 256 bytes - 11). 
      
    • During generateKeyPair, key size is hardcoded to 2048.

@personalizedrefrigerator personalizedrefrigerator changed the title iOS: Can't sync or accept encrypted shared notebooks iOS: Can't accept encrypted shared notebooks May 13, 2024
@personalizedrefrigerator
Copy link
Collaborator Author

personalizedrefrigerator commented May 13, 2024

There seem to be two separate issues described above.

1. Accepting shared notebooks can fail

react-native-rsa-native doesn't support decrypting long messages while node-rsa does.

node-rsa uses ECB mode to handle long messages — by dividing the message into blocks and encrypting/decrypting each block. See the relevant code in node-rsa.

Implementing this in RSA.react-native.ts seems to fix the issue (see this draft implementation).

This issue also happens on Android. I didn't observe the issue due to an incorrectly set-up testing environment (encryption disabled on desktop but master keys were present).

2. Sync issues with encrypted shared notebooks

After accepting a share on a desktop client, doing the following seems to have fixed the iOS sync issues with an encrypted shared notebook:

  1. Syncing the desktop client.
  2. Syncing the mobile client.
  3. Restarting the mobile client.
  4. Syncing the mobile client.

This part of the issue still needs to be investigated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug It's a bug high High priority issues iOS v3.0
Projects
None yet
Development

No branches or pull requests

2 participants