Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building End-to-End Encrypted Apps

Building End-to-End Encrypted Apps

Nikolaus Graf

June 19, 2024
Tweet

More Decks by Nikolaus Graf

Other Decks in Technology

Transcript

  1. key = "c0be" "a6ea178" = encrypt("my todo", key) key =

    "c0be" "my todo" = decrypt("a6ea178", key) "a6ea178" "a6ea178"
  2. encrypt({ todoListKey: "8-Aw_2UhSVrgKGp77jsnT", content: JSON.stringify({ "1": { text: "Wash the

    dishes", checked: false }, "2": { text: "Order sweets", checked: true }, }), }); Encrypt & send the entire list
  3. encrypt({ todoListKey: "8-Aw_2UhSVrgKGp77jsnT", content: JSON.stringify({ "1": { text: "Wash the

    dishes", checked: false }, "2": { text: "Order sweets", checked: true }, }), }); Encrypt & send the entire list content: JSON.stringify({ "1": { text: "Wash the dishes", checked: false }, "2": { text: "Order sweets", checked: true }, }),
  4. Add todo 1 Change todo 1 Change todo 1 Con

    fl icts Add todo 1 Change todo 1
  5. = CRDT Add todo 1 Add todo 1 Change todo

    1 Change todo 1 Change todo 1 Change todo 1
  6. c3 = encrypt( , key) Change todo 1 c1 =

    encrypt( , key) c2 = encrypt( , key) Add todo 1 Change todo 1
  7. c3 = encrypt( , key) c1 = encrypt( , key)

    c2 = encrypt( , key) c1 c1 c2 c2 c3 c3 Add todo 1 Change todo 1 Change todo 1
  8. import * as Yjs from "yjs"; // initializing CRDT document

    const doc = new Yjs.Doc(); const todoList = doc.getMap("todos"); // adding a todo const todo = new Yjs.Map(); todo.set("text", new Yjs.Text("wash the dishes")); todo.set("checked", false); todoList.set(generateId(), todo);
  9. import * as Yjs from "yjs"; // initializing CRDT document

    const doc = new Yjs.Doc(); const todoList = doc.getMap("todos"); // adding a todo const todo = new Yjs.Map(); todo.set("text", new Yjs.Text("wash the dishes")); todo.set("checked", false); todoList.set(generateId(), todo); import * as Yjs from "yjs";
  10. import * as Yjs from "yjs"; // initializing CRDT document

    const doc = new Yjs.Doc(); const todoList = doc.getMap("todos"); // adding a todo const todo = new Yjs.Map(); todo.set("text", new Yjs.Text("wash the dishes")); todo.set("checked", false); todoList.set(generateId(), todo); // initializing CRDT document const doc = new Yjs.Doc();
  11. import * as Yjs from "yjs"; // initializing CRDT document

    const doc = new Yjs.Doc(); const todoList = doc.getMap("todos"); // adding a todo const todo = new Yjs.Map(); todo.set("text", new Yjs.Text("wash the dishes")); todo.set("checked", false); todoList.set(generateId(), todo); const todoList = doc.getMap("todos");
  12. import * as Yjs from "yjs"; // initializing CRDT document

    const doc = new Yjs.Doc(); const todoList = doc.getMap("todos"); // adding a todo const todo = new Yjs.Map(); todo.set("text", new Yjs.Text("wash the dishes")); todo.set("checked", false); todoList.set(generateId(), todo); // adding a todo const todo = new Yjs.Map();
  13. import * as Yjs from "yjs"; // initializing CRDT document

    const doc = new Yjs.Doc(); const todoList = doc.getMap("todos"); // adding a todo const todo = new Yjs.Map(); todo.set("text", new Yjs.Text("wash the dishes")); todo.set("checked", false); todoList.set(generateId(), todo); todo.set("text", new Yjs.Text("wash the dishes")); todo.set("checked", false);
  14. import * as Yjs from "yjs"; // initializing CRDT document

    const doc = new Yjs.Doc(); const todoList = doc.getMap("todos"); // adding a todo const todo = new Yjs.Map(); todo.set("text", new Yjs.Text("wash the dishes")); todo.set("checked", false); todoList.set(generateId(), todo); todoList.set(generateId(), todo);
  15. doc.on("updateV2", (update) => { // send(update) }); Yjs.applyUpdateV2(doc, update); const

    docAsUpdate = Yjs.encodeStateAsUpdateV2(doc) doc.on("updateV2", (update) => { // send(update) });
  16. doc.on("updateV2", (update) => { // send(update) }); Yjs.applyUpdateV2(doc, update); const

    docAsUpdate = Yjs.encodeStateAsUpdateV2(doc) Yjs.applyUpdateV2(doc, update);
  17. doc.on("updateV2", (update) => { // send(update) }); Yjs.applyUpdateV2(doc, update); const

    docAsUpdate = Yjs.encodeStateAsUpdateV2(doc) const docAsUpdate = Yjs.encodeStateAsUpdateV2(doc)
  18. const yDocRef = useRef<Yjs.Doc>(new Yjs.Doc()); const [signatureKeyPair] = useState<KeyPair>(() =>

    sodium.crypto_sign_keypair() ); useYjsSync({ yDoc: yDocRef.current, documentId: todoListId, websocketEndpoint: "ws://localhost:3030", getSnapshotKey: () => todoListKey, getNewSnapshotData: () => { return { key: todoListKey, data: Yjs.encodeStateAsUpdateV2(yDocRef.current), publicData: {}, }; }, shouldSendSnapshot: ({ snapshotUpdatesCount }) => snapshotUpdatesCount > 50, signatureKeyPair, isValidClient: () => true, sodium, });
  19. key = "c0be" "a6ea178" = encrypt("my todo", key) key =

    "c0be" "my todo" = decrypt("a6ea178", key) "a6ea178" "a6ea178"
  20. const lockerKey = '8-Aw_2UhSVrgKGp77jsnTF03DPBvjvdLSUwp4savq8Q'; const locker = encrypt({ lockerKey, content:

    JSON.stringify({ "1": "aL1wBSONXU9eWD6oLdjCQ8H4jbFa762vbP6Iouk5A4U", "2": "mxKjG-qh-eg4y8N6B3cpGmC1uEn2c_YWi0s8T2H_WAo", }), }); const lockerKey = '8-Aw_2UhSVrgKGp77jsnTF03DPBvjvdLSUwp4savq8Q';
  21. const lockerKey = '8-Aw_2UhSVrgKGp77jsnTF03DPBvjvdLSUwp4savq8Q'; const locker = encrypt({ lockerKey, content:

    JSON.stringify({ "1": "aL1wBSONXU9eWD6oLdjCQ8H4jbFa762vbP6Iouk5A4U", "2": "mxKjG-qh-eg4y8N6B3cpGmC1uEn2c_YWi0s8T2H_WAo", }), }); const locker = encrypt({ });
  22. const lockerKey = '8-Aw_2UhSVrgKGp77jsnTF03DPBvjvdLSUwp4savq8Q'; const locker = encrypt({ lockerKey, content:

    JSON.stringify({ "1": "aL1wBSONXU9eWD6oLdjCQ8H4jbFa762vbP6Iouk5A4U", "2": "mxKjG-qh-eg4y8N6B3cpGmC1uEn2c_YWi0s8T2H_WAo", }), }); lockerKey,
  23. const lockerKey = '8-Aw_2UhSVrgKGp77jsnTF03DPBvjvdLSUwp4savq8Q'; const locker = encrypt({ lockerKey, content:

    JSON.stringify({ "1": "aL1wBSONXU9eWD6oLdjCQ8H4jbFa762vbP6Iouk5A4U", "2": "mxKjG-qh-eg4y8N6B3cpGmC1uEn2c_YWi0s8T2H_WAo", }), }); content: JSON.stringify({ "1": "aL1wBSONXU9eWD6oLdjCQ8H4jbFa762vbP6Iouk5A4U", "2": "mxKjG-qh-eg4y8N6B3cpGmC1uEn2c_YWi0s8T2H_WAo", }),
  24. const { addItem, content } = useLocker(); await addItem({ documentId,

    key }); const key = content[documentId]; const { addItem, content } = useLocker();
  25. const { addItem, content } = useLocker(); await addItem({ documentId,

    key }); const key = content[documentId]; await addItem({ documentId, key });
  26. const { addItem, content } = useLocker(); await addItem({ documentId,

    key }); const key = content[documentId]; const key = content[documentId];
  27. const { register, isPending } = useRegister(); const result =

    await register({ userIdentifier: username, password, }); result?.exportKey const { register, isPending } = useRegister();
  28. const { register, isPending } = useRegister(); const result =

    await register({ userIdentifier: username, password, }); result?.exportKey const result = await register({ userIdentifier: username, password, });
  29. const { register, isPending } = useRegister(); const result =

    await register({ userIdentifier: username, password, }); result?.exportKey result?.exportKey
  30. const { clientRegistrationState, registrationRequest } = opaque.startRegistration({ password }); const

    { registrationResponse } = await registerStartMutation.mutateAsync({ userIdentifier, registrationRequest, }); const { registrationRecord, exportKey } = opaque.finishRegistration({ clientRegistrationState, registrationResponse, password, }); await registerFinishMutation.mutateAsync({ userIdentifier, registrationRecord, }); const { clientRegistrationState, registrationRequest } = opaque.startRegistration({ password });
  31. const { clientRegistrationState, registrationRequest } = opaque.startRegistration({ password }); const

    { registrationResponse } = await registerStartMutation.mutateAsync({ userIdentifier, registrationRequest, }); const { registrationRecord, exportKey } = opaque.finishRegistration({ clientRegistrationState, registrationResponse, password, }); await registerFinishMutation.mutateAsync({ userIdentifier, registrationRecord, }); const { registrationResponse } = await registerStartMutation.mutateAsync({ userIdentifier, registrationRequest, });
  32. const { clientRegistrationState, registrationRequest } = opaque.startRegistration({ password }); const

    { registrationResponse } = await registerStartMutation.mutateAsync({ userIdentifier, registrationRequest, }); const { registrationRecord, exportKey } = opaque.finishRegistration({ clientRegistrationState, registrationResponse, password, }); await registerFinishMutation.mutateAsync({ userIdentifier, registrationRecord, }); const { registrationRecord, exportKey } = opaque.finishRegistration({ clientRegistrationState, registrationResponse, password, });
  33. const { clientRegistrationState, registrationRequest } = opaque.startRegistration({ password }); const

    { registrationResponse } = await registerStartMutation.mutateAsync({ userIdentifier, registrationRequest, }); const { registrationRecord, exportKey } = opaque.finishRegistration({ clientRegistrationState, registrationResponse, password, }); await registerFinishMutation.mutateAsync({ userIdentifier, registrationRecord, }); await registerFinishMutation.mutateAsync({ userIdentifier, registrationRecord, });
  34. const { login, isPending } = useLogin(); const result =

    await login({ userIdentifier: username, password, }); result?.exportKey const { login, isPending } = useLogin();
  35. const { login, isPending } = useLogin(); const result =

    await login({ userIdentifier: username, password, }); result?.exportKey const result = await login({ userIdentifier: username, password, });
  36. const { login, isPending } = useLogin(); const result =

    await login({ userIdentifier: username, password, }); result?.exportKey result?.exportKey
  37. const { clientLoginState, startLoginRequest } = opaque.startLogin({ password, }); const

    { loginResponse } = await loginStartMutation.mutateAsync({ userIdentifier, startLoginRequest, }); const loginResult = opaque.finishLogin({ clientLoginState, loginResponse, password, }); if (!loginResult) { return null; } const { finishLoginRequest, exportKey } = loginResult; const { success } = await loginFinishMutation.mutateAsync({ finishLoginRequest, userIdentifier, }); const { clientLoginState, startLoginRequest } = opaque.startLogin({ password, });
  38. const { clientLoginState, startLoginRequest } = opaque.startLogin({ password, }); const

    { loginResponse } = await loginStartMutation.mutateAsync({ userIdentifier, startLoginRequest, }); const loginResult = opaque.finishLogin({ clientLoginState, loginResponse, password, }); if (!loginResult) { return null; } const { finishLoginRequest, exportKey } = loginResult; const { success } = await loginFinishMutation.mutateAsync({ finishLoginRequest, userIdentifier, }); const { loginResponse } = await loginStartMutation.mutateAsync({ userIdentifier, startLoginRequest, });
  39. const { clientLoginState, startLoginRequest } = opaque.startLogin({ password, }); const

    { loginResponse } = await loginStartMutation.mutateAsync({ userIdentifier, startLoginRequest, }); const loginResult = opaque.finishLogin({ clientLoginState, loginResponse, password, }); if (!loginResult) { return null; } const { finishLoginRequest, exportKey } = loginResult; const { success } = await loginFinishMutation.mutateAsync({ finishLoginRequest, userIdentifier, }); const loginResult = opaque.finishLogin({ clientLoginState, loginResponse, password, }); if (!loginResult) { return null; } const { finishLoginRequest, exportKey } = loginResult;
  40. const { clientLoginState, startLoginRequest } = opaque.startLogin({ password, }); const

    { loginResponse } = await loginStartMutation.mutateAsync({ userIdentifier, startLoginRequest, }); const loginResult = opaque.finishLogin({ clientLoginState, loginResponse, password, }); if (!loginResult) { return null; } const { finishLoginRequest, exportKey } = loginResult; const { success } = await loginFinishMutation.mutateAsync({ finishLoginRequest, userIdentifier, }); const { success } = await loginFinishMutation.mutateAsync({ finishLoginRequest, userIdentifier, });
  41. const key = getHashParameter("key"); const data = await acceptDocumentInvitationMutation.mutateAsync({ token

    }); if (data?.documentId) { await addItem({ documentId: data.documentId, key, }); router.navigate({ pathname: `/list/${data.documentId}` }); } const key = getHashParameter("key");
  42. const key = getHashParameter("key"); const data = await acceptDocumentInvitationMutation.mutateAsync({ token

    }); if (data?.documentId) { await addItem({ documentId: data.documentId, key, }); router.navigate({ pathname: `/list/${data.documentId}` }); } const data = await acceptDocumentInvitationMutation.mutateAsync({ token });
  43. const key = getHashParameter("key"); const data = await acceptDocumentInvitationMutation.mutateAsync({ token

    }); if (data?.documentId) { await addItem({ documentId: data.documentId, key, }); router.navigate({ pathname: `/list/${data.documentId}` }); } await addItem({ documentId: data.documentId, key, });