Intial commit

This commit is contained in:
Willem Cazander 2023-09-24 12:41:43 +02:00
parent 7329fb8dea
commit 73f201bdac
268 changed files with 11220 additions and 5 deletions

16
no2all-octo/pom.xml Normal file
View file

@ -0,0 +1,16 @@
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>love.distributedrebirth.no2all</groupId>
<artifactId>no2all</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>no2all-octo</artifactId>
<name>No2All-Octo</name>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,194 @@
package love.distributedrebirth.no2all.octo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public final class OctoBech32 {
private static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
private static final byte[] CHARSET_REV = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23,
-1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13,
25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 };
public static OctoBech32String toBech32(String hrp, byte[] hexKey) {
byte[] data = convertBits(hexKey, 8, 5, true);
return encode(new OctoBech32Bucket(OctoBech32Variant.BECH32, hrp, data));
}
public static OctoBech32Bucket fromBech32(OctoBech32String link) {
OctoBech32Bucket shareB32 = decode(link);
byte[] data = shareB32.getData();
data = convertBits(data, 5, 8, true);
data = Arrays.copyOfRange(data, 0, data.length - 1);
return new OctoBech32Bucket(shareB32.getVariant(), shareB32.getHrp(), data);
}
public static OctoBech32String encode(OctoBech32Bucket shareB32) {
if (shareB32.getHrp().length() < 1) {
throw new IllegalArgumentException("Human-readable part is too short");
}
String hrp = shareB32.getHrp().toLowerCase(Locale.ROOT);
byte[] values = shareB32.getData();
byte[] checksum = createChecksum(shareB32.getVariant(), hrp, values);
byte[] combined = new byte[values.length + checksum.length];
System.arraycopy(values, 0, combined, 0, values.length);
System.arraycopy(checksum, 0, combined, values.length, checksum.length);
StringBuilder sb = new StringBuilder(hrp.length() + 1 + combined.length);
sb.append(hrp);
sb.append('1');
for (byte b : combined) {
sb.append(CHARSET.charAt(b));
}
return new OctoBech32String(sb.toString());
}
public static OctoBech32Bucket decode(final OctoBech32String link) {
final String str = link.getValue();
boolean lower = false;
boolean upper = false;
if (str.length() < 8) {
throw new IllegalArgumentException("Input too short: " + str.length());
}
for (int i = 0; i < str.length(); ++i) {
char c = str.charAt(i);
if (c < 33 || c > 126) {
throw new IllegalArgumentException(String.format("Invalid Character %c, %d", c, i));
}
if (c >= 'a' && c <= 'z') {
if (upper) {
throw new IllegalArgumentException(String.format("Invalid Character %c, %d", c, i));
}
lower = true;
}
if (c >= 'A' && c <= 'Z') {
if (lower) {
throw new IllegalArgumentException(String.format("Invalid Character %c, %d", c, i));
}
upper = true;
}
}
final int pos = str.lastIndexOf('1');
if (pos < 1) {
throw new IllegalArgumentException("Missing human-readable part");
}
final int dataPartLength = str.length() - 1 - pos;
if (dataPartLength < 6) {
throw new IllegalArgumentException(String.format("Data part too short: %d)", dataPartLength));
}
byte[] values = new byte[dataPartLength];
for (int i = 0; i < dataPartLength; ++i) {
char c = str.charAt(i + pos + 1);
if (CHARSET_REV[c] == -1) {
throw new IllegalArgumentException(String.format("Invalid Character %c, %d", c, i + pos + 1));
}
values[i] = CHARSET_REV[c];
}
String hrp = str.substring(0, pos).toLowerCase(Locale.ROOT);
OctoBech32Variant encoding = verifyChecksum(hrp, values);
if (encoding == null) {
throw new IllegalArgumentException("InvalidChecksum");
}
return new OctoBech32Bucket(encoding, hrp, Arrays.copyOfRange(values, 0, values.length - 6));
}
/**
* Find the polynomial with value coefficients mod the generator as 30-bit.
*/
private static int polymod(final byte[] values) {
int c = 1;
for (byte v_i : values) {
int c0 = (c >>> 25) & 0xff;
c = ((c & 0x1ffffff) << 5) ^ (v_i & 0xff);
if ((c0 & 1) != 0) {
c ^= 0x3b6a57b2;
}
if ((c0 & 2) != 0) {
c ^= 0x26508e6d;
}
if ((c0 & 4) != 0) {
c ^= 0x1ea119fa;
}
if ((c0 & 8) != 0) {
c ^= 0x3d4233dd;
}
if ((c0 & 16) != 0) {
c ^= 0x2a1462b3;
}
}
return c;
}
/**
* Expand a HRP for use in checksum computation.
*/
private static byte[] expandHrp(final String hrp) {
int hrpLength = hrp.length();
byte ret[] = new byte[hrpLength * 2 + 1];
for (int i = 0; i < hrpLength; ++i) {
int c = hrp.charAt(i) & 0x7f; // Limit to standard 7-bit ASCII
ret[i] = (byte) ((c >>> 5) & 0x07);
ret[i + hrpLength + 1] = (byte) (c & 0x1f);
}
ret[hrpLength] = 0;
return ret;
}
private static OctoBech32Variant verifyChecksum(final String hrp, final byte[] values) {
byte[] hrpExpanded = expandHrp(hrp);
byte[] combined = new byte[hrpExpanded.length + values.length];
System.arraycopy(hrpExpanded, 0, combined, 0, hrpExpanded.length);
System.arraycopy(values, 0, combined, hrpExpanded.length, values.length);
int check = polymod(combined);
for (OctoBech32Variant type:OctoBech32Variant.values()) {
if (type.getMarker() == check) {
return type;
}
}
return null;
}
private static byte[] createChecksum(final OctoBech32Variant encoding, final String hrp, final byte[] values) {
byte[] hrpExpanded = expandHrp(hrp);
byte[] enc = new byte[hrpExpanded.length + values.length + 6];
System.arraycopy(hrpExpanded, 0, enc, 0, hrpExpanded.length);
System.arraycopy(values, 0, enc, hrpExpanded.length, values.length);
int mod = polymod(enc) ^ encoding.getMarker();
byte[] ret = new byte[6];
for (int i = 0; i < 6; ++i) {
ret[i] = (byte) ((mod >>> (5 * (5 - i))) & 31);
}
return ret;
}
private static byte[] convertBits(byte[] data, int fromWidth, int toWidth, boolean pad) {
int acc = 0;
int bits = 0;
List<Byte> result = new ArrayList<>();
for (int i = 0; i < data.length; i++) {
int value = (data[i] & 0xff) & ((1 << fromWidth) - 1);
acc = (acc << fromWidth) | value;
bits += fromWidth;
while (bits >= toWidth) {
bits -= toWidth;
result.add((byte) ((acc >> bits) & ((1 << toWidth) - 1)));
}
}
if (pad) {
if (bits > 0) {
result.add((byte) ((acc << (toWidth - bits)) & ((1 << toWidth) - 1)));
}
} else if (bits == fromWidth || ((acc << (toWidth - bits)) & ((1 << toWidth) - 1)) != 0) {
return null;
}
byte[] output = new byte[result.size()];
for (int i = 0; i < output.length; i++) {
output[i] = result.get(i);
}
return output;
}
}

View file

@ -0,0 +1,28 @@
package love.distributedrebirth.no2all.octo;
import java.util.Objects;
public final class OctoBech32Bucket {
private final OctoBech32Variant variant;
private final String hrp;
private final byte[] data;
public OctoBech32Bucket(OctoBech32Variant variant, String hrp, byte[] data) {
this.variant = Objects.requireNonNull(variant);
this.hrp = Objects.requireNonNull(hrp);
this.data = Objects.requireNonNull(data);
}
public OctoBech32Variant getVariant() {
return variant;
}
public String getHrp() {
return hrp;
}
public byte[] getData() {
return data;
}
}

View file

@ -0,0 +1,20 @@
package love.distributedrebirth.no2all.octo;
import java.util.Objects;
public final class OctoBech32String {
private final String value;
public OctoBech32String(String value) {
this.value = Objects.requireNonNull(value);
}
public String getValue() {
return value;
}
public static OctoBech32String of(String value) {
return new OctoBech32String(value);
}
}

View file

@ -0,0 +1,19 @@
package love.distributedrebirth.no2all.octo;
public enum OctoBech32Variant {
//[BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)
BECH32(1),
//[BIP-0350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)
BECH32M(0x2bc830a3),
;
private final int marker;
private OctoBech32Variant(int marker) {
this.marker = marker;
}
public int getMarker() {
return marker;
}
}

View file

@ -0,0 +1,100 @@
package love.distributedrebirth.no2all.octo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public final class OctoBitChain {
private static final int INDEX_OF_FIRST = 0;
private final List<OctoBitChainFrame> frames = new ArrayList<>();
public OctoBitChain() {
}
public OctoBitChain(byte[] data) {
this(ByteBuffer.wrap(data));
}
public OctoBitChain(ByteBuffer buffer) {
while (buffer.hasRemaining()) {
byte frameType = buffer.get();
int frameLength = buffer.get();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < frameLength; i++) {
baos.write(buffer.get());
}
frames.add(new OctoBitChainFrame(frameType, baos.toByteArray()));
}
}
public void clear() {
frames.clear();
}
public int getFrameCount() {
return frames.size();
}
public void addFrame(byte type, byte[] data) {
frames.add(new OctoBitChainFrame(type, Objects.requireNonNull(data)));
}
public void addFrame(OctoBitChainFrameType type, byte[] data) {
addFrame((byte) Objects.requireNonNull(type).getTypeByte(), Objects.requireNonNull(data));
}
public List<OctoBitChainFrame> getFramesByType(OctoBitChainFrameType type) {
return getFramesByType((byte)Objects.requireNonNull(type).getTypeByte());
}
public List<OctoBitChainFrame> getFramesByType(byte type) {
List<OctoBitChainFrame> result = new ArrayList<>();
int framesLength = frames.size();
for (int frameIdx = 0; frameIdx < framesLength; frameIdx++) {
OctoBitChainFrame frame = frames.get(frameIdx);
if (frame.getType() == type) {
result.add(frame);
}
}
return result;
}
public byte[] getFirstDataOfType(OctoBitChainFrameType type) {
return getFirstDataOfType((byte)Objects.requireNonNull(type).getTypeByte());
}
public byte[] getFirstDataOfType(byte type) {
List<OctoBitChainFrame> frames = getFramesByType(type);
if (frames.isEmpty()) {
return null;
}
return frames.get(INDEX_OF_FIRST).getData();
}
public void writeOutputStream(OutputStream out) throws IOException {
int framesLength = frames.size();
for (int frameIdx = 0; frameIdx < framesLength; frameIdx++) {
OctoBitChainFrame frame = frames.get(frameIdx);
byte[] data = frame.getData();
out.write(frame.getType());
out.write(data.length);
for (int dataIdx = 0; dataIdx < data.length; dataIdx++) {
out.write(data[dataIdx]);
}
}
}
public byte[] toByteArray() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeOutputStream(baos);
} catch (IOException ignoredOnBAOS) {
}
return baos.toByteArray();
}
}

View file

@ -0,0 +1,22 @@
package love.distributedrebirth.no2all.octo;
import java.util.Objects;
public final class OctoBitChainFrame {
private final byte type;
private final byte[] data;
public OctoBitChainFrame(byte type, byte[] data) {
this.type = type;
this.data = Objects.requireNonNull(data);
}
public byte getType() {
return type;
}
public byte[] getData() {
return data;
}
}

View file

@ -0,0 +1,6 @@
package love.distributedrebirth.no2all.octo;
public interface OctoBitChainFrameType {
byte getTypeByte();
}

View file

@ -0,0 +1,8 @@
package love.distributedrebirth.no2all.octo;
public interface OctoBitConverter<T> {
T fromBytes(byte[] data);
byte[] toBytes(T value);
}

View file

@ -0,0 +1,39 @@
package love.distributedrebirth.no2all.octo;
import java.nio.charset.StandardCharsets;
import love.distributedrebirth.no2all.octo.conv.OctoConvSIntBigIndian;
import love.distributedrebirth.no2all.octo.conv.OctoConvSLongBigIndian;
import love.distributedrebirth.no2all.octo.conv.OctoConvStringCharset;
import love.distributedrebirth.no2all.octo.conv.OctoConvStringHex;
import love.distributedrebirth.no2all.octo.conv.OctoConvStringHexUtf8;
import love.distributedrebirth.no2all.octo.conv.OctoConvStringIDN;
import love.distributedrebirth.no2all.octo.conv.OctoConvUIntBigIndian;
public final class OctoBitFormat {
public static final OctoBitConverter<Long> BI_UI32 = new OctoConvUIntBigIndian();
public static final OctoBitConverter<Integer> BI_SI32 = new OctoConvSIntBigIndian();
public static final OctoBitConverter<Long> BI_SI64 = new OctoConvSLongBigIndian();
public static final OctoBitConverter<String> ASCII = new OctoConvStringCharset(StandardCharsets.US_ASCII);
public static final OctoBitConverter<String> UTF8 = new OctoConvStringCharset(StandardCharsets.UTF_8);
public static final OctoBitConverter<String> IDN = new OctoConvStringIDN();
public static final OctoBitConverter<String> HEX = new OctoConvStringHex();
// October is month 8 and only a real language has it own white space program
public static final OctoBitConverter<String> HEX_CCP = new OctoConvStringHexUtf8(new String[] {
"\u3000","\u205F","\u202F","\u200A","\u2009","\u2008","\u2007","\u2006",
"\u2005","\u2004","\u2003","\u2002","\u2001","\u2000","\u00A0","\u0020"
});
public static final OctoBitConverter<String> HEX_GREEK = new OctoConvStringHexUtf8(new String[] {
"ō","α","β","γ","δ","ε","ϝ","ζ","η","θ","ι","κ","λ","μ","ν","ξ"
});
public static final OctoBitConverter<String> HEX_DIPAVALI = new OctoConvStringHexUtf8(new String[] {
"˧˥˩","˧˩˥","˧˥˦","˧˩˨","˧˦˦","˧˨˨","˧˥˥","˧˩˩","˥˩˧","˩˥˧","˥˦˧","˩˨˧","˦˦˧","˨˨˧","˥˥˧","˩˩˧"
}); // note: In real unicode renderer this (_LRPATH) renders as a single glyph per hex nibble.
private OctoBitFormat() {
}
}

View file

@ -0,0 +1,27 @@
package love.distributedrebirth.no2all.octo.conv;
import love.distributedrebirth.no2all.octo.OctoBitConverter;
public final class OctoConvSIntBigIndian implements OctoBitConverter<Integer> {
@Override
public Integer fromBytes(byte[] data) {
int result = 0;
result += (data[0] << 0) & 0xFF;
result += (data[1] << 8) & 0xFF00;
result += (data[2] << 16) & 0xFF0000;
result += (data[3] << 24) & 0xFF000000;
return result;
}
@Override
public byte[] toBytes(Integer value) {
int data = value.intValue();
byte[] result = new byte[4];
result[0] = (byte)(data >> 0);
result[1] = (byte)(data >> 8);
result[2] = (byte)(data >> 16);
result[3] = (byte)(data >> 24);
return result;
}
}

View file

@ -0,0 +1,35 @@
package love.distributedrebirth.no2all.octo.conv;
import love.distributedrebirth.no2all.octo.OctoBitConverter;
public final class OctoConvSLongBigIndian implements OctoBitConverter<Long> {
@Override
public Long fromBytes(byte[] data) {
long result = 0;
result += (data[0] << 0) & 0xFFL;
result += (data[1] << 8) & 0xFF00L;
result += (data[2] << 16) & 0xFF0000L;
result += (data[3] << 24) & 0xFF000000L;
result += ((long)data[4] << 32) & 0xFF00000000L;
result += ((long)data[5] << 40) & 0xFF0000000000L;
result += ((long)data[6] << 48) & 0xFF000000000000L;
result += ((long)data[7] << 56) & 0xFF00000000000000L;
return result;
}
@Override
public byte[] toBytes(Long value) {
long data = value.longValue();
byte[] result = new byte[8];
result[0] = (byte)(data >> 0);
result[1] = (byte)(data >> 8);
result[2] = (byte)(data >> 16);
result[3] = (byte)(data >> 24);
result[4] = (byte)(data >> 32);
result[5] = (byte)(data >> 40);
result[6] = (byte)(data >> 48);
result[7] = (byte)(data >> 56);
return result;
}
}

View file

@ -0,0 +1,25 @@
package love.distributedrebirth.no2all.octo.conv;
import java.nio.charset.Charset;
import java.util.Objects;
import love.distributedrebirth.no2all.octo.OctoBitConverter;
public final class OctoConvStringCharset implements OctoBitConverter<String> {
private final Charset charset;
public OctoConvStringCharset(Charset charset) {
this.charset = Objects.requireNonNull(charset);
}
@Override
public String fromBytes(byte[] data) {
return new String(data, charset);
}
@Override
public byte[] toBytes(String value) {
return value.getBytes(charset);
}
}

View file

@ -0,0 +1,47 @@
package love.distributedrebirth.no2all.octo.conv;
import love.distributedrebirth.no2all.octo.OctoBitConverter;
public final class OctoConvStringHex implements OctoBitConverter<String> {
private final char[] valueToHex = "0123456789abcdef".toCharArray();
private final byte[] valueToBytes = new byte[128];
public OctoConvStringHex() {
for (int i = 0; i < valueToBytes.length; i++) {
valueToBytes[i] = (byte)-1;
}
for (int i = 0; i < valueToHex.length; i++) {
char c = valueToHex[i];
valueToBytes[c] = (byte)i;
if (!Character.isDigit(c)) {
valueToBytes[Character.toUpperCase(c)] = (byte)i;
}
}
}
@Override
public String fromBytes(byte[] data) {
int dataLength = data.length;
char[] hexChars = new char[dataLength * 2];
for (int j = 0; j < dataLength; j++) {
int v = data[j] & 0xFF;
int idx = j * 2;
hexChars[idx] = valueToHex[v >>> 4];
hexChars[idx + 1] = valueToHex[v & 0x0F];
}
return new String(hexChars);
}
@Override
public byte[] toBytes(String value) {
int valueLength = value.length();
byte[] buf = new byte[valueLength >>> 1];
for (int i = 0; i < valueLength; i += 2) {
byte v0 = valueToBytes[value.charAt(i)];
byte v1 = valueToBytes[value.charAt(i + 1)];
buf[i / 2] = (byte) ((v0 << 4) + v1);
}
return buf;
}
}

View file

@ -0,0 +1,61 @@
package love.distributedrebirth.no2all.octo.conv;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.PrimitiveIterator.OfInt;
import love.distributedrebirth.no2all.octo.OctoBitConverter;
public final class OctoConvStringHexUtf8 implements OctoBitConverter<String> {
private final String[] valueToHex;
private final Map<String, Byte> valueToBytes = new HashMap<>();
public OctoConvStringHexUtf8(String[] valueToHex) {
if (valueToHex.length != 16) {
throw new IllegalArgumentException("Need 16 chars for hex, got: " + valueToHex.length);
}
this.valueToHex = valueToHex;
for (int i = 0; i < valueToHex.length; i++) {
valueToBytes.put(valueToHex[i], (byte)i);
}
}
@Override
public String fromBytes(byte[] data) {
StringBuilder buf = new StringBuilder();
int dataLength = data.length;
for (int j = 0; j < dataLength; j++) {
int v = data[j] & 0xFF;
buf.append(valueToHex[v >>> 4]);
buf.append(valueToHex[v & 0x0F]);
}
return buf.toString();
}
@Override
public byte[] toBytes(String value) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
StringBuilder buf = new StringBuilder();
Byte digitPrev = null;
OfInt i = value.codePoints().iterator();
while (i.hasNext()) {
buf.appendCodePoint(i.nextInt());
String digitHex = buf.toString();
if (!valueToBytes.containsKey(digitHex)) {
continue;
}
buf = new StringBuilder();
if (digitPrev == null) {
digitPrev = valueToBytes.get(digitHex);
} else {
byte digit = valueToBytes.get(digitHex);
byte v = (byte) ((digitPrev << 4) + digit);
baos.write(v);
digitPrev = null;
}
}
return baos.toByteArray();
}
}

View file

@ -0,0 +1,19 @@
package love.distributedrebirth.no2all.octo.conv;
import java.net.IDN;
import java.nio.charset.StandardCharsets;
import love.distributedrebirth.no2all.octo.OctoBitConverter;
public final class OctoConvStringIDN implements OctoBitConverter<String> {
@Override
public String fromBytes(byte[] data) {
return IDN.toUnicode(new String(data, StandardCharsets.US_ASCII), IDN.ALLOW_UNASSIGNED);
}
@Override
public byte[] toBytes(String value) {
return IDN.toASCII(value, IDN.ALLOW_UNASSIGNED).getBytes(StandardCharsets.US_ASCII);
}
}

View file

@ -0,0 +1,27 @@
package love.distributedrebirth.no2all.octo.conv;
import love.distributedrebirth.no2all.octo.OctoBitConverter;
public final class OctoConvUIntBigIndian implements OctoBitConverter<Long> {
@Override
public Long fromBytes(byte[] data) {
long result = 0;
result += (data[0] << 0) & 0xFF;
result += (data[1] << 8) & 0xFF00;
result += (data[2] << 16) & 0xFF0000;
result += (data[3] << 24) & 0xFF000000;
return result;
}
@Override
public byte[] toBytes(Long value) {
long data = value.longValue();
byte[] result = new byte[4];
result[0] = (byte)(data >> 0);
result[1] = (byte)(data >> 8);
result[2] = (byte)(data >> 16);
result[3] = (byte)(data >> 24);
return result;
}
}

View file

@ -0,0 +1,39 @@
package love.distributedrebirth.no2all.octo.trust;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public final class OctoTrust {
public static final String KEY_SPEC = "secp256k1";
public static final int KEY_LENGTH = 32;
private static final BigInteger MAXPRIVATEKEY = new BigInteger(
"00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140", 16);
public static SecureRandom createSecureRandom() {
try {
return SecureRandom.getInstanceStrong();
} catch (NoSuchAlgorithmException e) {
throw new OctoTrustException(e);
}
}
public static byte[] generateSecretKey() {
SecureRandom secureRandom = createSecureRandom();
byte[] privateKeyAttempt = new byte[KEY_LENGTH];
secureRandom.nextBytes(privateKeyAttempt);
BigInteger privateKeyCheck = new BigInteger(1, privateKeyAttempt);
while (privateKeyCheck.compareTo(BigInteger.ZERO) == 0 || privateKeyCheck.compareTo(MAXPRIVATEKEY) == 1) {
secureRandom.nextBytes(privateKeyAttempt);
privateKeyCheck = new BigInteger(1, privateKeyAttempt);
}
return privateKeyAttempt;
}
public static byte[] createRandomByteArray(int length) {
byte[] result = new byte[length];
createSecureRandom().nextBytes(result);
return result;
}
}

View file

@ -0,0 +1,72 @@
package love.distributedrebirth.no2all.octo.trust;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public final class OctoTrustAES {
private static final String CIPHER = "AES/CBC/PKCS5Padding";
private static final String KEY_SPEC = "AES";
private static final int IV_LENGTH = 16;
private static final String PW_ALGORITHM = "PBKDF2WithHmacSHA256";
private static final int PW_ITERATION_CNT = 65536;
private static final int PW_KEY_LENGTH = 256;
public static IvParameterSpec createRandomIv() {
return new IvParameterSpec(OctoTrust.createRandomByteArray(IV_LENGTH));
}
public static boolean isSecretPasswordValid(char[] password, byte[] salt) {
try {
createSecretPassword(password, salt);
return true;
} catch (OctoTrustException e) {
return false;
}
}
public static SecretKey createSecretPassword(char[] password, byte[] salt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance(PW_ALGORITHM);
KeySpec spec = new PBEKeySpec(password, salt, PW_ITERATION_CNT, PW_KEY_LENGTH);
return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), KEY_SPEC);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new OctoTrustException(e);
}
}
public static byte[] encrypt(byte[] data, SecretKey key, IvParameterSpec iv) {
try {
Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
return cipher.doFinal(data);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new OctoTrustException(e);
}
}
public static byte[] decrypt(byte[] data, SecretKey key, IvParameterSpec iv) {
try {
Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(data);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new OctoTrustException(e);
}
}
}

View file

@ -0,0 +1,88 @@
package love.distributedrebirth.no2all.octo.trust;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
public final class OctoTrustAESCurve {
private static final String CIPHER = "AES/CBC/PKCS5Padding";
private static final String KEY_SPEC = "AES";
private static final int KEY_LENGTH = 32;
private static final int IV_LENGTH = 16;
private static byte[] bytesFromBigInteger(BigInteger n) {
byte[] b = n.toByteArray();
if (b.length == KEY_LENGTH) {
return b;
} else if (b.length > KEY_LENGTH) {
return java.util.Arrays.copyOfRange(b, b.length - KEY_LENGTH, b.length);
} else {
byte[] buf = new byte[KEY_LENGTH];
System.arraycopy(b, 0, buf, buf.length - b.length, b.length);
return buf;
}
}
public static BigInteger calculateKeyAgreement(BigInteger privKey, BigInteger theirPubKey) {
ECDomainParameters param = OctoTrustAESCurveX9EC.FACTORY.getCurveEC();
ECPrivateKeyParameters privKeyP = new ECPrivateKeyParameters(privKey, param);
byte[] compressed = new byte[] { 2 };
byte[] val = Arrays.concatenate(compressed, bytesFromBigInteger(theirPubKey));
ECPoint ecPoint = param.getCurve().decodePoint(val);
ECPublicKeyParameters pubKeyP = new ECPublicKeyParameters(ecPoint, param);
ECDHBasicAgreement agreement = new ECDHBasicAgreement();
agreement.init(privKeyP);
return agreement.calculateAgreement(pubKeyP);
}
public static String encrypt(BigInteger key, String msg) {
try {
Random r = SecureRandom.getInstanceStrong();
byte[] iv = new byte[IV_LENGTH];
r.nextBytes(iv);
Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(bytesFromBigInteger(key), KEY_SPEC), new IvParameterSpec(iv));
String ivBase64 = Base64.toBase64String(iv);
byte[] encryptedMsg = cipher.doFinal(msg.getBytes());
String encryptedMsgBase64 = Base64.toBase64String(encryptedMsg);
return String.format("%s?iv=%s", encryptedMsgBase64, ivBase64);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new OctoTrustException(e);
}
}
public static String decrypt(BigInteger key, String encryptedMsg) {
try {
int pos = encryptedMsg.indexOf("?iv=");
String msgPart = encryptedMsg.substring(0, pos);
String ivPart = encryptedMsg.substring(pos + 4);
byte[] decodedMsg = Base64.decode(msgPart);
byte[] iv = Base64.decode(ivPart);
Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(bytesFromBigInteger(key), KEY_SPEC), new IvParameterSpec(iv));
return new String(cipher.doFinal(decodedMsg));
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new OctoTrustException(e);
}
}
}

View file

@ -0,0 +1,26 @@
package love.distributedrebirth.no2all.octo.trust;
import org.bouncycastle.asn1.sec.SECNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
public enum OctoTrustAESCurveX9EC {
FACTORY;
private static final String CURVE_NAME = "secp256k1";
private final X9ECParameters curveX9;
private final ECDomainParameters curveEC;
private OctoTrustAESCurveX9EC() {
curveX9 = SECNamedCurves.getByName(CURVE_NAME);
curveEC = new ECDomainParameters(curveX9.getCurve(), curveX9.getG(), curveX9.getN(), curveX9.getH());
}
public X9ECParameters getCurveX9() {
return curveX9;
}
public ECDomainParameters getCurveEC() {
return curveEC;
}
}

View file

@ -0,0 +1,51 @@
package love.distributedrebirth.no2all.octo.trust;
import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class OctoTrustChaChaPx {
private static final String CIPHER = "ChaCha20-Poly1305";
private static final int NONCE_LENGTH = 12;
public static byte[] createRandomNonce() {
return OctoTrust.createRandomByteArray(NONCE_LENGTH);
}
public byte[] encrypt(byte[] data, SecretKey key, byte[] nonce) {
try {
Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(nonce));
byte[] encrypted = cipher.doFinal(data);
return ByteBuffer.allocate(encrypted.length + NONCE_LENGTH).put(encrypted).put(nonce).array();
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new OctoTrustException(e);
}
}
public byte[] decrypt(byte[] data, SecretKey key) {
byte[] encrypted = new byte[data.length - NONCE_LENGTH];
byte[] nonce = new byte[NONCE_LENGTH];
ByteBuffer bb = ByteBuffer.wrap(data);
bb.get(encrypted);
bb.get(nonce);
try {
Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(nonce));
return cipher.doFinal(encrypted);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new OctoTrustException(e);
}
}
}

View file

@ -0,0 +1,13 @@
package love.distributedrebirth.no2all.octo.trust;
@SuppressWarnings("serial")
public class OctoTrustException extends RuntimeException {
public OctoTrustException(String msg) {
super(msg);
}
public OctoTrustException(Exception e) {
super(e);
}
}

View file

@ -0,0 +1,21 @@
package love.distributedrebirth.no2all.octo.trust;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class OctoTrustHash {
private static final String SHA_256 = "SHA-256";
public static MessageDigest sha256Algorithm() {
try {
return MessageDigest.getInstance(SHA_256);
} catch (NoSuchAlgorithmException e) {
throw new OctoTrustException(e);
}
}
public static byte[] sha256(byte[] data) {
return sha256Algorithm().digest(data);
}
}

View file

@ -0,0 +1,127 @@
package love.distributedrebirth.no2all.octo.trust;
import java.math.BigInteger;
import java.util.Arrays;
public final class OctoTrustSchnorr {
public static byte[] sign(byte[] msg, byte[] secKey, byte[] auxRand) {
if (msg.length != 32) {
throw new IllegalArgumentException("The message must be a 32-byte array.");
}
BigInteger secKey0 = bytesToBigInt(secKey);
if (!(BigInteger.ONE.compareTo(secKey0) <= 0
&& secKey0.compareTo(OctoTrustSchnorrPoint.getn().subtract(BigInteger.ONE)) <= 0)) {
throw new IllegalArgumentException("The secret key must be an integer in the range 1..n-1.");
}
OctoTrustSchnorrPoint P = OctoTrustSchnorrPoint.mul(OctoTrustSchnorrPoint.getG(), secKey0);
if (!P.hasEvenY()) {
secKey0 = OctoTrustSchnorrPoint.getn().subtract(secKey0);
}
int len = bigIntToBytes(secKey0).length + P.toBytes().length + msg.length;
byte[] buf = new byte[len];
byte[] t = xor(bigIntToBytes(secKey0), OctoTrustSchnorrPoint.taggedHash("BIP0340/aux", auxRand));
System.arraycopy(t, 0, buf, 0, t.length);
System.arraycopy(P.toBytes(), 0, buf, t.length, P.toBytes().length);
System.arraycopy(msg, 0, buf, t.length + P.toBytes().length, msg.length);
BigInteger k0 = bytesToBigInt(OctoTrustSchnorrPoint.taggedHash("BIP0340/nonce", buf))
.mod(OctoTrustSchnorrPoint.getn());
if (k0.compareTo(BigInteger.ZERO) == 0) {
throw new IllegalArgumentException("Failure. This happens only with negligible probability.");
}
OctoTrustSchnorrPoint R = OctoTrustSchnorrPoint.mul(OctoTrustSchnorrPoint.getG(), k0);
BigInteger k;
if (!R.hasEvenY()) {
k = OctoTrustSchnorrPoint.getn().subtract(k0);
} else {
k = k0;
}
len = R.toBytes().length + P.toBytes().length + msg.length;
buf = new byte[len];
System.arraycopy(R.toBytes(), 0, buf, 0, R.toBytes().length);
System.arraycopy(P.toBytes(), 0, buf, R.toBytes().length, P.toBytes().length);
System.arraycopy(msg, 0, buf, R.toBytes().length + P.toBytes().length, msg.length);
BigInteger e = bytesToBigInt(OctoTrustSchnorrPoint.taggedHash("BIP0340/challenge", buf))
.mod(OctoTrustSchnorrPoint.getn());
BigInteger kes = k.add(e.multiply(secKey0)).mod(OctoTrustSchnorrPoint.getn());
len = R.toBytes().length + bigIntToBytes(kes).length;
byte[] sig = new byte[len];
System.arraycopy(R.toBytes(), 0, sig, 0, R.toBytes().length);
System.arraycopy(bigIntToBytes(kes), 0, sig, R.toBytes().length, bigIntToBytes(kes).length);
if (!verify(msg, P.toBytes(), sig)) {
throw new IllegalStateException("The signature does not pass verification.");
}
return sig;
}
public static boolean verify(byte[] msg, byte[] pubkey, byte[] sig) {
if (msg.length != 32) {
throw new IllegalArgumentException("The message must be a 32-byte array.");
}
if (pubkey.length != 32) {
throw new IllegalArgumentException("The public key must be a 32-byte array.");
}
if (sig.length != 64) {
throw new IllegalArgumentException("The signature must be a 64-byte array.");
}
OctoTrustSchnorrPoint P = OctoTrustSchnorrPoint.liftX(pubkey);
if (P == null) {
return false;
}
BigInteger r = bytesToBigInt(Arrays.copyOfRange(sig, 0, 32));
BigInteger s = bytesToBigInt(Arrays.copyOfRange(sig, 32, 64));
if (r.compareTo(OctoTrustSchnorrPoint.getp()) >= 0 || s.compareTo(OctoTrustSchnorrPoint.getn()) >= 0) {
return false;
}
int len = 32 + pubkey.length + msg.length;
byte[] buf = new byte[len];
System.arraycopy(sig, 0, buf, 0, 32);
System.arraycopy(pubkey, 0, buf, 32, pubkey.length);
System.arraycopy(msg, 0, buf, 32 + pubkey.length, msg.length);
BigInteger e = bytesToBigInt(OctoTrustSchnorrPoint.taggedHash("BIP0340/challenge", buf))
.mod(OctoTrustSchnorrPoint.getn());
OctoTrustSchnorrPoint R = OctoTrustSchnorrPoint.add(OctoTrustSchnorrPoint.mul(OctoTrustSchnorrPoint.getG(), s),
OctoTrustSchnorrPoint.mul(P, OctoTrustSchnorrPoint.getn().subtract(e)));
return R != null && R.hasEvenY() && R.getX().compareTo(r) == 0;
}
public static byte[] extractPublicKey(byte[] secKey) {
BigInteger x = bytesToBigInt(secKey);
if (!(BigInteger.ONE.compareTo(x) <= 0
&& x.compareTo(OctoTrustSchnorrPoint.getn().subtract(BigInteger.ONE)) <= 0)) {
throw new IllegalArgumentException("The secret key must be an integer in the range 1..n-1.");
}
OctoTrustSchnorrPoint result = OctoTrustSchnorrPoint.mul(OctoTrustSchnorrPoint.G, x);
return OctoTrustSchnorrPoint.bytesFromPoint(result);
}
protected static byte[] xor(byte[] b0, byte[] b1) {
if (b0.length != b1.length) {
return null;
}
byte[] result = new byte[b0.length];
int i = 0;
for (byte b : b0) {
result[i] = (byte) (b ^ b1[i]);
i++;
}
return result;
}
protected static byte[] bigIntToBytes(BigInteger n) {
byte[] b = n.toByteArray();
if (b.length == 32) {
return b;
} else if (b.length > 32) {
return Arrays.copyOfRange(b, b.length - 32, b.length);
} else {
byte[] buf = new byte[32];
System.arraycopy(b, 0, buf, buf.length - b.length, b.length);
return buf;
}
}
protected static BigInteger bytesToBigInt(byte[] b) {
return new BigInteger(1, b);
}
}

View file

@ -0,0 +1,181 @@
package love.distributedrebirth.no2all.octo.trust;
import java.math.BigInteger;
public class OctoTrustSchnorrPoint {
final static private BigInteger p = new BigInteger(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
final static private BigInteger n = new BigInteger(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
final static public OctoTrustSchnorrPoint G = new OctoTrustSchnorrPoint(
new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16),
new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16));
private static final BigInteger BI_TWO = BigInteger.valueOf(2);
private final BigInteger x;
private final BigInteger y;
public OctoTrustSchnorrPoint(BigInteger x, BigInteger y) {
this.x = x;
this.y = y;
}
public static BigInteger getp() {
return p;
}
public static BigInteger getn() {
return n;
}
public static OctoTrustSchnorrPoint getG() {
return G;
}
public BigInteger getX() {
return x;
}
public BigInteger getY() {
return y;
}
public static BigInteger getX(OctoTrustSchnorrPoint point) {
return point.getX();
}
public static BigInteger getY(OctoTrustSchnorrPoint point) {
return point.getY();
}
public boolean isInfinite() {
return x == null || y == null;
}
public static boolean isInfinite(OctoTrustSchnorrPoint point) {
return point.isInfinite();
}
public OctoTrustSchnorrPoint add(OctoTrustSchnorrPoint point) {
return add(this, point);
}
public static OctoTrustSchnorrPoint add(OctoTrustSchnorrPoint point1, OctoTrustSchnorrPoint point2) {
if ((point1 != null && point2 != null && point1.isInfinite() && point2.isInfinite())) {
return infinityPoint();
}
if (point1 == null || point1.isInfinite()) {
return point2;
}
if (point2 == null || point2.isInfinite()) {
return point1;
}
if (point1.getX().equals(point2.getX()) && !point1.getY().equals(point2.getY())) {
return infinityPoint();
}
BigInteger lam;
if (point1.equals(point2)) {
BigInteger base = point2.getY().multiply(BI_TWO);
lam = (BigInteger.valueOf(3L).multiply(point1.getX()).multiply(point1.getX())
.multiply(base.modPow(p.subtract(BI_TWO), p))).mod(p);
} else {
BigInteger base = point2.getX().subtract(point1.getX());
lam = ((point2.getY().subtract(point1.getY())).multiply(base.modPow(p.subtract(BI_TWO), p))).mod(p);
}
BigInteger x3 = (lam.multiply(lam).subtract(point1.getX()).subtract(point2.getX())).mod(p);
return new OctoTrustSchnorrPoint(x3, lam.multiply(point1.getX().subtract(x3)).subtract(point1.getY()).mod(p));
}
public OctoTrustSchnorrPoint mul(BigInteger n) {
return mul(this, n);
}
public static OctoTrustSchnorrPoint mul(OctoTrustSchnorrPoint point, BigInteger n) {
OctoTrustSchnorrPoint R = null;
for (int i = 0; i < 256; i++) {
if (n.shiftRight(i).and(BigInteger.ONE).compareTo(BigInteger.ZERO) > 0) {
R = add(R, point);
}
point = add(point, point);
}
return R;
}
public boolean hasEvenY() {
return hasEvenY(this);
}
public static boolean hasEvenY(OctoTrustSchnorrPoint point) {
if (point == null) {
return false;
}
if (point.getY() == null) { // added for junit
return false;
}
return point.getY().mod(BI_TWO).compareTo(BigInteger.ZERO) == 0;
}
public static boolean isSquare(BigInteger x) {
return x.modPow(p.subtract(BigInteger.ONE).mod(BI_TWO), p).longValue() == 1L;
}
public boolean hasSquareY() {
return hasSquareY(this);
}
public static boolean hasSquareY(OctoTrustSchnorrPoint point) {
return isSquare(point.getY());
}
public static byte[] taggedHash(String tag, byte[] msg) {
byte[] tagHash = OctoTrustHash.sha256(tag.getBytes());
int len = (tagHash.length * 2) + msg.length;
byte[] buf = new byte[len];
System.arraycopy(tagHash, 0, buf, 0, tagHash.length);
System.arraycopy(tagHash, 0, buf, tagHash.length, tagHash.length);
System.arraycopy(msg, 0, buf, tagHash.length * 2, msg.length);
return OctoTrustHash.sha256(buf);
}
public static byte[] genPubKey(byte[] secKey) {
BigInteger x = OctoTrustSchnorr.bytesToBigInt(secKey);
if (!(BigInteger.ONE.compareTo(x) <= 0 && x.compareTo(getn().subtract(BigInteger.ONE)) <= 0)) {
throw new IllegalArgumentException("The secret key must be an integer in the range 1..n-1.");
}
OctoTrustSchnorrPoint ret = OctoTrustSchnorrPoint.mul(G, x);
return bytesFromPoint(ret);
}
public byte[] toBytes() {
return bytesFromPoint(this);
}
public static byte[] bytesFromPoint(OctoTrustSchnorrPoint point) {
return OctoTrustSchnorr.bigIntToBytes(point.getX());
}
public static OctoTrustSchnorrPoint liftX(byte[] b) {
BigInteger x = OctoTrustSchnorr.bytesToBigInt(b);
if (x.compareTo(p) >= 0) {
return null;
}
BigInteger y_sq = x.modPow(BigInteger.valueOf(3L), p).add(BigInteger.valueOf(7L)).mod(p);
BigInteger y = y_sq.modPow(p.add(BigInteger.ONE).divide(BigInteger.valueOf(4L)), p);
if (y.modPow(BI_TWO, p).compareTo(y_sq) != 0) {
return null;
} else {
return new OctoTrustSchnorrPoint(x, y.and(BigInteger.ONE).compareTo(BigInteger.ZERO) == 0 ? y : p.subtract(y));
}
}
public static OctoTrustSchnorrPoint infinityPoint() {
return new OctoTrustSchnorrPoint(null, null);
}
public boolean equals(OctoTrustSchnorrPoint point) {
return getX().equals(point.getX()) && getY().equals(point.getY());
}
}

View file

@ -0,0 +1,24 @@
package love.distributedrebirth.no2all.octo;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class OctoBech32Test {
@Test
public void testToBech32() {
byte[] data = "abcd".getBytes(StandardCharsets.UTF_8);
String b32 = OctoBech32.toBech32("junit", data).getValue();
Assertions.assertEquals("junit1v93xxeqlhr26v", b32);
}
@Test
public void testFromBech32() {
String b32 = "junit1v93xxeqlhr26v";
byte[] data = OctoBech32.fromBech32(OctoBech32String.of(b32)).getData();
String result = new String(data, StandardCharsets.UTF_8);
Assertions.assertEquals("abcd", result);
}
}

View file

@ -0,0 +1,41 @@
package love.distributedrebirth.no2all.octo;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class OctoBitChainTest {
private enum XHtml6 implements OctoBitChainFrameType {
HEAD, BODY, SOUL;
static String PREFIX = XHtml6.class.getSimpleName().toLowerCase();
@Override
public byte getTypeByte() {
return (byte)ordinal();
}
}
@Test
public void testCustomTLV() {
// Setup test
OctoBitChain chain = new OctoBitChain();
chain.addFrame(XHtml6.HEAD, "<meta name=\"foo1\" content=\"bar1\"/>".getBytes(StandardCharsets.UTF_8));
chain.addFrame(XHtml6.HEAD, "<meta name=\"foo2\" content=\"bar2\"/>".getBytes(StandardCharsets.UTF_8));
chain.addFrame(XHtml6.HEAD, "<meta name=\"foo3\" content=\"bar3\"/>".getBytes(StandardCharsets.UTF_8));
chain.addFrame(XHtml6.BODY, "<body/>".getBytes(StandardCharsets.UTF_8));
chain.addFrame(XHtml6.SOUL, "𑀳𑁂𑀮𑀺𑀉𑁄𑀤𑁄𑀭𑁂𑀡 𑀪𑀸𑀕".getBytes(StandardCharsets.UTF_8));
// Convert over wire
String linkB32 = OctoBech32.toBech32(XHtml6.PREFIX, chain.toByteArray()).getValue();
byte[] linkB32Data = OctoBech32.fromBech32(new OctoBech32String(linkB32)).getData();
// Test byte parser
OctoBitChain test = new OctoBitChain(linkB32Data);
Assertions.assertEquals(5, test.getFrameCount());
Assertions.assertEquals(3, test.getFramesByType(XHtml6.HEAD).size());
Assertions.assertEquals("𑀳𑁂𑀮𑀺𑀉𑁄𑀤𑁄𑀭𑁂𑀡 𑀪𑀸𑀕", new String(test.getFirstDataOfType(XHtml6.SOUL), StandardCharsets.UTF_8));
// Test full binary
Assertions.assertArrayEquals(chain.toByteArray(), test.toByteArray());
}
}

View file

@ -0,0 +1,27 @@
package love.distributedrebirth.no2all.octo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import love.distributedrebirth.no2all.octo.trust.OctoTrust;
public class OctoBitFormatTest {
@Test
public void testFormatCCP() {
byte[] data = OctoTrust.createRandomByteArray(32);
String hex = OctoBitFormat.HEX_CCP.fromBytes(data);
byte[] result = OctoBitFormat.HEX_CCP.toBytes(hex);
Assertions.assertArrayEquals(data, result);
System.out.println("HEX_CCP: \"" + hex + "\" <== 32 bytes");
}
@Test
public void testFormatDipavali() {
byte[] data = OctoTrust.createRandomByteArray(11);
String hex = OctoBitFormat.HEX_DIPAVALI.fromBytes(data);
byte[] result = OctoBitFormat.HEX_DIPAVALI.toBytes(hex);
Assertions.assertArrayEquals(data, result);
System.out.println("HEX_DIPAVALI: \"" + hex + "\" <== 11 bytes, copy to text editor");
}
}

View file

@ -0,0 +1,21 @@
package love.distributedrebirth.no2all.octo.conv;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import love.distributedrebirth.no2all.octo.OctoBitFormat;
public class OctoConvSIntBigIndianTest {
@Test
public void testI32() {
Assertions.assertEquals(0, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(0)));
Assertions.assertEquals(1, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(1)));
Assertions.assertEquals(13, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(13)));
Assertions.assertEquals(4096, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(4096)));
Assertions.assertEquals(782356423, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(782356423)));
Assertions.assertEquals(1992356423, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(1992356423)));
Assertions.assertEquals(-88378353, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(-88378353)));
Assertions.assertEquals(-1, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(-1)));
}
}

View file

@ -0,0 +1,22 @@
package love.distributedrebirth.no2all.octo.conv;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import love.distributedrebirth.no2all.octo.OctoBitFormat;
public class OctoConvSLongBigIndianTest {
@Test
public void testI64() {
Assertions.assertEquals(0L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(0L)));
Assertions.assertEquals(1L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(1L)));
Assertions.assertEquals(13L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(13L)));
Assertions.assertEquals(4096L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(4096L)));
Assertions.assertEquals(1992356423L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(1992356423L)));
Assertions.assertEquals(81992356423L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(81992356423L)));
Assertions.assertEquals(7825780943356423L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(7825780943356423L)));
Assertions.assertEquals(-89548957835783434L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(-89548957835783434L)));
Assertions.assertEquals(-1L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(-1L)));
}
}

View file

@ -0,0 +1,35 @@
package love.distributedrebirth.no2all.octo.conv;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class OctoConvStringIDNTest {
String validUrlFB = "facebook.com";
String validUrlAbc = "www.google.com";
String[][] testIDN = {
{"www.fаcebook.com", "www.xn--fcebook-2fg.com"},
{"www.faϲebook.com", "www.xn--faebook-6pf.com"},
{"www.facеbook.com", "www.xn--facbook-9gg.com"},
{"www.facebооk.com", "www.xn--facebk-0qfa.com"},
{"ԝԝԝ.facebook.com", "xn--07aaa.facebook.com"},
{"ԝԝԝ.fаϲеbооk.com", "xn--07aaa.xn--fbk-qzc85c5a5da.com"},
{"www.googlе.com", "www.xn--googl-3we.com"},
{"www.gооgle.com", "www.xn--ggle-55da.com"},
{"ԝԝԝ.google.com", "xn--07aaa.google.com"},
{"ԝԝԝ.gооglе.com", "xn--07aaa.xn--ggl-tdd6ba.com"},
};
@Test
public void testFormatIDN() {
OctoConvStringIDN IDN = new OctoConvStringIDN();
for (String[] test: testIDN) {
String ascii = new String(IDN.toBytes(test[0]), StandardCharsets.US_ASCII);
Assertions.assertEquals(test[1], ascii);
Assertions.assertFalse(validUrlFB.equals(ascii));
Assertions.assertFalse(validUrlAbc.equals(ascii));
}
}
}

View file

@ -0,0 +1,39 @@
package love.distributedrebirth.no2all.octo.trust;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import love.distributedrebirth.no2all.octo.OctoBitFormat;
public class OctaTrustAESCurveTest {
private BigInteger createSharedKey() {
byte[] secKey = OctoBitFormat.HEX.toBytes("0000000000000000000000000000000000000000000000000000000000002222");
byte[] pubKey = OctoBitFormat.HEX.toBytes("2ef93f01cd2493e04235a6b87b10d3c4a74e2a7eb7c3caf168268f6af73314b5");
return OctoTrustAESCurve.calculateKeyAgreement(new BigInteger(secKey), new BigInteger(pubKey));
}
@Test
public void testCypherSimple() throws Exception {
BigInteger sharedKey = createSharedKey();
String wireMessage = OctoTrustAESCurve.encrypt(sharedKey, "hi");
String decodedMessage = OctoTrustAESCurve.decrypt(sharedKey, wireMessage);
Assertions.assertEquals("733642b9f465207b29ca9070b17104db5e18f6fb6de1a88ee494f00f01eaca7", sharedKey.toString(16));
Assertions.assertEquals("hi", decodedMessage);
}
@Test
public void testCypherRepeat() throws Exception {
BigInteger sharedKey = createSharedKey();
Set<String> results = new HashSet<>();
for (int i = 0; i < 33; i++) {
String wireMessage = OctoTrustAESCurve.encrypt(sharedKey, "foo");
Assertions.assertFalse(results.contains(wireMessage));
results.add(wireMessage);
}
}
}

View file

@ -0,0 +1,67 @@
package love.distributedrebirth.no2all.octo.trust;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import love.distributedrebirth.no2all.octo.OctoBitFormat;
public class OctoTrustAESTest {
@Test
public void testPrivateKeyPassword() throws Exception {
byte[] salt = "1234567890".getBytes();
SecretKey k1 = OctoTrustAES.createSecretPassword("foobar".toCharArray(), salt);
SecretKey k2 = OctoTrustAES.createSecretPassword("foobar".toCharArray(), salt);
IvParameterSpec iv1 = OctoTrustAES.createRandomIv();
IvParameterSpec iv2 = iv1;
byte[] nsec = OctoTrust.generateSecretKey();
System.out.println("nsec: " + OctoBitFormat.HEX.fromBytes(nsec));
byte[] nsecpw = OctoTrustAES.encrypt(nsec, k1, iv1);
System.out.println("nsecpw: " + OctoBitFormat.HEX.fromBytes(nsecpw));
byte[] nsec2 = OctoTrustAES.decrypt(nsecpw, k2, iv2);
System.out.println("nsec2: " + OctoBitFormat.HEX.fromBytes(nsec2));
Assertions.assertArrayEquals(nsec, nsec2);
//
// // TEST END HERE
// // wip
//
// //String algo = "PBKDF2WithHmacSHA256";
// String algo = "PBEWithSHA1AndDESede";
// AlgorithmParameters algparms = AlgorithmParameters.getInstance(algo);
// PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 33);
// algparms.init(pbeParamSpec);
// //algparms.init(new IvParameterSpec(iv)); // for "AES" params
// EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(algparms, nsecpw);
//
// byte[] encryptedPkcs8 = encinfo.getEncoded();
// String b32Pkcs8 = OctoBech32.toBech32("ntest", encryptedPkcs8).getValue();
// System.out.println("pkcs8-hex: " + OctoBitFormat.HEX.fromBytes(encryptedPkcs8));
// System.out.println("pkcs8-b32: " + b32Pkcs8);
// byte[] linkData = OctoBech32.fromBech32(OctoBech32String.of(b32Pkcs8)).getData();
//
// EncryptedPrivateKeyInfo info = new EncryptedPrivateKeyInfo(linkData);
// byte[] nsec3 = info.getEncryptedData();
// System.out.println("asn.name: " + info.getAlgName());
// System.out.println("asn.param: " + info.getAlgParameters());
// System.out.println("nsec3: " + OctoBitFormat.HEX.fromBytes(nsec3));
// Assertions.assertArrayEquals(nsecpw, nsec3);
//
// Cipher cipher = Cipher.getInstance(info.getAlgName());
// PBEKeySpec pbeKeySpec = new PBEKeySpec("foobar".toCharArray());
// SecretKeyFactory secFac = SecretKeyFactory.getInstance(info.getAlgName());
// Key pbeKey = secFac.generateSecret(pbeKeySpec);
// AlgorithmParameters algParams = info.getAlgParameters();
// cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
// err
//KeySpec pkcs8KeySpec = info.getKeySpec(cipher);
//KeyFactory kf = KeyFactory.getInstance("PBEWithSHA1AndDESede");
//byte[] nsec4 = kf.generatePrivate(pkcs8KeySpec).getEncoded();
//System.out.println("nsec4: " + NoStrOctoBitFormat.HEX.fromBytes(nsec4));
}
}

View file

@ -0,0 +1,82 @@
package love.distributedrebirth.no2all.octo.trust;
import java.util.Arrays;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import love.distributedrebirth.no2all.octo.OctoBitFormat;
public class OctoTrustSchnorrTest {
String[] data = {
// index,secret key,public key, aux rand, message,signature,verification
// result,comment
"0,0000000000000000000000000000000000000000000000000000000000000003,F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9,0000000000000000000000000000000000000000000000000000000000000000,0000000000000000000000000000000000000000000000000000000000000000,E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0,TRUE",
"1,B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,0000000000000000000000000000000000000000000000000000000000000001,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A,TRUE",
"2,C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9,DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8,C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906,7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C,5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7,TRUE",
"3,0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710,25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3,TRUE,test fails if msg is reduced modulo p or n",
"4,,D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9,,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4,TRUE",
"5,,EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key not on the curve",
"6,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2,FALSE,has_even_y(R) is false",
"7,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD,FALSE,negated message",
"8,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6,FALSE,negated s value",
"9,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0",
"10,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1",
"11,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is not an X coordinate on the curve",
"12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is equal to field size",
"13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order",
"14,,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key is not a valid X coordinate because it exceeds the field size", };
@Test
public void testDataSet() throws Exception {
boolean allSigsPassed = true;
boolean allVerifsPassed = true;
for (int i = 0; i < data.length; i++) {
String[] s = data[i].split(",");
String index = s[0];
String seckey = s[1];
String pubkey = s[2];
String auxrand = s[3];
String msg = s[4];
String sig = s[5];
String result = s[6];
String comment = (s.length == 8) ? s[7] : null;
byte[] _pubkey = OctoBitFormat.HEX.toBytes(pubkey);
byte[] _auxrand = OctoBitFormat.HEX.toBytes(auxrand);
byte[] _msg = OctoBitFormat.HEX.toBytes(msg);
byte[] _sig = OctoBitFormat.HEX.toBytes(sig);
boolean _result = result.equals("TRUE");
if (seckey != null && seckey.length() > 0) {
byte[] _seckey = OctoBitFormat.HEX.toBytes(seckey);
byte[] _pubkeyActual = OctoTrustSchnorrPoint.genPubKey(_seckey);
if (!Arrays.equals(_pubkey, _pubkeyActual)) {
System.out.println(" * Failed key generation: " + index);
System.out.println("\tExpected key:" + OctoBitFormat.HEX.fromBytes(_pubkey));
System.out.println("\tActual key:" + OctoBitFormat.HEX.fromBytes(_pubkeyActual));
allSigsPassed = false;
}
byte[] _sigActual = OctoTrustSchnorr.sign(_msg, _seckey, _auxrand);
if (!Arrays.equals(_sig, _sigActual)) {
System.out.println(" * Failed signing test: " + index);
System.out.println("\tExpected signature:" + OctoBitFormat.HEX.fromBytes(_sig));
System.out.println("\tActual signature:" + OctoBitFormat.HEX.fromBytes(_sigActual));
allSigsPassed = false;
}
}
boolean resultActual = OctoTrustSchnorr.verify(_msg, _pubkey, _sig);
if (_result != resultActual) {
System.out.println(" * Failed verification test.");
System.out.println("\tExpected verification result:" + result);
System.out.println("\tActual verification result:" + resultActual);
if (comment != null) {
System.out.println("\tComment:" + comment);
}
allVerifsPassed = false;
}
}
Assertions.assertTrue(allSigsPassed && allVerifsPassed);
}
}