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

Java implementation #19

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions ref/java/README.md
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)
92 changes: 92 additions & 0 deletions ref/java/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?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>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
</dependencies>

</project>
154 changes: 154 additions & 0 deletions ref/java/src/main/java/org/bech32/Bech32.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package org.bech32;

import java.util.Locale;

public class Bech32 {

private static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";

public static String bech32Encode(String hrp, byte[] data) {

byte[] chk = createChecksum(hrp.getBytes(), 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.getBytes().length + xlat.length + 1];
System.arraycopy(hrp.getBytes(), 0, ret, 0, hrp.getBytes().length);
System.arraycopy(new byte[] { 0x31 }, 0, ret, hrp.getBytes().length, 1);
System.arraycopy(xlat, 0, ret, hrp.getBytes().length + 1, xlat.length);

return new String(ret);
}

public static Pair<String, byte[]> bech32Decode(String bech) throws Exception {

byte[] buffer = bech.getBytes();
for(byte b : buffer) {
if(b < 0x21 || b > 0x7e) {
throw new Exception("bech32 characters out of range");
}
}

if(!bech.equals(bech.toLowerCase(Locale.ROOT)) && !bech.equals(bech.toUpperCase(Locale.ROOT))) {
throw new Exception("bech32 cannot mix upper and lower case");
}

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(new String(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) {

final 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;
}

}
Loading