mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-06-13 14:38:43 +00:00
Ver 3
This commit is contained in:
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority;
|
||||
|
||||
public class Authentication
|
||||
{
|
||||
AuthenticationMode type;
|
||||
|
||||
public AuthenticationMethod Method { get; set; }
|
||||
|
||||
public ulong TokenIndex { get; set; }
|
||||
|
||||
public string Username { get; set; }
|
||||
public Certificate Certificate { get; set; }
|
||||
public string Domain { get; set; }
|
||||
|
||||
public string FullName => Username + "@" + Domain;
|
||||
|
||||
public Source Source { get; } = new Source();
|
||||
|
||||
public AuthenticationState State
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public AuthenticationMode Type
|
||||
{
|
||||
get => type;
|
||||
}
|
||||
|
||||
public Authentication(AuthenticationMode type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,14 @@ namespace Esiur.Security.Authority
|
||||
{
|
||||
public class AuthenticationContext
|
||||
{
|
||||
public AuthenticationMode Mode { get; }
|
||||
public AuthenticationDirection Direction { get; set; }
|
||||
public AuthenticationMode Mode { get; set; }
|
||||
|
||||
public string? LocalDomain { get; }
|
||||
public string? RemoteDomain { get; }
|
||||
|
||||
public string? LocalHost { get; }
|
||||
public string? RemoteHost { get; }
|
||||
|
||||
//public AuthenticationComponentContext LocalToRemote { get; } = new();
|
||||
//public AuthenticationComponentContext RemoteToLocal { get; } = new();
|
||||
public string Domain { get; set; }
|
||||
public string? InitiatorIdentity { get; set; }
|
||||
public string? ResponderIdentity { get; set; }
|
||||
public AuthenticationMaterial[] Materials { get; set; }
|
||||
|
||||
public string? HostName { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@ using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public interface IAuthenticationResponder
|
||||
public enum AuthenticationDirection
|
||||
{
|
||||
public AuthenticationResult Process(Session session);
|
||||
|
||||
Initiator,
|
||||
Responder
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
internal class AuthenticationHandler
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class AuthenticationMaterial
|
||||
{
|
||||
public AuthenticationMaterialType Type { get; set; }
|
||||
public object Value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public enum AuthenticationMaterialType: byte
|
||||
{
|
||||
Secret,
|
||||
Key,
|
||||
Identity,
|
||||
Data,
|
||||
}
|
||||
}
|
||||
@@ -8,20 +8,24 @@ namespace Esiur.Security.Authority
|
||||
public class AuthenticationResult
|
||||
{
|
||||
public AuthenticationRuling Ruling { get; internal set; }
|
||||
public string Identity { get; internal set; }
|
||||
public string LocalIdentity { get; internal set; }
|
||||
public string RemoteIdentity { get; internal set; }
|
||||
|
||||
public object HandshakePayload { get; internal set; }
|
||||
//public object HandshakePayload { get; internal set; }
|
||||
|
||||
public byte[] SessionKey { get; internal set; }
|
||||
|
||||
public ExceptionCode? ExceptionCode { get; internal set; }
|
||||
public string ExceptionMessage { get; internal set; }
|
||||
|
||||
public AuthenticationResult(AuthenticationRuling ruling, string identity, object handshakePayload, byte[] sessionKey)
|
||||
public object AuthenticationData { get; internal set; }
|
||||
|
||||
public AuthenticationResult(AuthenticationRuling ruling, object authenticationData, string localIdentity = null, string remoteIdentity = null, byte[] sessionKey = null)
|
||||
{
|
||||
Ruling = ruling;
|
||||
Identity = identity;
|
||||
HandshakePayload = handshakePayload;
|
||||
LocalIdentity = localIdentity;
|
||||
RemoteIdentity = remoteIdentity;
|
||||
AuthenticationData = authenticationData;
|
||||
SessionKey = sessionKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,12 @@ namespace Esiur.Security.Authority
|
||||
public interface IAuthenticationHandler
|
||||
{
|
||||
|
||||
public AuthenticationMode Mode { get; }
|
||||
public AuthenticationResult Initialize(Session session, object authenticationData);
|
||||
public string Protocol { get; }
|
||||
//public AuthenticationMode Mode { get; }
|
||||
//public AuthenticationResult Initialize(object authData);
|
||||
|
||||
public AuthenticationResult Process(object authenticationData);
|
||||
public AuthenticationResult Process(object authData);
|
||||
|
||||
public void Terminate(Session session);
|
||||
|
||||
public void Update(Session session, object authData);
|
||||
//public AuthenticationResult? Result { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public interface IAuthenticationInitiator
|
||||
{
|
||||
public AuthenticationResult Initiate(Session session);
|
||||
|
||||
public AuthenticationResult Process(object handshakePayload);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Text;
|
||||
|
||||
//namespace Esiur.Security.Authority
|
||||
//{
|
||||
// public interface IAuthenticationMethodProvider
|
||||
// {
|
||||
// string Method { get; }
|
||||
|
||||
// bool CanHandle(AuthenticationCreationContext context);
|
||||
|
||||
// IAuthenticator CreateAuthenticator(AuthenticationCreationContext context);
|
||||
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public interface IAuthenticationProvider
|
||||
{
|
||||
public IAuthenticationHandler
|
||||
CreateAuthenticationHandler(AuthenticationContext context);
|
||||
|
||||
public string DefaultName { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public sealed class InitiatorAuthenticationContext
|
||||
{
|
||||
public string LocalIdentity { get; } = string.Empty;
|
||||
public string RemoteIdentity { get; } = string.Empty;
|
||||
|
||||
public string? RemoteDomain { get; }
|
||||
public string? LocalDomain { get; }
|
||||
|
||||
public string? RemoteIpAddress { get; }
|
||||
|
||||
public AuthenticationMode Mode { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
internal class PpapAuthenticationProvider : IAuthenticationProvider
|
||||
{
|
||||
public string DefaultName => "PPAP";
|
||||
|
||||
public IAuthenticationHandler CreateAuthenticationHandler(AuthenticationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,606 @@
|
||||
using Esiur.Misc;
|
||||
using Esiur.Security.Permissions;
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Esiur.Data;
|
||||
using Esiur.Data.Types;
|
||||
|
||||
namespace Esiur.Security.Authority.Providers
|
||||
{
|
||||
internal class PasswordAuthenticationHandler : IAuthenticationHandler
|
||||
{
|
||||
public string Protocol => "hash";
|
||||
|
||||
byte[] localNonce, remoteNonce;
|
||||
byte[] localSalt, remoteSalt;
|
||||
|
||||
string initiatorIdentity, responderIdentity;
|
||||
|
||||
byte[] initiatorPassword, responderPassword;
|
||||
|
||||
string hostName, domain;
|
||||
|
||||
int step = 0;
|
||||
|
||||
AuthenticationMode mode;
|
||||
AuthenticationDirection direction;
|
||||
|
||||
PasswordAuthenticationProvider provider;
|
||||
|
||||
|
||||
public byte[] ComputeSha3(byte[] data, int bitLength = 256)
|
||||
{
|
||||
// 1. Initialize the digest (supports 224, 256, 384, 512)
|
||||
var digest = new Sha3Digest(bitLength);
|
||||
|
||||
// 3. Update the digest with data
|
||||
digest.BlockUpdate(data, 0, data.Length);
|
||||
|
||||
// 4. Retrieve the final hash
|
||||
byte[] result = new byte[digest.GetDigestSize()];
|
||||
digest.DoFinal(result, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public AuthenticationResult Process(object authData)
|
||||
{
|
||||
var remoteAuthData = (object[])authData;
|
||||
var localAuthData = new List<object>();
|
||||
|
||||
if (direction == AuthenticationDirection.Initiator)
|
||||
{
|
||||
if (mode == AuthenticationMode.None)
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
else if (mode == AuthenticationMode.InitializerIdentity)
|
||||
{
|
||||
if (step == 0)
|
||||
{
|
||||
// step 0: send local nonce and initiator identity.
|
||||
if (initiatorIdentity == null)
|
||||
(initiatorIdentity, initiatorPassword) = provider.GetSelfIdentityAndCredential(domain, hostName);
|
||||
else
|
||||
initiatorPassword = provider.GetSelfCredential(initiatorIdentity, domain, hostName);
|
||||
|
||||
if (initiatorPassword == null || initiatorIdentity == null)
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
|
||||
// send local nonce and initiator identity
|
||||
localAuthData.Add(localNonce);
|
||||
localAuthData.Add(initiatorIdentity);
|
||||
|
||||
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
|
||||
|
||||
}
|
||||
else if (step == 1)
|
||||
{
|
||||
// expect remote nonce, salt and challenge.
|
||||
remoteNonce = (byte[])remoteAuthData[0];
|
||||
remoteSalt = (byte[])remoteAuthData[1];
|
||||
var remoteChallenge = (byte[])remoteAuthData[2];
|
||||
|
||||
// prevent reply attack by checking if remote nonce is same as local nonce.
|
||||
if (remoteNonce.SequenceEqual(localNonce))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// make salted hash of password.
|
||||
var hashedPassword = ComputeSha3(initiatorPassword.Concat(remoteSalt).ToArray());
|
||||
|
||||
|
||||
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(hashedPassword)
|
||||
.Concat(localNonce)
|
||||
.ToArray());
|
||||
|
||||
// compare remote challenge
|
||||
if (!remoteChallenge.SequenceEqual(expectedRemoteChallenge))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// make hash challenge response.
|
||||
var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword)
|
||||
.Concat(remoteNonce)
|
||||
.ToArray());
|
||||
|
||||
localAuthData.Add(localChallenge);
|
||||
step = -1;
|
||||
|
||||
// derive a session key from nonces and password.
|
||||
// initiator identity + initiator password + initiator nonce + responder nonce
|
||||
|
||||
var sessionKey = ComputeSha3(initiatorIdentity.ToBytes()
|
||||
.Concat(hashedPassword)
|
||||
.Concat(localNonce)
|
||||
.Concat(remoteNonce)
|
||||
.ToArray(), 512);
|
||||
|
||||
return new AuthenticationResult(AuthenticationRuling.Succeeded, localAuthData, initiatorIdentity, null, sessionKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
}
|
||||
else if (mode == AuthenticationMode.ResponderIdentity)
|
||||
{
|
||||
if (step == 0)
|
||||
{
|
||||
// just send local nonce.
|
||||
localAuthData.Add(localNonce);
|
||||
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
|
||||
}
|
||||
else if (step == 1)
|
||||
{
|
||||
// expect responder identity and nonce.
|
||||
remoteNonce = (byte[])remoteAuthData[0];
|
||||
responderIdentity = (string)remoteAuthData[1];
|
||||
|
||||
// prevent reply attack by checking if remote nonce is same as local nonce.
|
||||
if (remoteNonce.SequenceEqual(localNonce))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// check if responder identity is valid and get password.
|
||||
(localSalt, responderPassword) = provider.GetHostedAccountCredential(responderIdentity, domain);
|
||||
|
||||
if (responderPassword == null)
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// make hash challenge response.
|
||||
var localChallenge = ComputeSha3(localNonce.Concat(responderPassword)
|
||||
.Concat(remoteNonce)
|
||||
.ToArray());
|
||||
// send localSalt and challenge
|
||||
localAuthData.Add(localSalt);
|
||||
localAuthData.Add(localChallenge);
|
||||
step = 2;
|
||||
|
||||
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
|
||||
|
||||
}
|
||||
else if (step == 2)
|
||||
{
|
||||
// expect remote challenge.
|
||||
var remoteChallenge = (byte[])remoteAuthData[0];
|
||||
|
||||
// compare remote challenge
|
||||
|
||||
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(responderPassword)
|
||||
.Concat(localNonce)
|
||||
.ToArray());
|
||||
|
||||
if (!remoteChallenge.SequenceEqual(expectedRemoteChallenge))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// derive a session key from nonces and password.
|
||||
// responder identity + responder hashed password + initiator nonce + responder nonce
|
||||
|
||||
var sessionKey = ComputeSha3(responderIdentity.ToBytes()
|
||||
.Concat(responderPassword)
|
||||
.Concat(localNonce)
|
||||
.Concat(remoteNonce)
|
||||
.ToArray(), 512);
|
||||
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Succeeded, null, initiatorIdentity, responderIdentity, sessionKey);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
}
|
||||
else if (mode == AuthenticationMode.DualIdentity)
|
||||
{
|
||||
if (step == 0)
|
||||
{
|
||||
// step 0: send local nonce and initiator identity.
|
||||
if (initiatorIdentity == null)
|
||||
(initiatorIdentity, initiatorPassword) = provider.GetSelfIdentityAndCredential(domain, hostName);
|
||||
else
|
||||
initiatorPassword = provider.GetSelfCredential(initiatorIdentity, domain, hostName);
|
||||
|
||||
if (initiatorPassword == null || initiatorIdentity == null)
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
localAuthData.Add(localNonce);
|
||||
localAuthData.Add(initiatorIdentity);
|
||||
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
|
||||
|
||||
}
|
||||
else if (step == 1)
|
||||
{
|
||||
// expect responder identity, nonce and salt.
|
||||
remoteNonce = (byte[])remoteAuthData[0];
|
||||
responderIdentity = (string)remoteAuthData[1];
|
||||
remoteSalt = (byte[])remoteAuthData[2];
|
||||
|
||||
// prevent reply attack by checking if remote nonce is same as local nonce.
|
||||
if (remoteNonce.SequenceEqual(localNonce))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// check if responder identity is valid and get password.
|
||||
(localSalt, responderPassword) = provider.GetHostedAccountCredential(responderIdentity, domain);
|
||||
|
||||
if (responderPassword == null)
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// make salted hash of password.
|
||||
var hashedPassword = ComputeSha3(initiatorPassword.Concat(remoteSalt).ToArray());
|
||||
|
||||
// make hash challenge response.
|
||||
var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword)
|
||||
.Concat(responderPassword)
|
||||
.Concat(remoteNonce)
|
||||
.ToArray());
|
||||
|
||||
// send localSalt and challenge
|
||||
localAuthData.Add(localSalt);
|
||||
localAuthData.Add(localChallenge);
|
||||
step = 2;
|
||||
|
||||
return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData);
|
||||
|
||||
}
|
||||
else if (step == 2)
|
||||
{
|
||||
// expect remote challenge.
|
||||
var remoteChallenge = (byte[])remoteAuthData[0];
|
||||
|
||||
// make salted hash of password.
|
||||
var hashedPassword = ComputeSha3(initiatorPassword.Concat(remoteSalt).ToArray());
|
||||
|
||||
// compare remote challenge
|
||||
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(hashedPassword)
|
||||
.Concat(responderPassword)
|
||||
.Concat(localNonce)
|
||||
.ToArray());
|
||||
|
||||
if (!remoteChallenge.SequenceEqual(expectedRemoteChallenge))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// derive a session key from nonces and password.
|
||||
// responder identity + responder password + initiator nonce + responder nonce
|
||||
|
||||
var sessionKey = ComputeSha3(initiatorIdentity.ToBytes()
|
||||
.Concat(responderIdentity.ToBytes())
|
||||
.Concat(hashedPassword)
|
||||
.Concat(responderPassword)
|
||||
.Concat(localNonce)
|
||||
.Concat(remoteNonce)
|
||||
.ToArray(), 512);
|
||||
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Succeeded, null, initiatorIdentity, responderIdentity, sessionKey);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (direction == AuthenticationDirection.Responder)
|
||||
{
|
||||
if (mode == AuthenticationMode.None)
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
else if (mode == AuthenticationMode.InitializerIdentity)
|
||||
{
|
||||
if (step == 0)
|
||||
{
|
||||
if (remoteAuthData.Length < 2)
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
|
||||
// step 0: expect remote nonce and initiator identity.
|
||||
remoteNonce = (byte[])remoteAuthData[0];
|
||||
initiatorIdentity = (string)remoteAuthData[1];
|
||||
|
||||
// prevent reply attack by checking if remote nonce is same as local nonce.
|
||||
// @TODO: We can change our localNonce then send it
|
||||
if (remoteNonce.SequenceEqual(localNonce))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// get initiator password from provider.
|
||||
(localSalt, initiatorPassword) = provider.GetHostedAccountCredential(initiatorIdentity, domain);
|
||||
|
||||
// account not found or no password for this account.
|
||||
if (initiatorPassword == null || initiatorIdentity == null)
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
var localChallenge = ComputeSha3(localNonce.Concat(initiatorPassword)
|
||||
.Concat(remoteNonce)
|
||||
.ToArray());
|
||||
|
||||
// send local nonce, salt and challenge.
|
||||
localAuthData.Add(localNonce);
|
||||
localAuthData.Add(localSalt);
|
||||
localAuthData.Add(localChallenge);
|
||||
|
||||
step = 1;
|
||||
return new AuthenticationResult(AuthenticationRuling.InProgress,
|
||||
localAuthData);
|
||||
}
|
||||
else if (step == 1)
|
||||
{
|
||||
// expect challenge response.
|
||||
var remoteChallenge = (byte[])remoteAuthData[0];
|
||||
|
||||
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(initiatorPassword)
|
||||
.Concat(localNonce)
|
||||
.ToArray());
|
||||
// compare remote challenge
|
||||
if (!expectedRemoteChallenge.SequenceEqual(remoteChallenge))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// compute session key.
|
||||
|
||||
// derive a session key from nonces and password.
|
||||
// initiator identity + initiator password + initiator nonce + responder nonce
|
||||
|
||||
var sessionKey = ComputeSha3(initiatorIdentity.ToBytes()
|
||||
.Concat(initiatorPassword)
|
||||
.Concat(remoteNonce)
|
||||
.Concat(localNonce)
|
||||
.ToArray(), 512);
|
||||
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Succeeded, null, initiatorIdentity, responderIdentity, sessionKey);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
}
|
||||
else if (mode == AuthenticationMode.ResponderIdentity)
|
||||
{
|
||||
if (step == 0)
|
||||
{
|
||||
if (remoteAuthData.Length < 1)
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
|
||||
// step 0: receive remote nonce.
|
||||
remoteNonce = (byte[])remoteAuthData[0];
|
||||
|
||||
// prevent reply attack by checking if remote nonce is same as local nonce.
|
||||
// @TODO: We can change our localNonce then send it
|
||||
if (remoteNonce.SequenceEqual(localNonce))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// get responder identity from provider.
|
||||
if (responderIdentity == null)
|
||||
(responderIdentity, responderPassword) = provider.GetSelfIdentityAndCredential(domain, hostName);
|
||||
else
|
||||
responderPassword = provider.GetSelfCredential(responderIdentity, domain, hostName);
|
||||
|
||||
if (responderPassword == null || responderIdentity == null)
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
localAuthData.Add(localNonce);
|
||||
localAuthData.Add(responderIdentity);
|
||||
|
||||
step = 1;
|
||||
// send local nonce and identity.
|
||||
return new AuthenticationResult(AuthenticationRuling.InProgress,
|
||||
localAuthData
|
||||
);
|
||||
}
|
||||
else if (step == 1)
|
||||
{
|
||||
// expect remote salt and challenge.
|
||||
remoteSalt = (byte[])remoteAuthData[0];
|
||||
var remoteChallenge = (byte[])remoteAuthData[1];
|
||||
|
||||
// compute expected challenge response.
|
||||
var hashedPassword = ComputeSha3(responderPassword.Concat(remoteSalt).ToArray());
|
||||
|
||||
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(hashedPassword)
|
||||
.Concat(localNonce)
|
||||
.ToArray());
|
||||
|
||||
// compare remote challenge
|
||||
if (!expectedRemoteChallenge.SequenceEqual(remoteChallenge))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// compute our challenge response.
|
||||
var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword)
|
||||
.Concat(remoteNonce)
|
||||
.ToArray());
|
||||
|
||||
// derive a session key from nonces and password.
|
||||
// responder identity + responder hashed password + initiator nonce + responder nonce
|
||||
|
||||
var sessionKey = ComputeSha3(responderIdentity.ToBytes()
|
||||
.Concat(hashedPassword)
|
||||
.Concat(remoteNonce)
|
||||
.Concat(localNonce)
|
||||
.ToArray(), 512);
|
||||
|
||||
localAuthData.Add(localChallenge);
|
||||
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Succeeded, localAuthData, responderIdentity, null, sessionKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
}
|
||||
else if (mode == AuthenticationMode.DualIdentity)
|
||||
{
|
||||
if (step == 0)
|
||||
{
|
||||
if (remoteAuthData.Length < 2)
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
|
||||
// step 0: receive remote nonce and initiator identity.
|
||||
remoteNonce = (byte[])remoteAuthData[0];
|
||||
initiatorIdentity = (string)remoteAuthData[1];
|
||||
|
||||
// prevent reply attack by checking if remote nonce is same as local nonce.
|
||||
// @TODO: We can change our localNonce then send it
|
||||
if (remoteNonce.SequenceEqual(localNonce))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// get responder identity from provider.
|
||||
if (responderIdentity == null)
|
||||
(responderIdentity, responderPassword) = provider.GetSelfIdentityAndCredential(domain, hostName);
|
||||
else
|
||||
responderPassword = provider.GetSelfCredential(responderIdentity, domain, hostName);
|
||||
|
||||
if (responderPassword == null || responderIdentity == null)
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// get initiator password from provider.
|
||||
(localSalt, initiatorPassword) = provider.GetHostedAccountCredential(initiatorIdentity, domain);
|
||||
|
||||
// account not found or no password for this account.
|
||||
if (initiatorPassword == null || initiatorIdentity == null)
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// send local nonce, salt and responder identity.
|
||||
localAuthData.Add(localNonce);
|
||||
localAuthData.Add(localSalt);
|
||||
localAuthData.Add(responderIdentity);
|
||||
|
||||
step = 1;
|
||||
// send local nonce and identity.
|
||||
return new AuthenticationResult(AuthenticationRuling.InProgress,
|
||||
localAuthData
|
||||
);
|
||||
|
||||
}
|
||||
else if (step == 1)
|
||||
{
|
||||
// expect initiator salt and challenge.
|
||||
var remoteSalt = (byte[])remoteAuthData[0];
|
||||
var remoteChallenge = (byte[])remoteAuthData[1];
|
||||
|
||||
// compute expected challenge response.
|
||||
var hashedPassword = ComputeSha3(responderPassword.Concat(remoteSalt).ToArray());
|
||||
|
||||
// compare remote challenge
|
||||
var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(initiatorPassword)
|
||||
.Concat(hashedPassword)
|
||||
.Concat(localNonce)
|
||||
.ToArray());
|
||||
|
||||
// compare remote challenge
|
||||
if (!expectedRemoteChallenge.SequenceEqual(remoteChallenge))
|
||||
{
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
// compute our challenge
|
||||
var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword)
|
||||
.Concat(initiatorPassword)
|
||||
.Concat(remoteNonce)
|
||||
.ToArray());
|
||||
|
||||
localAuthData.Add(localChallenge);
|
||||
|
||||
// derive a session key from nonces and password.
|
||||
// responder identity + responder password + initiator nonce + responder nonce
|
||||
|
||||
var sessionKey = ComputeSha3(initiatorIdentity.ToBytes()
|
||||
.Concat(responderIdentity.ToBytes())
|
||||
.Concat(initiatorPassword)
|
||||
.Concat(hashedPassword)
|
||||
.Concat(remoteNonce)
|
||||
.Concat(localNonce)
|
||||
.ToArray(), 512);
|
||||
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Succeeded, localAuthData, initiatorIdentity, responderIdentity, sessionKey);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
step = -1;
|
||||
return new AuthenticationResult(AuthenticationRuling.Failed, null);
|
||||
}
|
||||
|
||||
public PasswordAuthenticationHandler(AuthenticationMode mode,
|
||||
AuthenticationDirection direction,
|
||||
string initiatorIdentity,
|
||||
string responderIdentity,
|
||||
string hostName,
|
||||
string domain,
|
||||
PasswordAuthenticationProvider provider)
|
||||
{
|
||||
localNonce = Global.GenerateBytes(20);
|
||||
|
||||
this.provider = provider;
|
||||
this.initiatorIdentity = initiatorIdentity;
|
||||
this.responderIdentity = responderIdentity;
|
||||
this.mode = mode;
|
||||
this.direction = direction;
|
||||
this.domain = domain;
|
||||
this.hostName = hostName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority.Providers
|
||||
{
|
||||
internal class PasswordAuthenticationProvider : IAuthenticationProvider
|
||||
{
|
||||
public string DefaultName => "hash";
|
||||
|
||||
public IAuthenticationHandler CreateAuthenticationHandler(AuthenticationContext context)
|
||||
{
|
||||
var authHandler = new PasswordAuthenticationHandler(context.Mode,
|
||||
context.Direction,
|
||||
context.InitiatorIdentity,
|
||||
context.ResponderIdentity,
|
||||
context.HostName,
|
||||
context.Domain,
|
||||
this);
|
||||
|
||||
return authHandler;
|
||||
}
|
||||
|
||||
public virtual (byte[], byte[]) GetHostedAccountCredential(string identity, string domain)
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
public virtual (string, byte[]) GetSelfIdentityAndCredential(string domain, string hostname)
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
public virtual byte[] GetSelfCredential(string identity, string domain, string hostname)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public sealed class ResponderAuthenticationContext
|
||||
{
|
||||
public string? RemoteIpAddress { get; }
|
||||
|
||||
public string? LocalDomain { get; }
|
||||
|
||||
public AuthenticationMode Mode { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, string> Headers { get; }
|
||||
= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
@@ -59,5 +59,6 @@ public class Session
|
||||
public IAuthenticationHandler AuthenticationHandler { get; set; }
|
||||
//public IAuthenticationHandler AuthenticationResponder { get; set; }
|
||||
|
||||
public string AuthorizedIdentity { get; set; }
|
||||
public string LocalIdentity { get; set; }
|
||||
public string RemoteIdentity { get; set; }
|
||||
}
|
||||
|
||||
@@ -52,4 +52,5 @@ public interface IPermissionsManager
|
||||
bool Initialize(Map<string, object> settings, IResource resource);
|
||||
|
||||
Map<string, object> Settings { get; }
|
||||
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ public class UserPermissionsManager : IPermissionsManager
|
||||
{
|
||||
Map<string,object> userPermissions = null;
|
||||
|
||||
if (settings.ContainsKey(session.AuthorizedIdentity))
|
||||
userPermissions = settings[session.AuthorizedIdentity] as Map<string, object>;
|
||||
if (settings.ContainsKey(session.RemoteIdentity))
|
||||
userPermissions = settings[session.RemoteIdentity] as Map<string, object>;
|
||||
else if (settings.ContainsKey("public"))
|
||||
userPermissions = settings["public"] as Map<string,object>;
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user