/** * Validates the client challenge, and create the encryption backend for the channel from the * parameters sent by the client. * * @param clientChallenge The challenge from the client. * @return A response to be sent to the client. */ ServerResponse respond(ClientChallenge clientChallenge) throws GeneralSecurityException { SecretKeySpec authKey = generateKey(clientChallenge.kdf, clientChallenge.iterations, clientChallenge.nonce, clientChallenge.keyLength); initializeForAuth(clientChallenge.cipher, clientChallenge.nonce, authKey); byte[] challenge = validateChallenge(clientChallenge.nonce, clientChallenge.challenge); byte[] response = challenge(appId, clientChallenge.nonce, rawResponse(challenge)); byte[] sessionNonce = randomBytes(conf.encryptionKeyLength() / Byte.SIZE); byte[] inputIv = randomBytes(conf.ivLength()); byte[] outputIv = randomBytes(conf.ivLength()); SecretKeySpec sessionKey = generateKey(clientChallenge.kdf, clientChallenge.iterations, sessionNonce, clientChallenge.keyLength); this.sessionCipher = new TransportCipher(cryptoConf, clientChallenge.cipher, sessionKey, inputIv, outputIv); // Note the IVs are swapped in the response. return new ServerResponse(response, encrypt(sessionNonce), encrypt(outputIv), encrypt(inputIv)); }
/** * Validates an encrypted challenge as defined in the protocol, and returns the byte array * that corresponds to the actual challenge data. */ private byte[] validateChallenge(byte[] nonce, byte[] encryptedChallenge) throws GeneralSecurityException { byte[] challenge = decrypt(encryptedChallenge); checkSubArray(appId, challenge, 0); checkSubArray(nonce, challenge, appId.length); return Arrays.copyOfRange(challenge, appId.length + nonce.length, challenge.length); }
private byte[] decrypt(byte[] in) throws GeneralSecurityException { return doCipherOp(decryptor, in, false); }
@Test public void testAuthEngine() throws Exception { AuthEngine client = new AuthEngine("appId", "secret", conf); AuthEngine server = new AuthEngine("appId", "secret", conf); try { ClientChallenge clientChallenge = client.challenge(); ServerResponse serverResponse = server.respond(clientChallenge); client.validate(serverResponse); TransportCipher serverCipher = server.sessionCipher(); TransportCipher clientCipher = client.sessionCipher(); assertTrue(Arrays.equals(serverCipher.getInputIv(), clientCipher.getOutputIv())); assertTrue(Arrays.equals(serverCipher.getOutputIv(), clientCipher.getInputIv())); assertEquals(serverCipher.getKey(), clientCipher.getKey()); } finally { client.close(); server.close(); } }
@Test(expected = IllegalArgumentException.class) public void testWrongAppId() throws Exception { AuthEngine engine = new AuthEngine("appId", "secret", conf); ClientChallenge challenge = engine.challenge(); byte[] badChallenge = engine.challenge(new byte[] { 0x00 }, challenge.nonce, engine.rawResponse(engine.challenge)); engine.respond(new ClientChallenge(challenge.appId, challenge.kdf, challenge.iterations, challenge.cipher, challenge.keyLength, challenge.nonce, badChallenge)); }
private void doSparkAuth(TransportClient client, Channel channel) throws GeneralSecurityException, IOException { String secretKey = secretKeyHolder.getSecretKey(appId); try (AuthEngine engine = new AuthEngine(appId, secretKey, conf)) { ClientChallenge challenge = engine.challenge(); ByteBuf challengeData = Unpooled.buffer(challenge.encodedLength()); challenge.encode(challengeData); ByteBuffer responseData = client.sendRpcSync(challengeData.nioBuffer(), conf.authRTTimeoutMs()); ServerResponse response = ServerResponse.decodeMessage(responseData); engine.validate(response); engine.sessionCipher().addToChannel(channel); } }
"Trying to authenticate non-registered app %s.", challenge.appId); LOG.debug("Authenticating challenge for app {}.", challenge.appId); engine = new AuthEngine(challenge.appId, secret, conf); ServerResponse response = engine.respond(challenge); ByteBuf responseData = Unpooled.buffer(response.encodedLength()); response.encode(responseData); callback.onSuccess(responseData.nioBuffer()); engine.sessionCipher().addToChannel(channel); } catch (Exception e) { if (engine != null) { try { engine.close(); } catch (Exception e) { throw Throwables.propagate(e);
@Test public void testMismatchedSecret() throws Exception { AuthEngine client = new AuthEngine("appId", "secret", conf); AuthEngine server = new AuthEngine("appId", "different_secret", conf); ClientChallenge clientChallenge = client.challenge(); try { server.respond(clientChallenge); fail("Should have failed to validate response."); } catch (IllegalArgumentException e) { // Expected. } }
/** * Create the client challenge. * * @return A challenge to be sent the remote side. */ ClientChallenge challenge() throws GeneralSecurityException { this.authNonce = randomBytes(conf.encryptionKeyLength() / Byte.SIZE); SecretKeySpec authKey = generateKey(conf.keyFactoryAlgorithm(), conf.keyFactoryIterations(), authNonce, conf.encryptionKeyLength()); initializeForAuth(conf.cipherTransformation(), authNonce, authKey); this.challenge = randomBytes(conf.encryptionKeyLength() / Byte.SIZE); return new ClientChallenge(new String(appId, UTF_8), conf.keyFactoryAlgorithm(), conf.keyFactoryIterations(), conf.cipherTransformation(), conf.encryptionKeyLength(), authNonce, challenge(appId, authNonce, challenge)); }
/** * Validates the server response and initializes the cipher to use for the session. * * @param serverResponse The response from the server. */ void validate(ServerResponse serverResponse) throws GeneralSecurityException { byte[] response = validateChallenge(authNonce, serverResponse.response); byte[] expected = rawResponse(challenge); Preconditions.checkArgument(Arrays.equals(expected, response)); byte[] nonce = decrypt(serverResponse.nonce); byte[] inputIv = decrypt(serverResponse.inputIv); byte[] outputIv = decrypt(serverResponse.outputIv); SecretKeySpec sessionKey = generateKey(conf.keyFactoryAlgorithm(), conf.keyFactoryIterations(), nonce, conf.encryptionKeyLength()); this.sessionCipher = new TransportCipher(cryptoConf, conf.cipherTransformation(), sessionKey, inputIv, outputIv); }
@Test public void testAuthEngine() throws Exception { AuthEngine client = new AuthEngine("appId", "secret", conf); AuthEngine server = new AuthEngine("appId", "secret", conf); try { ClientChallenge clientChallenge = client.challenge(); ServerResponse serverResponse = server.respond(clientChallenge); client.validate(serverResponse); TransportCipher serverCipher = server.sessionCipher(); TransportCipher clientCipher = client.sessionCipher(); assertTrue(Arrays.equals(serverCipher.getInputIv(), clientCipher.getOutputIv())); assertTrue(Arrays.equals(serverCipher.getOutputIv(), clientCipher.getInputIv())); assertEquals(serverCipher.getKey(), clientCipher.getKey()); } finally { client.close(); server.close(); } }
@Test(expected = IllegalArgumentException.class) public void testWrongNonce() throws Exception { AuthEngine engine = new AuthEngine("appId", "secret", conf); ClientChallenge challenge = engine.challenge(); byte[] badChallenge = engine.challenge(challenge.appId.getBytes(UTF_8), new byte[] { 0x00 }, engine.rawResponse(engine.challenge)); engine.respond(new ClientChallenge(challenge.appId, challenge.kdf, challenge.iterations, challenge.cipher, challenge.keyLength, challenge.nonce, badChallenge)); }
private void doSparkAuth(TransportClient client, Channel channel) throws GeneralSecurityException, IOException { String secretKey = secretKeyHolder.getSecretKey(appId); try (AuthEngine engine = new AuthEngine(appId, secretKey, conf)) { ClientChallenge challenge = engine.challenge(); ByteBuf challengeData = Unpooled.buffer(challenge.encodedLength()); challenge.encode(challengeData); ByteBuffer responseData = client.sendRpcSync(challengeData.nioBuffer(), conf.authRTTimeoutMs()); ServerResponse response = ServerResponse.decodeMessage(responseData); engine.validate(response); engine.sessionCipher().addToChannel(channel); } }
"Trying to authenticate non-registered app %s.", challenge.appId); LOG.debug("Authenticating challenge for app {}.", challenge.appId); engine = new AuthEngine(challenge.appId, secret, conf); ServerResponse response = engine.respond(challenge); ByteBuf responseData = Unpooled.buffer(response.encodedLength()); response.encode(responseData); callback.onSuccess(responseData.nioBuffer()); engine.sessionCipher().addToChannel(channel); } catch (Exception e) { if (engine != null) { try { engine.close(); } catch (Exception e) { throw Throwables.propagate(e);
@Test public void testMismatchedSecret() throws Exception { AuthEngine client = new AuthEngine("appId", "secret", conf); AuthEngine server = new AuthEngine("appId", "different_secret", conf); ClientChallenge clientChallenge = client.challenge(); try { server.respond(clientChallenge); fail("Should have failed to validate response."); } catch (IllegalArgumentException e) { // Expected. } }
/** * Create the client challenge. * * @return A challenge to be sent the remote side. */ ClientChallenge challenge() throws GeneralSecurityException { this.authNonce = randomBytes(conf.encryptionKeyLength() / Byte.SIZE); SecretKeySpec authKey = generateKey(conf.keyFactoryAlgorithm(), conf.keyFactoryIterations(), authNonce, conf.encryptionKeyLength()); initializeForAuth(conf.cipherTransformation(), authNonce, authKey); this.challenge = randomBytes(conf.encryptionKeyLength() / Byte.SIZE); return new ClientChallenge(new String(appId, UTF_8), conf.keyFactoryAlgorithm(), conf.keyFactoryIterations(), conf.cipherTransformation(), conf.encryptionKeyLength(), authNonce, challenge(appId, authNonce, challenge)); }
/** * Validates the server response and initializes the cipher to use for the session. * * @param serverResponse The response from the server. */ void validate(ServerResponse serverResponse) throws GeneralSecurityException { byte[] response = validateChallenge(authNonce, serverResponse.response); byte[] expected = rawResponse(challenge); Preconditions.checkArgument(Arrays.equals(expected, response)); byte[] nonce = decrypt(serverResponse.nonce); byte[] inputIv = decrypt(serverResponse.inputIv); byte[] outputIv = decrypt(serverResponse.outputIv); SecretKeySpec sessionKey = generateKey(conf.keyFactoryAlgorithm(), conf.keyFactoryIterations(), nonce, conf.encryptionKeyLength()); this.sessionCipher = new TransportCipher(cryptoConf, conf.cipherTransformation(), sessionKey, inputIv, outputIv); }
/** * Validates the client challenge, and create the encryption backend for the channel from the * parameters sent by the client. * * @param clientChallenge The challenge from the client. * @return A response to be sent to the client. */ ServerResponse respond(ClientChallenge clientChallenge) throws GeneralSecurityException { SecretKeySpec authKey = generateKey(clientChallenge.kdf, clientChallenge.iterations, clientChallenge.nonce, clientChallenge.keyLength); initializeForAuth(clientChallenge.cipher, clientChallenge.nonce, authKey); byte[] challenge = validateChallenge(clientChallenge.nonce, clientChallenge.challenge); byte[] response = challenge(appId, clientChallenge.nonce, rawResponse(challenge)); byte[] sessionNonce = randomBytes(conf.encryptionKeyLength() / Byte.SIZE); byte[] inputIv = randomBytes(conf.ivLength()); byte[] outputIv = randomBytes(conf.ivLength()); SecretKeySpec sessionKey = generateKey(clientChallenge.kdf, clientChallenge.iterations, sessionNonce, clientChallenge.keyLength); this.sessionCipher = new TransportCipher(cryptoConf, clientChallenge.cipher, sessionKey, inputIv, outputIv); // Note the IVs are swapped in the response. return new ServerResponse(response, encrypt(sessionNonce), encrypt(outputIv), encrypt(inputIv)); }
@Test(expected = IllegalArgumentException.class) public void testWrongAppId() throws Exception { AuthEngine engine = new AuthEngine("appId", "secret", conf); ClientChallenge challenge = engine.challenge(); byte[] badChallenge = engine.challenge(new byte[] { 0x00 }, challenge.nonce, engine.rawResponse(engine.challenge)); engine.respond(new ClientChallenge(challenge.appId, challenge.kdf, challenge.iterations, challenge.cipher, challenge.keyLength, challenge.nonce, badChallenge)); }
private void doSparkAuth(TransportClient client, Channel channel) throws GeneralSecurityException, IOException { String secretKey = secretKeyHolder.getSecretKey(appId); try (AuthEngine engine = new AuthEngine(appId, secretKey, conf)) { ClientChallenge challenge = engine.challenge(); ByteBuf challengeData = Unpooled.buffer(challenge.encodedLength()); challenge.encode(challengeData); ByteBuffer responseData = client.sendRpcSync(challengeData.nioBuffer(), conf.authRTTimeoutMs()); ServerResponse response = ServerResponse.decodeMessage(responseData); engine.validate(response); engine.sessionCipher().addToChannel(channel); } }