mirror of
https://github.com/unclebob/more-speech.git
synced 2024-09-30 10:30:56 +00:00
200 lines
5.7 KiB
Java
200 lines
5.7 KiB
Java
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());
|
|
}
|
|
|
|
}
|