Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for SEP-0010. #264

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ public InvalidSep10ChallengeException() {
public InvalidSep10ChallengeException(String message) {
super(message);
}

public InvalidSep10ChallengeException(String message, Throwable cause) {
super(message, cause);
}
}
8 changes: 7 additions & 1 deletion src/main/java/org/stellar/sdk/Sep10Challenge.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,13 @@ public static ChallengeTransaction readChallengeTransaction(String challengeXdr,
}
overcat marked this conversation as resolved.
Show resolved Hide resolved

BaseEncoding base64Encoding = BaseEncoding.base64();
byte[] nonce = base64Encoding.decode(new String(manageDataOperation.getValue()));
byte[] nonce;
try {
nonce = base64Encoding.decode(new String(manageDataOperation.getValue()));
} catch (IllegalArgumentException e) {
throw new InvalidSep10ChallengeException("Failed to decode random nonce provided in ManageData operation.", e);
leighmcculloch marked this conversation as resolved.
Show resolved Hide resolved
}
overcat marked this conversation as resolved.
Show resolved Hide resolved

if (nonce.length != 48) {
throw new InvalidSep10ChallengeException("Random nonce before encoding as base64 should be 48 bytes long.");
}
Expand Down
109 changes: 103 additions & 6 deletions src/test/java/org/stellar/sdk/Sep10ChallengeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void testChallenge() throws IOException {
}

@Test
public void testReadChallengeTransactionValidSignedByServerAndClient() throws InvalidSep10ChallengeException, IOException {
public void testReadChallengeTransactionValidSignedByServer() throws InvalidSep10ChallengeException, IOException {
KeyPair server = KeyPair.random();
KeyPair client = KeyPair.random();
Network network = Network.TESTNET;
Expand All @@ -73,11 +73,11 @@ public void testReadChallengeTransactionValidSignedByServerAndClient() throws In
);

Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET);
assertEquals(challengeTransaction, new Sep10Challenge.ChallengeTransaction(Transaction.fromEnvelopeXdr(challenge, Network.TESTNET), client.getAccountId()));
assertEquals(new Sep10Challenge.ChallengeTransaction(Transaction.fromEnvelopeXdr(challenge, Network.TESTNET), client.getAccountId()), challengeTransaction);
}

@Test
public void testReadChallengeTransactionValidSignedByServer() throws InvalidSep10ChallengeException, IOException {
public void testReadChallengeTransactionValidSignedByServerAndClient() throws InvalidSep10ChallengeException, IOException {
KeyPair server = KeyPair.random();
KeyPair client = KeyPair.random();
Network network = Network.TESTNET;
Expand All @@ -97,8 +97,8 @@ public void testReadChallengeTransactionValidSignedByServer() throws InvalidSep1
Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network);
transaction.sign(client);

Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET);
assertEquals(challengeTransaction, new Sep10Challenge.ChallengeTransaction(Transaction.fromEnvelopeXdr(challenge, Network.TESTNET), client.getAccountId()));
Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), server.getAccountId(), Network.TESTNET);
assertEquals(new Sep10Challenge.ChallengeTransaction(Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdrBase64(), Network.TESTNET), client.getAccountId()), challengeTransaction);
}
overcat marked this conversation as resolved.
Show resolved Hide resolved

@Test
Expand Down Expand Up @@ -143,6 +143,34 @@ public void testReadChallengeTransactionInvalidNotSignedByServer() throws IOExce
}
}

leighmcculloch marked this conversation as resolved.
Show resolved Hide resolved
@Test
public void testReadChallengeTransactionInvalidCorrupted() throws InvalidSep10ChallengeException, IOException {
KeyPair server = KeyPair.random();
KeyPair client = KeyPair.random();
Network network = Network.TESTNET;

long now = System.currentTimeMillis() / 1000L;
long end = now + 300;
TimeBounds timeBounds = new TimeBounds(now, end);

String challenge = Sep10Challenge.newChallenge(
server,
network,
client.getAccountId(),
"Stellar Test",
timeBounds
);

challenge = challenge.replace("A", "B");

try {
Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET);
fail();
} catch (RuntimeException ignored) {
// TODO: I think we should add an exception signature about the failure to read XDR on Transaction.fromEnvelopeXdr.
}
leighmcculloch marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
public void testReadChallengeTransactionInvalidServerAccountIDMismatch() throws IOException {
KeyPair server = KeyPair.random();
Expand Down Expand Up @@ -511,6 +539,45 @@ public void testReadChallengeTransactionInvalidDataValueWrongEncodedLength() thr
}
}

leighmcculloch marked this conversation as resolved.
Show resolved Hide resolved
@Test
public void testReadChallengeTransactionInvalidDataValueCorruptBase64() throws IOException {
KeyPair server = KeyPair.random();
KeyPair client = KeyPair.random();
String anchorName = "Stellar Test";
Network network = Network.TESTNET;

long now = System.currentTimeMillis() / 1000L;
long end = now + 300;
TimeBounds timeBounds = new TimeBounds(now, end);

byte[] encodedNonce = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?AAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes("UTF-8");
Account sourceAccount = new Account(server.getAccountId(), -1L);
ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce)
.setSourceAccount(client.getAccountId())
.build();

Operation[] operations = new Operation[]{manageDataOperation1};
Transaction transaction = new Transaction(
sourceAccount.getAccountId(),
100 * operations.length,
sourceAccount.getIncrementedSequenceNumber(),
operations,
Memo.none(),
timeBounds,
network
);
transaction.sign(server);
String challenge = transaction.toEnvelopeXdrBase64();

try {
Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET);
fail();
} catch (InvalidSep10ChallengeException e) {
assertEquals("Failed to decode random nonce provided in ManageData operation.", e.getMessage());
assertEquals("com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: ?", e.getCause().getMessage());
}
}

@Test
public void testReadChallengeTransactionInvalidDataValueWrongByteLength() throws IOException {
KeyPair server = KeyPair.random();
Expand Down Expand Up @@ -1314,7 +1381,7 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFa
}

@Test
public void testVerifyChallengeTransactionSignersInvalidNoSigners() throws IOException {
public void testVerifyChallengeTransactionSignersInvalidNoSignersNull() throws IOException {
KeyPair server = KeyPair.random();
KeyPair masterClient = KeyPair.random();
KeyPair signerClient1 = KeyPair.random();
Expand Down Expand Up @@ -1342,4 +1409,34 @@ public void testVerifyChallengeTransactionSignersInvalidNoSigners() throws IOExc
assertEquals("No verifiable signers provided, at least one G... address must be provided.", e.getMessage());
}
}

@Test
public void testVerifyChallengeTransactionSignersInvalidNoSignersEmptySet() throws IOException {
KeyPair server = KeyPair.random();
KeyPair masterClient = KeyPair.random();
KeyPair signerClient1 = KeyPair.random();
Network network = Network.TESTNET;

long now = System.currentTimeMillis() / 1000L;
long end = now + 300;
TimeBounds timeBounds = new TimeBounds(now, end);

String challenge = Sep10Challenge.newChallenge(
server,
network,
masterClient.getAccountId(),
"Stellar Test",
timeBounds
);

Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network);
transaction.sign(signerClient1);

try {
Sep10Challenge.verifyChallengeTransactionSigners(transaction.toEnvelopeXdrBase64(), server.getAccountId(), network, Collections.<String>emptySet());
fail();
} catch (InvalidSep10ChallengeException e) {
assertEquals("No verifiable signers provided, at least one G... address must be provided.", e.getMessage());
}
}
}