Intial commit
This commit is contained in:
parent
7329fb8dea
commit
73f201bdac
268 changed files with 11220 additions and 5 deletions
16
no2all-octo/pom.xml
Normal file
16
no2all-octo/pom.xml
Normal 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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package love.distributedrebirth.no2all.octo;
|
||||
|
||||
public interface OctoBitChainFrameType {
|
||||
|
||||
byte getTypeByte();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package love.distributedrebirth.no2all.octo;
|
||||
|
||||
public interface OctoBitConverter<T> {
|
||||
|
||||
T fromBytes(byte[] data);
|
||||
|
||||
byte[] toBytes(T value);
|
||||
}
|
||||
|
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue