1
0
mirror of https://github.com/trezor/trezor-wallet synced 2024-11-27 10:48:22 +00:00

Feature/limit passphrase length to 50 bytes (#542)

Feature/limit passphrase length to 50 bytes
This commit is contained in:
martin 2019-05-02 11:49:50 +02:00 committed by GitHub
commit f7a8fbeb3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 10 deletions

View File

@ -9,6 +9,7 @@ __changed__
- static (without animation) active tab indicator
- input validation - mandatory leading 0 for float numbers
- regexps refactored to functions, added unit tests
- limit passphrase length to 50 bytes
__removed__
- Text "already used" from token select in case of already added tokens

View File

@ -2,6 +2,7 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { byteLength } from 'utils/formatUtils';
import { FormattedMessage } from 'react-intl';
import { Button, Input, Checkbox, P, H5, colors } from 'trezor-ui-components';
import { FONT_SIZE } from 'config/variables';
@ -24,6 +25,7 @@ type State = {
passphraseCheckInputValue: string,
doPassphraseInputsMatch: boolean,
isPassphraseHidden: boolean,
byteLength: number,
};
const Wrapper = styled.div`
@ -91,6 +93,7 @@ class Passphrase extends PureComponent<Props, State> {
passphraseCheckInputValue: '',
doPassphraseInputsMatch: true,
isPassphraseHidden: true,
byteLength: 0,
};
}
@ -117,6 +120,9 @@ class Passphrase extends PureComponent<Props, State> {
let doPassphraseInputsMatch = false;
if (inputName === 'passphraseInputValue') {
this.setState({
byteLength: byteLength(inputValue),
});
// If passphrase is not hidden the second input should get filled automatically
// and should be disabled
if (this.state.isPassphraseHidden) {
@ -181,13 +187,28 @@ class Passphrase extends PureComponent<Props, State> {
handleKeyPress(event: KeyboardEvent) {
if (event.key === 'Enter') {
event.preventDefault();
if (this.state.doPassphraseInputsMatch) {
if (this.state.doPassphraseInputsMatch && this.state.byteLength <= 50) {
this.submitPassphrase();
}
}
}
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 (
<Wrapper>
<H5>
@ -236,11 +257,7 @@ class Passphrase extends PureComponent<Props, State> {
/>
</Row>
)}
{!this.state.doPassphraseInputsMatch && (
<PassphraseError>
<FormattedMessage {...l10nMessages.TR_PASSPHRASES_DO_NOT_MATCH} />
</PassphraseError>
)}
{error}
<Row>
<Checkbox
isChecked={!this.state.isPassphraseHidden}
@ -250,10 +267,7 @@ class Passphrase extends PureComponent<Props, State> {
</Checkbox>
</Row>
<Row>
<Button
isDisabled={!this.state.doPassphraseInputsMatch}
onClick={() => this.submitPassphrase()}
>
<Button isDisabled={!!error} onClick={() => this.submitPassphrase()}>
<FormattedMessage {...l10nMessages.TR_ENTER} />
</Button>
</Row>

View File

@ -24,6 +24,10 @@ const definedMessages: Messages = defineMessages({
id: 'TR_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: {
id: 'TR_SHOW_PASSPHRASE',
defaultMessage: 'Show passphrase',

View File

@ -21,4 +21,14 @@ describe('format utils', () => {
expect(utils.fromDecimalAmount('a', 'a')).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);
});
});
});

View File

@ -27,3 +27,14 @@ export const fromDecimalAmount = (amount: string | number, decimals: number): st
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;
}
};