PIN authentication error handling
This commit is contained in:
parent
de81d72e60
commit
e910e0b6c4
50
src/Main.tsx
50
src/Main.tsx
|
@ -6,6 +6,7 @@ import DiscoveryScreen from './components/steps/DiscoveryScreen';
|
|||
import InitializationScreen from './components/steps/InitializationScreen';
|
||||
import MnemonicScreen from './components/steps/MnemonicScreen';
|
||||
import AuthenticationScreen from './components/steps/AuthenticationScreen';
|
||||
import FactoryResetScreen from './components/steps/FactoryResetScreen';
|
||||
import NFCModal from './NFCModal';
|
||||
|
||||
//@ts-ignore
|
||||
|
@ -20,8 +21,11 @@ enum Step {
|
|||
}
|
||||
|
||||
const Main = () => {
|
||||
const PIN_MAX_RETRY = 3;
|
||||
|
||||
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
|
||||
const [step, setStep] = useState(Step.Discovery);
|
||||
const [pinCounter, setPinCounter] = useState(PIN_MAX_RETRY);
|
||||
|
||||
const didMount = useRef(false);
|
||||
const stepRef = useRef(step);
|
||||
|
@ -41,14 +45,18 @@ const Main = () => {
|
|||
}
|
||||
|
||||
const isCardLost = (err) => {
|
||||
return (err == "Tag was lost.") || err.includes("NFCError:100");
|
||||
return (err == "Tag was lost.") || /"NFCError:10[02]"/.test(err)
|
||||
}
|
||||
|
||||
const wrongPINCounter = (err) => {
|
||||
const matches = /Unexpected error SW, 0x63C(\d+)|wrongPIN\(retryCounter: (\d+)\)/.exec(err)
|
||||
const matches = /Unexpected error SW, 0x63C(\d)|wrongPIN\(retryCounter: (\d+)\)/.exec(err)
|
||||
|
||||
if (matches && matches.length == 2) {
|
||||
return parseInt(matches[1])
|
||||
if (matches) {
|
||||
if (matches[1] !== undefined) {
|
||||
return parseInt(matches[1])
|
||||
} else {
|
||||
return parseInt(matches[2])
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -59,12 +67,18 @@ const Main = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
var newPinCounter = pinCounter;
|
||||
|
||||
try {
|
||||
const appInfo = await Keycard.getApplicationInfo();
|
||||
if (appInfo["new-pairing"]) {
|
||||
await addPairing(appInfo["instance-uid"], appInfo["new-pairing"]);
|
||||
}
|
||||
|
||||
if (appInfo["pin-retry-counter"] !== null) {
|
||||
newPinCounter = appInfo["pin-retry-counter"];
|
||||
}
|
||||
|
||||
switch (stepRef.current) {
|
||||
case Step.Discovery:
|
||||
if (appInfo["initialized?"]) {
|
||||
|
@ -105,14 +119,20 @@ const Main = () => {
|
|||
const pinRetryCounter = wrongPINCounter(err.message);
|
||||
|
||||
if (pinRetryCounter !== null) {
|
||||
//TODO: better handling
|
||||
pinRef.current = ""
|
||||
newPinCounter = pinRetryCounter;
|
||||
console.log("wrong PIN. Retry counter: " + pinRetryCounter);
|
||||
return;
|
||||
} else {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
if (newPinCounter == 0) {
|
||||
setStep(Step.FactoryReset);
|
||||
}
|
||||
|
||||
setPinCounter(newPinCounter);
|
||||
|
||||
await Keycard.stopNFC("");
|
||||
setIsModalVisible(false);
|
||||
}
|
||||
|
@ -155,10 +175,9 @@ const Main = () => {
|
|||
setIsModalVisible(true);
|
||||
}
|
||||
|
||||
const factoryResetCard = async () => {
|
||||
const startFactoryReset = async () => {
|
||||
stepRef.current = Step.FactoryReset;
|
||||
setStep(Step.FactoryReset);
|
||||
return connectCard();
|
||||
}
|
||||
|
||||
const initPin = async (p: string) => {
|
||||
|
@ -180,12 +199,17 @@ const Main = () => {
|
|||
setStep(Step.Discovery);
|
||||
}
|
||||
|
||||
const pinDisplayCounter = () => {
|
||||
return pinCounter == PIN_MAX_RETRY ? -1 : pinCounter;
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
{step == Step.Discovery && <DiscoveryScreen onPressFunc={connectCard} onFactoryResetFunc={factoryResetCard}></DiscoveryScreen>}
|
||||
{step == Step.Discovery && <DiscoveryScreen onPressFunc={connectCard} onFactoryResetFunc={startFactoryReset}></DiscoveryScreen>}
|
||||
{step == Step.Initialization && <InitializationScreen onPressFunc={initPin} onCancelFunc={cancel}></InitializationScreen>}
|
||||
{step == Step.Loading && <MnemonicScreen onPressFunc={loadMnemonic} pinRequired={pinRef.current ? false : true} onCancelFunc={cancel}></MnemonicScreen>}
|
||||
{step == Step.Authentication && <AuthenticationScreen onPressFunc={() => {} } onCancelFunc={cancel}></AuthenticationScreen>}
|
||||
{step == Step.Loading && <MnemonicScreen pinRequired={pinRef.current ? false : true} pinRetryCounter={pinDisplayCounter()} onPressFunc={loadMnemonic} onCancelFunc={cancel}></MnemonicScreen>}
|
||||
{step == Step.Authentication && <AuthenticationScreen pinRetryCounter={pinDisplayCounter()} onPressFunc={() => {} } onCancelFunc={cancel}></AuthenticationScreen>}
|
||||
{step == Step.FactoryReset && <FactoryResetScreen pinRetryCounter={pinDisplayCounter()} onPressFunc={connectCard} onCancelFunc={cancel}></FactoryResetScreen>}
|
||||
<NFCModal isVisible={isModalVisible} onChangeFunc={setIsModalVisible}></NFCModal>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
|
|
@ -7,17 +7,19 @@ import DialpadPin from "./DialpadPin";
|
|||
const { width } = Dimensions.get("window");
|
||||
|
||||
type DialpadProps = {
|
||||
pinRetryCounter: number;
|
||||
prompt: string;
|
||||
onCancelFunc: () => void;
|
||||
onNextFunc: (p?: any) => boolean;
|
||||
};
|
||||
|
||||
const Dialpad: FC<DialpadProps> = props => {
|
||||
const {prompt, onNextFunc, onCancelFunc} = props;
|
||||
const {pinRetryCounter, prompt, onNextFunc, onCancelFunc} = props;
|
||||
const dialPadContent = [1, 2, 3, 4, 5, 6, 7, 8, 9, "", 0, "X"];
|
||||
const dialPadSize = width * 0.2;
|
||||
const dialPadTextSize = dialPadSize * 0.36;
|
||||
const [code, setCode] = useState([]);
|
||||
const [wrongRepeat, setWrongRepeat] = useState(false);
|
||||
const pinLength = 6;
|
||||
const pinContainerSize = width / 2;
|
||||
const pinSize = (pinContainerSize / pinLength) + 8;
|
||||
|
@ -34,9 +36,8 @@ const Dialpad: FC<DialpadProps> = props => {
|
|||
}
|
||||
|
||||
const onNext = () => {
|
||||
if (!onNextFunc(code.join(''))) {
|
||||
setCode([]);
|
||||
}
|
||||
setWrongRepeat(!onNextFunc(code.join('')));
|
||||
setCode([]);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -44,6 +45,8 @@ const Dialpad: FC<DialpadProps> = props => {
|
|||
<View style={styles.textContainer}>
|
||||
<Text style={styles.pinText}>{prompt}</Text>
|
||||
<Text style={styles.pinSubText}>Enter your secure six-digit code</Text>
|
||||
{pinRetryCounter >= 0 && <Text style={styles.pinAttempts}>Remaining attempts: {pinRetryCounter}</Text>}
|
||||
{wrongRepeat && <Text style={styles.pinAttempts}>The PINs do not match</Text>}
|
||||
<DialpadPin pinLength={pinLength} pinSize={pinSize} code={code} dialPadContent={dialPadContent} />
|
||||
<DialpadKeypad dialPadContent={dialPadContent} dialPadSize={dialPadSize} dialPadTextSize={dialPadTextSize} updateCodeFunc={updateCode} code={code}/>
|
||||
</View>
|
||||
|
@ -76,6 +79,14 @@ const styles = StyleSheet.create({
|
|||
color: "white",
|
||||
marginVertical: 30,
|
||||
},
|
||||
|
||||
pinAttempts: {
|
||||
fontSize: 18,
|
||||
fontFamily: 'Inconsolata Regular',
|
||||
color: "white",
|
||||
marginTop: -10,
|
||||
marginBottom: 30
|
||||
},
|
||||
btnContainer: {
|
||||
flexDirection: 'row',
|
||||
width: '74%',
|
||||
|
|
|
@ -3,12 +3,13 @@ import { StyleSheet, Text, View } from "react-native";
|
|||
import Button from "../Button";
|
||||
|
||||
type AuthenticationScreenProps = {
|
||||
pinRetryCounter: number;
|
||||
onPressFunc: () => void;
|
||||
onCancelFunc: () => void;
|
||||
};
|
||||
|
||||
const AuthenticationScreen: FC<AuthenticationScreenProps> = props => {
|
||||
const {onPressFunc, onCancelFunc} = props;
|
||||
const {pinRetryCounter, onPressFunc, onCancelFunc} = props;
|
||||
|
||||
return (
|
||||
<View>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import {FC } from "react";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import Button from "../Button";
|
||||
|
||||
type FactoryResetScreenProps = {
|
||||
pinRetryCounter: number;
|
||||
onPressFunc: () => void;
|
||||
onCancelFunc: () => void;
|
||||
};
|
||||
|
||||
const FactoryResetScreen: FC<FactoryResetScreenProps> = props => {
|
||||
const {pinRetryCounter, onPressFunc, onCancelFunc} = props;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View>
|
||||
<Text style={styles.heading}>Factory reset</Text>
|
||||
<Text style={styles.prompt}>This will remove the keys from your card. Are you sure?</Text>
|
||||
<Button label="Next" disabled={false} btnColor="#199515" btnBorderColor="#199515" btnFontSize={17} btnBorderWidth={1} btnWidth="100%" onChangeFunc={onPressFunc} btnJustifyContent='center'></Button>
|
||||
<Button label="Cancel" disabled={false} btnColor="#199515" btnBorderColor="#199515" btnFontSize={17} btnBorderWidth={1} btnWidth="100%" onChangeFunc={onCancelFunc} btnJustifyContent='center'></Button>
|
||||
</View>
|
||||
</View>
|
||||
)};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
heading: {
|
||||
textAlign: 'center',
|
||||
fontSize: 30,
|
||||
fontFamily: 'Inconsolata Medium',
|
||||
color: '#199515',
|
||||
marginTop: '50%'
|
||||
},
|
||||
prompt: {
|
||||
textAlign: 'center',
|
||||
fontSize: 18,
|
||||
fontFamily: 'Inconsolata Regular',
|
||||
color: 'white',
|
||||
marginTop: '5%'
|
||||
}
|
||||
});
|
||||
|
||||
export default FactoryResetScreen;
|
|
@ -24,23 +24,19 @@ const InitializationScreen: FC<InitializationScreenProps> = props => {
|
|||
return true;
|
||||
}
|
||||
|
||||
const isSamePin = (p: string) => {
|
||||
return pin === p;
|
||||
}
|
||||
|
||||
const submitPin = (p: string) => {
|
||||
if(!isSamePin(p)) {
|
||||
return false;
|
||||
if(pin == p) {
|
||||
onPressFunc(pin);
|
||||
return true;
|
||||
}
|
||||
|
||||
onPressFunc(pin);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
{ step == PinSteps.InsertPin && <Dialpad prompt={"Choose PIN"} onCancelFunc={onCancelFunc} onNextFunc={insertPin}></Dialpad> }
|
||||
{ step == PinSteps.RepeatPin && <Dialpad prompt={"Repeat PIN"} onCancelFunc={() => setStep(PinSteps.InsertPin)} onNextFunc={submitPin}></Dialpad> }
|
||||
{ step == PinSteps.InsertPin && <Dialpad pinRetryCounter={-1} prompt={"Choose PIN"} onCancelFunc={onCancelFunc} onNextFunc={insertPin}></Dialpad> }
|
||||
{ step == PinSteps.RepeatPin && <Dialpad pinRetryCounter={-1} prompt={"Repeat PIN"} onCancelFunc={() => setStep(PinSteps.InsertPin)} onNextFunc={submitPin}></Dialpad> }
|
||||
</View>
|
||||
)};
|
||||
|
||||
|
|
|
@ -11,12 +11,13 @@ enum LoadMnemonicSteps {
|
|||
|
||||
type MnemonicScreenProps = {
|
||||
pinRequired: boolean;
|
||||
pinRetryCounter: number;
|
||||
onPressFunc: (mn: string, pin?: string) => void;
|
||||
onCancelFunc: () => void;
|
||||
};
|
||||
|
||||
const MnemonicScreen: FC<MnemonicScreenProps> = props => {
|
||||
const {pinRequired, onPressFunc, onCancelFunc} = props;
|
||||
const {pinRequired, pinRetryCounter, onPressFunc, onCancelFunc} = props;
|
||||
const [mnemonic, setMnemonic] = useState('');
|
||||
const [errMessage, setErrMessage] = useState('');
|
||||
const [step, setStep] = useState(LoadMnemonicSteps.InsertMnemonic);
|
||||
|
@ -63,7 +64,7 @@ const MnemonicScreen: FC<MnemonicScreenProps> = props => {
|
|||
<Text style={styles.errorMessage}>{errMessage}</Text>
|
||||
</View>
|
||||
}
|
||||
{ step == LoadMnemonicSteps.InsertPin && <Dialpad prompt={"Enter PIN"} onCancelFunc={() => setStep(LoadMnemonicSteps.InsertMnemonic)} onNextFunc={submitPin}></Dialpad>}
|
||||
{ step == LoadMnemonicSteps.InsertPin && <Dialpad pinRetryCounter={pinRetryCounter} prompt={"Enter PIN"} onCancelFunc={() => setStep(LoadMnemonicSteps.InsertMnemonic)} onNextFunc={submitPin}></Dialpad>}
|
||||
</View>
|
||||
)};
|
||||
|
||||
|
|
Loading…
Reference in New Issue