From af001d31d231f4263f85076d89226094e47acd9b Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi Date: Tue, 24 May 2022 05:16:50 -0700 Subject: [PATCH] Add Audit Dialog --- frontend/src/components/Dialogs/AuditPGP.tsx | 200 +++++++++++++++++++ frontend/src/components/Dialogs/index.ts | 1 + frontend/src/components/EncryptedChat.js | 77 ++++--- frontend/src/components/Icons/Export.tsx | 10 + frontend/src/components/Icons/index.ts | 2 + frontend/src/components/MakerPage.js | 2 +- frontend/src/utils/pgp.js | 2 +- frontend/src/utils/saveFile.js | 18 ++ 8 files changed, 286 insertions(+), 26 deletions(-) create mode 100644 frontend/src/components/Dialogs/AuditPGP.tsx create mode 100644 frontend/src/components/Icons/Export.tsx diff --git a/frontend/src/components/Dialogs/AuditPGP.tsx b/frontend/src/components/Dialogs/AuditPGP.tsx new file mode 100644 index 00000000..51b43d9b --- /dev/null +++ b/frontend/src/components/Dialogs/AuditPGP.tsx @@ -0,0 +1,200 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { + Dialog, + DialogTitle, + Tooltip, + IconButton, + TextField, + DialogActions, + DialogContent, + DialogContentText, + Button, + Grid, + Link, +} from "@mui/material" + +import { saveAsJson, saveAsTxt } from "../../utils/saveFile"; + +// Icons +import KeyIcon from '@mui/icons-material/Key'; +import ContentCopy from "@mui/icons-material/ContentCopy"; +import ForumIcon from '@mui/icons-material/Forum'; +import { ExportIcon } from '../Icons'; + +type Props = { + open: boolean; + onClose: () => void; + orderId: number; + messages: array; + own_pub_key: string; + own_enc_priv_key: string; + peer_pub_key: string; + passphrase: string; + onClickBack: () => void; +} + +const AuditPGPDialog = ({ + open, + onClose, + orderId, + messages, + own_pub_key, + own_enc_priv_key, + peer_pub_key, + passphrase, + onClickBack, +}: Props): JSX.Element => { + const { t } = useTranslation(); + + return ( + + + {t("This chat is PGP Encrypted")} + + + + + {t("Your communication is end-to-end encrypted with OpenPGP. You can verify the privacy of this chat using any third party tool based on the OpenPGP standard.")} + + + + + + + + + {t("Your public key")}} + value={own_pub_key} + variant='filled' + size='small' + InputProps={{ + endAdornment: + + navigator.clipboard.writeText(own_pub_key)}> + + + , + }} + /> + + + + {t("Peer public key")}} + value={peer_pub_key} + variant='filled' + size='small' + InputProps={{ + endAdornment: + + navigator.clipboard.writeText(peer_pub_key)}> + + + , + }} + /> + + + + {t("Your encrypted private key")}} + value={own_enc_priv_key} + variant='filled' + size='small' + InputProps={{ + endAdornment: + + navigator.clipboard.writeText(own_enc_priv_key)}> + + + , + }} + /> + + + + {t("Your private key passphrase (keep secure!)")}} + value={passphrase} + variant='filled' + size='small' + InputProps={{ + endAdornment: + + navigator.clipboard.writeText(passphrase)}> + + + , + }} + /> + + +
+ + + + + + {/* */} + + {/* */} + + + +
+
+ + + + + +
+ ) +} + +export default AuditPGPDialog; diff --git a/frontend/src/components/Dialogs/index.ts b/frontend/src/components/Dialogs/index.ts index 518f5c9e..cad8b05c 100644 --- a/frontend/src/components/Dialogs/index.ts +++ b/frontend/src/components/Dialogs/index.ts @@ -1,3 +1,4 @@ +export { default as AuditPGPDialog } from "./AuditPGP"; export { default as CommunityDialog } from "./Community"; export { default as InfoDialog } from "./Info"; export { default as LearnDialog } from "./Learn"; diff --git a/frontend/src/components/EncryptedChat.js b/frontend/src/components/EncryptedChat.js index 59db5365..4b4e43e8 100644 --- a/frontend/src/components/EncryptedChat.js +++ b/frontend/src/components/EncryptedChat.js @@ -1,10 +1,19 @@ import React, { Component } from 'react'; import { withTranslation } from "react-i18next"; -import {Button, Badge, TextField, Grid, Container, Card, CardHeader, Paper, Avatar, Typography} from "@mui/material"; +import {Button, Badge, ToolTip, TextField, Grid, Container, Card, CardHeader, Paper, Avatar, Typography} from "@mui/material"; import ReconnectingWebSocket from 'reconnecting-websocket'; import { encryptMessage , decryptMessage} from "../utils/pgp"; import { getCookie } from "../utils/cookies"; -import { saveAsTxt } from "../utils/saveFile"; +import { saveAsJson, saveAsTxt } from "../utils/saveFile"; +import { AuditPGPDialog } from "./Dialogs" + +// Icons +import CheckIcon from '@mui/icons-material/Check'; +import CloseIcon from '@mui/icons-material/Close'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import KeyIcon from '@mui/icons-material/Key'; +import { ExportIcon } from './Icons'; class Chat extends Component { constructor(props) { @@ -65,7 +74,12 @@ class Chat extends Component { // If we receive a public key other than ours (our peer key!) if (dataFromServer.message.substring(0,36) == `-----BEGIN PGP PUBLIC KEY BLOCK-----` & dataFromServer.message != this.state.own_pub_key) { - console.log("PEER KEY RECEIVED!!") + if (dataFromServer.message == this.state.peer_pub_key){ + console.log("PEER HAS RECONNECTED USING HIS PREVIOUSLY KNOWN PUBKEY") + } else if (dataFromServer.message != this.state.peer_pub_key & this.state.peer_pub_key != null){ + console.log("PEER PUBKEY HAS CHANGED") + } + console.log("PEER KEY PUBKEY RECEIVED!!") this.setState({peer_pub_key:dataFromServer.message}) } else @@ -85,6 +99,7 @@ class Chat extends Component { plainTextMessage: decryptedData.decryptedMessage, validSignature: decryptedData.validSignature, userNick: dataFromServer.user_nick, + showPGP: false, time: dataFromServer.time }], }) @@ -102,18 +117,6 @@ class Chat extends Component { this.rws.addEventListener('error', () => { console.error('Socket encountered error: Closing socket'); }); - - // Encryption/Decryption Example - // console.log(encryptMessage('Example text to encrypt!', - // getCookie('pub_key').split('\\').join('\n'), - // getCookie('enc_priv_key').split('\\').join('\n'), - // getCookie('robot_token')) - // .then((encryptedMessage)=> decryptMessage( - // encryptedMessage, - // getCookie('pub_key').split('\\').join('\n'), - // getCookie('enc_priv_key').split('\\').join('\n'), - // getCookie('robot_token')) - // )) } componentDidUpdate() { @@ -140,6 +143,17 @@ class Chat extends Component { e.preventDefault(); } + createJsonFile = () => { + return ({ + "credentials": { + "own_public_key": this.state.own_pub_key, + "peer_public_key":this.state.peer_pub_key, + "encrypted_private_key":this.state.own_enc_priv_key, + "passphrase":this.state.token}, + "messages": this.state.messages, + }) + } + render() { const { t } = this.props; return ( @@ -195,7 +209,7 @@ class Chat extends Component { } style={{backgroundColor: '#fafafa'}} title={message.userNick} - subheader={this.state.audit ? message.plaintTextEncrypted : message.plainTextMessage} + subheader={this.state.audit ? message.encryptedMessage : message.plainTextMessage} subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444'}}} />} @@ -223,15 +237,30 @@ class Chat extends Component { - - - - - - - - +
+ + this.setState({audit:false})} + orderId={Number(this.props.orderId)} + messages={this.state.messages} + own_pub_key={this.state.own_pub_key} + own_enc_priv_key={this.state.own_enc_priv_key} + peer_pub_key={this.state.peer_pub_key ? this.state.peer_pub_key : "Not received yet"} + passphrase={this.state.token} + onClickBack={() => this.setState({audit:false})} + /> + + + + + + {/* */} + + {/* */} + + ) } diff --git a/frontend/src/components/Icons/Export.tsx b/frontend/src/components/Icons/Export.tsx new file mode 100644 index 00000000..038ee27f --- /dev/null +++ b/frontend/src/components/Icons/Export.tsx @@ -0,0 +1,10 @@ +import React, { Component } from "react"; +import { SvgIcon } from "@mui/material" + +export default function ExportIcon(props) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/components/Icons/index.ts b/frontend/src/components/Icons/index.ts index e63c964e..dce24d8f 100644 --- a/frontend/src/components/Icons/index.ts +++ b/frontend/src/components/Icons/index.ts @@ -12,3 +12,5 @@ export { default as RoboSatsTextIcon } from "./RoboSatsText"; export { default as SellSatsCheckedIcon } from "./SellSatsChecked"; export { default as SellSatsIcon } from "./SellSats"; export { default as SendReceiveIcon } from "./SendReceive"; +export { default as ExportIcon } from "./Export"; + diff --git a/frontend/src/components/MakerPage.js b/frontend/src/components/MakerPage.js index d1c4c64a..8adb9a2f 100644 --- a/frontend/src/components/MakerPage.js +++ b/frontend/src/components/MakerPage.js @@ -577,7 +577,7 @@ class MakerPage extends Component { - + }> {t("Expiry Timers")} diff --git a/frontend/src/utils/pgp.js b/frontend/src/utils/pgp.js index e75f6a71..5e2a01dc 100644 --- a/frontend/src/utils/pgp.js +++ b/frontend/src/utils/pgp.js @@ -6,7 +6,7 @@ export async function genKey(highEntropyToken) { const keyPair = await openpgp.generateKey({ type: 'ecc', // Type of the key, defaults to ECC curve: 'curve25519', // ECC curve name, defaults to curve25519 - userIDs: [{name: 'RoboSats Avatar'}], + userIDs: [{name: 'RoboSats Avatar ID'+ parseInt(Math.random() * 1000000)}], //Just for identification. Ideally it would be the avatar nickname, but the nickname is generated only after submission passphrase: highEntropyToken, format: 'armored' }) diff --git a/frontend/src/utils/saveFile.js b/frontend/src/utils/saveFile.js index 00eaddce..02423f37 100644 --- a/frontend/src/utils/saveFile.js +++ b/frontend/src/utils/saveFile.js @@ -17,6 +17,24 @@ export const saveAsTxt = (filename, dataObjToWrite) => { cancelable: true, }); + link.dispatchEvent(evt); + link.remove() +}; + +export const saveAsJson = (filename, dataObjToWrite) => { + const blob = new Blob([JSON.stringify(dataObjToWrite, null, 2)], { type: "text/json" }); + const link = document.createElement("a"); + + link.download = filename; + link.href = window.URL.createObjectURL(blob); + link.dataset.downloadurl = ["text/json", link.download, link.href].join(":"); + + const evt = new MouseEvent("click", { + view: window, + bubbles: true, + cancelable: true, + }); + link.dispatchEvent(evt); link.remove() }; \ No newline at end of file