diff --git a/package-lock.json b/package-lock.json index 54420c4..f34bcf5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "KeycardExit", "version": "0.0.1", "dependencies": { + "@noble/hashes": "^1.5.0", "@react-native-async-storage/async-storage": "^2.0.0", "react": "18.3.1", "react-native": "0.75.3", @@ -3111,6 +3112,18 @@ "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": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index 3e8c860..090ea31 100644 --- a/package.json +++ b/package.json @@ -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" }, "dependencies": { + "@noble/hashes": "^1.5.0", "@react-native-async-storage/async-storage": "^2.0.0", "react": "18.3.1", "react-native": "0.75.3", diff --git a/src/Main.tsx b/src/Main.tsx index 77ba145..bfb20e7 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -12,6 +12,8 @@ import NFCModal from './NFCModal'; //@ts-ignore import Keycard from "react-native-status-keycard"; import Dialpad from './components/Dialpad'; +import { sha256 } from '@noble/hashes/sha256'; +import { bytesToHex } from '@noble/hashes/utils'; enum Step { Discovery, @@ -25,6 +27,7 @@ enum Step { const Main = () => { const PIN_MAX_RETRY = 3; const WALLET_DERIVATION_PATH = "m/84'/0'/0'/0/0"; + const LOGIN_ENDPOINT = "https://exit.logos.co/keycard/auth"; const [isModalVisible, setIsModalVisible] = useState(false); const [step, setStep] = useState(Step.Discovery); @@ -36,8 +39,8 @@ const Main = () => { const mnemonicRef = useRef(""); const isListeningCard = useRef(false); const walletKey = useRef(""); - const sessionId = useRef("") - const challenge = useRef("") + const sessionRef = useRef("") + const challengeRef = useRef("") const getPairings = async () => { const pairingJSON = await AsyncStorage.getItem("pairings"); @@ -68,12 +71,29 @@ const Main = () => { 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 () => { if (!isListeningCard.current) { return; } var newPinCounter = pinCounter; + var loginReq = ""; try { const appInfo = await Keycard.getApplicationInfo(); @@ -108,6 +128,9 @@ const Main = () => { walletKey.current = await Keycard.exportKeyWithPath(pinRef.current, WALLET_DERIVATION_PATH); setStep(Step.Home); break; + case Step.Home: + loginReq = await loginRequest(); + break; case Step.FactoryReset: await Keycard.factoryReset(); setStep(Step.Discovery); @@ -141,6 +164,27 @@ const Main = () => { await Keycard.stopNFC(""); 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(() => { @@ -208,7 +252,9 @@ const Main = () => { } const login = (sessionId: string, challenge: string) => { - + sessionRef.current = sessionId; + challengeRef.current = challenge; + return connectCard(); } const cancel = () => { diff --git a/src/components/steps/HomeScreen.tsx b/src/components/steps/HomeScreen.tsx index dd12667..02ed5f5 100644 --- a/src/components/steps/HomeScreen.tsx +++ b/src/components/steps/HomeScreen.tsx @@ -23,9 +23,20 @@ const HomeScreen: FC = props => { const codeScanner = useCodeScanner({ codeTypes: ['qr'], onCodeScanned: (codes) => { - console.log(`Scanned ${codes.length} codes!`) - //TODO: implement - setStep(HomeSteps.Home); + if ((codes.length != 1) || !codes[0].value) { + return; + } + + try { + const payload = JSON.parse(codes[0].value); + + if (!payload["challenge"] || !payload["session-id"]) { + return; + } + + setStep(HomeSteps.Home); + onPressFunc(payload["session-id"], payload["challenge"]); + } catch(e) {} } });