@Override public SecurityKeyData processRegistrationResponse( RegistrationResponse registrationResponse, long currentTimeInMillis) throws U2FException { Log.info(">> processRegistrationResponse"); String sessionId = registrationResponse.getSessionId(); String clientDataBase64 = registrationResponse.getClientData(); String rawRegistrationDataBase64 = registrationResponse.getRegistrationData(); Log.info(">> rawRegistrationDataBase64: " + rawRegistrationDataBase64); EnrollSessionData sessionData = dataStore.getEnrollSessionData(sessionId); if (sessionData == null) { throw new U2FException("Unknown session_id"); } String appId = sessionData.getAppId(); String clientData = new String(Base64.decodeBase64(clientDataBase64)); byte[] rawRegistrationData = Base64.decodeBase64(rawRegistrationDataBase64); Log.info("-- Input --"); Log.info(" sessionId: " + sessionId); Log.info(" challenge: " + Hex.encodeHexString(sessionData.getChallenge())); Log.info(" accountName: " + sessionData.getAccountName()); Log.info(" clientData: " + clientData); Log.info(" rawRegistrationData: " + Hex.encodeHexString(rawRegistrationData)); RegisterResponse registerResponse = RawMessageCodec.decodeRegisterResponse(rawRegistrationData); byte[] userPublicKey = registerResponse.getUserPublicKey(); byte[] keyHandle = registerResponse.getKeyHandle(); X509Certificate attestationCertificate = registerResponse.getAttestationCertificate(); byte[] signature = registerResponse.getSignature(); List<Transports> transports = null; try { transports = U2fAttestation.Parse(attestationCertificate).getTransports(); } catch (CertificateParsingException e) { Log.warning("Could not parse transports extension " + e.getMessage()); } Log.info("-- Parsed rawRegistrationResponse --"); Log.info(" userPublicKey: " + Hex.encodeHexString(userPublicKey)); Log.info(" keyHandle: " + Hex.encodeHexString(keyHandle)); Log.info(" attestationCertificate: " + attestationCertificate.toString()); Log.info(" transports: " + transports); try { Log.info( " attestationCertificate bytes: " + Hex.encodeHexString(attestationCertificate.getEncoded())); } catch (CertificateEncodingException e) { throw new U2FException("Cannot encode certificate", e); } Log.info(" signature: " + Hex.encodeHexString(signature)); byte[] appIdSha256 = cryto.computeSha256(appId.getBytes()); byte[] clientDataSha256 = cryto.computeSha256(clientData.getBytes()); byte[] signedBytes = RawMessageCodec.encodeRegistrationSignedBytes( appIdSha256, clientDataSha256, keyHandle, userPublicKey); Set<X509Certificate> trustedCertificates = dataStore.getTrustedCertificates(); if (!trustedCertificates.contains(attestationCertificate)) { Log.warning("attestion cert is not trusted"); } verifyBrowserData( new JsonParser().parse(clientData), "navigator.id.finishEnrollment", sessionData); Log.info("Verifying signature of bytes " + Hex.encodeHexString(signedBytes)); if (!cryto.verifySignature(attestationCertificate, signedBytes, signature)) { throw new U2FException("Signature is invalid"); } // The first time we create the SecurityKeyData, we set the counter value to 0. // We don't actually know what the counter value of the real device is - but it will // be something bigger (or equal) to 0, so subsequent signatures will check out ok. SecurityKeyData securityKeyData = new SecurityKeyData( currentTimeInMillis, transports, keyHandle, userPublicKey, attestationCertificate, /* initial counter value */ 0); dataStore.addSecurityKeyData(sessionData.getAccountName(), securityKeyData); Log.info("<< processRegistrationResponse"); return securityKeyData; }
@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; }