Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client security, Core #64

Merged
merged 68 commits into from
Aug 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
6b1b353
aes and argon2
polymorpher Aug 2, 2021
8d32fe8
batch hash functions; genOTP - do not compute report interval inside
polymorpher Aug 2, 2021
3a68c25
double otp and controlled randomness support; argon2 support; functio…
polymorpher Aug 2, 2021
78ff562
store randomnessResults and return in merkle tree computation
polymorpher Aug 2, 2021
993b66b
fix bug with sha256b
polymorpher Aug 2, 2021
c68b9db
fix deprecated method in wallet test
polymorpher Aug 2, 2021
e4225ef
expand test util for createWallet
polymorpher Aug 2, 2021
f77b7b7
make computeMerkleTree and recoverRandomness async
polymorpher Aug 2, 2021
ec451dd
sha256b async
polymorpher Aug 2, 2021
dac8df1
make worker onmessagelisteners async
polymorpher Aug 2, 2021
12dd2b7
return counter on test util createwallet
polymorpher Aug 2, 2021
7119e60
client security test
polymorpher Aug 2, 2021
658f052
Add use two otp options on create wallet
haolinj Aug 3, 2021
0d7ce6b
Use same name for second otp with suffix and adjust the instruction text
haolinj Aug 4, 2021
0f497a4
Preset second otp qr data and not overriding otp name in the state
haolinj Aug 4, 2021
bef6494
Merge branch 'master' of github.com:polymorpher/one-wallet into clien…
haolinj Aug 4, 2021
7a845ae
Add otp2 for transfer and potentially restore wallet
haolinj Aug 4, 2021
0deeefc
Merge branch 'master' into client-security
polymorpher Aug 4, 2021
a9adb9d
Merge branch 'client-security' of github.com:polymorpher/one-wallet i…
haolinj Aug 4, 2021
e1bff30
Auto focus otp2 input box when otp1 input box is filled
haolinj Aug 4, 2021
f606270
fix issues with wasm in webpack compilation
polymorpher Aug 5, 2021
73deab0
improve Create screen texts, positioning, and user experience
polymorpher Aug 5, 2021
a5b9f93
remove redundant new lines
polymorpher Aug 5, 2021
3294346
decodeOtp util
polymorpher Aug 5, 2021
7b0748d
fix bug with hseed setter
polymorpher Aug 5, 2021
9d166f8
fix progress observer
polymorpher Aug 5, 2021
eff7ca2
computeEOTP with async, hasher, double OTP, and randomness
polymorpher Aug 5, 2021
01081a3
transfer test with double otp, randomness, argon2 and sha256
polymorpher Aug 5, 2021
97a36a0
await eotp in all tests; make all tests pass
polymorpher Aug 5, 2021
eb497c9
await EOTP everywhere
polymorpher Aug 5, 2021
d3253c2
more parameters in eotpbuilder.fromOtp
polymorpher Aug 5, 2021
41c3f4a
Merge branch 'client-security' into client-security-frontend
polymorpher Aug 5, 2021
b549b6e
sync flow.js from frontend branch
polymorpher Aug 5, 2021
106cbef
rand, hasher, nonce, otp2 in commitReveal flow
polymorpher Aug 5, 2021
76d6e48
allow arbitrary paramsHash in recover; use last leaf as recover EOTP
polymorpher Aug 5, 2021
54a78a8
compile contracts
polymorpher Aug 5, 2021
c2eaa4c
new computeRecoveryHash; deprecate bruteforceEOTP
polymorpher Aug 5, 2021
a7e5845
special treatment for recover operation in _verifyReveal and _complet…
polymorpher Aug 5, 2021
7b57e9f
compiled contracts
polymorpher Aug 5, 2021
dcce7a2
fix bug with computeRecoveryHash
polymorpher Aug 5, 2021
8fc1c24
new recovery flow
polymorpher Aug 5, 2021
dfcc39b
new recovery test; all tests passing
polymorpher Aug 5, 2021
266c747
Merge remote-tracking branch 'origin/client-security' into client-sec…
polymorpher Aug 5, 2021
57ef96b
minor
polymorpher Aug 5, 2021
f4346b8
clean up
polymorpher Aug 5, 2021
ace8dd7
ditto
polymorpher Aug 5, 2021
ca1f770
ONE Wallet -> 1wallet
polymorpher Aug 5, 2021
dc64057
otp entry name change
polymorpher Aug 5, 2021
b6ebe9b
text update
polymorpher Aug 5, 2021
26a0b54
better syntax for computeRecoveryHash
polymorpher Aug 5, 2021
e213814
v8 contract
polymorpher Aug 5, 2021
ec5ab14
revealRecovery with data
polymorpher Aug 5, 2021
36a3594
doRecovery
polymorpher Aug 5, 2021
8c3478e
skip checking minor version
polymorpher Aug 5, 2021
4d658c5
flow: allow index override
polymorpher Aug 5, 2021
344f9bd
relayer: show reveal params on verbose mode
polymorpher Aug 5, 2021
5970e3e
doRecovery: fix issues
polymorpher Aug 5, 2021
7c13508
rearrange ui elements
polymorpher Aug 5, 2021
45af94f
make restore work
polymorpher Aug 5, 2021
08834d7
Add 2 otp for recover address setup
haolinj Aug 5, 2021
e11ec8d
Merge branch 'client-security-frontend' of github.com:polymorpher/one…
haolinj Aug 5, 2021
6cb198c
Merge pull request #65 from polymorpher/client-security-frontend
polymorpher Aug 5, 2021
f55ab96
Merge branch 'master' into client-security
polymorpher Aug 5, 2021
608426b
add randomness by default
polymorpher Aug 5, 2021
7d32482
better test progress bar and fix a null issue
polymorpher Aug 6, 2021
c6ec27b
versioned security parameters; use that everywhere applicable
polymorpher Aug 6, 2021
cdbec6e
move recoverRandomness to worker
polymorpher Aug 6, 2021
4d4b1b5
Merge pull request #66 from polymorpher/client-security-randomness
polymorpher Aug 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35,162 changes: 19,237 additions & 15,925 deletions code/build/contracts/ONEWallet.json

Large diffs are not rendered by default.

13,776 changes: 6,888 additions & 6,888 deletions code/build/contracts/TokenTracker.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions code/cli/src/ONEWalletWorker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ONE from '../../lib/onewallet'
import { parentPort } from 'worker_threads'

parentPort.once('message', ({ seed, effectiveTime, duration, slotSize, interval } = {}) => {
parentPort.once('message', async ({ seed, effectiveTime, duration, slotSize, interval } = {}) => {
if (!seed) {
console.log('Worker: received event but it has no valid data')
return
Expand All @@ -14,7 +14,7 @@ parentPort.once('message', ({ seed, effectiveTime, duration, slotSize, interval
root,
layers,
maxOperationsPerInterval,
} = ONE.computeMerkleTree({
} = await ONE.computeMerkleTree({
otpSeed: seed,
effectiveTime,
duration,
Expand Down
4 changes: 2 additions & 2 deletions code/cli/src/make.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const MakeWallet = ({ lastResortAddress, otpInput }) => {
return setError('Authenticator code is invalid: ' + otpInput)
}
const otp = ONEUtil.encodeNumericalOtp(otpNum)
const eotp = ONE.computeEOTP({ hseed: ONEUtil.hexToBytes(hseed), otp })
const eotp = await ONE.computeEOTP({ hseed: ONEUtil.hexToBytes(hseed), otp })
const computedLeaf = ONEUtil.sha256(eotp)
if (!ONEUtil.bytesEqual(computedLeaf, leaf)) {
setError(`The OTP code you provided [${otpInput}] is incorrect.`)
Expand Down Expand Up @@ -86,7 +86,7 @@ const MakeWallet = ({ lastResortAddress, otpInput }) => {
{wallet &&
<>
<Text>
Wallet deployed:
Wallet deployed:
</Text>
<Info wallet={wallet} />
</>}
Expand Down
1 change: 1 addition & 0 deletions code/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"babel-loader": "^8.2.2",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"babel-preset-react": "^6.24.1",
"base64-loader": "^1.0.0",
"copy-webpack-plugin": "^5.0.5",
"core-js": "^3.13.0",
"css-loader": "^5.2.6",
Expand Down
2 changes: 1 addition & 1 deletion code/client/src/components/CommitRevealProgress.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { Step } = Steps
export const CommitRevealProgress = ({ stage, style }) => {
return (
<>
{stage > 0 && (
{stage >= 0 && (
<Row style={style}>
<Steps current={stage}>
<Step title='Prepare' description='Preparing signature' />
Expand Down
150 changes: 118 additions & 32 deletions code/client/src/pages/Create.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,26 @@ import { useDispatch, useSelector } from 'react-redux'
import { useHistory } from 'react-router'
import Paths from '../constants/paths'
import api from '../api'
import config from '../config'
import ONEUtil from '../../../lib/util'
import ONEConstants from '../../../lib/constants'
import ONENames from '../../../lib/names'
// import { uniqueNamesGenerator, colors, animals } from 'unique-names-generator'
import { Button, Row, Space, Typography, Slider, Image, message, Progress, Timeline, Select } from 'antd'
import { RedoOutlined, LoadingOutlined, SearchOutlined } from '@ant-design/icons'
import {
Button,
Row,
Space,
Typography,
Slider,
Image,
message,
Progress,
Timeline,
Select,
Checkbox,
Tooltip
} from 'antd'
import { RedoOutlined, LoadingOutlined, SearchOutlined, QuestionCircleOutlined } from '@ant-design/icons'
import humanizeDuration from 'humanize-duration'
import AnimatedSection from '../components/AnimatedSection'
import b32 from 'hi-base32'
Expand Down Expand Up @@ -37,16 +52,32 @@ const genName = (existingNames) => {
return name
}

const generateOtpSeed = () => {
const otpSeedBuffer = new Uint8Array(20)
return window.crypto.getRandomValues(otpSeedBuffer)
}

const sectionViews = {
setupWalletDetails: 1,
setupOtp: 2,
setupSecondOtp: 3,
prepareWallet: 4,
walletSetupDone: 5
}

const Create = () => {
const generateNewOtpName = () => genName(Object.keys(wallets).map(k => wallets[k].name))

const { isMobile } = useWindowDimensions()
const dispatch = useDispatch()
const history = useHistory()
const network = useSelector(state => state.wallet.network)
const wallets = useSelector(state => state.wallet.wallets)
const [name, setName] = useState(genName(Object.keys(wallets).map(k => wallets[k].name)))
const otpSeedBuffer = new Uint8Array(20)
const [name, setName] = useState(generateNewOtpName())
// eslint-disable-next-line no-unused-vars
const [seed, setSeed] = useState(generateOtpSeed())
// eslint-disable-next-line no-unused-vars
const [seed, setSeed] = useState(window.crypto.getRandomValues(otpSeedBuffer))
const [seed2, setSeed2] = useState(generateOtpSeed())
const [duration, setDuration] = useState(WalletConstants.defaultDuration)
const [lastResortAddress, setLastResortAddress] = useState()
const [dailyLimit] = useState(WalletConstants.defaultDailyLimit)
Expand All @@ -60,52 +91,73 @@ const Create = () => {
const [progressStage, setProgressStage] = useState(0)
const [address, setAddress] = useState() // '0x12345678901234567890'
const [effectiveTime, setEffectiveTime] = useState()
const [doubleOtp, setDoubleOtp] = useState(false)

const [durationVisible, setDurationVisible] = useState(false)
const [section, setSection] = useState(2)
const [section, setSection] = useState(sectionViews.setupOtp)
const [qrCodeData, setQRCodeData] = useState()
const [secondOtpQrCodeData, setSecondOtpQrCodeData] = useState()
const [otp, setOtp] = useState('')

const [deploying, setDeploying] = useState()

const otpRef = useRef()

const getQRCodeUri = () => {
const securityParameters = ONEUtil.securityParameters({
majorVersion: ONEConstants.MajorVersion,
minorVersion: ONEConstants.MinorVersion,
})

const getQRCodeUri = (otpSeed, otpDisplayName) => {
// otpauth://TYPE/LABEL?PARAMETERS
return `otpauth://totp/${name}?secret=${b32.encode(seed)}&issuer=Harmony`
return `otpauth://totp/${otpDisplayName}?secret=${b32.encode(otpSeed)}&issuer=Harmony`
}

useEffect(() => {
(async function () {
const uri = getQRCodeUri()
const data = await qrcode.toDataURL(uri, { errorCorrectionLevel: 'low', width: isMobile ? 192 : 256 })
setQRCodeData(data)
const otpUri = getQRCodeUri(seed, name)
const secondOtpUri = getQRCodeUri(seed2, `${name} - 2nd`)
const otpQrCodeData = await qrcode.toDataURL(otpUri, { errorCorrectionLevel: 'low', width: isMobile ? 192 : 256 })
const secondOtpQrCodeData = await qrcode.toDataURL(secondOtpUri, { errorCorrectionLevel: 'low', width: isMobile ? 192 : 256 })
setQRCodeData(otpQrCodeData)
setSecondOtpQrCodeData(secondOtpQrCodeData)
})()
}, [name])

useEffect(() => {
if (section === 2 && worker) {
console.log('posting to worker')
if (section === sectionViews.setupOtp && worker) {
console.log('Posting to worker. Security parameters:', securityParameters)
const t = Math.floor(Date.now() / WalletConstants.interval) * WalletConstants.interval
setEffectiveTime(t)
worker && worker.postMessage({
seed, effectiveTime: t, duration, slotSize, interval: WalletConstants.interval
seed,
seed2,
effectiveTime: t,
duration,
slotSize,
interval: WalletConstants.interval,
...securityParameters,
})
}
}, [section, worker])

useEffect(() => {
const settingUpSecondOtp = section === sectionViews.setupSecondOtp
if (otp.length !== 6) {
return
}
const expected = ONEUtil.genOTP({ seed })
const currentSeed = settingUpSecondOtp ? seed2 : seed
const expected = ONEUtil.genOTP({ seed: currentSeed })
const code = new DataView(expected.buffer).getUint32(0, false).toString()
setOtp('')
if (code.padStart(6, '0') !== otp.padStart(6, '0')) {
console.log(`Expected: ${code}. Got: ${otp}`)
message.error('Code is incorrect. Please try again.')
setOtp('')
otpRef?.current?.focusInput(0)
} else if (doubleOtp && !settingUpSecondOtp) {
setSection(sectionViews.setupSecondOtp)
otpRef?.current?.focusInput(0)
} else {
setSection(3)
setSection(sectionViews.prepareWallet)
}
}, [otp])

Expand All @@ -122,7 +174,6 @@ const Create = () => {
message.error('Cannot deploy wallet. Error: root is not set.')
return
}

let normalizedAddress = ''
if (lastResortAddress !== '') {
// Ensure valid address for both 0x and one1 formats
Expand Down Expand Up @@ -155,6 +206,8 @@ const Create = () => {
dailyLimit: ONEUtil.toFraction(dailyLimit).toString(),
hseed: ONEUtil.hexView(hseed),
network,
doubleOtp,
...securityParameters,
}
await storeLayers()
dispatch(walletActions.updateWallet(wallet))
Expand All @@ -171,7 +224,7 @@ const Create = () => {
}

useEffect(() => {
const worker = new Worker('ONEWalletWorker.js')
const worker = new Worker('/ONEWalletWorker.js')
worker.onmessage = (event) => {
const { status, current, total, stage, result } = event.data
if (status === 'working') {
Expand All @@ -193,7 +246,7 @@ const Create = () => {

return (
<>
<AnimatedSection show={section === 1} style={{ maxWidth: 640 }}>
<AnimatedSection show={section === sectionViews.setupWalletDetails} style={{ maxWidth: 640 }}>
<Heading>What do you want to call your wallet?</Heading>
<Hint>This is only stored on your computer to distinguish your wallets.</Hint>
<Row align='middle' style={{ marginBottom: 32, marginTop: 16 }}>
Expand All @@ -203,12 +256,11 @@ const Create = () => {
value={name} onChange={({ target: { value } }) => setName(value)}
style={{ padding: 0 }}
/>
<Button type='primary' shape='round' size='large' onClick={() => setSection(2)}>Next</Button>
<Button type='primary' shape='round' size='large' onClick={() => setSection(sectionViews.setupOtp)}>Next</Button>
</Space>
</Row>

<Space direction='vertical'>
<Hint>Next, we will set up a ONE Wallet that expires in a year. When the wallet expires, you may create a new wallet and transfer the funds. The funds can also be recovered to an address you set later.</Hint>
<Hint>Next, we will set up a 1wallet that expires in a year. When the wallet expires, you may create a new wallet and transfer the funds. The funds can also be recovered to an address you set later.</Hint>
<Link onClick={() => setDurationVisible(true)}>Need more time?</Link>
{durationVisible &&
<Space>
Expand All @@ -221,11 +273,11 @@ const Create = () => {
</Space>}
</Space>
</AnimatedSection>
<AnimatedSection show={section === 2} style={{ maxWidth: 640 }}>
<AnimatedSection show={section === sectionViews.setupOtp} style={{ maxWidth: 640 }}>
<Row>
<Space direction='vertical'>
{/* <Heading>Now, scan the QR code with your Google Authenticator</Heading> */}
<Heading>Create Your ONE Wallet</Heading>
<Heading>Create Your 1wallet</Heading>
<Hint>You need the 6-digit code from Google authenticator to transfer funds. You can restore your wallet using Google authenticator on any device.</Hint>
<Row justify='center'>
{qrCodeData && <Image src={qrCodeData} preview={false} width={isMobile ? 192 : 256} />}
Expand All @@ -234,7 +286,41 @@ const Create = () => {
</Row>
<Row>
<Space direction='vertical' size='large' align='center'>
<Hint>After you are done, type in the 6-digit code from Google authenticator.</Hint>
<Hint>After you are done, type in the 6-digit code from Google authenticator</Hint>
<Hint>Code for <b>Harmony ({name})</b></Hint>
<OtpBox
shouldAutoFocus
ref={otpRef}
value={otp}
onChange={setOtp}
/>
<Checkbox onChange={() => setDoubleOtp(!doubleOtp)}>
<Space>
<Hint>
Use two codes to enhance security
</Hint>
<Tooltip title={<div>You will need to scan another QR-code on the next page. Each time you make a transaction, you will need to type in two 6-digit codes, which are shown simultaneously next to each other on your Google authenticator.<br /><br />This is advisable if you intend to make larger transactions with this wallet</div>}>
<QuestionCircleOutlined />
</Tooltip>
</Space>
</Checkbox>
</Space>
</Row>
</AnimatedSection>
<AnimatedSection show={section === sectionViews.setupSecondOtp} style={{ maxWidth: 640 }}>
<Row>
<Space direction='vertical'>
<Heading>Create Your 1wallet (second code)</Heading>
<Hint align='center'>Scan with your Google Authenticator to setup the <b>second</b> code</Hint>
<Row justify='center'>
{secondOtpQrCodeData && <Image src={secondOtpQrCodeData} preview={false} width={isMobile ? 192 : 256} />}
</Row>
</Space>
</Row>
<Row>
<Space direction='vertical' size='large' align='center'>
<Hint>Type in the <b>second</b> 6-digit code from Google authenticator</Hint>
<Hint>Code for <b>Harmony ({name} - 2nd)</b></Hint>
<OtpBox
shouldAutoFocus
ref={otpRef}
Expand All @@ -244,10 +330,10 @@ const Create = () => {
</Space>
</Row>
</AnimatedSection>
<AnimatedSection show={section === 3} style={{ maxWidth: 640 }}>
<AnimatedSection show={section === sectionViews.prepareWallet} style={{ maxWidth: 640 }}>
<Row>
<Space direction='vertical'>
<Heading>Prepare Your ONE Wallet</Heading>
<Heading>Prepare Your 1wallet</Heading>
</Space>
</Row>
{/* <Row style={{ marginBottom: 16 }}> */}
Expand Down Expand Up @@ -300,7 +386,7 @@ const Create = () => {
percent={progress}
/>
<Space direction='vertical'>
<Timeline pending={progressStage < 2 && 'Securing your keyless ONE Wallet'}>
<Timeline pending={progressStage < 2 && 'Securing your keyless 1wallet'}>
<Timeline.Item color={progressStage < 1 ? 'grey' : 'green'}>Securing the wallet</Timeline.Item>
<Timeline.Item color={progressStage < 2 ? 'grey' : 'green'}>Preparing signatures</Timeline.Item>
</Timeline>
Expand All @@ -312,12 +398,12 @@ const Create = () => {
<Row>
<Space direction='vertical'>
<Hint>No private key. No mnemonic. Simple and Secure. </Hint>
<Hint>To learn more, visit <Link href='https://github.com/polymorpher/one-wallet/wiki'>ONE Wallet Wiki</Link></Hint>
<Hint>To learn more, visit <Link href='https://github.com/polymorpher/one-wallet/wiki'>1wallet Wiki</Link></Hint>
<Hint>In Beta, your wallet is subject to a daily spending limit of {WalletConstants.defaultDailyLimit} ONE</Hint>
</Space>
</Row>
</AnimatedSection>
<AnimatedSection show={section === 4}>
<AnimatedSection show={section === sectionViews.walletSetupDone}>
<Space direction='vertical'>
<Heading>You are all set!</Heading>
<Space direction='vertical' size='small'>
Expand Down
Loading