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

Add use two otp options on create wallet #65

Merged
merged 30 commits into from
Aug 5, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
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
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
41c3f4a
Merge branch 'client-security' into client-security-frontend
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
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
108 changes: 89 additions & 19 deletions code/client/src/pages/Create.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import api from '../api'
import ONEUtil from '../../../lib/util'
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 { Button, Row, Space, Typography, Slider, Image, message, Progress, Timeline, Select, Checkbox } from 'antd'
import { RedoOutlined, LoadingOutlined, SearchOutlined } from '@ant-design/icons'
import humanizeDuration from 'humanize-duration'
import AnimatedSection from '../components/AnimatedSection'
Expand Down Expand Up @@ -37,16 +37,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 +76,72 @@ 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 [otp, setOtp] = useState('')

const [deploying, setDeploying] = useState()

const otpRef = useRef()

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to use a slightly different name here. e.g. instead of ${name}, use ${name} (2nd)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the name is the same, but append (2nd)?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

}

useEffect(() => {
(async function () {
const uri = getQRCodeUri()
const otpSeed = section === sectionViews.setupSecondOtp ? seed2 : seed

const uri = getQRCodeUri(otpSeed)

const data = await qrcode.toDataURL(uri, { errorCorrectionLevel: 'low', width: isMobile ? 192 : 256 })

setQRCodeData(data)
})()
}, [name])

useEffect(() => {
if (section === 2 && worker) {
if (section === sectionViews.setupOtp && worker) {
console.log('posting to worker')
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
})
}
}, [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)

setName(`${name} (2nd)`)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be incorrect to change the name state variable because the wallet would be saved as ${name} (2nd) in redux

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. So we still want to save the name as original name in the state, but the authenticator would have a different name for the second OTP (that is only append (2nd) text).


otpRef?.current?.focusInput(0)
} else {
setSection(3)
setSection(sectionViews.prepareWallet)
}
}, [otp])

Expand All @@ -124,14 +160,17 @@ const Create = () => {
}

let normalizedAddress = ''

if (lastResortAddress !== '') {
// Ensure valid address for both 0x and one1 formats
normalizedAddress = util.safeExec(util.normalizedAddress, [lastResortAddress], handleAddressError)
if (!normalizedAddress) {
return
}
}

setDeploying(true)

try {
const { address } = await api.relayer.create({
root: ONEUtil.hexString(root),
Expand All @@ -155,6 +194,7 @@ const Create = () => {
dailyLimit: ONEUtil.toFraction(dailyLimit).toString(),
hseed: ONEUtil.hexView(hseed),
network,
doubleOtp,
}
await storeLayers()
dispatch(walletActions.updateWallet(wallet))
Expand Down Expand Up @@ -193,7 +233,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,7 +243,7 @@ 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>

Expand All @@ -221,7 +261,7 @@ 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> */}
Expand All @@ -243,8 +283,38 @@ const Create = () => {
/>
</Space>
</Row>
<Row justify='center'>
<Checkbox onChange={() => setDoubleOtp(!doubleOtp)}>
<Hint>Setup second One Time Password for enhanced security</Hint>
<br />
<Hint>You will need to scan the QR code again in next step</Hint>
</Checkbox>
</Row>
</AnimatedSection>
<AnimatedSection show={section === sectionViews.setupSecondOtp} style={{ maxWidth: 640 }}>
<Row>
<Space direction='vertical'>
{/* <Heading>Now, scan the QR code with your Google Authenticator</Heading> */}
<Heading>Setup Second One Time Password</Heading>
<Hint align='center'>Use two One Time Password for enhanced security</Hint>
<Row justify='center'>
{qrCodeData && <Image src={qrCodeData} preview={false} width={isMobile ? 192 : 256} />}
</Row>
</Space>
</Row>
<Row>
<Space direction='vertical' size='large' align='center'>
<Hint>After you are done, type in the 6-digit code from Google authenticator.</Hint>
<OtpBox
shouldAutoFocus
ref={otpRef}
value={otp}
onChange={setOtp}
/>
</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>
Expand Down Expand Up @@ -317,7 +387,7 @@ const Create = () => {
</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
3 changes: 2 additions & 1 deletion code/client/src/worker/ONEWalletWorker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const ONE = require('../../../lib/onewallet')

onmessage = async function (event) {
const { seed, effectiveTime, duration, slotSize, interval } = event.data
const { seed, seed2, effectiveTime, duration, slotSize, interval } = event.data
if (!seed) {
// console.log('worker: received event but it has no valid data', event)
return
Expand All @@ -16,6 +16,7 @@ onmessage = async function (event) {
maxOperationsPerInterval,
} = await ONE.computeMerkleTree({
otpSeed: seed,
otpSeed2: seed2,
effectiveTime,
duration,
maxOperationsPerInterval: slotSize,
Expand Down