This commit is contained in:
2020-08-22 16:26:09 +02:00
parent 26028445bf
commit 7750829b39
8 changed files with 205 additions and 77 deletions

View File

@@ -5,8 +5,10 @@ import {
Route, Route,
} from 'react-router-dom'; } from 'react-router-dom';
import Intro from './screens/Intro';
import Encrypt from './screens/Encrypt'; import Encrypt from './screens/Encrypt';
import Decrypt from './screens/Decrypt'; import Decrypt from './screens/Decrypt';
import SetupKey from './screens/SetupKey';
import Welcome from './screens/Welcome'; import Welcome from './screens/Welcome';
import Debug from './screens/Debug'; import Debug from './screens/Debug';
@@ -19,12 +21,18 @@ const AppRouter: React.FC = () => (
<Route path="/welcome"> <Route path="/welcome">
<Welcome /> <Welcome />
</Route> </Route>
<Route path="/decrypt"> <Route path="/key">
<SetupKey />
</Route>
<Route path="/receive">
<Decrypt /> <Decrypt />
</Route> </Route>
<Route path="/"> <Route path="/send">
<Encrypt /> <Encrypt />
</Route> </Route>
<Route path="/">
<Intro />
</Route>
</Switch> </Switch>
</Router> </Router>
); );

View File

@@ -19,15 +19,22 @@ interface Props {
file: FileType; file: FileType;
} }
const iconStyle = {
style: {
fontSize: 18,
},
};
const icons: {[name: string]: any} = { const icons: {[name: string]: any} = {
processing: <SyncOutlined spin />, processing: <SyncOutlined spin {...iconStyle} />,
failed: <IssuesCloseOutlined />, failed: <IssuesCloseOutlined {...iconStyle} />,
success: <LockOutlined />, success: <LockOutlined {...iconStyle} />,
}; };
const IconText = ({ icon, text, ...props }) => ( const IconText = ({ icon, text, ...props }) => (
<Button <Button
{...props} {...props}
shape="round"
icon={React.createElement(icon)} icon={React.createElement(icon)}
/> />
); );

View File

@@ -6,6 +6,7 @@ import { createFile } from '../helpers/files';
interface DecryptionContextType { interface DecryptionContextType {
publicKey: string | undefined; publicKey: string | undefined;
createKey: (name: string, email: string) => void;
files: {[id: string]: FileType}; files: {[id: string]: FileType};
addFile: (file: File) => Promise<void>; addFile: (file: File) => Promise<void>;
deleteFile: (id: string) => void; deleteFile: (id: string) => void;
@@ -14,6 +15,7 @@ interface DecryptionContextType {
const DecryptionContext = createContext<DecryptionContextType>({ const DecryptionContext = createContext<DecryptionContextType>({
publicKey: undefined, publicKey: undefined,
files: {}, files: {},
createKey: async () => { throw new Error('Not using provider'); },
addFile: async () => { throw new Error('Not using provider'); }, addFile: async () => { throw new Error('Not using provider'); },
deleteFile: async () => { throw new Error('Not using provider'); }, deleteFile: async () => { throw new Error('Not using provider'); },
}); });
@@ -55,21 +57,23 @@ const DecryptionProvider: React.FC = ({
setPrivateKey(currentRawKey); setPrivateKey(currentRawKey);
const key = await openpgp.key.readArmored(currentRawKey); const key = await openpgp.key.readArmored(currentRawKey);
setPublicKey(key.keys[0].toPublic().armor()); setPublicKey(key.keys[0].toPublic().armor());
} else {
const key = await openpgp.generateKey({
userIds: [{ name: 'unknown unknown', email: 'unknown@unknown.foo'}],
curve: 'ed25519',
});
setPrivateKey(key.privateKeyArmored);
setPublicKey(key.publicKeyArmored);
localStorage.setItem('key', key.privateKeyArmored);
} }
}; };
run(); run();
}, []); }, []);
const createKey = async () => {
const key = await openpgp.generateKey({
userIds: [{ name: 'unknown unknown', email: 'unknown@unknown.foo'}],
curve: 'ed25519',
});
setPrivateKey(key.privateKeyArmored);
setPublicKey(key.publicKeyArmored);
localStorage.setItem('key', key.privateKeyArmored);
}
const addFile = useCallback(async (file: File) => { const addFile = useCallback(async (file: File) => {
if (!keys || !privateKey) return; if (!keys || !privateKey) return;
const addedFile = createFile(setFiles, file.name); const addedFile = createFile(setFiles, file.name);
@@ -89,6 +93,7 @@ const DecryptionProvider: React.FC = ({
<DecryptionContext.Provider <DecryptionContext.Provider
value={{ value={{
publicKey, publicKey,
createKey,
files, files,
addFile, addFile,
deleteFile, deleteFile,

View File

@@ -1,32 +1,14 @@
import React, { useContext, useEffect, useCallback } from 'react'; import React, { useContext } from 'react';
import { Divider, Button } from 'antd'; import { Divider, Button } from 'antd';
import { useHistory } from 'react-router';
import FileList from '../components/FileList'; import FileList from '../components/FileList';
import Add from '../components/decrypt/AddFile'; import Add from '../components/decrypt/AddFile';
import DecryptionContext from '../contexts/Decryption'; import DecryptionContext from '../contexts/Decryption';
import { downloadLink } from '../helpers/files';
const Decrypt: React.FC = () => { const Decrypt: React.FC = () => {
const history = useHistory(); const { files, deleteFile } = useContext(DecryptionContext);
const { publicKey, files, deleteFile } = useContext(DecryptionContext);
useEffect(() => {
if (localStorage.getItem('welcome') !== 'seen') {
history.replace('/welcome');
}
}, []);
const downloadPublicKey = useCallback(() => {
const publicKeyBlob = new Blob([publicKey!]);
downloadLink('public-key.asc', publicKeyBlob);
}, []);
return ( return (
<> <>
<Button
onClick={downloadPublicKey}
>
Download you sharing key
</Button>
<Add /> <Add />
{Object.keys(files).length > 0 && ( {Object.keys(files).length > 0 && (
<> <>

View File

@@ -1,18 +1,11 @@
import React, { useContext, useEffect } from 'react'; import React, { useContext } from 'react';
import { Divider } from 'antd'; import { Divider } from 'antd';
import { useHistory } from 'react-router';
import Add from '../components/encrypt/Add'; import Add from '../components/encrypt/Add';
import FileList from '../components/FileList'; import FileList from '../components/FileList';
import EncryptionContext from '../contexts/Encryption'; import EncryptionContext from '../contexts/Encryption';
const Encrypt: React.FC = () => { const Encrypt: React.FC = () => {
const history = useHistory();
const { files, deleteFile } = useContext(EncryptionContext); const { files, deleteFile } = useContext(EncryptionContext);
useEffect(() => {
if (localStorage.getItem('welcome') !== 'seen') {
history.replace('/welcome');
}
}, []);
return ( return (
<> <>

60
src/screens/Intro.tsx Normal file
View File

@@ -0,0 +1,60 @@
import React from 'react';
import { useHistory } from 'react-router';
import Welcome from './Welcome';
import {
Button,
Space,
} from 'antd';
import {
UploadOutlined,
DownloadOutlined,
KeyOutlined,
} from '@ant-design/icons';
const Thumb: React.FC = ({
title,
Icon,
link,
}) => {
const history = useHistory();
return (
<Button
size="large"
shape="round"
type="link"
icon={<Icon />}
onClick={() => history.push(link)}
>
{title}
</Button>
);
};
const Intro = () => {
return (
<>
<Welcome />
<Space style={{ width: '100%' }} align="center" direction="vertical">
<b>What do you want to do?</b>
<Thumb
title="I want to send a text/file"
link="/send"
Icon={UploadOutlined}
/>
<Thumb
link="/key"
title="I want to receive a file"
Icon={KeyOutlined}
/>
<Thumb
link="/receive"
title="I have received a file"
Icon={DownloadOutlined}
/>
</Space>
</>
);
};
export default Intro;

101
src/screens/SetupKey.tsx Normal file
View File

@@ -0,0 +1,101 @@
import React, { useContext, useState, useCallback } from 'react';
import { Space, Typography, Input, Button, Form } from 'antd';
import DecryptionContext from '../contexts/Decryption';
import { downloadLink } from '../helpers/files';
import {
RocketTwoTone,
LockTwoTone,
UserOutlined,
MailOutlined,
} from '@ant-design/icons';
const SetupKey: React.FC = () => {
const {
createKey,
publicKey,
} = useContext(DecryptionContext);
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const downloadPublicKey = useCallback(() => {
const publicKeyBlob = new Blob([publicKey!]);
downloadLink('public-key.asc', publicKeyBlob);
}, []);
const setupKey = useCallback(() => {
createKey(name, email);
}, [name, email]);
if (!publicKey) {
return (
<>
<Space direction="vertical" style={{ textAlign: 'center' }} >
<LockTwoTone style={{ fontSize: 150 }} />
<Typography.Title>Create your sharing key</Typography.Title>
<p>
Before I can send protected information to you I need a "sharing" key, which is a key that gets stored this device, allowing this device (and this device only) to read the informations I am sending.
</p>
<p>
After creating it you need to send it to me
</p>
</Space>
<Form>
<Form.Item>
<Input
placeholder="Your name"
size="large"
prefix={<UserOutlined />}
value={name}
onChange={evt => setName(evt.target.value)}
/>
</Form.Item>
<Form.Item>
<Input
placeholder="Your e-mail"
size="large"
prefix={<MailOutlined />}
value={email}
onChange={evt => setEmail(evt.target.value)}
/>
</Form.Item>
<Form.Item style={{ textAlign: 'center' }} >
<Button
disabled={!name || !email}
type="primary"
onClick={setupKey}
size="large"
shape="round"
>
Create sharing key
</Button>
</Form.Item>
</Form>
</>
);
} else {
return (
<div style={{ textAlign: 'center' }}>
<RocketTwoTone style={{ fontSize: 150 }} />
<Typography.Title>Okay, you are all set.</Typography.Title>
<p>
Just send me your sharing key, and I will send files using it.
</p>
<p>
Remember that you need to go to this website on this device to decrypt the files after receiving them
</p>
<Button
onClick={downloadPublicKey}
type="primary"
size="large"
shape="round"
>
Download sharing key
</Button>
</div>
);
}
};
export default SetupKey;

View File

@@ -1,51 +1,23 @@
import React, { useEffect } from 'react'; import React from 'react';
import { Space, Layout, Button, Typography, notification } from 'antd'; import { Space, Layout, Typography } from 'antd';
import { AlignLeftOutlined, EyeInvisibleTwoTone, ArrowRightOutlined } from '@ant-design/icons'; import { EyeInvisibleTwoTone } from '@ant-design/icons';
import { useHistory } from 'react-router';
const openNotification = () => {
notification.warn({
message: 'Slow down!',
description: 'I am still working on this, but thanks for the interrest.'
});
};
const Welcome: React.FC = () => { const Welcome: React.FC = () => {
const history = useHistory();
useEffect(() => {
localStorage.setItem('welcome', 'seen');
});
return ( return (
<Layout style={{ maxWidth: 800, margin: 'auto', textAlign: 'center' }}> <Layout style={{ maxWidth: 800, margin: 'auto', textAlign: 'center' }}>
<Space direction="vertical"> <Space direction="vertical">
<EyeInvisibleTwoTone style={{ fontSize: 200 }} /> <EyeInvisibleTwoTone style={{ fontSize: 200 }} />
<Typography.Title level={1}>Protect before sending</Typography.Title> <Typography.Title level={1}>Protect before sending</Typography.Title>
<p> <p>
The internet can seem like a scary place, especially if you want to send sensitiv information across it. The internet can seem like a scary place...
</p> </p>
<p> <p>
The truth is that a lot of systems, including e-mails, was not build for the internet that we have today. Especially because a lot of the tools we use everyday (such as e-mail) wasn't build for the internet that we have today.
This is why it is important to have an additional layer of security when sending sensitive information.
</p> </p>
<p> <p>
This is why it is so important to make sure your documents are well protected before sharing. This is a tool that will help you have that extra layer of security when sharing files with me.
</p> </p>
<p>
This tool can be used to protect information before sharing them with me. The documents will be encrypted so that only I can ever unlock them, so no snoppy man in the middle...
</p>
<Button
type="primary"
icon={<ArrowRightOutlined />}
onClick={() => history.push('/')}
>
Start protecting!
</Button>
<Button
icon={<AlignLeftOutlined />}
onClick={openNotification}
>
Read all the technical stuff
</Button>
</Space> </Space>
</Layout> </Layout>
); );