mirror of
https://github.com/trezor/trezor-wallet
synced 2024-11-30 12:18:09 +00:00
Feature/limit passphrase length to 50 bytes (#542)
Feature/limit passphrase length to 50 bytes
This commit is contained in:
commit
f7a8fbeb3b
@ -9,6 +9,7 @@ __changed__
|
|||||||
- static (without animation) active tab indicator
|
- static (without animation) active tab indicator
|
||||||
- input validation - mandatory leading 0 for float numbers
|
- input validation - mandatory leading 0 for float numbers
|
||||||
- regexps refactored to functions, added unit tests
|
- regexps refactored to functions, added unit tests
|
||||||
|
- limit passphrase length to 50 bytes
|
||||||
|
|
||||||
__removed__
|
__removed__
|
||||||
- Text "already used" from token select in case of already added tokens
|
- Text "already used" from token select in case of already added tokens
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { byteLength } from 'utils/formatUtils';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { Button, Input, Checkbox, P, H5, colors } from 'trezor-ui-components';
|
import { Button, Input, Checkbox, P, H5, colors } from 'trezor-ui-components';
|
||||||
import { FONT_SIZE } from 'config/variables';
|
import { FONT_SIZE } from 'config/variables';
|
||||||
@ -24,6 +25,7 @@ type State = {
|
|||||||
passphraseCheckInputValue: string,
|
passphraseCheckInputValue: string,
|
||||||
doPassphraseInputsMatch: boolean,
|
doPassphraseInputsMatch: boolean,
|
||||||
isPassphraseHidden: boolean,
|
isPassphraseHidden: boolean,
|
||||||
|
byteLength: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
@ -91,6 +93,7 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
passphraseCheckInputValue: '',
|
passphraseCheckInputValue: '',
|
||||||
doPassphraseInputsMatch: true,
|
doPassphraseInputsMatch: true,
|
||||||
isPassphraseHidden: true,
|
isPassphraseHidden: true,
|
||||||
|
byteLength: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +120,9 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
let doPassphraseInputsMatch = false;
|
let doPassphraseInputsMatch = false;
|
||||||
if (inputName === 'passphraseInputValue') {
|
if (inputName === 'passphraseInputValue') {
|
||||||
|
this.setState({
|
||||||
|
byteLength: byteLength(inputValue),
|
||||||
|
});
|
||||||
// If passphrase is not hidden the second input should get filled automatically
|
// If passphrase is not hidden the second input should get filled automatically
|
||||||
// and should be disabled
|
// and should be disabled
|
||||||
if (this.state.isPassphraseHidden) {
|
if (this.state.isPassphraseHidden) {
|
||||||
@ -181,13 +187,28 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
handleKeyPress(event: KeyboardEvent) {
|
handleKeyPress(event: KeyboardEvent) {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (this.state.doPassphraseInputsMatch) {
|
if (this.state.doPassphraseInputsMatch && this.state.byteLength <= 50) {
|
||||||
this.submitPassphrase();
|
this.submitPassphrase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let error = null;
|
||||||
|
if (this.state.byteLength > 50) {
|
||||||
|
error = (
|
||||||
|
<PassphraseError>
|
||||||
|
<FormattedMessage {...l10nMessages.TR_PASSPHRASE_IS_TOO_LONG} />
|
||||||
|
</PassphraseError>
|
||||||
|
);
|
||||||
|
} else if (!this.state.doPassphraseInputsMatch) {
|
||||||
|
error = (
|
||||||
|
<PassphraseError>
|
||||||
|
<FormattedMessage {...l10nMessages.TR_PASSPHRASES_DO_NOT_MATCH} />
|
||||||
|
</PassphraseError>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<H5>
|
<H5>
|
||||||
@ -236,11 +257,7 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{!this.state.doPassphraseInputsMatch && (
|
{error}
|
||||||
<PassphraseError>
|
|
||||||
<FormattedMessage {...l10nMessages.TR_PASSPHRASES_DO_NOT_MATCH} />
|
|
||||||
</PassphraseError>
|
|
||||||
)}
|
|
||||||
<Row>
|
<Row>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
isChecked={!this.state.isPassphraseHidden}
|
isChecked={!this.state.isPassphraseHidden}
|
||||||
@ -250,10 +267,7 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
</Checkbox>
|
</Checkbox>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Button
|
<Button isDisabled={!!error} onClick={() => this.submitPassphrase()}>
|
||||||
isDisabled={!this.state.doPassphraseInputsMatch}
|
|
||||||
onClick={() => this.submitPassphrase()}
|
|
||||||
>
|
|
||||||
<FormattedMessage {...l10nMessages.TR_ENTER} />
|
<FormattedMessage {...l10nMessages.TR_ENTER} />
|
||||||
</Button>
|
</Button>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -24,6 +24,10 @@ const definedMessages: Messages = defineMessages({
|
|||||||
id: 'TR_PASSPHRASES_DO_NOT_MATCH',
|
id: 'TR_PASSPHRASES_DO_NOT_MATCH',
|
||||||
defaultMessage: 'Passphrases do not match!',
|
defaultMessage: 'Passphrases do not match!',
|
||||||
},
|
},
|
||||||
|
TR_PASSPHRASE_IS_TOO_LONG: {
|
||||||
|
id: 'TR_PASSPHRASE_IS_TOO_LONG',
|
||||||
|
defaultMessage: 'Passphrase is too long!',
|
||||||
|
},
|
||||||
TR_SHOW_PASSPHRASE: {
|
TR_SHOW_PASSPHRASE: {
|
||||||
id: 'TR_SHOW_PASSPHRASE',
|
id: 'TR_SHOW_PASSPHRASE',
|
||||||
defaultMessage: 'Show passphrase',
|
defaultMessage: 'Show passphrase',
|
||||||
|
@ -21,4 +21,14 @@ describe('format utils', () => {
|
|||||||
expect(utils.fromDecimalAmount('a', 'a')).toBe('0');
|
expect(utils.fromDecimalAmount('a', 'a')).toBe('0');
|
||||||
expect(utils.fromDecimalAmount('a', '1')).toBe('0');
|
expect(utils.fromDecimalAmount('a', '1')).toBe('0');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('byteLength', () => {
|
||||||
|
it('should return correct byte length for strings with special ASCII characters', () => {
|
||||||
|
expect(utils.byteLength('testString')).toEqual(10);
|
||||||
|
expect(utils.byteLength('~!@#$%^&*()_+{}|:?><')).toEqual(20);
|
||||||
|
expect(utils.byteLength('😀')).toEqual(4);
|
||||||
|
expect(utils.byteLength('ä')).toEqual(2);
|
||||||
|
expect(utils.byteLength('áľščť')).toEqual(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -27,3 +27,14 @@ export const fromDecimalAmount = (amount: string | number, decimals: number): st
|
|||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const byteLength = (text: string): number => {
|
||||||
|
// returns length of the text in bytes, 0 in case of error.
|
||||||
|
try {
|
||||||
|
// regexp is handling cases when encodeURI returns '%uXXXX' or %XX%XX
|
||||||
|
return encodeURI(text).split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./).length - 1;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user