@Override public U2fSignRequest getSignRequest(String accountName, String appId) throws U2FException { Log.info(">> getSignRequest " + accountName); List<SecurityKeyData> securityKeyDataList = dataStore.getSecurityKeyData(accountName); byte[] challenge = challengeGenerator.generateChallenge(accountName); String challengeBase64 = Base64.encodeBase64URLSafeString(challenge); ImmutableList.Builder<RegisteredKey> registeredKeys = ImmutableList.builder(); Log.info(" challenge: " + Hex.encodeHexString(challenge)); for (SecurityKeyData securityKeyData : securityKeyDataList) { SignSessionData sessionData = new SignSessionData(accountName, appId, challenge, securityKeyData.getPublicKey()); String sessionId = dataStore.storeSessionData(sessionData); byte[] keyHandle = securityKeyData.getKeyHandle(); List<Transports> transports = securityKeyData.getTransports(); Log.info("-- Output --"); Log.info(" sessionId: " + sessionId); Log.info(" keyHandle: " + Hex.encodeHexString(keyHandle)); String keyHandleBase64 = Base64.encodeBase64URLSafeString(keyHandle); Log.info("<< getRegisteredKey " + accountName); registeredKeys.add( new RegisteredKey(U2FConsts.U2F_V2, keyHandleBase64, transports, appId, sessionId)); } return new U2fSignRequest(challengeBase64, registeredKeys.build()); }
@Override public SecurityKeyData processSignResponse(SignResponse signResponse) throws U2FException { Log.info(">> processSignResponse"); String sessionId = signResponse.getSessionId(); String browserDataBase64 = signResponse.getClientData(); String rawSignDataBase64 = signResponse.getSignatureData(); SignSessionData sessionData = dataStore.getSignSessionData(sessionId); if (sessionData == null) { throw new U2FException("Unknown session_id"); } String appId = sessionData.getAppId(); SecurityKeyData securityKeyData = null; for (SecurityKeyData temp : dataStore.getSecurityKeyData(sessionData.getAccountName())) { if (Arrays.equals(sessionData.getPublicKey(), temp.getPublicKey())) { securityKeyData = temp; break; } } if (securityKeyData == null) { throw new U2FException("No security keys registered for this user"); } String browserData = new String(Base64.decodeBase64(browserDataBase64)); byte[] rawSignData = Base64.decodeBase64(rawSignDataBase64); Log.info("-- Input --"); Log.info(" sessionId: " + sessionId); Log.info(" publicKey: " + Hex.encodeHexString(securityKeyData.getPublicKey())); Log.info(" challenge: " + Hex.encodeHexString(sessionData.getChallenge())); Log.info(" accountName: " + sessionData.getAccountName()); Log.info(" browserData: " + browserData); Log.info(" rawSignData: " + Hex.encodeHexString(rawSignData)); verifyBrowserData( new JsonParser().parse(browserData), "navigator.id.getAssertion", sessionData); AuthenticateResponse authenticateResponse = RawMessageCodec.decodeAuthenticateResponse(rawSignData); byte userPresence = authenticateResponse.getUserPresence(); int counter = authenticateResponse.getCounter(); byte[] signature = authenticateResponse.getSignature(); Log.info("-- Parsed rawSignData --"); Log.info(" userPresence: " + Integer.toHexString(userPresence & 0xFF)); Log.info(" counter: " + counter); Log.info(" signature: " + Hex.encodeHexString(signature)); if ((userPresence & UserPresenceVerifier.USER_PRESENT_FLAG) == 0) { throw new U2FException("User presence invalid during authentication"); } if (counter <= securityKeyData.getCounter()) { throw new U2FException("Counter value smaller than expected!"); } byte[] appIdSha256 = cryto.computeSha256(appId.getBytes()); byte[] browserDataSha256 = cryto.computeSha256(browserData.getBytes()); byte[] signedBytes = RawMessageCodec.encodeAuthenticateSignedBytes( appIdSha256, userPresence, counter, browserDataSha256); Log.info("Verifying signature of bytes " + Hex.encodeHexString(signedBytes)); if (!cryto.verifySignature( cryto.decodePublicKey(securityKeyData.getPublicKey()), signedBytes, signature)) { throw new U2FException("Signature is invalid"); } dataStore.updateSecurityKeyCounter( sessionData.getAccountName(), securityKeyData.getPublicKey(), counter); Log.info("<< processSignResponse"); return securityKeyData; }