/// <summary> /// This method asks the server for a list of identifiers paired with method /// names and -parameter types. This is used when invoking methods server side. /// </summary> protected override void SyncInterface(Type serviceType, string username = null, string password = null) { if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) { var sw = Stopwatch.StartNew(); _logger.Debug("Zk authentiation started for: {0}, {1}", username, password); //do zk protocol authentication var sr = new ZkProtocol(); // Step 1. Client sends username and ephemeral hash of random number. var aRand = sr.CryptRand(); var aClientEphemeral = sr.GetClientEphemeralA(aRand); // send username and aClientEphemeral to server _binWriter.Write((int)MessageType.ZkInitiate); _binWriter.Write(username); _logger.Debug("username sent to server: {0}", username); _binWriter.Write(aClientEphemeral); //always 32 bytes _logger.Debug("ClientEphemeral (A) sent to server: {0}", Convert.ToBase64String(aClientEphemeral)); // get response from server var userFound = _binReader.ReadBoolean(); if (!userFound) { _logger.Debug("User not found. InvalidCredentialException thrown."); throw new InvalidCredentialException("authentication failed"); } var salt = _binReader.ReadBytes(32); _logger.Debug("Salt received from server: {0}", Convert.ToBase64String(salt)); var bServerEphemeral = _binReader.ReadBytes(32); _logger.Debug("ServerEphemeral (B) received from server: {0}", Convert.ToBase64String(bServerEphemeral)); // Step 3. Client and server calculate random scramble of ephemeral hash values exchanged. var clientScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral); // Step 4. Client computes session key var clientSessionKey = sr.ClientComputeSessionKey(salt, username, password, aClientEphemeral, bServerEphemeral, clientScramble); // Step 6. Client creates hash of session key and sends to server. Server creates same key and verifies. var clientSessionHash = sr.ClientCreateSessionHash(username, salt, aClientEphemeral, bServerEphemeral, clientSessionKey); // send to server and server verifies _binWriter.Write((int)MessageType.ZkProof); _binWriter.Write(clientSessionHash); //always 32 bytes _logger.Debug("ClientSessionKey Hash sent to server: {0}", Convert.ToBase64String(clientSessionHash)); // get response var serverVerified = _binReader.ReadBoolean(); if (!serverVerified) { _logger.Debug("Server verification failed. InvalidCredentialException thrown."); throw new InvalidCredentialException("authentication failed"); } var serverSessionHash = _binReader.ReadBytes(32); var clientServerSessionHash = sr.ServerCreateSessionHash(aClientEphemeral, clientSessionHash, clientSessionKey); if (!serverSessionHash.IsEqualTo(clientServerSessionHash)) { _logger.Debug("Server hash mismatch. InvalidCredentialException thrown. Has received: {0}", Convert.ToBase64String(serverSessionHash)); throw new InvalidCredentialException("authentication failed"); } _logger.Debug("Server Hash match. Received from server: {0}", Convert.ToBase64String(serverSessionHash)); _zkCrypto = new ZkCrypto(clientSessionKey, clientScramble); _logger.Debug("Zk authentiation completed successfully."); sw.Stop(); _stats.Log("ZkAuthentication", sw.ElapsedMilliseconds); } if (!SyncInfoCache.TryGetValue(serviceType, out _syncInfo)) { //write the message type _binWriter.Write((int)MessageType.SyncInterface); if (null != _zkCrypto) { //sync interface with encryption var assemName = serviceType.ToConfigName(); var assemblyNameEncrypted = _zkCrypto.Encrypt(assemName.ConvertToBytes()); _binWriter.Write(assemblyNameEncrypted.Length); _binWriter.Write(assemblyNameEncrypted); } else { _binWriter.Write(serviceType.ToConfigName()); } //read sync data var len = _binReader.ReadInt32(); //len is zero when AssemblyQualifiedName not same version or not found if (len == 0) { throw new TypeAccessException("SyncInterface failed. Type or version of type unknown."); } var bytes = _binReader.ReadBytes(len); if (null != _zkCrypto) { _logger.Debug("Encrypted data received from server: {0}", Convert.ToBase64String(bytes)); bytes = _zkCrypto.Decrypt(bytes); _logger.Debug("Decrypted data received from server: {0}", Convert.ToBase64String(bytes)); } _syncInfo = _serializer.Deserialize <ServiceSyncInfo>(bytes); SyncInfoCache.AddOrUpdate(serviceType, _syncInfo, (t, info) => _syncInfo); } }
public void SimpleProtocolTest() { var sr = new ZkProtocol(); var username = "******"; var pwd = "cc3a6a12-0e5b-47fb-ae45-3485e34582d4"; // prerequisit: generate password hash that would be stored on server var pwdHash = sr.HashCredentials(username, pwd); // Step 1. Client sends username and ephemeral hash of random number. var aRand = sr.CryptRand(); var aClientEphemeral = sr.GetClientEphemeralA(aRand); // send username and aClientEphemeral to server // Step 2. Server looks up username, gets pwd hash, and sends client ephemeral has of params. var bRand = sr.CryptRand(); var bServerEphemeral = sr.GetServerEphemeralB(pwdHash.Salt, pwdHash.Verifier, bRand); // send salt and bServerEphemeral to client var clientSalt = pwdHash.Salt; // Step 3. Client and server calculate random scramble of ephemeral hash values exchanged. var clientScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral); var serverScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral); var scrambleSame = clientScramble.IsEqualTo(serverScramble); // Step 4. Client computes session key var clientSessionKey = sr.ClientComputeSessionKey(clientSalt, username, pwd, aClientEphemeral, bServerEphemeral, clientScramble); // Step 5. Server computes session key var serverSessionKey = sr.ServerComputeSessionKey(pwdHash.Salt, pwdHash.Key, aClientEphemeral, bServerEphemeral, serverScramble); var sessionKeysSame = clientSessionKey.IsEqualTo(serverSessionKey); // Step 6. Client creates hash of session key and sends to server. Server creates same key and verifies. var clientSessionHash = sr.ClientCreateSessionHash(username, pwdHash.Salt, aClientEphemeral, bServerEphemeral, clientSessionKey); // send to server and server verifies // server validates clientSessionHash is same as serverClientSessionHash var serverClientSessionHash = sr.ClientCreateSessionHash(username, pwdHash.Salt, aClientEphemeral, bServerEphemeral, serverSessionKey); var clientEqualToServer = clientSessionHash.IsEqualTo(serverClientSessionHash); // Step 7. Server creates hash of session key and sends to client. Client creates same key and verifies. var serverSessionHash = sr.ServerCreateSessionHash(aClientEphemeral, clientSessionHash, serverSessionKey); // server sends serverSessionHash to client // validate that serverSessionHash is same as clientServerSessionHash var clientServerSessionHash = sr.ServerCreateSessionHash(aClientEphemeral, clientSessionHash, clientSessionKey); var serverEqualToClient = serverSessionHash.IsEqualTo(clientServerSessionHash); //proof Assert.True(sessionKeysSame); Assert.True(scrambleSame); Assert.True(clientEqualToServer); Assert.True(serverEqualToClient); var data = sr.Combine(sr.CryptRand(), sr.CryptRand(), sr.CryptRand()); var crypto = new ZkCrypto(clientSessionKey, clientScramble); var encrypted = crypto.Encrypt(data); var decrypted = crypto.Decrypt(encrypted); var cryptSame = data.IsEqualTo(decrypted); Assert.True(cryptSame); }
/// <summary> /// This method asks the server for a list of identifiers paired with method /// names and -parameter types. This is used when invoking methods server side. /// </summary> protected override void SyncInterface(Type serviceType, string username = null, string password = null) { if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) { //do zk protocol authentication var sr = new ZkProtocol(); // Step 1. Client sends username and ephemeral hash of random number. var aRand = sr.CryptRand(); var aClientEphemeral = sr.GetClientEphemeralA(aRand); // send username and aClientEphemeral to server _binWriter.Write((int)MessageType.ZkInitiate); _binWriter.Write(username); _binWriter.Write(aClientEphemeral); //always 32 bytes // get response from server var userFound = _binReader.ReadBoolean(); if (!userFound) { throw new InvalidCredentialException("authentication failed"); } var salt = _binReader.ReadBytes(32); var bServerEphemeral = _binReader.ReadBytes(32); // Step 3. Client and server calculate random scramble of ephemeral hash values exchanged. var clientScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral); // Step 4. Client computes session key var clientSessionKey = sr.ClientComputeSessionKey(salt, username, password, aClientEphemeral, bServerEphemeral, clientScramble); // Step 6. Client creates hash of session key and sends to server. Server creates same key and verifies. var clientSessionHash = sr.ClientCreateSessionHash(username, salt, aClientEphemeral, bServerEphemeral, clientSessionKey); // send to server and server verifies _binWriter.Write((int)MessageType.ZkProof); _binWriter.Write(clientSessionHash); //always 32 bytes // get response var serverVerified = _binReader.ReadBoolean(); if (!serverVerified) { throw new InvalidCredentialException("authentication failed"); } var serverSessionHash = _binReader.ReadBytes(32); var clientServerSessionHash = sr.ServerCreateSessionHash(aClientEphemeral, clientSessionHash, clientSessionKey); if (!serverSessionHash.IsEqualTo(clientServerSessionHash)) { throw new InvalidCredentialException("authentication failed"); } _zkCrypto = new ZkCrypto(clientSessionKey, clientScramble); } if (!_syncInfoCache.TryGetValue(serviceType, out _syncInfo)) { //write the message type _binWriter.Write((int)MessageType.SyncInterface); if (null != _zkCrypto) { //sync interface with encryption var assemName = serviceType.FullName; var assemblyNameEncrypted = _zkCrypto.Encrypt(assemName.ConvertToBytes()); _binWriter.Write(assemblyNameEncrypted.Length); _binWriter.Write(assemblyNameEncrypted); } else { _binWriter.Write(serviceType.FullName); } //read sync data var len = _binReader.ReadInt32(); //len is zero when AssemblyQualifiedName not same version or not found #if (!NET35) if (len == 0) { throw new TypeAccessException("SyncInterface failed. Type or version of type unknown."); } #else if (len == 0) { throw new Exception("SyncInterface failed. Type or version of type unknown."); } #endif var bytes = _binReader.ReadBytes(len); if (null != _zkCrypto) { bytes = _zkCrypto.Decrypt(bytes); } _syncInfo = bytes.ToDeserializedObject <ServiceSyncInfo>(); _syncInfoCache.AddOrUpdate(serviceType, _syncInfo, (t, info) => _syncInfo); } }