Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Commit

Permalink
Merge 10a1114 into afe345b
Browse files Browse the repository at this point in the history
  • Loading branch information
Kirbyrawr committed Nov 10, 2020
2 parents afe345b + 10a1114 commit dcc2035
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 7 deletions.
37 changes: 37 additions & 0 deletions stellar-dotnet-sdk-test/WebAuthenticationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public void TestBuildChallengeTransaction()
CheckOperation(back, clientAccountId);
}


[TestMethod]
public void TestBuildChallengeTransactionWithOptions()
{
Expand Down Expand Up @@ -1562,5 +1563,41 @@ public void TestVerifyChallengeTransactionNotValidSubsequentDataOperation()
Assert.IsTrue(exception.Message.Contains("The transaction has operations that are unrecognized"));
}
}

[TestMethod]
public void TestBuildChallengeTransactionBadHomeDomain()
{
var serverKeypair = KeyPair.Random();
var clientAccountId = "GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF";
var anchorName = "NET";
Network.UseTestNetwork();
try
{
var tx = WebAuthentication.BuildChallengeTransaction(serverKeypair, clientAccountId, anchorName);
WebAuthentication.ReadChallengeTransaction(tx, serverKeypair.AccountId, $"{anchorName}bad");
}
catch (InvalidWebAuthenticationException e)
{
Assert.AreEqual(e.Message, "Invalid homeDomains: the transaction's operation key name does not match the expected home domain");
}
}

[TestMethod]
public void TestBuildChallengeTransactionNoHomeDomain()
{
var serverKeypair = KeyPair.Random();
var clientAccountId = "GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF";
var anchorName = "NET";
Network.UseTestNetwork();
try
{
var tx = WebAuthentication.BuildChallengeTransaction(serverKeypair, clientAccountId, anchorName);
WebAuthentication.ReadChallengeTransaction(tx, serverKeypair.AccountId, new string[0]);
}
catch (InvalidWebAuthenticationException e)
{
Assert.AreEqual(e.Message, "Invalid homeDomains: a home domain must be provided for verification");
}
}
}
}
56 changes: 49 additions & 7 deletions stellar-dotnet-sdk/WebAuthentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static class WebAuthentication
/// </summary>
/// <param name="serverKeypair">Server signing keypair</param>
/// <param name="clientAccountId">The client account id that needs authentication</param>
/// <param name="domainName">The domain name</param>
/// <param name="homeDomain">The server home domain</param>
/// <param name="nonce">48 bytes long cryptographic-quality random data</param>
/// <param name="now">The datetime from which the transaction is valid</param>
/// <param name="timeout">The transaction lifespan</param>
Expand All @@ -25,15 +25,15 @@ public static class WebAuthentication
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public static Transaction BuildChallengeTransaction(KeyPair serverKeypair, string clientAccountId,
string domainName, byte[] nonce = null, DateTimeOffset? now = null, TimeSpan? timeout = null,
string homeDomain, byte[] nonce = null, DateTimeOffset? now = null, TimeSpan? timeout = null,
Network network = null)
{
if (string.IsNullOrEmpty(clientAccountId)) throw new ArgumentNullException(nameof(clientAccountId));

if (StrKey.DecodeVersionByte(clientAccountId) != StrKey.VersionByte.ACCOUNT_ID)
throw new InvalidWebAuthenticationException($"{nameof(clientAccountId)} is not a valid account id");
var clientAccountKeypair = KeyPair.FromAccountId(clientAccountId);
return BuildChallengeTransaction(serverKeypair, clientAccountKeypair, domainName, nonce, now, timeout,
return BuildChallengeTransaction(serverKeypair, clientAccountKeypair, homeDomain, nonce, now, timeout,
network);
}

Expand All @@ -42,7 +42,7 @@ public static class WebAuthentication
/// </summary>
/// <param name="serverKeypair">Server signing keypair</param>
/// <param name="clientAccountId">The client account id that needs authentication</param>
/// <param name="domainName">The domain name</param>
/// <param name="homeDomain">The server home domain</param>
/// <param name="nonce">48 bytes long cryptographic-quality random data</param>
/// <param name="now">The datetime from which the transaction is valid</param>
/// <param name="timeout">The transaction lifespan</param>
Expand All @@ -51,12 +51,12 @@ public static class WebAuthentication
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public static Transaction BuildChallengeTransaction(KeyPair serverKeypair, KeyPair clientAccountId,
string domainName, byte[] nonce = null, DateTimeOffset? now = null, TimeSpan? timeout = null,
string homeDomain, byte[] nonce = null, DateTimeOffset? now = null, TimeSpan? timeout = null,
Network network = null)
{
if (serverKeypair is null) throw new ArgumentNullException(nameof(serverKeypair));
if (clientAccountId is null) throw new ArgumentNullException(nameof(clientAccountId));
if (string.IsNullOrEmpty(domainName)) throw new ArgumentNullException(nameof(domainName));
if (string.IsNullOrEmpty(homeDomain)) throw new ArgumentNullException(nameof(homeDomain));

if (nonce is null)
{
Expand All @@ -78,7 +78,7 @@ public static class WebAuthentication
// Sequence number is incremented by 1 before building the transaction, set it to -1 to have 0
var serverAccount = new Account(serverKeypair, -1);

var manageDataKey = $"{domainName} auth";
var manageDataKey = $"{homeDomain} auth";
var manageDataValue = Encoding.UTF8.GetBytes(Convert.ToBase64String(nonce));

var timeBounds = new TimeBounds(validFrom, validFor);
Expand Down Expand Up @@ -118,6 +118,31 @@ public static class WebAuthentication
/// <exception cref="InvalidWebAuthenticationException"></exception>
public static string ReadChallengeTransaction(Transaction transaction, string serverAccountId, string homeDomain,
Network network = null, DateTimeOffset? now = null)
{
return ReadChallengeTransaction(transaction, serverAccountId, new string[1] { homeDomain }, network, now);
}

/// <summary>
/// Read a SEP 10 challenge transaction and return the client account id.
///
/// Performs the following checks:
///
/// 1. Transaction sequence number is 0
/// 2. Transaction source account is <paramref name="serverAccountId"/>
/// 3. Transaction has one operation only, of type ManageDataOperation
/// 4. The ManageDataOperation name and value are correct
/// 5. Transaction time bounds are still valid
/// 6. Transaction is signed by server
/// </summary>
/// <param name="transaction">The challenge transaction</param>
/// <param name="serverAccountId">The server account id</param>
/// <param name="homeDomain">The server home domain</param>
/// <param name="network">The network the transaction was submitted to, defaults to Network.Current</param>
/// <param name="now">Current time, defaults to DateTimeOffset.Now</param>
/// <returns>The client account id</returns>
/// <exception cref="InvalidWebAuthenticationException"></exception>
public static string ReadChallengeTransaction(Transaction transaction, string serverAccountId, string[] homeDomains,
Network network = null, DateTimeOffset? now = null)
{
network = network ?? Network.Current;

Expand Down Expand Up @@ -145,6 +170,23 @@ public static class WebAuthentication
if (operation.SourceAccount is null)
throw new InvalidWebAuthenticationException("Challenge transaction operation must have source account");

if (homeDomains == null || homeDomains.Length == 0)
throw new InvalidWebAuthenticationException("Invalid homeDomains: a home domain must be provided for verification");

string matchedHomeDomain = "";

foreach (var domain in homeDomains)
{
if ($"{domain} auth" == operation.Name)
{
matchedHomeDomain = domain;
break;
}
}

if (string.IsNullOrEmpty(matchedHomeDomain))
throw new InvalidWebAuthenticationException("Invalid homeDomains: the transaction's operation key name does not match the expected home domain");

var subsequentOperations = transaction.Operations;
foreach (var op in subsequentOperations.Skip(1))
{
Expand Down

0 comments on commit dcc2035

Please sign in to comment.