Add missing classes for event composer

This commit is contained in:
@s.roertgen 2025-01-16 22:43:25 +01:00
parent ca68ff05bf
commit bea0d5822e
7 changed files with 538 additions and 1 deletions

View file

@ -4,6 +4,8 @@ Do nostr stuff with Clojure.
Heavy WIP. At this point it probably won't make much sense too you. Heavy WIP. At this point it probably won't make much sense too you.
You might have to run `javac -d java $(find java -name "*.java")`
## Usage ## Usage
FIXME: write usage documentation! FIXME: write usage documentation!

View file

@ -1,4 +1,4 @@
{:paths ["src" "resources"] {:paths ["src" "resources" "java"]
:deps {org.clojure/clojure {:mvn/version "1.12.0"} :deps {org.clojure/clojure {:mvn/version "1.12.0"}
cheshire/cheshire {:mvn/version "5.13.0"} cheshire/cheshire {:mvn/version "5.13.0"}
hato/hato {:mvn/version "1.0.0"}} hato/hato {:mvn/version "1.0.0"}}

128
java/schnorr/Main.java Normal file
View file

@ -0,0 +1,128 @@
package schnorr;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
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",
};
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 = Util.hexToBytes(pubkey);
byte[] _auxrand = Util.hexToBytes(auxrand);
byte[] _msg = Util.hexToBytes(msg);
byte[] _sig = Util.hexToBytes(sig);
boolean _result = result.equals("TRUE");
System.out.println("\nTest vector:" + Integer.valueOf(index));
if(seckey != null && seckey.length() > 0) {
byte[] _seckey = Util.hexToBytes(seckey);
try {
byte[] _pubkeyActual = Point.genPubKey(_seckey);
if(!Arrays.equals(_pubkey, _pubkeyActual)) {
System.out.println(" * Failed key generation.");
System.out.println("\tExpected key:" + Util.bytesToHex(_pubkey));
System.out.println("\tActual key:" + Util.bytesToHex(_pubkeyActual));
allSigsPassed = false;
}
}
catch(Exception e) {
System.out.println(e.getMessage());
}
try {
byte[] _sigActual = Schnorr.sign(_msg, _seckey, _auxrand);
if(Arrays.equals(_sig, _sigActual)) {
System.out.println(" * Passed signing test.");
}
else {
System.out.println(" * Failed signing test.");
System.out.println("\tExpected signature:" + Util.bytesToHex(_sig));
System.out.println("\tActual signature:" + Util.bytesToHex(_sigActual));
allSigsPassed = false;
}
}
catch(Exception e) {
System.out.println(e.getMessage());
allSigsPassed = false;
}
}
try {
boolean resultActual = Schnorr.verify(_msg, _pubkey, _sig);
if(_result == resultActual) {
System.out.println(" * Passed verification test.");
}
else {
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;
}
}
catch(Exception e) {
if(_result) {
System.out.println("Exception:" + e.getMessage());
System.out.println(" * Failed verification test.");
allVerifsPassed = false;
}
else {
System.out.println(" * Passed verification test.");
}
}
}
if(allSigsPassed && allVerifsPassed) {
System.out.println("All test vectors passed.");
}
else {
System.out.println("Some test vectors failed.");
}
}
}

34
java/schnorr/Pair.java Normal file
View file

@ -0,0 +1,34 @@
package schnorr;
// clone of org.apache.commons.lang3.tuple.Pair;
public class Pair<K, V> {
private K elementLeft = null;
private V elementRight = null;
protected Pair() {
}
public static <K, V> Pair<K, V> of(K elementLeft, V elementRight) {
return new Pair<>(elementLeft, elementRight);
}
public Pair(K elementLeft, V elementRight) {
this.elementLeft = elementLeft;
this.elementRight = elementRight;
}
public K getLeft() {
return elementLeft;
}
public V getRight() {
return elementRight;
}
public boolean equals(Pair<K, V> p) {
return (this.elementLeft.equals(p.getLeft())) && (this.elementRight.equals(p.getRight()));
}
}

199
java/schnorr/Point.java Normal file
View file

@ -0,0 +1,199 @@
package schnorr;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Point {
final static private BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
final static private BigInteger n = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
final static public Point G = new Point(
new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16),
new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
);
private static final BigInteger BI_TWO = BigInteger.valueOf(2);
private final Pair<BigInteger,BigInteger> pair;
public Point(BigInteger x , BigInteger y) {
pair = Pair.of(x, y);
}
public Point(byte[] b0, byte[] b1) {
pair = Pair.of(new BigInteger(1, b0), new BigInteger(1, b1));
}
public static BigInteger getp() {
return p;
}
public static BigInteger getn() {
return n;
}
public static Point getG() {
return G;
}
public BigInteger getX() {
return pair.getLeft();
}
public BigInteger getY() {
return pair.getRight();
}
public static BigInteger getX(Point P) {
assert !P.isInfinite();
return P.getX();
}
public static BigInteger getY(Point P) {
assert !P.isInfinite();
return P.getY();
}
public Pair<BigInteger,BigInteger> getPair() {
return pair;
}
public boolean isInfinite() {
return pair == null || pair.getLeft() == null || pair.getRight() == null;
}
public static boolean isInfinite(Point P) {
return P.isInfinite();
}
public Point add(Point P) {
return add(this, P);
}
public static Point add(Point P1, Point P2) {
if((P1 != null && P2 != null && P1.isInfinite() && P2.isInfinite())) {
return infinityPoint();
}
if(P1 == null || P1.isInfinite()) {
return P2;
}
if(P2 == null || P2.isInfinite()) {
return P1;
}
if(P1.getX().equals(P2.getX()) && !P1.getY().equals(P2.getY())) {
return infinityPoint();
}
BigInteger lam;
if(P1.equals(P2)) {
BigInteger base = P2.getY().multiply(BI_TWO);
lam = (BigInteger.valueOf(3L).multiply(P1.getX()).multiply(P1.getX()).multiply(base.modPow(p.subtract(BI_TWO), p))).mod(p);
}
else {
BigInteger base = P2.getX().subtract(P1.getX());
lam = ((P2.getY().subtract(P1.getY())).multiply(base.modPow(p.subtract(BI_TWO), p))).mod(p);
}
BigInteger x3 = (lam.multiply(lam).subtract(P1.getX()).subtract(P2.getX())).mod(p);
return new Point(x3, lam.multiply(P1.getX().subtract(x3)).subtract(P1.getY()).mod(p));
}
public Point mul(BigInteger n) {
return mul(this, n);
}
public static Point mul(Point P, BigInteger n) {
Point R = null;
for(int i = 0; i < 256; i++) {
if (n.shiftRight(i).and(BigInteger.ONE).compareTo(BigInteger.ZERO) > 0) {
R = add(R, P);
}
P = add(P, P);
}
return R;
}
public boolean hasEvenY() {
return hasEvenY(this);
}
public static boolean hasEvenY(Point P) {
return P.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(Point P) {
assert !isInfinite(P);
return isSquare(P.getY());
}
public static byte[] taggedHash(String tag, byte[] msg) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] tagHash = Util.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 Util.sha256(buf);
}
public static byte[] genPubKey(byte[] secKey) throws Exception {
BigInteger x = Util.bigIntFromBytes(secKey);
if(!(BigInteger.ONE.compareTo(x) <= 0 && x.compareTo(getn().subtract(BigInteger.ONE)) <= 0)) {
throw new Exception("The secret key must be an integer in the range 1..n-1.");
}
Point ret = Point.mul(G, x);
return bytesFromPoint(ret);
}
public byte[] toBytes() {
return bytesFromPoint(this);
}
public static byte[] bytesFromPoint(Point P) {
return Util.bytesFromBigInteger(P.getX());
}
// previously 'pointFromBytes()'
public static Point liftX(byte[] b) {
BigInteger x = Util.bigIntFromBytes(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 Point(x, y.and(BigInteger.ONE).compareTo(BigInteger.ZERO) == 0 ? y : p.subtract(y));
}
}
public static Point infinityPoint() {
return new Point(null, (BigInteger) null);
}
public boolean equals(Point P) {
return getPair().equals(P.getPair());
}
}

95
java/schnorr/Schnorr.java Normal file
View file

@ -0,0 +1,95 @@
package schnorr;
import java.math.BigInteger;
import java.util.Arrays;
public class Schnorr {
public static byte[] sign(byte[] msg, byte[] secKey, byte[] auxRand) throws Exception {
if(msg.length != 32) {
throw new Exception("The message must be a 32-byte array.");
}
BigInteger secKey0 = Util.bigIntFromBytes(secKey);
if(!(BigInteger.ONE.compareTo(secKey0) <= 0 && secKey0.compareTo(Point.getn().subtract(BigInteger.ONE)) <= 0)) {
throw new Exception("The secret key must be an integer in the range 1..n-1.");
}
Point P = Point.mul(Point.getG(), secKey0);
if(!P.hasEvenY()) {
secKey0 = Point.getn().subtract(secKey0);
}
int len = Util.bytesFromBigInteger(secKey0).length + P.toBytes().length + msg.length;
byte[] buf = new byte[len];
byte[] t = Util.xor(Util.bytesFromBigInteger(secKey0), Point.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 = Util.bigIntFromBytes(Point.taggedHash("BIP0340/nonce", buf)).mod(Point.getn());
if(k0.compareTo(BigInteger.ZERO) == 0) {
throw new Exception("Failure. This happens only with negligible probability.");
}
Point R = Point.mul(Point.getG(), k0);
BigInteger k;
if(!R.hasEvenY()) {
k = Point.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 = Util.bigIntFromBytes(Point.taggedHash("BIP0340/challenge", buf)).mod(Point.getn());
BigInteger kes = k.add(e.multiply(secKey0)).mod(Point.getn());
len = R.toBytes().length + Util.bytesFromBigInteger(kes).length;
byte[] sig = new byte[len];
System.arraycopy(R.toBytes(), 0, sig, 0, R.toBytes().length);
System.arraycopy(Util.bytesFromBigInteger(kes), 0, sig, R.toBytes().length, Util.bytesFromBigInteger(kes).length);
if(!verify(msg, P.toBytes(), sig)) {
throw new Exception("The signature does not pass verification.");
}
return sig;
}
public static boolean verify(byte[] msg, byte[] pubkey, byte[] sig) throws Exception {
if(msg.length != 32) {
throw new Exception("The message must be a 32-byte array.");
}
if(pubkey.length != 32) {
throw new Exception("The public key must be a 32-byte array.");
}
if(sig.length != 64) {
throw new Exception("The signature must be a 64-byte array.");
}
Point P = Point.liftX(pubkey);
if(P == null) {
return false;
}
BigInteger r = Util.bigIntFromBytes(Arrays.copyOfRange(sig,0, 32));
BigInteger s = Util.bigIntFromBytes(Arrays.copyOfRange(sig,32, 64));
if(r.compareTo(Point.getp()) >= 0 || s.compareTo(Point.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 = Util.bigIntFromBytes(Point.taggedHash("BIP0340/challenge", buf)).mod(Point.getn());
Point R = Point.add(Point.mul(Point.getG(), s), Point.mul(P, Point.getn().subtract(e)));
return R != null && R.hasEvenY() && R.getX().compareTo(r) == 0;
}
public static byte[] genPubKey(byte[] secKey) throws Exception {
BigInteger x = Util.bigIntFromBytes(secKey);
if(!(BigInteger.ONE.compareTo(x) <= 0 && x.compareTo(Point.getn().subtract(BigInteger.ONE)) <= 0)) {
throw new Exception("The secret key must be an integer in the range 1..n-1.");
}
Point ret = Point.mul(Point.G, x);
return Point.bytesFromPoint(ret);
}
}

79
java/schnorr/Util.java Normal file
View file

@ -0,0 +1,79 @@
package schnorr;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class Util {
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] b) {
char[] hexChars = new char[b.length * 2];
for (int j = 0; j < b.length; j++) {
int v = b[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
public static byte[] hexToBytes(String s) {
int len = s.length();
byte[] buf = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
buf[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return buf;
}
public static byte[] bytesFromInt(int n) {
return ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(n).array();
}
public static byte[] bytesFromBigInteger(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;
}
}
public static BigInteger bigIntFromBytes(byte[] b) {
return new BigInteger(1, b);
}
public static byte[] sha256(byte[] b) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return digest.digest(b);
}
public static byte[] xor(byte[] b0, byte[] b1) {
if(b0.length != b1.length) {
return null;
}
byte[] ret = new byte[b0.length];
int i = 0;
for (byte b : b0) {
ret[i] = (byte)(b ^ b1[i]);
i++;
}
return ret;
}
}