Skip to content

Commit

Permalink
feat: add support for SEP-10 v3.1.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
overcat committed Jan 26, 2021
1 parent ddba21b commit e697d86
Show file tree
Hide file tree
Showing 4 changed files with 386 additions and 91 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

As this project is pre 1.0, breaking changes may happen for minor version bumps. A breaking change will get clearly notified in this log.

## 0.23.0
### Breaking change
- Updates the SEP-10 utility function parameters to support [SEP-10 v3.1](https://github.com/stellar/stellar-protocol/commit/6c8c9cf6685c85509835188a136ffb8cd6b9c11c) [(#319)](https://github.com/stellar/java-stellar-sdk/pull/319)
- A new required `webAuthDomain` parameter was added to the following functions
- `Sep10Challenge#newChallenge(KeyPair, Network, String, String, String, TimeBounds)`
- `Sep10Challenge#readChallengeTransaction(String, String, Network, String, String)`
- `Sep10Challenge#readChallengeTransaction(String, String, Network, String[], String)`
- `Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String, String, Set)`
- `Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String[], String, Set)`
- `Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String[], String, int, Set)`
- `Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String, String, int, Set)`
- The `webAuthDomain` parameter is expected to match the value of the Manage Data operation with the 'web_auth_domain' key, if present.

## 0.22.1
- Fix several bugs in revoke operations. ([#317](https://github.com/stellar/java-stellar-sdk/pull/317))

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ apply plugin: 'com.github.ben-manes.versions' // gradle dependencyUpdates -Drevi
apply plugin: 'project-report' // gradle htmlDependencyReport

sourceCompatibility = 1.6
version = '0.22.1'
version = '0.23.0'
group = 'stellar'

jar {
Expand Down
69 changes: 45 additions & 24 deletions src/main/java/org/stellar/sdk/Sep10Challenge.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,23 @@
import java.util.*;

public class Sep10Challenge {
private static final String MANAGER_DATA_NAME_FLAG = "auth";
private static final String HOME_DOMAIN_MANAGER_DATA_NAME_FLAG = "auth";
private static final String WEB_AUTH_DOMAIN_MANAGER_DATA_NAME = "web_auth_domain";
/**
* Returns a valid <a href="https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#response" target="_blank">SEP 10</a> challenge, for use in web authentication.
* @param signer The server's signing account.
* @param network The Stellar network used by the server.
* @param clientAccountId The stellar account belonging to the client.
* @param domainName The <a href="https://en.wikipedia.org/wiki/Fully_qualified_domain_name" target="_blank">fully qualified domain name</a> of the service requiring authentication.
* @param webAuthDomain The fully qualified domain name of the service issuing the challenge.
* @param timebounds The lifetime of the challenge token.
*/
public static Transaction newChallenge(
KeyPair signer,
Network network,
String clientAccountId,
String domainName,
String webAuthDomain,
TimeBounds timebounds
) throws InvalidSep10ChallengeException {
byte[] nonce = new byte[48];
Expand All @@ -41,13 +44,17 @@ public static Transaction newChallenge(
}

Account sourceAccount = new Account(signer.getAccountId(), -1L);
ManageDataOperation operation = new ManageDataOperation.Builder(String.format("%s %s", domainName, MANAGER_DATA_NAME_FLAG), encodedNonce)
ManageDataOperation domainNameOperation = new ManageDataOperation.Builder(String.format("%s %s", domainName, HOME_DOMAIN_MANAGER_DATA_NAME_FLAG), encodedNonce)
.setSourceAccount(clientAccountId)
.build();
ManageDataOperation webAuthDomainOperation = new ManageDataOperation.Builder(WEB_AUTH_DOMAIN_MANAGER_DATA_NAME, webAuthDomain.getBytes())
.setSourceAccount(sourceAccount.getAccountId())
.build();
Transaction transaction = new Transaction.Builder(sourceAccount, network)
.addTimeBounds(timebounds)
.setBaseFee(100)
.addOperation(operation)
.addOperation(domainNameOperation)
.addOperation(webAuthDomainOperation)
.build();

transaction.sign(signer);
Expand All @@ -63,20 +70,21 @@ public static Transaction newChallenge(
* It does not verify that the transaction has been signed by the client or
* that any signatures other than the servers on the transaction are valid. Use
* one of the following functions to completely verify the transaction:
* {@link Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String, int, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String[], Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String[], int, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String, String, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String, String, int, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String[], String, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String[], String, int, Set)} or
*
* @param challengeXdr SEP-0010 transaction challenge transaction in base64.
* @param serverAccountId Account ID for server's account.
* @param network The network to connect to for verifying and retrieving.
* @param domainNames An array of home domains, one of which is expected to be included in the first Manage Data operation's string key.
* @param webAuthDomain The home domain that is expected to be included as the value of the Manage Data operation with the 'web_auth_domain' key. If no such operation is included, this parameter is not used.
* @return {@link ChallengeTransaction}, the decoded transaction envelope and client account ID contained within.
* @throws InvalidSep10ChallengeException If the SEP-0010 validation fails, the exception will be thrown.
* @throws IOException If read XDR string fails, the exception will be thrown.
*/
public static ChallengeTransaction readChallengeTransaction(String challengeXdr, String serverAccountId, Network network, String[] domainNames) throws InvalidSep10ChallengeException, IOException {
public static ChallengeTransaction readChallengeTransaction(String challengeXdr, String serverAccountId, Network network, String[] domainNames, String webAuthDomain) throws InvalidSep10ChallengeException, IOException {
if (domainNames == null || domainNames.length == 0) {
throw new IllegalArgumentException("At least one domain name must be included in domainNames.");
}
Expand Down Expand Up @@ -138,7 +146,7 @@ public static ChallengeTransaction readChallengeTransaction(String challengeXdr,

String matchedDomainName = null;
for (String homeDomain : domainNames) {
if ((homeDomain + " " + MANAGER_DATA_NAME_FLAG).equals(manageDataOperation.getName())) {
if ((homeDomain + " " + HOME_DOMAIN_MANAGER_DATA_NAME_FLAG).equals(manageDataOperation.getName())) {
matchedDomainName = homeDomain;
break;
}
Expand Down Expand Up @@ -185,6 +193,14 @@ public static ChallengeTransaction readChallengeTransaction(String challengeXdr,
if (!manageDataOp.getSourceAccount().equals(serverAccountId)) {
throw new InvalidSep10ChallengeException("Subsequent operations are unrecognized.");
}
if (WEB_AUTH_DOMAIN_MANAGER_DATA_NAME.equals(manageDataOp.getName())) {
if (manageDataOp.getValue() == null) {
throw new InvalidSep10ChallengeException("'web_auth_domain' operation value should not be null.");
}
if (!Arrays.equals(webAuthDomain.getBytes(), manageDataOp.getValue())) {
throw new InvalidSep10ChallengeException(String.format("'web_auth_domain' operation value does not match %s.", webAuthDomain));
}
}
}

if (!verifyTransactionSignature(transaction, serverAccountId)) {
Expand All @@ -202,21 +218,22 @@ public static ChallengeTransaction readChallengeTransaction(String challengeXdr,
* It does not verify that the transaction has been signed by the client or
* that any signatures other than the servers on the transaction are valid. Use
* one of the following functions to completely verify the transaction:
* {@link Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String, int, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String[], Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String[], int, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String, String, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String, String, int, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionSigners(String, String, Network, String[], String, Set)} or
* {@link Sep10Challenge#verifyChallengeTransactionThreshold(String, String, Network, String[], String, int, Set)} or
*
* @param challengeXdr SEP-0010 transaction challenge transaction in base64.
* @param serverAccountId Account ID for server's account.
* @param network The network to connect to for verifying and retrieving.
* @param domainName The home domain that is expected to be included in the first Manage Data operation's string key.
* @param webAuthDomain The home domain that is expected to be included as the value of the Manage Data operation with the 'web_auth_domain' key, if present.
* @return {@link ChallengeTransaction}, the decoded transaction envelope and client account ID contained within.
* @throws InvalidSep10ChallengeException If the SEP-0010 validation fails, the exception will be thrown.
* @throws IOException If read XDR string fails, the exception will be thrown.
*/
public static ChallengeTransaction readChallengeTransaction(String challengeXdr, String serverAccountId, Network network, String domainName) throws InvalidSep10ChallengeException, IOException {
return readChallengeTransaction(challengeXdr, serverAccountId, network, new String[]{domainName});
public static ChallengeTransaction readChallengeTransaction(String challengeXdr, String serverAccountId, Network network, String domainName, String webAuthDomain) throws InvalidSep10ChallengeException, IOException {
return readChallengeTransaction(challengeXdr, serverAccountId, network, new String[]{domainName}, webAuthDomain);
}

/**
Expand All @@ -232,13 +249,14 @@ public static ChallengeTransaction readChallengeTransaction(String challengeXdr,
* @param serverAccountId Account ID for server's account.
* @param network The network to connect to for verifying and retrieving.
* @param domainName The home domain that is expected to be included in the first Manage Data operation's string key.
* @param webAuthDomain The home domain that is expected to be included as the value of the Manage Data operation with the 'web_auth_domain' key, if present.
* @param signers The signers of client account.
* @return a list of signers that were found is returned, excluding the server account ID.
* @throws InvalidSep10ChallengeException If the SEP-0010 validation fails, the exception will be thrown.
* @throws IOException If read XDR string fails, the exception will be thrown.
*/
public static Set<String> verifyChallengeTransactionSigners(String challengeXdr, String serverAccountId, Network network, String domainName, Set<String> signers) throws InvalidSep10ChallengeException, IOException {
return verifyChallengeTransactionSigners(challengeXdr, serverAccountId, network, new String[]{domainName}, signers);
public static Set<String> verifyChallengeTransactionSigners(String challengeXdr, String serverAccountId, Network network, String domainName, String webAuthDomain, Set<String> signers) throws InvalidSep10ChallengeException, IOException {
return verifyChallengeTransactionSigners(challengeXdr, serverAccountId, network, new String[]{domainName}, webAuthDomain, signers);
}

/**
Expand All @@ -254,18 +272,19 @@ public static Set<String> verifyChallengeTransactionSigners(String challengeXdr,
* @param serverAccountId Account ID for server's account.
* @param network The network to connect to for verifying and retrieving.
* @param domainNames An array of home domains, one of which is expected to be included in the first Manage Data operation's string key.
* @param webAuthDomain The home domain that is expected to be included as the value of the Manage Data operation with the 'web_auth_domain' key, if present.
* @param signers The signers of client account.
* @return a list of signers that were found is returned, excluding the server account ID.
* @throws InvalidSep10ChallengeException If the SEP-0010 validation fails, the exception will be thrown.
* @throws IOException If read XDR string fails, the exception will be thrown.
*/
public static Set<String> verifyChallengeTransactionSigners(String challengeXdr, String serverAccountId, Network network, String[] domainNames, Set<String> signers) throws InvalidSep10ChallengeException, IOException {
public static Set<String> verifyChallengeTransactionSigners(String challengeXdr, String serverAccountId, Network network, String[] domainNames, String webAuthDomain, Set<String> signers) throws InvalidSep10ChallengeException, IOException {
if (signers == null || signers.isEmpty()) {
throw new InvalidSep10ChallengeException("No verifiable signers provided, at least one G... address must be provided.");
}

// Read the transaction which validates its structure.
ChallengeTransaction parsedChallengeTransaction = readChallengeTransaction(challengeXdr, serverAccountId, network, domainNames);
ChallengeTransaction parsedChallengeTransaction = readChallengeTransaction(challengeXdr, serverAccountId, network, domainNames, webAuthDomain);
Transaction transaction = parsedChallengeTransaction.getTransaction();

// Ensure the server account ID is an address and not a seed.
Expand Down Expand Up @@ -344,13 +363,14 @@ public static Set<String> verifyChallengeTransactionSigners(String challengeXdr,
* @param serverAccountId Account ID for server's account.
* @param network The network to connect to for verifying and retrieving.
* @param domainNames An array of home domains, one of which is expected to be included in the first Manage Data operation's string key.
* @param webAuthDomain The home domain that is expected to be included as the value of the Manage Data operation with the 'web_auth_domain' key, if present.
* @param threshold The threshold on the client account.
* @param signers The signers of client account.
* @return a list of signers that were found is returned, excluding the server account ID.
* @throws InvalidSep10ChallengeException If the SEP-0010 validation fails, the exception will be thrown.
* @throws IOException If read XDR string fails, the exception will be thrown.
*/
public static Set<String> verifyChallengeTransactionThreshold(String challengeXdr, String serverAccountId, Network network, String[] domainNames, int threshold, Set<Signer> signers) throws InvalidSep10ChallengeException, IOException {
public static Set<String> verifyChallengeTransactionThreshold(String challengeXdr, String serverAccountId, Network network, String[] domainNames, String webAuthDomain, int threshold, Set<Signer> signers) throws InvalidSep10ChallengeException, IOException {
if (signers == null || signers.isEmpty()) {
throw new InvalidSep10ChallengeException("No verifiable signers provided, at least one G... address must be provided.");
}
Expand All @@ -360,7 +380,7 @@ public static Set<String> verifyChallengeTransactionThreshold(String challengeXd
weightsForSigner.put(signer.getKey(), signer.getWeight());
}

Set<String> signersFound = verifyChallengeTransactionSigners(challengeXdr, serverAccountId, network, domainNames, weightsForSigner.keySet());
Set<String> signersFound = verifyChallengeTransactionSigners(challengeXdr, serverAccountId, network, domainNames, webAuthDomain, weightsForSigner.keySet());

int sum = 0;
for (String signer : signersFound) {
Expand Down Expand Up @@ -389,14 +409,15 @@ public static Set<String> verifyChallengeTransactionThreshold(String challengeXd
* @param serverAccountId Account ID for server's account.
* @param network The network to connect to for verifying and retrieving.
* @param domainName The home domain that is expected to be included in the first Manage Data operation's string key.
* @param webAuthDomain The home domain that is expected to be included as the value of the Manage Data operation with the 'web_auth_domain' key, if present.
* @param threshold The threshold on the client account.
* @param signers The signers of client account.
* @return a list of signers that were found is returned, excluding the server account ID.
* @throws InvalidSep10ChallengeException If the SEP-0010 validation fails, the exception will be thrown.
* @throws IOException If read XDR string fails, the exception will be thrown.
*/
public static Set<String> verifyChallengeTransactionThreshold(String challengeXdr, String serverAccountId, Network network, String domainName, int threshold, Set<Signer> signers) throws InvalidSep10ChallengeException, IOException {
return verifyChallengeTransactionThreshold(challengeXdr, serverAccountId, network, new String[]{domainName}, threshold, signers);
public static Set<String> verifyChallengeTransactionThreshold(String challengeXdr, String serverAccountId,Network network, String domainName, String webAuthDomain, int threshold, Set<Signer> signers) throws InvalidSep10ChallengeException, IOException {
return verifyChallengeTransactionThreshold(challengeXdr, serverAccountId, network, new String[]{domainName}, webAuthDomain, threshold, signers);
}

private static Set<String> verifyTransactionSignatures(Transaction transaction, Set<String> signers) throws InvalidSep10ChallengeException {
Expand Down Expand Up @@ -436,7 +457,7 @@ private static boolean verifyTransactionSignature(Transaction transaction, Strin
}

/**
* Used to store the results produced by {@link Sep10Challenge#readChallengeTransaction(String, String, Network, String[])}.
* Used to store the results produced by {@link Sep10Challenge#readChallengeTransaction(String, String, Network, String[], String)}.
*/
public static class ChallengeTransaction {
private final Transaction transaction;
Expand Down
Loading

0 comments on commit e697d86

Please sign in to comment.