{
"$type": "site.standard.document",
"content": {
"$type": "site.standard.content.markdown",
"text": "## Introduction\n\nNow that Magiedit is kind of done (except for user experience, I guess), I figured I had to tackle the security / sync of notes; at the beginning of this project, I thought I could store everything locally (first versions did not even have authentication), and later on implement a solution to sync back and forth with a remote server using something like couchdb and pouchdb. When I tried implementing things like those, however, I ran into all kinds of problems, namely making it play nice with sveltekit (and vite) and syncing different models (articles and settings for each user). And so I decided, at least for now, to ditch storing articles locally, and simply store them on a remote db (right now [turso](https://turso.tech), btw). The thing is, I didn't want to store raw article data for reasons like data privacy and privacy in general (I don't want to know what you write using Magiedit). To solve this problem, I started looking into cryptography, specifically the web-cryptography api.\n\n## How this all works\n\n### Genering a master password\n\nWhen a user creates an account, it is now required to create a master password; this will then be stored (hashed, obviously) on the server and at each new session they will need to enter it again. This master password is the origin for every cryptographic key used to encrypt and decrypt the articles.\n\n**NB: except for the master password creation and unlocking, the clear password is never sent to the server and is instead stored in the browser's session storage**\n\n### How and where things happen\n\n#### How it works (algorithms and stuff)\n\n**DISCLAIMER: I'm not an expert in cryptography, so take everything I say with a grain of salt**\n\n- The key is based on a hash of the master password using SHA-256\n- The algorithm used for encryption is AES-CBC, since it is symmetric and does not make me manage private and public keys (they would work better, but let's be honest; they are just articles, after all)\n\n#### Where it works\n\nLet's first take a look at this schema:\n\n\nWhen a user creates, loads (from a file) or saves an article, the same steps happen:\n\n- A cryptographic key is generated using the master password hashed using SHA-256\n\n```js\nconst keyBytes = await crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(keyData),\n)\nconst key = await crypto.subtle.importKey('raw', keyBytes, 'AES-CBC', false, [\n 'encrypt',\n])\n```\n\n- We generate an iv (initialization vector, kind of like a seed)\n\n```js\nfunction generateIv() {\n const data = new Uint8Array(16)\n crypto.getRandomValues(data)\n return data\n}\n```\n\n- The text is then encrypted using the key and the iv\n\n```js\nconst encodedContent = await crypto.subtle.encrypt(\n { name: 'AES-CBC', iv },\n key,\n new TextEncoder().encode('write here'),\n)\n```\n\n- It is then converted to base64\n\n```js\nconst base64 = btoa(String.fromCharCode(...new Uint8Array(encodedContent)))\n```\n\n- and finally sent to the server (along with its iv) to get it stored securely\n\nAs for decrypting, it is the same process, but in reverse:\n\n- get the article from the db\n- convert the base64 string to a buffer\n- generate a key from the master password\n- decrypt the buffer\n- convert the buffer back to a string\n\n\nI really like this gif, you know?\n\n## Why end to end encryption ?\n\nBecause I wanted users to know that even if I wanted to (and I don't) read what they wrote before publishing, I couldn't, because I wanted to be in line with current regulations about data privacy and, most of all, because it looked like it could be fun!",
"version": "1.0"
},
"path": "/articles/end-to-end-sveltekit",
"publishedAt": "2023-09-25T00:00:00.000Z",
"site": "at://did:plc:dgtaz4vldacvqhvvmdvoc4ad/site.standard.publication/3mfbydibiwc7f",
"tags": [
"sveltekit",
"typescript",
"cryptography"
],
"textContent": "Introduction\n\nNow that Magiedit is kind of done (except for user experience, I guess), I figured I had to tackle the security / sync of notes; at the beginning of this project, I thought I could store everything locally (first versions did not even have authentication), and later on implement a solution to sync back and forth with a remote server using something like couchdb and pouchdb. When I tried implementing things like those, however, I ran into all kinds of problems, namely making it play nice with sveltekit (and vite) and syncing different models (articles and settings for each user). And so I decided, at least for now, to ditch storing articles locally, and simply store them on a remote db (right now turso, btw). The thing is, I didn't want to store raw article data for reasons like data privacy and privacy in general (I don't want to know what you write using Magiedit). To solve this problem, I started looking into cryptography, specifically the web-cryptography api.\n\nHow this all works\n\nGenering a master password\n\nWhen a user creates an account, it is now required to create a master password; this will then be stored (hashed, obviously) on the server and at each new session they will need to enter it again. This master password is the origin for every cryptographic key used to encrypt and decrypt the articles.\n\nNB: except for the master password creation and unlocking, the clear password is never sent to the server and is instead stored in the browser's session storage\n\nHow and where things happen\n\nHow it works (algorithms and stuff)\n\nDISCLAIMER: I'm not an expert in cryptography, so take everything I say with a grain of salt\nThe key is based on a hash of the master password using SHA-256\nThe algorithm used for encryption is AES-CBC, since it is symmetric and does not make me manage private and public keys (they would work better, but let's be honest; they are just articles, after all)\n\nWhere it works\n\nLet's first take a look at this schema:\n\nWhen a user creates, loads (from a file) or saves an article, the same steps happen:\nA cryptographic key is generated using the master password hashed using SHA-256\nWe generate an iv (initialization vector, kind of like a seed)\nThe text is then encrypted using the key and the iv\nIt is then converted to base64\nand finally sent to the server (along with its iv) to get it stored securely\n\nAs for decrypting, it is the same process, but in reverse:\nget the article from the db\nconvert the base64 string to a buffer\ngenerate a key from the master password\ndecrypt the buffer\nconvert the buffer back to a string\n\nI really like this gif, you know?\n\nWhy end to end encryption ?\n\nBecause I wanted users to know that even if I wanted to (and I don't) read what they wrote before publishing, I couldn't, because I wanted to be in line with current regulations about data privacy and, most of all, because it looked like it could be fun!",
"title": "end-to-end encryption with sveltekit",
"updatedAt": "2026-05-17T13:09:10.570Z"
}