+
+ 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})}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+}
+
+export default withTranslation()(Chat);
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/components/TradeBox.js b/frontend/src/components/TradeBox.js
index 0ecc4a36..127d9cdc 100644
--- a/frontend/src/components/TradeBox.js
+++ b/frontend/src/components/TradeBox.js
@@ -3,7 +3,7 @@ import { withTranslation, Trans} from "react-i18next";
import { IconButton, Box, Link, Paper, Rating, Button, Tooltip, CircularProgress, Grid, Typography, TextField, List, ListItem, ListItemText, Divider, ListItemIcon, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
import QRCode from "react-qr-code";
import Countdown, { zeroPad} from 'react-countdown';
-import Chat from "./Chat"
+import Chat from "./EncryptedChat"
import MediaQuery from 'react-responsive'
import QrReader from 'react-qr-reader'
diff --git a/frontend/src/components/UserGenPage.js b/frontend/src/components/UserGenPage.js
index 4acfdd0b..18dc2bda 100644
--- a/frontend/src/components/UserGenPage.js
+++ b/frontend/src/components/UserGenPage.js
@@ -11,8 +11,12 @@ import ContentCopy from "@mui/icons-material/ContentCopy";
import BoltIcon from '@mui/icons-material/Bolt';
import { RoboSatsNoTextIcon } from "./Icons";
+import { sha256 } from 'js-sha256';
+import { genBase62Token, tokenStrength } from "../utils/token";
+import { genKey } from "../utils/pgp";
import { getCookie, writeCookie } from "../utils/cookies";
+
class UserGenPage extends Component {
constructor(props) {
super(props);
@@ -37,7 +41,7 @@ class UserGenPage extends Component {
});
}
else{
- var newToken = this.genBase62Token(36)
+ var newToken = genBase62Token(36)
this.setState({
token: newToken
});
@@ -45,46 +49,61 @@ class UserGenPage extends Component {
}
}
- // sort of cryptographically strong function to generate Base62 token client-side
- genBase62Token(length)
- {
- return window.btoa(Array.from(
- window.crypto.getRandomValues(
- new Uint8Array(length * 2)))
- .map((b) => String.fromCharCode(b))
- .join("")).replace(/[+/]/g, "")
- .substring(0, length);
- }
-
getGeneratedUser=(token)=>{
- fetch('/api/user' + '?token=' + token + '&ref_code=' + this.refCode)
- .then((response) => response.json())
- .then((data) => {
- this.setState({
+
+ const strength = tokenStrength(token);
+ const refCode = this.refCode
+
+ const requestOptions = genKey(token).then(function(key) {
+ return {
+ method: 'POST',
+ headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken')},
+ body: JSON.stringify({
+ token_sha256: sha256(token),
+ public_key: key.publicKeyArmored,
+ encrypted_private_key: key.encryptedPrivateKeyArmored,
+ unique_values: strength.uniqueValues,
+ counts: strength.counts,
+ length: token.length,
+ ref_code: refCode,
+ })
+ }}
+ );
+
+ console.log(requestOptions)
+
+ requestOptions.then((options) =>
+ fetch("/api/user/",options)
+ .then((response) => response.json())
+ .then((data) => { console.log(data) &
+ this.setState({
+ nickname: data.nickname,
+ bit_entropy: data.token_bits_entropy,
+ avatar_url: '/static/assets/avatars/' + data.nickname + '.png',
+ shannon_entropy: data.token_shannon_entropy,
+ bad_request: data.bad_request,
+ found: data.found,
+ loadingRobot:false,
+ })
+ &
+ // Add nick and token to App state (token only if not a bad request)
+ (data.bad_request ? this.props.setAppState({
nickname: data.nickname,
- bit_entropy: data.token_bits_entropy,
- avatar_url: '/static/assets/avatars/' + data.nickname + '.png',
- shannon_entropy: data.token_shannon_entropy,
- bad_request: data.bad_request,
- found: data.found,
- loadingRobot:false,
- })
- &
- // Add nick and token to App state (token only if not a bad request)
- (data.bad_request ? this.props.setAppState({
- nickname: data.nickname,
- avatarLoaded: false,
- })
- :
- (this.props.setAppState({
- nickname: data.nickname,
- token: token,
- avatarLoaded: false,
- })) & writeCookie("robot_token",token))
- &
- // If the robot has been found (recovered) we assume the token is backed up
- (data.found ? this.props.setAppState({copiedToken:true}) : null)
- });
+ avatarLoaded: false,
+ })
+ :
+ (this.props.setAppState({
+ nickname: data.nickname,
+ token: token,
+ avatarLoaded: false,
+ })) & writeCookie("robot_token",token)
+ & writeCookie("pub_key",data.public_key.split('\n').join('\\'))
+ & writeCookie("enc_priv_key",data.encrypted_private_key.split('\n').join('\\')))
+ &
+ // If the robot has been found (recovered) we assume the token is backed up
+ (data.found ? this.props.setAppState({copiedToken:true}) : null)
+ })
+ );
}
delGeneratedUser() {
@@ -97,7 +116,7 @@ class UserGenPage extends Component {
}
handleClickNewRandomToken=()=>{
- var token = this.genBase62Token(36);
+ var token = genBase62Token(36);
this.setState({
token: token,
tokenHasChanged: true,
@@ -164,7 +183,7 @@ class UserGenPage extends Component {
{
this.state.found ?
-
+
{this.state.found ? t("A robot avatar was found, welcome back!"):null}
@@ -174,7 +193,7 @@ class UserGenPage extends Component {
{
+ 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
diff --git a/frontend/src/utils/token.js b/frontend/src/utils/token.js
new file mode 100644
index 00000000..181f99c5
--- /dev/null
+++ b/frontend/src/utils/token.js
@@ -0,0 +1,17 @@
+// sort of cryptographically strong function to generate Base62 token client-side
+export function genBase62Token(length){
+ return window.btoa(Array.from(
+ window.crypto.getRandomValues(
+ new Uint8Array(length * 2)))
+ .map((b) => String.fromCharCode(b))
+ .join("")).replace(/[+/]/g, "")
+ .substring(0, length);
+}
+
+export function tokenStrength(token) {
+ const characters = token.split("").reduce(function(obj, s){
+ obj[s] = (obj[s] || 0) + 1;
+ return obj;
+ }, {});
+ return {uniqueValues:Object.keys(characters).length,counts:Object.values(characters)}
+}
\ No newline at end of file