add auth
This commit is contained in:
parent
349b817fb9
commit
b542c32e6d
|
@ -8,6 +8,7 @@
|
||||||
"name": "KeycardExit",
|
"name": "KeycardExit",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@noble/hashes": "^1.5.0",
|
||||||
"@react-native-async-storage/async-storage": "^2.0.0",
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-native": "0.75.3",
|
"react-native": "0.75.3",
|
||||||
|
@ -3111,6 +3112,18 @@
|
||||||
"eslint-scope": "5.1.1"
|
"eslint-scope": "5.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@noble/hashes": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.21.3 || >=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"android-build-dev": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res && cd android && gradlew assembleDebug"
|
"android-build-dev": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res && cd android && gradlew assembleDebug"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@noble/hashes": "^1.5.0",
|
||||||
"@react-native-async-storage/async-storage": "^2.0.0",
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-native": "0.75.3",
|
"react-native": "0.75.3",
|
||||||
|
|
52
src/Main.tsx
52
src/Main.tsx
|
@ -12,6 +12,8 @@ import NFCModal from './NFCModal';
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import Keycard from "react-native-status-keycard";
|
import Keycard from "react-native-status-keycard";
|
||||||
import Dialpad from './components/Dialpad';
|
import Dialpad from './components/Dialpad';
|
||||||
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { bytesToHex } from '@noble/hashes/utils';
|
||||||
|
|
||||||
enum Step {
|
enum Step {
|
||||||
Discovery,
|
Discovery,
|
||||||
|
@ -25,6 +27,7 @@ enum Step {
|
||||||
const Main = () => {
|
const Main = () => {
|
||||||
const PIN_MAX_RETRY = 3;
|
const PIN_MAX_RETRY = 3;
|
||||||
const WALLET_DERIVATION_PATH = "m/84'/0'/0'/0/0";
|
const WALLET_DERIVATION_PATH = "m/84'/0'/0'/0/0";
|
||||||
|
const LOGIN_ENDPOINT = "https://exit.logos.co/keycard/auth";
|
||||||
|
|
||||||
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
|
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
|
||||||
const [step, setStep] = useState(Step.Discovery);
|
const [step, setStep] = useState(Step.Discovery);
|
||||||
|
@ -36,8 +39,8 @@ const Main = () => {
|
||||||
const mnemonicRef = useRef("");
|
const mnemonicRef = useRef("");
|
||||||
const isListeningCard = useRef(false);
|
const isListeningCard = useRef(false);
|
||||||
const walletKey = useRef("");
|
const walletKey = useRef("");
|
||||||
const sessionId = useRef("")
|
const sessionRef = useRef("")
|
||||||
const challenge = useRef("")
|
const challengeRef = useRef("")
|
||||||
|
|
||||||
const getPairings = async () => {
|
const getPairings = async () => {
|
||||||
const pairingJSON = await AsyncStorage.getItem("pairings");
|
const pairingJSON = await AsyncStorage.getItem("pairings");
|
||||||
|
@ -68,12 +71,29 @@ const Main = () => {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loginRequest = async () => {
|
||||||
|
var req = {'session-id': sessionRef.current};
|
||||||
|
|
||||||
|
const identChallenge = bytesToHex(sha256('Keycard auth' + challengeRef.current));
|
||||||
|
const data = await Keycard.verifyCard(identChallenge);
|
||||||
|
req['keycard-auth'] = data['tlv-data'];
|
||||||
|
|
||||||
|
const walletChallenge = bytesToHex(sha256('Wallet auth' + challengeRef.current));
|
||||||
|
req['wallet-auth'] = await Keycard.signWithPath(pinRef.current, WALLET_DERIVATION_PATH, walletChallenge);
|
||||||
|
|
||||||
|
challengeRef.current = "";
|
||||||
|
sessionRef.current = "";
|
||||||
|
|
||||||
|
return JSON.stringify(req);
|
||||||
|
}
|
||||||
|
|
||||||
const keycardConnectHandler = async () => {
|
const keycardConnectHandler = async () => {
|
||||||
if (!isListeningCard.current) {
|
if (!isListeningCard.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newPinCounter = pinCounter;
|
var newPinCounter = pinCounter;
|
||||||
|
var loginReq = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const appInfo = await Keycard.getApplicationInfo();
|
const appInfo = await Keycard.getApplicationInfo();
|
||||||
|
@ -108,6 +128,9 @@ const Main = () => {
|
||||||
walletKey.current = await Keycard.exportKeyWithPath(pinRef.current, WALLET_DERIVATION_PATH);
|
walletKey.current = await Keycard.exportKeyWithPath(pinRef.current, WALLET_DERIVATION_PATH);
|
||||||
setStep(Step.Home);
|
setStep(Step.Home);
|
||||||
break;
|
break;
|
||||||
|
case Step.Home:
|
||||||
|
loginReq = await loginRequest();
|
||||||
|
break;
|
||||||
case Step.FactoryReset:
|
case Step.FactoryReset:
|
||||||
await Keycard.factoryReset();
|
await Keycard.factoryReset();
|
||||||
setStep(Step.Discovery);
|
setStep(Step.Discovery);
|
||||||
|
@ -141,6 +164,27 @@ const Main = () => {
|
||||||
|
|
||||||
await Keycard.stopNFC("");
|
await Keycard.stopNFC("");
|
||||||
setIsModalVisible(false);
|
setIsModalVisible(false);
|
||||||
|
|
||||||
|
if (loginReq) {
|
||||||
|
console.log(loginReq);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch(LOGIN_ENDPOINT, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
||||||
|
body: loginReq,
|
||||||
|
});
|
||||||
|
|
||||||
|
const respJson = resp.json();
|
||||||
|
if (respJson['error']) {
|
||||||
|
//TODO: handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: handle success
|
||||||
|
} catch (e) {
|
||||||
|
//TODO: handle error
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -208,7 +252,9 @@ const Main = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const login = (sessionId: string, challenge: string) => {
|
const login = (sessionId: string, challenge: string) => {
|
||||||
|
sessionRef.current = sessionId;
|
||||||
|
challengeRef.current = challenge;
|
||||||
|
return connectCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
|
|
|
@ -23,9 +23,20 @@ const HomeScreen: FC<HomeScreenProps> = props => {
|
||||||
const codeScanner = useCodeScanner({
|
const codeScanner = useCodeScanner({
|
||||||
codeTypes: ['qr'],
|
codeTypes: ['qr'],
|
||||||
onCodeScanned: (codes) => {
|
onCodeScanned: (codes) => {
|
||||||
console.log(`Scanned ${codes.length} codes!`)
|
if ((codes.length != 1) || !codes[0].value) {
|
||||||
//TODO: implement
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = JSON.parse(codes[0].value);
|
||||||
|
|
||||||
|
if (!payload["challenge"] || !payload["session-id"]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setStep(HomeSteps.Home);
|
setStep(HomeSteps.Home);
|
||||||
|
onPressFunc(payload["session-id"], payload["challenge"]);
|
||||||
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue