mostly works now, except for decrypting the file, but not getting any helpful error messages from SubtleCrypto to fix this

main
Johannes Bülow 2023-01-04 18:08:58 +01:00
parent 917797c9be
commit dfcacf7bbf
5 changed files with 78 additions and 56 deletions

View File

@ -2,10 +2,18 @@
import EncryptButton from './lib/EncryptButton.svelte' import EncryptButton from './lib/EncryptButton.svelte'
import PasswordButton from './lib/PasswordButton.svelte'; import PasswordButton from './lib/PasswordButton.svelte';
import DecryptButton from './lib/DecryptButton.svelte'; import DecryptButton from './lib/DecryptButton.svelte';
import { errorMessage } from './lib/generalStore';
let errorMessageContent
errorMessage.subscribe( value => {
errorMessageContent = value;
});
</script> </script>
<main> <main>
<h1>Encrypt and decrypt files</h1> <h1>Encrypt and decrypt files</h1>
{errorMessageContent}
<div class="container"> <div class="container">
<EncryptButton /> <EncryptButton />

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { escape } from 'svelte/internal'; import { escape } from 'svelte/internal';
import { decrypt, encrypt } from './cryptlib'; import { decryptFileContent } from './cryptlib';
let filename: string = ""; let filename: string = "";
let originalFilename: string; let originalFilename: string;
@ -44,23 +44,18 @@
return; return;
} }
readFileContent(file).then((fileContent) => { readFileContent(file).then((fileContent) => {
decrypt(atob(fileContent)).then(decryptedContent => { let outputFile: Blob;
console.log("Decrypted Base64: ", decryptedContent) decryptFileContent(fileContent).then((outputArray) => {
console.log(decodeURIComponent(decryptedContent)); console.log(outputArray)
const decodedData = decodeURIComponent(escape(atob((decryptedContent)))); outputFile = new Blob([outputArray], { type: 'application/octet-stream'});
const outputArray = new Uint8Array(decodedData.length)
for (let i = 0; i < decodedData.length; i++) {
outputArray[i] = decodedData.charCodeAt(i); // Populate the array with the decoded data
}
const outputFile = new Blob([outputArray], { type: 'application/octet-stream'});
let url = URL.createObjectURL(outputFile);
let a = document.createElement("a");
a.href = url;
a.download = originalFilename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}); });
let url = URL.createObjectURL(outputFile);
let a = document.createElement("a");
a.href = url;
a.download = originalFilename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}); });
} }

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { encrypt } from './cryptlib'; import { encryptFile } from './cryptlib';
let filename: string; let filename: string = "";
function convertToBase64(blob: File): Promise<string> { function convertToBase64(blob: File): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -17,7 +17,16 @@
}); });
} }
function downloadAsFile(string) {
let encrypted_file: Blob = new Blob([string], {type: "text/plain"});
let url = URL.createObjectURL(encrypted_file);
let a = document.createElement("a");
a.href = url;
a.download = filename + ".crypt";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
let file: File | undefined; let file: File | undefined;
@ -34,22 +43,12 @@
} }
filename = file.name; filename = file.name;
console.log("Uploaded ", filename) console.log("Uploaded ", filename)
convertToBase64(file).then((base64) => { encryptFile(file).then(base64String => {
encrypt(base64).then((encrypted_content => { downloadAsFile(base64String)
console.log(encrypted_content)
let encrypted_b64: string = btoa(unescape(encodeURIComponent(encrypted_content)));
let encrypted_file: Blob = new Blob([encrypted_b64], {type: "text/plain"});
let url = URL.createObjectURL(encrypted_file);
let a = document.createElement("a");
a.href = url;
a.download = filename + ".crypt";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}));
}) })
} }
</script> </script>
<style> <style>
.upload-button { .upload-button {

View File

@ -1,9 +1,10 @@
//import crypto from 'crypto' //import crypto from 'crypto'
import { globalPassword } from './pwgen'; import { globalPassword } from './pwgen';
import { errorMessage } from './generalStore';
let password: string; let password: string;
let salt; let salt: Uint8Array;
let ciphertext; let ciphertext: BufferSource;
let iv; let iv: Uint8Array;
globalPassword.subscribe(value => { globalPassword.subscribe(value => {
password = value; password = value;
}) })
@ -33,12 +34,14 @@ function getKeyMaterial() {
/* /*
Given some key material and some random salt Given some key material and some random salt
derive an AES-GCM key using PBKDF2. derive an AES-GCM key using PBKDF2.
Using the Password itself as salt isn't really a great idea, but this is not a hyper secure implementation yet
*/ */
function getKey(keyMaterial, salt) { function getKey(keyMaterial: CryptoKey) {
let enc = new TextEncoder();
return window.crypto.subtle.deriveKey( return window.crypto.subtle.deriveKey(
{ {
"name": "PBKDF2", "name": "PBKDF2",
salt: salt, salt: enc.encode(password),
"iterations": 100000, "iterations": 100000,
"hash": "SHA-256" "hash": "SHA-256"
}, },
@ -55,13 +58,12 @@ to encrypt the message.
Update the "ciphertextValue" box with a representation of part of Update the "ciphertextValue" box with a representation of part of
the ciphertext. the ciphertext.
*/ */
export async function encrypt(string: string) { export async function encryptFile(file: File) {
let dec = new TextDecoder();
let keyMaterial = await getKeyMaterial(); let keyMaterial = await getKeyMaterial();
salt = window.crypto.getRandomValues(new Uint8Array(16)); let key = await getKey(keyMaterial);
let key = await getKey(keyMaterial, salt);
iv = window.crypto.getRandomValues(new Uint8Array(12)); iv = window.crypto.getRandomValues(new Uint8Array(12));
let encoded = getStringEncoding(string); const fileReader = new FileReader();
let byteArray = await file.arrayBuffer();
ciphertext = await window.crypto.subtle.encrypt( ciphertext = await window.crypto.subtle.encrypt(
{ {
@ -69,11 +71,10 @@ export async function encrypt(string: string) {
iv: iv iv: iv
}, },
key, key,
encoded byteArray
); );
let decodedCiphertext = dec.decode(ciphertext); const base64Encoded = btoa(String.fromCharCode(... new Uint8Array(ciphertext)));
return decodedCiphertext return base64Encoded
} }
/* /*
@ -84,9 +85,24 @@ update the "decryptedValue" box with the decrypted value.
If there was an error decrypting, If there was an error decrypting,
update the "decryptedValue" box with an error message. update the "decryptedValue" box with an error message.
*/ */
export async function decrypt(encryptedContent) { export async function decryptFileContent(base64Encoded: string) {
//prepare cryptography
let keyMaterial = await getKeyMaterial(); let keyMaterial = await getKeyMaterial();
let key = await getKey(keyMaterial, salt); console.log(keyMaterial)
let key = await getKey(keyMaterial);
//Remove anything form the Base64 String that isn't base64
const cleanString = base64Encoded.replace(/[^A-Za-z0-9+/=]/g, '');
// create Uint8 Array from base64 string
let encryptedString = atob(cleanString)
console.log(encryptedString)
const encryptedContent = new Uint8Array(encryptedString.length)
for (let i = 0; i < encryptedString.length; i++) {
encryptedContent[i] = encryptedString.charCodeAt(i); // Populate the array with the decoded data
}
console.log(encryptedContent)
const encryptedContentBuffer: ArrayBuffer = encryptedContent.buffer;
try { try {
let decrypted = await window.crypto.subtle.decrypt( let decrypted = await window.crypto.subtle.decrypt(
@ -95,14 +111,13 @@ export async function decrypt(encryptedContent) {
iv: iv iv: iv
}, },
key, key,
encryptedContent encryptedContentBuffer
); );
return decrypted;
let dec = new TextDecoder();
return dec.decode(decrypted);
} catch (e) { } catch (e) {
console.log(e) console.log(e)
return `Decryption error: ${e}` errorMessage.set(`Can not decrypt file: ${e}`)
} }
} }
// Hacked together version of https://github.com/mdn/dom-examples/blob/main/web-crypto/derive-key/pbkdf2.jshttps://gist.github.com/ChaoLiangSuper/0e13f77712b68682f0d8ebabb2d63aa8 // Hacked together version of https://github.com/mdn/dom-examples/blob/main/web-crypto/derive-key/pbkdf2.jshttps://gist.github.com/ChaoLiangSuper/0e13f77712b68682f0d8ebabb2d63aa8

View File

@ -1,3 +1,8 @@
import { writable} from 'svelte/store'; import { writable} from 'svelte/store';
export let filename = writable("Encrypted.b64"); export let filename = writable("Encrypted.b64");
export let encryptSource = writable<File>();
export let encryptTarget = writable();
export let decryptSource = writable();
export let decryptDestination = writable<File>();
export let errorMessage = writable<string>("");