mostly works now, except for decrypting the file, but not getting any helpful error messages from SubtleCrypto to fix this
parent
917797c9be
commit
dfcacf7bbf
|
@ -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 />
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,25 +85,39 @@ 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(
|
||||||
{
|
{
|
||||||
name: "AES-GCM",
|
name: "AES-GCM",
|
||||||
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
|
|
@ -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>("");
|
Loading…
Reference in New Issue