Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
httpjamesm committed Jan 8, 2023
2 parents 2030cb9 + fd6fbfe commit d7144c7
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 49 deletions.
19 changes: 19 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ tauri-build = { version = "1.2.1", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.2.2", features = ["dialog-message", "dialog-save", "fs-write-file"] }
tauri = { version = "1.2.2", features = ["dialog-message", "dialog-save", "fs-write-file", "shell-open"] }
shamir = "~2.0"
base64 = "~0.13"

Expand Down
13 changes: 8 additions & 5 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ fn main() {
}


// write tauri commands to split and recover secret
#[tauri::command]
fn do_split(secret: &str) -> Vec<String> {
let secret_data = SecretData::with_secret(&secret, 2);
Expand Down Expand Up @@ -44,11 +43,15 @@ fn do_recover(share1b64: &str, share2b64: &str) -> String {
let share2 = base64::decode(share2b64);

// unwrap shares
let share1 = share1.unwrap();
let share2 = share2.unwrap();
let share1 = share1.unwrap_or(vec![]);
let share2 = share2.unwrap_or(vec![]);

if share1.len() == 0 || share2.len() == 0 {
return String::from("");
}

let recovered = SecretData::recover_secret(2, vec![share1, share2]);

recovered.unwrap()

// unwrap but fallback to empty string if error
recovered.unwrap_or(String::from(""))
}
10 changes: 7 additions & 3 deletions src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"package": {
"productName": "ScatterSafe",
"version": "1.0.0"
"version": "1.1.0"
},
"tauri": {
"allowlist": {
Expand All @@ -29,6 +29,10 @@
"dialog": {
"save": true,
"message": true
},
"shell": {
"all": false,
"open": true
}
},
"bundle": {
Expand Down Expand Up @@ -74,9 +78,9 @@
{
"fullscreen": false,
"height": 800,
"resizable": false,
"resizable": true,
"title": "ScatterSafe",
"width": 800
"width": 700
}
]
}
Expand Down
91 changes: 66 additions & 25 deletions src/lib/Recover.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@
Accordion,
AccordionItem,
InlineNotification,
Tabs,
Tab,
TabContent,
TextInput,
} from "carbon-components-svelte";
let Html5Qrcode: any;
import { onMount } from "svelte";
let Html5Qrcode: any;
const init = async () => {
if (browser) {
Html5Qrcode = (await import("html5-qrcode")).Html5Qrcode;
Expand All @@ -37,10 +42,15 @@
let successMessage: string | undefined = undefined;
let errorMessage: string | null = null;
let share1Manual = "";
let share2Manual = "";
const doScan = async () => {
let html5Qrcode = new Html5Qrcode("file-uploader", {
useBarCodeDetectorIfSupported: false,
});
if (share1Manual && share2Manual) {
return await recoverSecret(share1Manual, share2Manual);
}
let html5Qrcode = new Html5Qrcode("file-uploader");
if (!files) {
errorMessage = "No QR files selected";
Expand All @@ -57,9 +67,8 @@
for (const file of files) {
let data: any;
try {
data = await html5Qrcode.scanFile(file, false);
} catch (e) {
console.log(e);
data = await html5Qrcode.scanFile(file, true);
} catch {
errorMessage =
"Unable to scan QR code. Please try again with a clearer image.";
return;
Expand All @@ -70,9 +79,18 @@
}
}
await recoverSecret(splits[0], splits[1]);
errorMessage = null;
successMessage = "Successfully recovered secret.";
fileUploader.clearFiles();
};
const recoverSecret = async (share1: string, share2: string) => {
const recovered: string = await invoke("do_recover", {
share1b64: splits[0],
share2b64: splits[1],
share1b64: share1,
share2b64: share2,
});
if (recovered.length == 0) {
Expand Down Expand Up @@ -129,11 +147,6 @@
}
decodedSecret = new TextDecoder().decode(decrypted);
errorMessage = null;
successMessage = "Successfully recovered secret.";
fileUploader.clearFiles();
};
const getPhoto = async () => {
Expand Down Expand Up @@ -213,17 +226,35 @@
/>
</FormGroup>
<FormGroup>
<FileUploader
accept={[".jpg", ".png"]}
multiple
labelTitle="Upload QR Codes"
labelDescription="Only .jpg and .png files are accepted."
buttonLabel="Add files"
bind:files
status="complete"
bind:this={fileUploader}
/>
<Button on:click={getPhoto} kind="tertiary">Take photo</Button>
<Tabs>
<Tab label="Scan" />
<Tab label="Manual" />
<svelte:fragment slot="content">
<TabContent>
<FileUploader
accept={[".jpg", ".png"]}
multiple
labelTitle="Upload QR Codes"
labelDescription="Only .jpg and .png files are accepted."
buttonLabel="Add files"
bind:files
status="complete"
bind:this={fileUploader}
/>
<Button on:click={getPhoto} kind="tertiary"
>Take photo</Button
>
</TabContent>
<TabContent>
<TextInput
labelText="Share 1"
bind:value={share1Manual}
style="margin-bottom: 1rem;"
/>
<TextInput labelText="Share 2" bind:value={share2Manual} />
</TabContent>
</svelte:fragment>
</Tabs>
</FormGroup>
<FormGroup>
<Button on:click={doScan}>Recover</Button>
Expand Down Expand Up @@ -260,4 +291,14 @@
recover your secret.
</p>
</AccordionItem>
<AccordionItem title="My QR codes aren't being recognized.">
<p>
Make sure your scene is well-lit and your camera quality is
sufficient to recognize details within the QR code. If needed, you
can scan the codes with another device and manually enter their
values in the Manual entry tab. Since the backups are encrypted, you
don't need to trust the device you're using to scan the individual
codes.
</p>
</AccordionItem>
</Accordion>
68 changes: 53 additions & 15 deletions src/lib/Split.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
AccordionItem,
InlineNotification,
TextInput,
MultiSelect,
} from "carbon-components-svelte";
import Password from "carbon-icons-svelte/lib/Password.svelte";
Expand All @@ -37,12 +38,15 @@
let label = "";
let qrCodes: string[] = [];
let splits: string[] = [];
let loading = false;
let successMessage: string | undefined = undefined;
let errorMessage: string | null = null;
let selectedExportTypes: string[] = [];
const encryptSecret = async () => {
if (!secret) {
errorMessage = "You must enter a secret.";
Expand Down Expand Up @@ -104,7 +108,7 @@
return;
}
const splits: string[] = await invoke("do_split", {
splits = await invoke("do_split", {
secret: everything,
});
Expand All @@ -130,25 +134,47 @@
password = await getHexPassword(8);
};
const generatePDF = async () => {
const doExport = async () => {
if (!selectedExportTypes.length) {
errorMessage = "You must select at least one export type.";
return;
}
loading = true;
let zip = new JSZip();
for (let i = 0; i < qrCodes.length; i++) {
const doc = new jsPDF();
if (selectedExportTypes.includes("0")) {
const doc = new jsPDF();
doc.addImage(qrCodes[i], "PNG", 10, 10, 50, 50);
doc.text(label, 35, 65, { align: "center" });
doc.addImage(qrCodes[i], "PNG", 10, 10, 50, 50);
doc.text(label, 35, 65, { align: "center" });
const pdf = doc.output("arraybuffer");
const pdf = doc.output("arraybuffer");
zip.file(`${label}-${i + 1}.pdf`, pdf);
}
zip.file(`${label}-${i + 1}.pdf`, pdf);
if (selectedExportTypes.includes("1")) {
const data = qrCodes[i].replace(/^data:image\/\w+;base64,/, "");
const buf = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
zip.file(`${label}-${i + 1}.png`, buf);
}
if (selectedExportTypes.includes("2")) {
zip.file(`${label}-${i + 1}.txt`, splits[i]);
}
}
const zipFile = await zip.generateAsync({ type: "arraybuffer" });
const unixTimestampNow = Math.floor(Date.now() / 1000);
const suggestedName = `${label}-backup-${unixTimestampNow}.zip`;
const suggestedName = `${
label || "scattersafe"
}-backup-${unixTimestampNow}.zip`;
const filePath = await save({
filters: [
Expand All @@ -168,6 +194,8 @@
await message("Backups compressed and saved successfully.");
}
loading = false;
};
</script>

Expand Down Expand Up @@ -231,7 +259,7 @@
>
</FormGroup>
<div class="codes">
{#if loading}
{#if loading && qrCodes.length === 0}
{#each Array(3) as _, i}
<SkeletonPlaceholder style="width: 7rem; height: 7rem;" />
{/each}
Expand All @@ -241,12 +269,22 @@
{/each}
</div>
{#if qrCodes.length > 0}
<Button
kind="secondary"
on:click={generatePDF}
style="margin-bottom: 1rem; margin-top: 1rem;"
>Download as PDF</Button
>
<FormGroup>
<MultiSelect
titleText="Export as..."
label="Select formats..."
items={[
{ id: "0", text: "PDF" },
{ id: "1", text: "JPEG" },
{ id: "2", text: "Text" },
]}
bind:selectedIds={selectedExportTypes}
disabled={loading}
/>
</FormGroup>
<FormGroup>
<Button on:click={doExport} disabled={loading}>Export</Button>
</FormGroup>
{/if}
</Form>

Expand Down
Loading

0 comments on commit d7144c7

Please sign in to comment.