From 15a15ac0247838d5191daef5647221409810930f Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Fri, 2 Mar 2018 11:31:50 +0100 Subject: [PATCH] AddressFormatException: Add InvalidCharacter exception that is thrown when a character is used that is invalid in the encoding. --- .../bitcoinj/core/AddressFormatException.java | 16 ++++++++++++++++ .../main/java/org/bitcoinj/core/Base58.java | 2 +- .../main/java/org/bitcoinj/core/Bech32.java | 19 +++++++++++++------ .../java/org/bitcoinj/core/Base58Test.java | 5 +++++ .../java/org/bitcoinj/core/Bech32Test.java | 10 ++++++++++ 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/AddressFormatException.java b/core/src/main/java/org/bitcoinj/core/AddressFormatException.java index 2ded260d..88843c57 100644 --- a/core/src/main/java/org/bitcoinj/core/AddressFormatException.java +++ b/core/src/main/java/org/bitcoinj/core/AddressFormatException.java @@ -27,6 +27,22 @@ public class AddressFormatException extends IllegalArgumentException { super(message); } + /** + * This exception is thrown by {@link Base58}, {@link Bech32} and the {@link PrefixedChecksummedBytes} hierarchy of + * classes when you try to decode data and a character isn't valid. You shouldn't allow the user to proceed in this + * case. + */ + public static class InvalidCharacter extends AddressFormatException { + public final char character; + public final int position; + + public InvalidCharacter(char character, int position) { + super("Invalid character '" + Character.toString(character) + "' at position " + position); + this.character = character; + this.position = position; + } + } + /** * This exception is thrown by {@link Base58}, {@link Bech32} and the {@link PrefixedChecksummedBytes} hierarchy of * classes when you try to decode data and the checksum isn't valid. You shouldn't allow the user to proceed in this diff --git a/core/src/main/java/org/bitcoinj/core/Base58.java b/core/src/main/java/org/bitcoinj/core/Base58.java index a4c159de..8d94a205 100644 --- a/core/src/main/java/org/bitcoinj/core/Base58.java +++ b/core/src/main/java/org/bitcoinj/core/Base58.java @@ -129,7 +129,7 @@ public class Base58 { char c = input.charAt(i); int digit = c < 128 ? INDEXES[c] : -1; if (digit < 0) { - throw new AddressFormatException("Illegal character " + c + " at position " + i); + throw new AddressFormatException.InvalidCharacter(c, i); } input58[i] = (byte) digit; } diff --git a/core/src/main/java/org/bitcoinj/core/Bech32.java b/core/src/main/java/org/bitcoinj/core/Bech32.java index 395d1c2a..ae059acf 100644 --- a/core/src/main/java/org/bitcoinj/core/Bech32.java +++ b/core/src/main/java/org/bitcoinj/core/Bech32.java @@ -126,18 +126,25 @@ public class Bech32 { if (str.length() > 90) throw new AddressFormatException("Input too long"); for (int i = 0; i < str.length(); ++i) { char c = str.charAt(i); - if (c < 33 || c > 126) throw new AddressFormatException("Characters out of range"); - if (c >= 'a' && c <= 'z') lower = true; - if (c >= 'A' && c <= 'Z') upper = true; + if (c < 33 || c > 126) throw new AddressFormatException.InvalidCharacter(c, i); + if (c >= 'a' && c <= 'z') { + if (upper) + throw new AddressFormatException.InvalidCharacter(c, i); + lower = true; + } + if (c >= 'A' && c <= 'Z') { + if (lower) + throw new AddressFormatException.InvalidCharacter(c, i); + upper = true; + } } - if (lower && upper) throw new AddressFormatException("Cannot mix upper and lower cases"); - int pos = str.lastIndexOf('1'); + final int pos = str.lastIndexOf('1'); if (pos < 1) throw new AddressFormatException("Missing human-readable part"); if (pos + 7 > str.length()) throw new AddressFormatException("Data part too short"); byte[] values = new byte[str.length() - 1 - pos]; for (int i = 0; i < str.length() - 1 - pos; ++i) { char c = str.charAt(i + pos + 1); - if (CHARSET_REV[c] == -1) throw new AddressFormatException("Characters out of range"); + if (CHARSET_REV[c] == -1) throw new AddressFormatException.InvalidCharacter(c, i + pos + 1); values[i] = CHARSET_REV[c]; } String hrp = str.substring(0, pos).toLowerCase(Locale.ROOT); diff --git a/core/src/test/java/org/bitcoinj/core/Base58Test.java b/core/src/test/java/org/bitcoinj/core/Base58Test.java index a081fe07..63097f2d 100644 --- a/core/src/test/java/org/bitcoinj/core/Base58Test.java +++ b/core/src/test/java/org/bitcoinj/core/Base58Test.java @@ -83,6 +83,11 @@ public class Base58Test { Base58.decodeChecked("93VYUMzRG9DdbRP72uQXjaWibbQwygnvaCu9DumcqDjGybD864T"); } + @Test(expected = AddressFormatException.InvalidCharacter.class) + public void decode_invalidCharacter_notInAlphabet() { + Base58.decodeChecked("J0F12TrwUP45BMd"); + } + @Test(expected = AddressFormatException.InvalidChecksum.class) public void testDecodeChecked_invalidChecksum() { Base58.decodeChecked("4stwEBjT6FYyVW"); diff --git a/core/src/test/java/org/bitcoinj/core/Bech32Test.java b/core/src/test/java/org/bitcoinj/core/Bech32Test.java index ea7ec597..e18842a8 100644 --- a/core/src/test/java/org/bitcoinj/core/Bech32Test.java +++ b/core/src/test/java/org/bitcoinj/core/Bech32Test.java @@ -71,6 +71,16 @@ public class Bech32Test { "de1lg7wt" + new String(new char[] { 0xff }), }; + @Test(expected = AddressFormatException.InvalidCharacter.class) + public void decode_invalidCharacter_notInAlphabet() { + Bech32.decode("A12OUEL5X"); + } + + @Test(expected = AddressFormatException.InvalidCharacter.class) + public void decode_invalidCharacter_upperLowerMix() { + Bech32.decode("A12UeL5X"); + } + @Test(expected = AddressFormatException.InvalidChecksum.class) public void decode_invalidNetwork() { Bech32.decode("A12UEL5X");