-
Notifications
You must be signed in to change notification settings - Fork 104
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
20c2746
commit 105cca3
Showing
5 changed files
with
576 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Bech32 | ||
|
||
Bech32 implementation in Java. | ||
|
||
## Build Process | ||
|
||
Install Maven 3.2 or higher. | ||
|
||
### Build: | ||
|
||
mvn clean | ||
|
||
mvn package | ||
|
||
Two .jar files will be created in the directory ./target : | ||
|
||
Bech32.jar : Can be included in any Java project 'as is' but requires inclusion of dependencies. Main.java harness not included. | ||
|
||
Bech32-jar-with-dependencies.jar : includes all dependencies and can be run from the command line using the Main.java harness. | ||
|
||
### Run using Main.java harness: | ||
|
||
java -jar -ea target/Bech32-jar-with-dependencies.jar | ||
|
||
### Dev contact: | ||
|
||
[PGP](http://pgp.mit.edu/pks/lookup?op=get&search=0x72B5BACDFEDF39D7) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
|
||
<modelVersion>4.0.0</modelVersion> | ||
<groupId>org.bech32</groupId> | ||
<artifactId>Bech32</artifactId> | ||
<packaging>jar</packaging> | ||
<version>1.0-SNAPSHOT</version> | ||
<name>Bech32</name> | ||
<url>http://maven.apache.org</url> | ||
|
||
<properties> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
</properties> | ||
|
||
<build> | ||
<finalName>Bech32</finalName> | ||
|
||
<plugins> | ||
|
||
<!-- download source code in Eclipse, best practice --> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-eclipse-plugin</artifactId> | ||
<version>2.9</version> | ||
<configuration> | ||
<downloadSources>true</downloadSources> | ||
<downloadJavadocs>false</downloadJavadocs> | ||
</configuration> | ||
</plugin> | ||
|
||
<!-- Set a compiler level --> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<version>2.3.2</version> | ||
<configuration> | ||
<source>${jdk.version}</source> | ||
<target>${jdk.version}</target> | ||
</configuration> | ||
</plugin> | ||
|
||
<!-- Make this jar executable --> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-jar-plugin</artifactId> | ||
<version>2.6</version> | ||
<configuration> | ||
<excludes> | ||
<exclude>**/Main*</exclude> | ||
</excludes> | ||
</configuration> | ||
</plugin> | ||
|
||
<plugin> | ||
<artifactId>maven-assembly-plugin</artifactId> | ||
<configuration> | ||
<archive> | ||
<manifest> | ||
<mainClass>org.bech32.Main</mainClass> | ||
</manifest> | ||
</archive> | ||
<descriptorRefs> | ||
<descriptorRef>jar-with-dependencies</descriptorRef> | ||
</descriptorRefs> | ||
</configuration> | ||
<executions> | ||
<execution> | ||
<id>make-assembly</id> | ||
<phase>package</phase> | ||
<goals> | ||
<goal>single</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
|
||
</plugins> | ||
|
||
</build> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.apache.commons</groupId> | ||
<artifactId>commons-lang3</artifactId> | ||
<version>3.0</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>commons-codec</groupId> | ||
<artifactId>commons-codec</artifactId> | ||
<version>1.9</version> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
package org.bech32; | ||
|
||
import org.apache.commons.lang3.tuple.Pair; | ||
|
||
public class Bech32 { | ||
|
||
private static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; | ||
|
||
public static String bech32Encode(byte[] hrp, byte[] data) { | ||
|
||
byte[] chk = createChecksum(hrp, data); | ||
byte[] combined = new byte[chk.length + data.length]; | ||
|
||
System.arraycopy(data, 0, combined, 0, data.length); | ||
System.arraycopy(chk, 0, combined, data.length, chk.length); | ||
|
||
byte[] xlat = new byte[combined.length]; | ||
for(int i = 0; i < combined.length; i++) { | ||
xlat[i] = (byte)CHARSET.charAt(combined[i]); | ||
} | ||
|
||
byte[] ret = new byte[hrp.length + xlat.length + 1]; | ||
System.arraycopy(hrp, 0, ret, 0, hrp.length); | ||
System.arraycopy(new byte[] { 0x31 }, 0, ret, hrp.length, 1); | ||
System.arraycopy(xlat, 0, ret, hrp.length + 1, xlat.length); | ||
|
||
return new String(ret); | ||
} | ||
|
||
public static Pair<byte[], byte[]> bech32Decode(String bech) throws Exception { | ||
|
||
if(!bech.equals(bech.toLowerCase()) && !bech.equals(bech.toUpperCase())) { | ||
throw new Exception("bech32 cannot mix upper and lower case"); | ||
} | ||
|
||
byte[] buffer = bech.getBytes(); | ||
for(byte b : buffer) { | ||
if(b < 0x21 || b > 0x7e) { | ||
throw new Exception("bech32 characters out of range"); | ||
} | ||
} | ||
|
||
bech = bech.toLowerCase(); | ||
int pos = bech.lastIndexOf("1"); | ||
if(pos < 1) { | ||
throw new Exception("bech32 missing separator"); | ||
} | ||
else if(pos + 7 > bech.length()) { | ||
throw new Exception("bech32 separator misplaced"); | ||
} | ||
else if(bech.length() < 8) { | ||
throw new Exception("bech32 input too short"); | ||
} | ||
else if(bech.length() > 90) { | ||
throw new Exception("bech32 input too long"); | ||
} | ||
else { | ||
; | ||
} | ||
|
||
String s = bech.substring(pos + 1); | ||
for(int i = 0; i < s.length(); i++) { | ||
if(CHARSET.indexOf(s.charAt(i)) == -1) { | ||
throw new Exception("bech32 characters out of range"); | ||
} | ||
} | ||
|
||
byte[] hrp = bech.substring(0, pos).getBytes(); | ||
|
||
byte[] data = new byte[bech.length() - pos - 1]; | ||
for(int j = 0, i = pos + 1; i < bech.length(); i++, j++) { | ||
data[j] = (byte)CHARSET.indexOf(bech.charAt(i)); | ||
} | ||
|
||
if (!verifyChecksum(hrp, data)) { | ||
throw new Exception("invalid bech32 checksum"); | ||
} | ||
|
||
byte[] ret = new byte[data.length - 6]; | ||
System.arraycopy(data, 0, ret, 0, data.length - 6); | ||
|
||
return Pair.of(hrp, ret); | ||
} | ||
|
||
private static int polymod(byte[] values) { | ||
|
||
final int[] GENERATORS = { 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3 }; | ||
|
||
int chk = 1; | ||
|
||
for(byte b : values) { | ||
byte top = (byte)(chk >> 0x19); | ||
chk = b ^ ((chk & 0x1ffffff) << 5); | ||
for(int i = 0; i < 5; i++) { | ||
chk ^= ((top >> i) & 1) == 1 ? GENERATORS[i] : 0; | ||
} | ||
} | ||
|
||
return chk; | ||
} | ||
|
||
private static byte[] hrpExpand(byte[] hrp) { | ||
|
||
byte[] buf1 = new byte[hrp.length]; | ||
byte[] buf2 = new byte[hrp.length]; | ||
byte[] mid = new byte[1]; | ||
|
||
for (int i = 0; i < hrp.length; i++) { | ||
buf1[i] = (byte)(hrp[i] >> 5); | ||
} | ||
mid[0] = 0x00; | ||
for (int i = 0; i < hrp.length; i++) { | ||
buf2[i] = (byte)(hrp[i] & 0x1f); | ||
} | ||
|
||
byte[] ret = new byte[(hrp.length * 2) + 1]; | ||
System.arraycopy(buf1, 0, ret, 0, buf1.length); | ||
System.arraycopy(mid, 0, ret, buf1.length, mid.length); | ||
System.arraycopy(buf2, 0, ret, buf1.length + mid.length, buf2.length); | ||
|
||
return ret; | ||
} | ||
|
||
private static boolean verifyChecksum(byte[] hrp, byte[] data) { | ||
|
||
byte[] exp = hrpExpand(hrp); | ||
|
||
byte[] values = new byte[exp.length + data.length]; | ||
System.arraycopy(exp, 0, values, 0, exp.length); | ||
System.arraycopy(data, 0, values, exp.length, data.length); | ||
|
||
return (1 == polymod(values)); | ||
} | ||
|
||
private static byte[] createChecksum(byte[] hrp, byte[] data) { | ||
|
||
byte[] zeroes = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
byte[] expanded = hrpExpand(hrp); | ||
byte[] values = new byte[zeroes.length + expanded.length + data.length]; | ||
|
||
System.arraycopy(expanded, 0, values, 0, expanded.length); | ||
System.arraycopy(data, 0, values, expanded.length, data.length); | ||
System.arraycopy(zeroes, 0, values, expanded.length + data.length, zeroes.length); | ||
|
||
int polymod = polymod(values) ^ 1; | ||
byte[] ret = new byte[6]; | ||
for(int i = 0; i < ret.length; i++) { | ||
ret[i] = (byte)((polymod >> 5 * (5 - i)) & 0x1f); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
} |
Oops, something went wrong.