├── .gitignore ├── .gitmodules ├── JsnarkCircuitBuilder ├── auction_10.arith ├── config.properties └── src │ ├── circuit │ ├── auxiliary │ │ └── LongElement.java │ ├── config │ │ └── Config.java │ ├── eval │ │ ├── CircuitEvaluator.java │ │ └── Instruction.java │ ├── operations │ │ ├── Gadget.java │ │ ├── WireLabelInstruction.java │ │ └── primitive │ │ │ ├── AddBasicOp.java │ │ │ ├── AssertBasicOp.java │ │ │ ├── BasicOp.java │ │ │ ├── ConstMulBasicOp.java │ │ │ ├── MulBasicOp.java │ │ │ ├── NonZeroCheckBasicOp.java │ │ │ ├── ORBasicOp.java │ │ │ ├── PackBasicOp.java │ │ │ ├── SplitBasicOp.java │ │ │ └── XorBasicOp.java │ ├── structure │ │ ├── BitWire.java │ │ ├── CircuitGenerator.java │ │ ├── ConstantWire.java │ │ ├── LinearCombinationBitWire.java │ │ ├── LinearCombinationWire.java │ │ ├── VariableBitWire.java │ │ ├── VariableWire.java │ │ ├── Wire.java │ │ └── WireArray.java │ └── tests │ │ ├── CachingTest.java │ │ └── PrimitiveOpTest.java │ ├── examples │ ├── gadgets │ │ ├── augmenter │ │ │ └── PinocchioGadget.java │ │ ├── blockciphers │ │ │ ├── AES128CipherGadget.java │ │ │ ├── ChaskeyLTS128CipherGadget.java │ │ │ ├── Speck128CipherGadget.java │ │ │ ├── SymmetricEncryptionCBCGadget.java │ │ │ └── sbox │ │ │ │ ├── AESSBoxComputeGadget.java │ │ │ │ ├── AESSBoxGadgetOptimized1.java │ │ │ │ ├── AESSBoxGadgetOptimized2.java │ │ │ │ ├── AESSBoxNaiveLookupGadget.java │ │ │ │ └── util │ │ │ │ └── LinearSystemSolver.java │ │ ├── diffieHellmanKeyExchange │ │ │ ├── ECDHKeyExchangeGadget.java │ │ │ └── FieldExtensionDHKeyExchange.java │ │ ├── hash │ │ │ ├── MerkleTreePathGadget.java │ │ │ ├── SHA256Gadget.java │ │ │ └── SubsetSumHashGadget.java │ │ ├── math │ │ │ ├── DotProductGadget.java │ │ │ ├── FieldDivisionGadget.java │ │ │ ├── LongIntegerModGadget.java │ │ │ ├── ModConstantGadget.java │ │ │ └── ModGadget.java │ │ └── rsa │ │ │ ├── RSAEncryptionOAEPGadget.java │ │ │ ├── RSAEncryptionV1_5_Gadget.java │ │ │ └── RSASigVerificationV1_5_Gadget.java │ ├── generators │ │ ├── SimpleCircuitGenerator.java │ │ ├── augmenter │ │ │ └── AugmentedAuctionCircuitGenerator.java │ │ ├── blockciphers │ │ │ └── AES128CipherCircuitGenerator.java │ │ ├── hash │ │ │ ├── MerkleTreeMembershipCircuitGenerator.java │ │ │ └── SHA2CircuitGenerator.java │ │ ├── hybridEncryption │ │ │ └── HybridEncryptionCircuitGenerator.java │ │ ├── math │ │ │ └── DotProductCircuitGenerator.java │ │ └── rsa │ │ │ ├── RSAEncryptionCircuitGenerator.java │ │ │ ├── RSAEncryptionOAEPCircuitGenerator.java │ │ │ ├── RSASigVerCircuitGenerator.java │ │ │ └── RSAUtil.java │ └── tests │ │ ├── blockciphers │ │ ├── AES128_Test.java │ │ ├── Chaskey128_Test.java │ │ └── Speck128_Test.java │ │ ├── diffieHellmanKeyExchange │ │ ├── ECDHKeyExchange_Test.java │ │ └── FieldExtensionDHKeyExchange_Test.java │ │ ├── hash │ │ └── SHA256_Test.java │ │ ├── math │ │ └── Mod_Test.java │ │ └── rsa │ │ ├── RSAEncryptionOAEP_Test.java │ │ ├── RSAEncryption_Test.java │ │ └── RSASignature_Test.java │ └── util │ ├── BigIntStorage.java │ └── Util.java ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.o 3 | *.so 4 | *.d 5 | *.*~ 6 | bin 7 | .project 8 | .settings 9 | .classpath 10 | *.arith 11 | !JsnarkCirctuitBuilder/auction_10.arith 12 | *.in 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libsnark"] 2 | path = libsnark 3 | url = https://github.com/akosba/libsnark 4 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/config.properties: -------------------------------------------------------------------------------- 1 | PATH_TO_LIBSNARK_EXEC =../libsnark/build/libsnark/jsnark_interface/run_ppzksnark 2 | FIELD_PRIME=21888242871839275222246405745257275088548364400416034343698204186575808495617 3 | DEBUG_VERBOSE=1 4 | OUTPUT_VERBOSE=1 5 | PRINT_HEX=0 6 | RUNNING_GENERATORS_IN_PARALLEL=0 7 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/config/Config.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.config; 5 | 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.math.BigInteger; 11 | import java.util.Properties; 12 | 13 | public class Config { 14 | 15 | static Properties properties = new Properties(); 16 | 17 | static{ 18 | try { 19 | InputStream inStream = new FileInputStream("config.properties"); 20 | properties.load(inStream); 21 | } catch (FileNotFoundException e) { 22 | System.err.println("config.properties file not found."); 23 | e.printStackTrace(); 24 | System.exit(0); 25 | } catch (IOException e) { 26 | System.err.println("config.properties not loaded properly."); 27 | e.printStackTrace(); 28 | } 29 | } 30 | 31 | public static final BigInteger FIELD_PRIME = new BigInteger(properties.getProperty("FIELD_PRIME")); 32 | public static final int LOG2_FIELD_PRIME = FIELD_PRIME.toString(2).length(); 33 | public static final String LIBSNARK_EXEC = properties.getProperty("PATH_TO_LIBSNARK_EXEC"); 34 | 35 | public static boolean runningMultiGenerators = properties.getProperty("RUNNING_GENERATORS_IN_PARALLEL").equals("1"); 36 | public static boolean hexOutputEnabled = properties.getProperty("PRINT_HEX").equals("1"); 37 | public static boolean outputVerbose = properties.getProperty("OUTPUT_VERBOSE").equals("1"); 38 | public static boolean debugVerbose = properties.getProperty("DEBUG_VERBOSE").equals("1"); 39 | 40 | public static boolean printStackTraceAtWarnings = false; 41 | } 42 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/eval/Instruction.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.eval; 5 | 6 | public interface Instruction { 7 | 8 | public void evaluate(CircuitEvaluator evaluator); 9 | 10 | public default void emit(CircuitEvaluator evaluator) { 11 | } 12 | 13 | public default boolean doneWithinCircuit() { 14 | return false; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/Gadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations; 5 | 6 | import circuit.structure.CircuitGenerator; 7 | import circuit.structure.Wire; 8 | 9 | public abstract class Gadget { 10 | 11 | protected CircuitGenerator generator; 12 | protected String description; 13 | 14 | public Gadget(String...desc) { 15 | this.generator = CircuitGenerator.getActiveCircuitGenerator(); 16 | if(desc.length > 0) 17 | this.description = desc[0]; 18 | else 19 | this.description = ""; 20 | } 21 | 22 | public abstract Wire[] getOutputWires(); 23 | 24 | public String toString() { 25 | return getClass().getSimpleName() + " " + description; 26 | } 27 | 28 | public String debugStr(String s) { 29 | return this + ":" + s; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/WireLabelInstruction.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations; 5 | 6 | import circuit.config.Config; 7 | import circuit.eval.CircuitEvaluator; 8 | import circuit.eval.Instruction; 9 | import circuit.structure.Wire; 10 | 11 | public class WireLabelInstruction implements Instruction { 12 | 13 | public enum LabelType { 14 | input, output, nizkinput, debug 15 | } 16 | 17 | private LabelType type; 18 | private Wire w; 19 | private String desc; 20 | 21 | public WireLabelInstruction(LabelType type, Wire w, String... desc) { 22 | this.type = type; 23 | this.w = w; 24 | if (desc.length > 0) { 25 | this.desc = desc[0]; 26 | } else { 27 | this.desc = ""; 28 | } 29 | } 30 | 31 | public Wire getWire() { 32 | return w; 33 | } 34 | 35 | public String toString() { 36 | return type + " " + w + (desc.length() == 0 ? "" : "\t\t\t # " + desc); 37 | } 38 | 39 | public void evaluate(CircuitEvaluator evaluator) { 40 | // nothing to do. 41 | } 42 | 43 | @Override 44 | public void emit(CircuitEvaluator evaluator) { 45 | if (type == LabelType.output && Config.outputVerbose || type == LabelType.debug && Config.debugVerbose) { 46 | System.out.println("\t[" + type + "] Value of Wire # " + w + (desc.length() > 0 ? " (" + desc + ")" : "") + " :: " 47 | + evaluator.getWireValue(w).toString(Config.hexOutputEnabled ? 16 : 10)); 48 | } 49 | } 50 | 51 | public LabelType getType() { 52 | return type; 53 | } 54 | 55 | public boolean doneWithinCircuit() { 56 | return type != LabelType.debug; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/AddBasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.config.Config; 9 | import circuit.structure.Wire; 10 | 11 | public class AddBasicOp extends BasicOp { 12 | 13 | public AddBasicOp(Wire[] ws, Wire output, String...desc) { 14 | super(ws, new Wire[] { output }, desc); 15 | } 16 | 17 | public String getOpcode(){ 18 | return "add"; 19 | } 20 | 21 | @Override 22 | public void compute(BigInteger[] assignment) { 23 | BigInteger s = BigInteger.ZERO; 24 | for (Wire w : inputs) { 25 | s = s.add(assignment[w.getWireId()]); 26 | } 27 | assignment[outputs[0].getWireId()] = s.mod(Config.FIELD_PRIME); 28 | } 29 | 30 | @Override 31 | public boolean equals(Object obj) { 32 | 33 | if (this == obj) 34 | return true; 35 | if (!(obj instanceof AddBasicOp)) { 36 | return false; 37 | } 38 | AddBasicOp op = (AddBasicOp) obj; 39 | if(op.inputs.length!=inputs.length ){ 40 | return false; 41 | } 42 | 43 | if(inputs.length == 2){ 44 | boolean check1 = inputs[0].equals(op.inputs[0]) 45 | && inputs[1].equals(op.inputs[1]); 46 | boolean check2 = inputs[1].equals(op.inputs[0]) 47 | && inputs[0].equals(op.inputs[1]); 48 | return check1 || check2; 49 | } else { 50 | boolean check = true; 51 | for(int i = 0; i < inputs.length; i++){ 52 | check = check && inputs[i].equals(op.inputs[i]); 53 | } 54 | return check; 55 | } 56 | } 57 | 58 | @Override 59 | public int getNumMulGates() { 60 | return 0; 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/AssertBasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.config.Config; 9 | import circuit.structure.Wire; 10 | 11 | public class AssertBasicOp extends BasicOp { 12 | 13 | public AssertBasicOp(Wire w1, Wire w2, Wire output, String...desc) { 14 | super(new Wire[] { w1, w2 }, new Wire[] { output }, desc); 15 | } 16 | 17 | @Override 18 | protected void compute(BigInteger[] assignment) { 19 | BigInteger leftSide = assignment[inputs[0].getWireId()].multiply( 20 | assignment[inputs[1].getWireId()]).mod( 21 | Config.FIELD_PRIME); 22 | BigInteger rightSide = assignment[outputs[0].getWireId()]; 23 | boolean check = leftSide.equals(rightSide); 24 | if (!check) { 25 | System.err.println("Error - Assertion Failed " + this); 26 | System.out.println(assignment[inputs[0].getWireId()] + "*" 27 | + assignment[inputs[1].getWireId()] + "!=" 28 | + assignment[outputs[0].getWireId()]); 29 | throw new RuntimeException("Error During Evaluation"); 30 | } 31 | } 32 | 33 | @Override 34 | protected void checkOutputs(BigInteger[] assignment) { 35 | // do nothing 36 | } 37 | 38 | public String getOpcode(){ 39 | return "assert"; 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | 45 | if (this == obj) 46 | return true; 47 | if (!(obj instanceof AssertBasicOp)) { 48 | return false; 49 | } 50 | AssertBasicOp op = (AssertBasicOp) obj; 51 | 52 | boolean check1 = inputs[0].equals(op.inputs[0]) 53 | && inputs[1].equals(op.inputs[1]); 54 | boolean check2 = inputs[1].equals(op.inputs[0]) 55 | && inputs[0].equals(op.inputs[1]); 56 | return (check1 || check2) && outputs[0].equals(op.outputs[0]); 57 | 58 | } 59 | 60 | @Override 61 | public int getNumMulGates() { 62 | return 1; 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/BasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import util.Util; 9 | import circuit.eval.CircuitEvaluator; 10 | import circuit.eval.Instruction; 11 | import circuit.structure.Wire; 12 | 13 | public abstract class BasicOp implements Instruction { 14 | 15 | protected Wire[] inputs; 16 | protected Wire[] outputs; 17 | protected String desc; 18 | 19 | public BasicOp(Wire[] inputs, Wire[] outputs, String... desc) { 20 | this.inputs = inputs; 21 | this.outputs = outputs; 22 | if (desc.length > 0) { 23 | this.desc = desc[0]; 24 | } else { 25 | this.desc = ""; 26 | } 27 | 28 | for (Wire w : inputs) { 29 | if (w == null) { 30 | System.err.println("One of the input wires is null: " + this); 31 | throw new NullPointerException("A null wire"); 32 | } else if (w.getWireId() == -1) { 33 | System.err.println("One of the input wires is not packed: " + this); 34 | throw new IllegalArgumentException("A wire with a negative id"); 35 | } 36 | } 37 | for (Wire w : outputs) { 38 | if (w == null) { 39 | System.err.println("One of the output wires is null" + this); 40 | throw new NullPointerException("A null wire"); 41 | } 42 | } 43 | 44 | } 45 | 46 | 47 | public BasicOp(Wire[] inputs, Wire[] outputs) { 48 | this(inputs, outputs, ""); 49 | } 50 | 51 | public void evaluate(CircuitEvaluator evaluator) { 52 | BigInteger[] assignment = evaluator.getAssignment(); 53 | checkInputs(assignment); 54 | checkOutputs(assignment); 55 | compute(assignment); 56 | } 57 | 58 | protected void checkInputs(BigInteger[] assignment) { 59 | for (Wire w : inputs) { 60 | if (assignment[w.getWireId()] == null) { 61 | System.err.println("Error - The inWire " + w + " has not been assigned\n" + this); 62 | throw new RuntimeException("Error During Evaluation"); 63 | } 64 | } 65 | } 66 | 67 | protected abstract void compute(BigInteger[] assignment); 68 | 69 | protected void checkOutputs(BigInteger[] assignment) { 70 | for (Wire w : outputs) { 71 | if (assignment[w.getWireId()] != null) { 72 | System.err.println("Error - The outWire " + w + " has already been assigned\n" + this); 73 | throw new RuntimeException("Error During Evaluation"); 74 | } 75 | } 76 | } 77 | 78 | public abstract String getOpcode(); 79 | public abstract int getNumMulGates(); 80 | 81 | public String toString() { 82 | return getOpcode() + " in " + inputs.length + " <" + Util.arrayToString(inputs, " ") + "> out " + outputs.length 83 | + " <" + Util.arrayToString(outputs, " ") + ">" + (desc.length() > 0 ? (" \t\t# " + desc) : ""); 84 | } 85 | 86 | public Wire[] getInputs() { 87 | return inputs; 88 | } 89 | 90 | public Wire[] getOutputs() { 91 | return outputs; 92 | } 93 | 94 | public boolean doneWithinCircuit() { 95 | return true; 96 | } 97 | 98 | @Override 99 | public int hashCode() { 100 | // this method should be overriden when a subclass can have more than one opcode, or have other arguments 101 | int h = getOpcode().hashCode(); 102 | for(Wire in:inputs){ 103 | h+=in.hashCode(); 104 | } 105 | return h; 106 | } 107 | 108 | 109 | 110 | @Override 111 | public boolean equals(Object obj) { 112 | if(this == obj) 113 | return true; 114 | else 115 | return false; 116 | 117 | // logic moved to subclasses 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/ConstMulBasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.config.Config; 9 | import circuit.structure.Wire; 10 | 11 | public class ConstMulBasicOp extends BasicOp { 12 | 13 | private BigInteger constInteger; 14 | private boolean inSign; 15 | 16 | public ConstMulBasicOp(Wire w, Wire out, BigInteger constInteger, 17 | String...desc) { 18 | super(new Wire[] { w }, new Wire[] { out }, desc); 19 | inSign = constInteger.signum() == -1; 20 | if (!inSign) { 21 | constInteger = constInteger.mod(Config.FIELD_PRIME); 22 | this.constInteger =constInteger; 23 | } else { 24 | constInteger = constInteger.negate(); 25 | constInteger = constInteger.mod(Config.FIELD_PRIME); 26 | this.constInteger = Config.FIELD_PRIME.subtract(constInteger); 27 | } 28 | } 29 | 30 | public String getOpcode(){ 31 | if (!inSign) { 32 | return "const-mul-" + constInteger.toString(16); 33 | } else{ 34 | return "const-mul-neg-" + Config.FIELD_PRIME.subtract(constInteger).toString(16); 35 | } 36 | } 37 | 38 | @Override 39 | public void compute(BigInteger[] assignment) { 40 | BigInteger result = assignment[inputs[0].getWireId()].multiply(constInteger); 41 | if (result.bitLength() >= Config.LOG2_FIELD_PRIME) { 42 | result = result.mod(Config.FIELD_PRIME); 43 | } 44 | assignment[outputs[0].getWireId()] = result; 45 | } 46 | 47 | @Override 48 | public boolean equals(Object obj) { 49 | if (this == obj) 50 | return true; 51 | if (!(obj instanceof ConstMulBasicOp)) { 52 | return false; 53 | } 54 | ConstMulBasicOp op = (ConstMulBasicOp) obj; 55 | return inputs[0].equals(op.inputs[0]) && constInteger.equals(op.constInteger); 56 | 57 | } 58 | 59 | @Override 60 | public int getNumMulGates() { 61 | return 0; 62 | } 63 | 64 | 65 | @Override 66 | public int hashCode() { 67 | int h = constInteger.hashCode(); 68 | for(Wire in:inputs){ 69 | h+=in.hashCode(); 70 | } 71 | return h; 72 | } 73 | 74 | 75 | } -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/MulBasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.config.Config; 9 | import circuit.structure.Wire; 10 | 11 | public class MulBasicOp extends BasicOp { 12 | 13 | public MulBasicOp(Wire w1, Wire w2, Wire output, String... desc) { 14 | super(new Wire[] { w1, w2 }, new Wire[] { output }, desc); 15 | } 16 | 17 | public String getOpcode(){ 18 | return "mul"; 19 | } 20 | 21 | @Override 22 | public void compute(BigInteger[] assignment) { 23 | BigInteger result = assignment[inputs[0].getWireId()] 24 | .multiply(assignment[inputs[1].getWireId()]); 25 | if (result.compareTo(Config.FIELD_PRIME) > 0) { 26 | result = result.mod(Config.FIELD_PRIME); 27 | } 28 | assignment[outputs[0].getWireId()] = result; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | 34 | if (this == obj) 35 | return true; 36 | if (!(obj instanceof MulBasicOp)) { 37 | return false; 38 | } 39 | MulBasicOp op = (MulBasicOp) obj; 40 | 41 | boolean check1 = inputs[0].equals(op.inputs[0]) 42 | && inputs[1].equals(op.inputs[1]); 43 | boolean check2 = inputs[1].equals(op.inputs[0]) 44 | && inputs[0].equals(op.inputs[1]); 45 | return check1 || check2; 46 | 47 | } 48 | 49 | @Override 50 | public int getNumMulGates() { 51 | return 1; 52 | } 53 | 54 | 55 | } -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/NonZeroCheckBasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.structure.Wire; 9 | 10 | public class NonZeroCheckBasicOp extends BasicOp { 11 | 12 | public NonZeroCheckBasicOp(Wire w, Wire out1, Wire out2 , String...desc) { 13 | super(new Wire[] { w }, new Wire[]{out1, out2}, desc); 14 | } 15 | 16 | public String getOpcode(){ 17 | return "zerop"; 18 | } 19 | @Override 20 | public void compute(BigInteger[] assignment) { 21 | 22 | if (assignment[inputs[0].getWireId()].signum() == 0) { 23 | assignment[outputs[1].getWireId()] = BigInteger.ZERO; 24 | } else { 25 | assignment[outputs[1].getWireId()] = BigInteger.ONE; 26 | } 27 | assignment[outputs[0].getWireId()] = BigInteger.ZERO; // a dummy value 28 | } 29 | 30 | @Override 31 | public boolean equals(Object obj) { 32 | 33 | if (this == obj) 34 | return true; 35 | if (!(obj instanceof NonZeroCheckBasicOp)) { 36 | return false; 37 | } 38 | NonZeroCheckBasicOp op = (NonZeroCheckBasicOp) obj; 39 | return inputs[0].equals(op.inputs[0]); 40 | 41 | } 42 | 43 | @Override 44 | public int getNumMulGates() { 45 | return 2; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/ORBasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import util.Util; 9 | import circuit.structure.Wire; 10 | 11 | public class ORBasicOp extends BasicOp { 12 | 13 | public ORBasicOp(Wire w1, Wire w2, Wire output, String...desc) { 14 | super(new Wire[] { w1, w2 }, new Wire[] { output }, desc); 15 | } 16 | 17 | public String getOpcode(){ 18 | return "or"; 19 | } 20 | 21 | public void checkInputs(BigInteger[] assignment) { 22 | super.checkInputs(assignment); 23 | boolean check = Util.isBinary(assignment[inputs[0].getWireId()]) 24 | && Util.isBinary(assignment[inputs[1].getWireId()]); 25 | if (!check){ 26 | System.err.println("Error - Input(s) to OR are not binary. " 27 | + this); 28 | throw new RuntimeException("Error During Evaluation"); 29 | 30 | } 31 | } 32 | 33 | @Override 34 | public void compute(BigInteger[] assignment) { 35 | assignment[outputs[0].getWireId()] = assignment[inputs[0].getWireId()].or( 36 | assignment[inputs[1].getWireId()]); 37 | } 38 | 39 | @Override 40 | public boolean equals(Object obj) { 41 | 42 | if (this == obj) 43 | return true; 44 | if (!(obj instanceof ORBasicOp)) { 45 | return false; 46 | } 47 | ORBasicOp op = (ORBasicOp) obj; 48 | 49 | boolean check1 = inputs[0].equals(op.inputs[0]) 50 | && inputs[1].equals(op.inputs[1]); 51 | boolean check2 = inputs[1].equals(op.inputs[0]) 52 | && inputs[0].equals(op.inputs[1]); 53 | return check1 || check2; 54 | 55 | } 56 | 57 | @Override 58 | public int getNumMulGates() { 59 | return 1; 60 | } 61 | } -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/PackBasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import util.Util; 9 | import circuit.config.Config; 10 | import circuit.structure.Wire; 11 | 12 | public class PackBasicOp extends BasicOp { 13 | 14 | public PackBasicOp(Wire[] inBits, Wire out, String... desc) { 15 | super(inBits, new Wire[] { out }, desc); 16 | } 17 | 18 | public String getOpcode(){ 19 | return "pack"; 20 | } 21 | 22 | @Override 23 | public void checkInputs(BigInteger[] assignment) { 24 | super.checkInputs(assignment); 25 | boolean check = true; 26 | for (int i = 0; i < inputs.length; i++) { 27 | check &= Util.isBinary(assignment[inputs[i].getWireId()]); 28 | } 29 | if (!check) { 30 | System.err.println("Error - Input(s) to Pack are not binary. " 31 | + this); 32 | throw new RuntimeException("Error During Evaluation"); 33 | 34 | } 35 | } 36 | 37 | @Override 38 | public void compute(BigInteger[] assignment) { 39 | BigInteger sum = BigInteger.ZERO; 40 | for (int i = 0; i < inputs.length; i++) { 41 | sum = sum.add(assignment[inputs[i].getWireId()] 42 | .multiply(new BigInteger("2").pow(i))); 43 | } 44 | assignment[outputs[0].getWireId()] = sum.mod(Config.FIELD_PRIME); 45 | } 46 | 47 | @Override 48 | public boolean equals(Object obj) { 49 | 50 | if (this == obj) 51 | return true; 52 | if (!(obj instanceof PackBasicOp)) { 53 | return false; 54 | } 55 | PackBasicOp op = (PackBasicOp) obj; 56 | if (op.inputs.length != inputs.length) 57 | return false; 58 | 59 | boolean check = true; 60 | for (int i = 0; i < inputs.length; i++) { 61 | check = check && inputs[i].equals(op.inputs[i]); 62 | } 63 | return check; 64 | } 65 | 66 | @Override 67 | public int getNumMulGates() { 68 | return 0; 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/SplitBasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.config.Config; 9 | import circuit.structure.Wire; 10 | 11 | public class SplitBasicOp extends BasicOp { 12 | 13 | public SplitBasicOp(Wire w, Wire[] outs, String...desc) { 14 | super(new Wire[] { w }, outs, desc); 15 | } 16 | 17 | public String getOpcode(){ 18 | return "split"; 19 | } 20 | 21 | protected void checkInputs(BigInteger[] assignment) { 22 | super.checkInputs(assignment); 23 | if (outputs.length < assignment[inputs[0].getWireId()].bitLength()) { 24 | System.err 25 | .println("Error in Split --- The number of bits does not fit -- Input: " 26 | + assignment[inputs[0].getWireId()].toString(16) + "\n\t" + this); 27 | 28 | throw new RuntimeException("Error During Evaluation -- " + this); 29 | } 30 | } 31 | 32 | @Override 33 | protected void compute(BigInteger[] assignment) { 34 | 35 | BigInteger inVal = assignment[inputs[0].getWireId()]; 36 | if (inVal.compareTo(Config.FIELD_PRIME) > 0) { 37 | inVal = inVal.mod(Config.FIELD_PRIME); 38 | } 39 | for (int i = 0; i < outputs.length; i++) { 40 | assignment[outputs[i].getWireId()] = inVal.testBit(i) ? BigInteger.ONE 41 | : BigInteger.ZERO; 42 | } 43 | } 44 | 45 | @Override 46 | public boolean equals(Object obj) { 47 | 48 | if (this == obj) 49 | return true; 50 | if (!(obj instanceof SplitBasicOp)) { 51 | return false; 52 | } 53 | SplitBasicOp op = (SplitBasicOp) obj; 54 | return inputs[0].equals(op.inputs[0]) && outputs.length == op.outputs.length; 55 | 56 | } 57 | 58 | @Override 59 | public int getNumMulGates() { 60 | return outputs.length + 1; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/operations/primitive/XorBasicOp.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.operations.primitive; 5 | 6 | import java.math.BigInteger; 7 | 8 | import util.Util; 9 | import circuit.structure.Wire; 10 | 11 | public class XorBasicOp extends BasicOp { 12 | 13 | public XorBasicOp(Wire w1, Wire w2, Wire output, String...desc) { 14 | super(new Wire[] { w1, w2 }, new Wire[] { output }, desc); 15 | } 16 | 17 | public String getOpcode(){ 18 | return "xor"; 19 | } 20 | 21 | public void checkInputs(BigInteger[] assignment) { 22 | super.checkInputs(assignment); 23 | boolean check = Util.isBinary(assignment[inputs[0].getWireId()]) 24 | && Util.isBinary(assignment[inputs[1].getWireId()]); 25 | if (!check){ 26 | System.err.println("Error - Input(s) to XOR are not binary. " 27 | + this); 28 | throw new RuntimeException("Error During Evaluation"); 29 | 30 | } 31 | } 32 | 33 | @Override 34 | public void compute(BigInteger[] assignment) { 35 | assignment[outputs[0].getWireId()] = assignment[inputs[0].getWireId()].xor( 36 | assignment[inputs[1].getWireId()]); 37 | } 38 | 39 | @Override 40 | public boolean equals(Object obj) { 41 | 42 | if (this == obj) 43 | return true; 44 | if (!(obj instanceof XorBasicOp)) { 45 | return false; 46 | } 47 | XorBasicOp op = (XorBasicOp) obj; 48 | 49 | boolean check1 = inputs[0].equals(op.inputs[0]) 50 | && inputs[1].equals(op.inputs[1]); 51 | boolean check2 = inputs[1].equals(op.inputs[0]) 52 | && inputs[0].equals(op.inputs[1]); 53 | return check1 || check2; 54 | 55 | } 56 | 57 | @Override 58 | public int getNumMulGates() { 59 | return 1; 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/structure/BitWire.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.structure; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.eval.Instruction; 9 | import circuit.operations.primitive.AddBasicOp; 10 | import circuit.operations.primitive.ConstMulBasicOp; 11 | import circuit.operations.primitive.MulBasicOp; 12 | import circuit.operations.primitive.ORBasicOp; 13 | import circuit.operations.primitive.XorBasicOp; 14 | 15 | 16 | public class BitWire extends Wire { 17 | 18 | public BitWire(int wireId) { 19 | super(wireId); 20 | } 21 | 22 | public Wire mul(Wire w, String desc) { 23 | if (w instanceof ConstantWire) { 24 | return this.mul(((ConstantWire) w).getConstant(), desc); 25 | } else { 26 | Wire output; 27 | if (w instanceof BitWire) 28 | output = new VariableBitWire(generator.currentWireId++); 29 | else 30 | output = new VariableWire(generator.currentWireId++); 31 | Instruction op = new MulBasicOp(this, w, output, desc); 32 | Wire[] cachedOutputs = generator.addToEvaluationQueue(op); 33 | if(cachedOutputs == null){ 34 | return output; 35 | } 36 | else{ 37 | generator.currentWireId--; 38 | return cachedOutputs[0]; 39 | } 40 | } 41 | } 42 | 43 | public Wire mul(BigInteger b, String... desc) { 44 | Wire out; 45 | if(b.equals(BigInteger.ZERO)){ 46 | return generator.zeroWire; 47 | } else if(b.equals(BigInteger.ONE)){ 48 | return this; 49 | } else{ 50 | out = new LinearCombinationWire(generator.currentWireId++); 51 | Instruction op = new ConstMulBasicOp(this, out, b, desc); 52 | // generator.addToEvaluationQueue(op); 53 | // return out; 54 | Wire[] cachedOutputs = generator.addToEvaluationQueue(op); 55 | if(cachedOutputs == null){ 56 | return out; 57 | } 58 | else{ 59 | generator.currentWireId--; 60 | return cachedOutputs[0]; 61 | } 62 | } 63 | } 64 | 65 | public Wire invAsBit(String...desc) { 66 | // Wire neg = new Wire(generator.currentWireId++); 67 | // Instruction op = new ConstMulBasicOp(this, neg, -1, desc); 68 | // generator.addToEvaluationQueue(op); 69 | Wire neg = this.mul(-1, desc); 70 | Wire out = new LinearCombinationBitWire(generator.currentWireId++); 71 | Instruction op = new AddBasicOp(new Wire[] { generator.oneWire, neg }, out, desc); 72 | // generator.addToEvaluationQueue(op); 73 | Wire[] cachedOutputs = generator.addToEvaluationQueue(op); 74 | if(cachedOutputs == null){ 75 | return out; 76 | } 77 | else{ 78 | generator.currentWireId--; 79 | return cachedOutputs[0]; 80 | } 81 | } 82 | 83 | public Wire or(Wire w, String...desc) { 84 | if (w instanceof ConstantWire) { 85 | return w.or(this, desc); 86 | } else { 87 | Wire out; 88 | if (w instanceof BitWire) { 89 | out = new VariableBitWire(generator.currentWireId++); 90 | Instruction op = new ORBasicOp(this, w, out, desc); 91 | Wire[] cachedOutputs = generator.addToEvaluationQueue(op); 92 | if(cachedOutputs == null){ 93 | return out; 94 | } 95 | else{ 96 | generator.currentWireId--; 97 | return cachedOutputs[0]; 98 | } 99 | } else { 100 | return super.or(w, desc); 101 | } 102 | } 103 | } 104 | 105 | 106 | public Wire xor(Wire w, String...desc) { 107 | if (w instanceof ConstantWire) { 108 | return w.xor(this, desc); 109 | } else { 110 | Wire out; 111 | if (w instanceof BitWire) { 112 | out = new VariableBitWire(generator.currentWireId++); 113 | Instruction op = new XorBasicOp(this, w, out, desc); 114 | Wire[] cachedOutputs = generator.addToEvaluationQueue(op); 115 | if(cachedOutputs == null){ 116 | return out; 117 | } 118 | else{ 119 | generator.currentWireId--; 120 | return cachedOutputs[0]; 121 | } 122 | } else { 123 | return super.xor(w, desc); 124 | } 125 | } 126 | } 127 | 128 | public WireArray getBits(Wire w, int bitwidth, String...desc) { 129 | return new WireArray( new Wire[]{this}).adjustLength(bitwidth); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/structure/ConstantWire.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.structure; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.config.Config; 9 | import circuit.eval.Instruction; 10 | import circuit.operations.primitive.ConstMulBasicOp; 11 | 12 | public class ConstantWire extends Wire { 13 | 14 | protected BigInteger constant; 15 | 16 | public ConstantWire(int wireId, BigInteger value) { 17 | super(wireId); 18 | constant = value.mod(Config.FIELD_PRIME); 19 | } 20 | 21 | public BigInteger getConstant() { 22 | return constant; 23 | } 24 | 25 | public boolean isBinary() { 26 | return constant.equals(BigInteger.ONE) 27 | || constant.equals(BigInteger.ZERO); 28 | } 29 | 30 | public Wire mul(Wire w, String... desc) { 31 | if (w instanceof ConstantWire) { 32 | return generator.createConstantWire( 33 | constant.multiply(((ConstantWire) w).constant), desc); 34 | } else { 35 | return w.mul(constant, desc); 36 | } 37 | } 38 | 39 | public Wire mul(BigInteger b, String... desc) { 40 | Wire out; 41 | boolean sign = b.signum() == -1; 42 | BigInteger newConstant = constant.multiply(b).mod(Config.FIELD_PRIME); 43 | 44 | out = generator.knownConstantWires.get(newConstant); 45 | if (out == null) { 46 | 47 | if(!sign){ 48 | out = new ConstantWire(generator.currentWireId++, newConstant); 49 | } else{ 50 | out = new ConstantWire(generator.currentWireId++, newConstant.subtract(Config.FIELD_PRIME)); 51 | } 52 | Instruction op = new ConstMulBasicOp(this, out, 53 | b, desc); 54 | Wire[] cachedOutputs = generator.addToEvaluationQueue(op); 55 | if(cachedOutputs == null){ 56 | generator.knownConstantWires.put(newConstant, out); 57 | return out; 58 | } 59 | else{ 60 | // this branch might not be needed 61 | generator.currentWireId--; 62 | return cachedOutputs[0]; 63 | } 64 | 65 | } 66 | return out; 67 | } 68 | 69 | public Wire checkNonZero(Wire w, String... desc) { 70 | if (constant.equals(BigInteger.ZERO)) { 71 | return generator.zeroWire; 72 | } else { 73 | return generator.oneWire; 74 | } 75 | } 76 | 77 | public Wire invAsBit(String... desc) { 78 | if (!isBinary()) { 79 | throw new RuntimeException( 80 | "Trying to invert a non-binary constant!"); 81 | } 82 | if (constant.equals(BigInteger.ZERO)) { 83 | return generator.oneWire; 84 | } else { 85 | return generator.zeroWire; 86 | } 87 | } 88 | 89 | public Wire or(Wire w, String... desc) { 90 | if (w instanceof ConstantWire) { 91 | ConstantWire cw = (ConstantWire) w; 92 | if (isBinary() && cw.isBinary()) { 93 | if (constant.equals(BigInteger.ZERO) 94 | && cw.getConstant().equals(BigInteger.ZERO)) { 95 | return generator.zeroWire; 96 | } else { 97 | return generator.oneWire; 98 | } 99 | } else { 100 | throw new RuntimeException( 101 | "Trying to OR two non-binary constants"); 102 | } 103 | } else { 104 | if (constant.equals(BigInteger.ONE)) { 105 | return generator.oneWire; 106 | } else { 107 | return w; 108 | } 109 | } 110 | } 111 | 112 | public Wire xor(Wire w, String... desc) { 113 | if (w instanceof ConstantWire) { 114 | ConstantWire cw = (ConstantWire) w; 115 | if (isBinary() && cw.isBinary()) { 116 | if (constant.equals(cw.getConstant())) { 117 | return generator.zeroWire; 118 | } else { 119 | return generator.oneWire; 120 | } 121 | } else { 122 | throw new RuntimeException( 123 | "Trying to XOR two non-binary constants"); 124 | } 125 | } else { 126 | if (constant.equals(BigInteger.ONE)) { 127 | return w.invAsBit(desc); 128 | } else { 129 | return w; 130 | } 131 | } 132 | } 133 | 134 | public WireArray getBitWires(int bitwidth, String... desc) { 135 | if (constant.bitLength() > bitwidth) { 136 | throw new RuntimeException("Trying to split a constant of " 137 | + constant.bitLength() + " bits into " + bitwidth + "bits"); 138 | } else { 139 | Wire[] bits = new ConstantWire[bitwidth]; 140 | for (int i = 0; i < bitwidth; i++) { 141 | bits[i] = constant.testBit(i) ? generator.oneWire : generator.zeroWire; 142 | } 143 | return new WireArray(bits); 144 | } 145 | } 146 | 147 | public void restrictBitLength(int bitwidth, String...desc) { 148 | getBitWires(bitwidth, desc); 149 | } 150 | 151 | protected void pack(String...desc){ 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/structure/LinearCombinationBitWire.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.structure; 5 | 6 | public class LinearCombinationBitWire extends BitWire { 7 | 8 | public LinearCombinationBitWire(int wireId) { 9 | super(wireId); 10 | } 11 | 12 | public WireArray getBitWires() { 13 | return new WireArray(new Wire[]{this}); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/structure/LinearCombinationWire.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.structure; 5 | 6 | public class LinearCombinationWire extends Wire { 7 | 8 | private WireArray bitWires; 9 | 10 | public LinearCombinationWire(int wireId) { 11 | super(wireId); 12 | } 13 | 14 | public LinearCombinationWire(WireArray bits) { 15 | super(bits); 16 | } 17 | 18 | WireArray getBitWires() { 19 | return bitWires; 20 | } 21 | 22 | void setBits(WireArray bitWires) { 23 | this.bitWires = bitWires; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/structure/VariableBitWire.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.structure; 5 | 6 | public class VariableBitWire extends BitWire { 7 | 8 | public VariableBitWire(int wireId) { 9 | super(wireId); 10 | } 11 | 12 | public WireArray getBitWires() { 13 | return new WireArray(new Wire[] { this }); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/circuit/structure/VariableWire.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package circuit.structure; 5 | 6 | public class VariableWire extends Wire { 7 | 8 | private WireArray bitWires; 9 | 10 | public VariableWire(int wireId) { 11 | super(wireId); 12 | } 13 | 14 | public VariableWire(WireArray bits) { 15 | super(bits); 16 | } 17 | 18 | 19 | WireArray getBitWires() { 20 | return bitWires; 21 | } 22 | 23 | void setBits(WireArray bitWires) { 24 | this.bitWires = bitWires; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/augmenter/PinocchioGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.augmenter; 5 | 6 | import java.io.File; 7 | import java.io.FileNotFoundException; 8 | import java.math.BigInteger; 9 | import java.util.ArrayList; 10 | import java.util.Scanner; 11 | 12 | import circuit.operations.Gadget; 13 | import circuit.structure.Wire; 14 | 15 | public class PinocchioGadget extends Gadget { 16 | 17 | private Wire[] inputWires; 18 | private Wire[] proverWitnessWires; 19 | private Wire[] outputWires; 20 | 21 | public PinocchioGadget(Wire[] inputWires, String pathToArithFile, String... desc) { 22 | super(desc); 23 | this.inputWires = inputWires; 24 | try { 25 | buildCircuit(pathToArithFile); 26 | } catch (Exception e) { 27 | e.printStackTrace(); 28 | } 29 | } 30 | 31 | private void buildCircuit(String path) throws FileNotFoundException { 32 | 33 | ArrayList proverWitnessWires = new ArrayList(); 34 | ArrayList outputWires = new ArrayList(); 35 | 36 | Wire[] wireMapping; 37 | Scanner scanner = new Scanner(new File(path)); 38 | 39 | if (!scanner.next().equals("total")) { 40 | scanner.close(); 41 | throw new RuntimeException("Expected total %d in the first line"); 42 | } 43 | int numWires = scanner.nextInt(); 44 | scanner.nextLine(); 45 | wireMapping = new Wire[numWires]; 46 | 47 | int inputCount = 0; 48 | while (scanner.hasNext()) { 49 | String line = scanner.nextLine(); 50 | // remove comments 51 | if (line.contains("#")) { 52 | line = line.substring(0, line.indexOf("#")); 53 | } 54 | if (line.equals("")) { 55 | continue; 56 | } else if (line.startsWith("input")) { 57 | String[] tokens = line.split("\\s+"); 58 | int wireIndex = Integer.parseInt(tokens[1]); 59 | if (wireMapping[wireIndex] != null) { 60 | throwParsingError(scanner, "Wire assigned twice! " + wireIndex); 61 | } 62 | if (inputCount < inputWires.length) { 63 | wireMapping[wireIndex] = inputWires[inputCount]; 64 | } else { 65 | // the last input wire is assumed to be the one wire 66 | wireMapping[wireIndex] = generator.getOneWire(); 67 | } 68 | inputCount++; 69 | } else if (line.startsWith("output")) { 70 | String[] tokens = line.split("\\s+"); 71 | int wireIndex = Integer.parseInt(tokens[1]); 72 | outputWires.add(wireMapping[wireIndex]); 73 | } else if (line.startsWith("nizk")) { 74 | String[] tokens = line.split("\\s+"); 75 | int wireIndex = Integer.parseInt(tokens[1]); 76 | if (wireMapping[wireIndex] != null) { 77 | throwParsingError(scanner, "Wire assigned twice! " + wireIndex); 78 | } 79 | Wire w = generator.createProverWitnessWire(); 80 | proverWitnessWires.add(w); 81 | wireMapping[wireIndex] = w; 82 | } else { 83 | ArrayList ins = getInputs(line); 84 | for (int in : ins) { 85 | if (wireMapping[in] == null) { 86 | throwParsingError(scanner, "Undefined input wire " + in + " at line " + line); 87 | } 88 | } 89 | ArrayList outs = getOutputs(line); 90 | if (line.startsWith("mul ")) { 91 | wireMapping[outs.get(0)] = wireMapping[ins.get(0)].mul(wireMapping[ins.get(1)]); 92 | } else if (line.startsWith("add ")) { 93 | Wire result = wireMapping[ins.get(0)]; 94 | for (int i = 1; i < ins.size(); i++) { 95 | result = result.add(wireMapping[ins.get(i)]); 96 | } 97 | wireMapping[outs.get(0)] = result; 98 | } else if (line.startsWith("zerop ")) { 99 | wireMapping[outs.get(1)] = wireMapping[ins.get(0)].checkNonZero(); 100 | } else if (line.startsWith("split ")) { 101 | Wire[] bits = wireMapping[ins.get(0)].getBitWires(outs.size()).asArray(); 102 | for (int i = 0; i < outs.size(); i++) { 103 | wireMapping[outs.get(i)] = bits[i]; 104 | } 105 | } else if (line.startsWith("const-mul-neg-")) { 106 | String constantStr = line.substring("const-mul-neg-".length(), line.indexOf(" ")); 107 | BigInteger constant = new BigInteger(constantStr, 16); 108 | wireMapping[outs.get(0)] = wireMapping[ins.get(0)].mul(constant.negate()); 109 | } else if (line.startsWith("const-mul-")) { 110 | String constantStr = line.substring("const-mul-".length(), line.indexOf(" ")); 111 | BigInteger constant = new BigInteger(constantStr, 16); 112 | wireMapping[outs.get(0)] = wireMapping[ins.get(0)].mul(constant); 113 | } else { 114 | throwParsingError(scanner, "Unsupport Circuit Line " + line); 115 | } 116 | 117 | } 118 | } 119 | 120 | scanner.close(); 121 | 122 | this.proverWitnessWires = new Wire[proverWitnessWires.size()]; 123 | proverWitnessWires.toArray(this.proverWitnessWires); 124 | this.outputWires = new Wire[outputWires.size()]; 125 | outputWires.toArray(this.outputWires); 126 | } 127 | 128 | private ArrayList getOutputs(String line) { 129 | Scanner scanner = new Scanner(line.substring(line.lastIndexOf("<") + 1, line.lastIndexOf(">"))); 130 | ArrayList outs = new ArrayList<>(); 131 | while (scanner.hasNextInt()) { 132 | int v = scanner.nextInt(); 133 | outs.add(v); 134 | } 135 | scanner.close(); 136 | return outs; 137 | } 138 | 139 | private ArrayList getInputs(String line) { 140 | Scanner scanner = new Scanner(line.substring(line.indexOf("<") + 1, line.indexOf(">"))); 141 | ArrayList ins = new ArrayList<>(); 142 | while (scanner.hasNextInt()) { 143 | ins.add(scanner.nextInt()); 144 | } 145 | scanner.close(); 146 | return ins; 147 | } 148 | 149 | @Override 150 | public Wire[] getOutputWires() { 151 | return outputWires; 152 | } 153 | 154 | public Wire[] getProverWitnessWires() { 155 | return proverWitnessWires; 156 | } 157 | 158 | private void throwParsingError(Scanner s, String m) { 159 | s.close(); 160 | throw new RuntimeException(m); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/blockciphers/ChaskeyLTS128CipherGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.blockciphers; 6 | 7 | import circuit.operations.Gadget; 8 | import circuit.structure.Wire; 9 | 10 | /** 11 | * Implements the light weight cipher Chaskey128, the LTS version with 16 rounds 12 | * https://eprint.iacr.org/2014/386.pdf. 13 | * 14 | * The gadget follows the reference implementation from this project: 15 | * https://www.nist.gov/sites/default/files/documents/2016/10/18/perrin-paper-lwc2016.pdf 16 | * https://www.cryptolux.org/index.php/FELICS 17 | */ 18 | public class ChaskeyLTS128CipherGadget extends Gadget { 19 | 20 | private Wire[] plaintext; // 4 32-bit words 21 | private Wire[] key; // 4 32-bit words 22 | private Wire[] ciphertext; // 4 32-bit words 23 | 24 | public ChaskeyLTS128CipherGadget(Wire[] inputs, Wire[] key, String... desc) { 25 | super(desc); 26 | if (inputs.length != 4 || key.length != 4) { 27 | throw new IllegalArgumentException("Invalid Input"); 28 | } 29 | this.plaintext = inputs; 30 | this.key = key; 31 | 32 | buildCircuit(); 33 | 34 | } 35 | 36 | protected void buildCircuit() { 37 | 38 | Wire[] v = new Wire[4]; 39 | for (int i = 0; i < 4; i++) { 40 | v[i] = (plaintext[i].xorBitwise(key[i], 32)); 41 | } 42 | 43 | for (int i = 0; i < 16; i++) { 44 | 45 | v[0] = v[0].add(v[1]); 46 | v[0] = v[0].trimBits(33, 32); 47 | v[1] = v[1].rotateLeft(32, 5).xorBitwise(v[0], 32); 48 | v[0] = v[0].rotateLeft(32, 16); 49 | 50 | v[2] = v[2].add(v[3]).trimBits(33, 32); 51 | v[3] = v[3].rotateLeft(32, 8).xorBitwise(v[2], 32); 52 | 53 | v[0] = v[0].add(v[3]).trimBits(33, 32); 54 | v[3] = v[3].rotateLeft(32, 13).xorBitwise(v[0], 32); 55 | 56 | v[2] = v[2].add(v[1]).trimBits(33, 32); 57 | ; 58 | v[1] = v[1].rotateLeft(32, 7).xorBitwise(v[2], 32); 59 | v[2] = v[2].rotateLeft(32, 16); 60 | 61 | } 62 | 63 | for (int i = 0; i < 4; i++) { 64 | v[i] = v[i].xorBitwise(key[i], 32); 65 | } 66 | ciphertext = v; 67 | } 68 | 69 | @Override 70 | public Wire[] getOutputWires() { 71 | return ciphertext; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/blockciphers/Speck128CipherGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.blockciphers; 6 | 7 | import circuit.operations.Gadget; 8 | import circuit.structure.CircuitGenerator; 9 | import circuit.structure.Wire; 10 | 11 | /** 12 | * Implements the Speck lightweight block cipher 13 | * https://eprint.iacr.org/2015/585.pdf 14 | * 15 | */ 16 | 17 | public class Speck128CipherGadget extends Gadget { 18 | 19 | private Wire[] plaintext; 20 | private Wire[] expandedKey; 21 | private Wire[] ciphertext; 22 | 23 | 24 | /** 25 | * 26 | * @param inputs 27 | * : Array of 2 64-bit elements. 28 | * @param expandedKey 29 | * : Array of 32 64-bit elements. (Call expandKey(..)) 30 | * @param desc 31 | */ 32 | public Speck128CipherGadget(Wire[] plaintext, Wire[] expandedKey, 33 | String... desc) { 34 | super(desc); 35 | if (plaintext.length != 2 || expandedKey.length != 32) { 36 | throw new IllegalArgumentException("Invalid Input"); 37 | } 38 | this.plaintext = plaintext; 39 | this.expandedKey = expandedKey; 40 | buildCircuit(); 41 | } 42 | 43 | protected void buildCircuit() { 44 | 45 | Wire x, y; 46 | x = plaintext[1]; 47 | y = plaintext[0]; 48 | ciphertext = new Wire[2]; 49 | for (int i = 0; i <= 31; i++) { 50 | x = x.rotateRight(64, 8).add(y); 51 | x = x.trimBits(65, 64); 52 | x = x.xorBitwise(expandedKey[i], 64); 53 | y = y.rotateLeft(64, 3).xorBitwise(x, 64); 54 | } 55 | ciphertext[1] = x; 56 | ciphertext[0] = y; 57 | } 58 | 59 | /** 60 | * 61 | * @param key 62 | * : 2 64-bit words 63 | * @return 64 | */ 65 | public static Wire[] expandKey(Wire[] key) { 66 | CircuitGenerator generator = CircuitGenerator 67 | .getActiveCircuitGenerator(); 68 | Wire[] k = new Wire[32]; 69 | Wire[] l = new Wire[32]; 70 | k[0] = key[0]; 71 | l[0] = key[1]; 72 | for (int i = 0; i <= 32 - 2; i++) { 73 | l[i + 1] = k[i].add(l[i].rotateLeft(64, 56)); 74 | l[i + 1] = l[i + 1].trimBits(65, 64); 75 | l[i + 1] = l[i + 1].xorBitwise(generator.createConstantWire(i), 64); 76 | k[i + 1] = k[i].rotateLeft(64, 3).xorBitwise(l[i + 1], 64); 77 | } 78 | return k; 79 | } 80 | 81 | @Override 82 | public Wire[] getOutputWires() { 83 | return ciphertext; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/blockciphers/SymmetricEncryptionCBCGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.blockciphers; 6 | 7 | import java.util.Arrays; 8 | 9 | import util.Util; 10 | import circuit.operations.Gadget; 11 | import circuit.structure.Wire; 12 | import circuit.structure.WireArray; 13 | 14 | /** 15 | * Performs symmetric encryption in the CBC mode. 16 | * Only supports one cipher (speck128) as an example at the moment. Other ciphers will be integrated soon. 17 | * 18 | */ 19 | public class SymmetricEncryptionCBCGadget extends Gadget { 20 | 21 | private Wire[] ciphertext; 22 | private String cipherName; 23 | 24 | private Wire[] keyBits; 25 | private Wire[] plaintextBits; 26 | private Wire[] ivBits; 27 | 28 | private int blocksize = 128; 29 | private int keysize = 128; 30 | 31 | public SymmetricEncryptionCBCGadget(Wire[] plaintextBits, Wire[] keyBits, 32 | Wire[] ivBits, String cipherName, String... desc) { 33 | super(desc); 34 | if(keyBits.length != keysize || ivBits.length != keysize){ 35 | throw new IllegalArgumentException("Key and IV bit vectors should be of length 128"); 36 | } 37 | this.plaintextBits = plaintextBits; 38 | this.ivBits = ivBits; 39 | this.keyBits = keyBits; 40 | this.cipherName = cipherName; 41 | buildCircuit(); 42 | } 43 | 44 | protected void buildCircuit() { 45 | 46 | int numBlocks = (int) Math.ceil(plaintextBits.length * 1.0 / blocksize); 47 | plaintextBits = new WireArray(plaintextBits).adjustLength(numBlocks * blocksize) 48 | .asArray(); 49 | 50 | Wire[] preparedKey = prepareKey(); 51 | WireArray prevCipher = new WireArray(ivBits); 52 | 53 | ciphertext = new Wire[0]; 54 | for (int i = 0; i < numBlocks; i++) { 55 | WireArray msgBlock = new WireArray(Arrays.copyOfRange(plaintextBits, i 56 | * blocksize, (i + 1) * blocksize)); 57 | Wire[] xored = msgBlock.xorWireArray(prevCipher).asArray(); 58 | if (cipherName.equals("speck128")) { 59 | Wire[] tmp = new WireArray(xored).packBitsIntoWords(64); 60 | Gadget gadget = new Speck128CipherGadget(tmp, preparedKey, ""); 61 | Wire[] outputs = gadget.getOutputWires(); 62 | prevCipher = new WireArray(outputs).getBits(64); 63 | } else { 64 | throw new UnsupportedOperationException("Other Ciphers not supported in this version!"); 65 | } 66 | ciphertext = Util.concat(ciphertext, 67 | prevCipher.packBitsIntoWords(64)); 68 | } 69 | } 70 | 71 | private Wire[] prepareKey() { 72 | 73 | Wire[] preparedKey; 74 | if (cipherName.equals("speck128")) { 75 | Wire[] packedKey = new WireArray(keyBits).packBitsIntoWords(64); 76 | preparedKey = Speck128CipherGadget.expandKey(packedKey); 77 | } else { 78 | throw new UnsupportedOperationException("Other Ciphers not supported in this version!"); 79 | } 80 | return preparedKey; 81 | } 82 | 83 | @Override 84 | public Wire[] getOutputWires() { 85 | return ciphertext; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/blockciphers/sbox/AESSBoxComputeGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.blockciphers.sbox; 6 | 7 | import circuit.eval.CircuitEvaluator; 8 | import circuit.eval.Instruction; 9 | import circuit.operations.Gadget; 10 | import circuit.structure.Wire; 11 | 12 | /** 13 | * This gadget does not apply any lookups in the circuit. Instead, it verifies 14 | * the solution using the AES S-Box properties. 15 | * (Might need to be revisited in 16 | * the future to include other ways that have better circuit representation). 17 | * 18 | */ 19 | 20 | public class AESSBoxComputeGadget extends Gadget { 21 | 22 | private final Wire input; 23 | private Wire inverse; 24 | private Wire output; 25 | 26 | public AESSBoxComputeGadget(Wire input, String... desc) { 27 | super(desc); 28 | this.input = input; 29 | buildCircuit(); 30 | } 31 | 32 | protected void buildCircuit() { 33 | inverse = generator.createProverWitnessWire(); 34 | 35 | generator.addToEvaluationQueue(new Instruction() { 36 | 37 | @Override 38 | public void evaluate(CircuitEvaluator evaluator) { 39 | int p = evaluator.getWireValue(input).intValue(); 40 | int q = findInv(p); 41 | evaluator.setWireValue(inverse, q); 42 | 43 | } 44 | }); 45 | 46 | inverse.restrictBitLength(8); 47 | Wire v = gmul(input, inverse); 48 | generator.addAssertion(v.sub(generator.getOneWire()), 49 | input.add(inverse), generator.getZeroWire()); 50 | Wire constant = generator.createConstantWire(0x63L); 51 | output = constant.xorBitwise(inverse, 8); 52 | output = output.xorBitwise(inverse.rotateLeft(8, 1), 8); 53 | output = output.xorBitwise(inverse.rotateLeft(8, 2), 8); 54 | output = output.xorBitwise(inverse.rotateLeft(8, 3), 8); 55 | output = output.xorBitwise(inverse.rotateLeft(8, 4), 8); 56 | } 57 | 58 | @Override 59 | public Wire[] getOutputWires() { 60 | return new Wire[] { output }; 61 | } 62 | 63 | private Wire gmul(Wire a, Wire b) { 64 | Wire p = generator.getZeroWire(); 65 | int counter; 66 | for (counter = 0; counter < 8; counter++) { 67 | Wire tmp = p.xorBitwise(a, 8); 68 | Wire bit = b.getBitWires(8).get(0); 69 | p = p.add(bit.mul(tmp.sub(p))); 70 | 71 | Wire bit2 = a.getBitWires(8).get(7); 72 | a = a.shiftLeft(8, 1); 73 | 74 | Wire tmp2 = a.xorBitwise(generator.createConstantWire(0x1bL), 8); 75 | a = a.add(bit2.mul(tmp2.sub(a))); 76 | b = b.shiftRight(8, 1); 77 | } 78 | return p; 79 | } 80 | 81 | private int gmul(int a, int b) { 82 | int p = 0; 83 | int j; 84 | for (j = 0; j < 8; j++) { 85 | if ((b & 1) != 0) 86 | p ^= a; 87 | a <<= 1; 88 | if ((a & 0x100) != 0) 89 | a ^= 0x11b; 90 | b >>= 1; 91 | } 92 | return p; 93 | } 94 | 95 | private int findInv(int a) { 96 | if (a == 0) 97 | return 0; 98 | for (int i = 0; i < 256; i++) { 99 | if (gmul(i, a) == 1) { 100 | return i; 101 | } 102 | } 103 | return -1; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/blockciphers/sbox/AESSBoxGadgetOptimized1.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.blockciphers.sbox; 6 | 7 | import java.math.BigInteger; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.HashSet; 11 | 12 | import circuit.config.Config; 13 | import circuit.eval.CircuitEvaluator; 14 | import circuit.eval.Instruction; 15 | import circuit.operations.Gadget; 16 | import circuit.structure.Wire; 17 | import examples.gadgets.blockciphers.AES128CipherGadget; 18 | import examples.gadgets.blockciphers.sbox.util.LinearSystemSolver; 19 | 20 | /** 21 | * This gadget implements the efficient read-only memory access from xjsnark 22 | * (the generic way). A more efficient variant is implemented in 23 | * AESSBoxGadgetOptimized2.java 24 | * 25 | * Note that we can code the preprocessing of this method using a simpler way 26 | * (by finding 16 polynomials with specific root points) instead of computing 27 | * the coefficients using a linear system of equations, but this was kept as it 28 | * inspired the other optimization in AESSBoxGadgetOptimized2.java, which saves 29 | * half of the cost of a single access. 30 | */ 31 | 32 | public class AESSBoxGadgetOptimized1 extends Gadget { 33 | 34 | private static int SBox[] = AES128CipherGadget.SBox; 35 | 36 | static ArrayList allCoeffSet; 37 | 38 | static { 39 | // preprocessing 40 | solveLinearSystems(); 41 | } 42 | 43 | private final Wire input; 44 | private Wire output; 45 | 46 | public AESSBoxGadgetOptimized1(Wire input, String... desc) { 47 | super(desc); 48 | this.input = input; 49 | buildCircuit(); 50 | } 51 | 52 | public static void solveLinearSystems() { 53 | allCoeffSet = new ArrayList(); 54 | ArrayList list = new ArrayList(); 55 | for (int i = 0; i <= 255; i++) { 56 | list.add(256 * i + SBox[i]); 57 | } 58 | 59 | for (int i = 0; i <= 15; i++) { 60 | HashSet memberValueSet = new HashSet<>(); 61 | BigInteger[][] mat = new BigInteger[16][17]; 62 | 63 | // used for sanity checks 64 | BigInteger[] polyCoeffs = new BigInteger[] { BigInteger.ONE }; 65 | 66 | for (int k = 0; k < mat.length; k++) { 67 | int value = list.get(k + i * 16); 68 | memberValueSet.add(value); 69 | BigInteger p = BigInteger.valueOf(value); 70 | mat[k][0] = BigInteger.ONE; 71 | for (int j = 1; j <= 16; j++) { 72 | mat[k][j] = p.multiply(mat[k][j - 1]).mod( 73 | Config.FIELD_PRIME); 74 | } 75 | // negate the last element, just to make things consistent with 76 | // the paper notations 77 | mat[k][16] = Config.FIELD_PRIME.subtract(mat[k][16]); 78 | 79 | 80 | // used for a sanity check (verifying that the output solution 81 | // is equivalent to coefficients of polynomial that has roots at 82 | // memberValueSet. see note above) 83 | polyCoeffs = polyMul(polyCoeffs, new BigInteger[] { 84 | Config.FIELD_PRIME.subtract(p), BigInteger.ONE }); 85 | } 86 | 87 | new LinearSystemSolver(mat).solveInPlace(); 88 | 89 | // Note that this is just a sanity check here. It should be always 90 | // the case that the prover cannot cheat using this method, 91 | // because this method is equivalent to finding a polynomial with 92 | // \sqrt{n} roots. No other point will satisfy this property. 93 | // However, when we do further optimizations in 94 | // AESBoxGadgetOptimized2.java, this check becomes 95 | // necessary, and other trials could be needed. 96 | if (checkIfProverCanCheat(mat, memberValueSet)) { 97 | throw new RuntimeException("The prover can cheat."); 98 | } 99 | 100 | BigInteger[] coeffs = new BigInteger[16]; 101 | for (int ii = 0; ii < 16; ii++) { 102 | coeffs[ii] = mat[ii][16]; 103 | if (!coeffs[ii].equals(polyCoeffs[ii])) { 104 | throw new RuntimeException("Inconsistency found."); 105 | } 106 | } 107 | allCoeffSet.add(coeffs); 108 | } 109 | 110 | } 111 | 112 | // method for sanity checks during preprocessing 113 | private static BigInteger[] polyMul(BigInteger[] a1, BigInteger[] a2) { 114 | BigInteger[] out = new BigInteger[a1.length + a2.length - 1]; 115 | Arrays.fill(out, BigInteger.ZERO); 116 | for (int i = 0; i < a1.length; i++) { 117 | for (int j = 0; j < a2.length; j++) { 118 | out[i + j] = out[i + j].add(a1[i].multiply(a2[j])).mod( 119 | Config.FIELD_PRIME); 120 | } 121 | } 122 | return out; 123 | } 124 | 125 | private static boolean checkIfProverCanCheat(BigInteger[][] mat, 126 | HashSet valueSet) { 127 | 128 | BigInteger[] coeffs = new BigInteger[16]; 129 | for (int i = 0; i < 16; i++) { 130 | coeffs[i] = mat[i][16]; 131 | } 132 | 133 | int validResults = 0; 134 | int outsidePermissibleSet = 0; 135 | 136 | // loop over the whole permissible domain (recall that input & output 137 | // are bounded) 138 | for (int k = 0; k < 256 * 256; k++) { 139 | 140 | BigInteger result = coeffs[0]; 141 | BigInteger p = BigInteger.valueOf(k); 142 | for (int i = 1; i < 16; i++) { 143 | result = result.add(p.multiply(coeffs[i])); 144 | p = p.multiply(BigInteger.valueOf(k)).mod(Config.FIELD_PRIME); 145 | } 146 | result = result.mod(Config.FIELD_PRIME); 147 | 148 | if (result.equals(Config.FIELD_PRIME.subtract(p))) { 149 | validResults++; 150 | if (!valueSet.contains(k)) { 151 | outsidePermissibleSet++; 152 | } 153 | } 154 | 155 | } 156 | if (validResults != 16 || outsidePermissibleSet != 0) { 157 | System.out.println("Prover can cheat with linear system solution"); 158 | System.out.println("Num of valid values that the prover can use = " 159 | + validResults); 160 | System.out.println("Num of valid values outside permissible set = " 161 | + validResults); 162 | return true; 163 | } else { 164 | return false; 165 | } 166 | } 167 | 168 | protected void buildCircuit() { 169 | 170 | output = generator.createProverWitnessWire(); 171 | input.restrictBitLength(8); 172 | generator.specifyProverWitnessComputation(new Instruction() { 173 | 174 | @Override 175 | public void evaluate(CircuitEvaluator evaluator) { 176 | // TODO Auto-generated method stub 177 | BigInteger value = evaluator.getWireValue(input); 178 | evaluator.setWireValue(output, 179 | BigInteger.valueOf(SBox[value.intValue()])); 180 | } 181 | }); 182 | 183 | output.restrictBitLength(8); 184 | Wire[] vars = new Wire[16]; 185 | Wire p = input.mul(256).add(output); 186 | vars[0] = generator.getOneWire(); 187 | for (int i = 1; i < 16; i++) { 188 | vars[i] = vars[i - 1].mul(p); 189 | } 190 | 191 | Wire product = generator.getOneWire(); 192 | for (BigInteger[] coeffs : allCoeffSet) { 193 | Wire accum = generator.getZeroWire(); 194 | for (int j = 0; j < vars.length; j++) { 195 | accum = accum.add(vars[j].mul(coeffs[j])); 196 | } 197 | accum = accum.add(vars[15].mul(p)); 198 | product = product.mul(accum); 199 | } 200 | generator.addZeroAssertion(product); 201 | } 202 | 203 | @Override 204 | public Wire[] getOutputWires() { 205 | return new Wire[] { output }; 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/blockciphers/sbox/AESSBoxGadgetOptimized2.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.blockciphers.sbox; 6 | 7 | import java.math.BigInteger; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.HashSet; 11 | import java.util.Random; 12 | 13 | import circuit.config.Config; 14 | import circuit.eval.CircuitEvaluator; 15 | import circuit.eval.Instruction; 16 | import circuit.operations.Gadget; 17 | import circuit.structure.Wire; 18 | import examples.gadgets.blockciphers.AES128CipherGadget; 19 | import examples.gadgets.blockciphers.sbox.util.LinearSystemSolver; 20 | 21 | /** 22 | * This gadget implements the efficient read-only memory access from xjsnark, 23 | * while making use of some properties of the AES circuit to gain more savings. 24 | * 25 | * Instead of constructing the linear systems using vector of powers like the 26 | * AESSBoxGadgetOptimized1, this gadget relies on the observation that the bits 27 | * of the input and output (to the lookup operations) are already available or 28 | * will be needed later in the circuit. The gadget uses these bits partially to 29 | * construct the linear systems, but this has to be done carefully to make sure 30 | * that the prover cannot cheat. This might require shuffling and multiple 31 | * attempts, while checking all other possibilities that a prover could use to 32 | * cheat. See the bitCount parameter below. 33 | * 34 | */ 35 | 36 | public class AESSBoxGadgetOptimized2 extends Gadget { 37 | 38 | private static int SBox[] = AES128CipherGadget.SBox; 39 | 40 | private static ArrayList allCoeffSet; 41 | 42 | /* 43 | * bitCount represents how many bits are going to be used to construct the 44 | * linear systems. Setting bitCount to 0 will yield almost the same circuit 45 | * size as in AESBoxGadgetOptimized1.java. Setting bitcount to 16 will 46 | * almost make it very hard to find a solution. Setting bitCount to x, where 47 | * 16 > x > 0, means that x columns from the linear system will be based on 48 | * the bits of the element (input*256+output), and the rest are based on 49 | * products (as in AESSBoxGadgetOptimized1). As x increases, the more 50 | * savings. x cannot increase beyond 16. 51 | */ 52 | private static int bitCount = 15; 53 | 54 | public static void setBitCount(int x) { 55 | if (x < 0 || x > 16) 56 | throw new IllegalArgumentException(); 57 | else 58 | bitCount = x; 59 | } 60 | 61 | static { 62 | // preprocessing 63 | solveLinearSystems(); 64 | } 65 | 66 | private final Wire input; 67 | private Wire output; 68 | 69 | public AESSBoxGadgetOptimized2(Wire input, String... desc) { 70 | super(desc); 71 | this.input = input; 72 | buildCircuit(); 73 | } 74 | 75 | public static void solveLinearSystems() { 76 | 77 | long seed = 1; 78 | ArrayList allCoeffSet = new ArrayList(); 79 | ArrayList list = new ArrayList(); 80 | for (int i = 0; i <= 255; i++) { 81 | list.add(256 * i + SBox[i]); 82 | } 83 | boolean done = false; 84 | int trialCounter = 0; 85 | loop1: while (!done) { 86 | trialCounter++; 87 | if (trialCounter == 100) { 88 | throw new RuntimeException( 89 | "Was not possible to find an adequate solution to the current setting of the AES gadget sbox"); 90 | } 91 | System.out 92 | .println("Attempting to solve linear systems for efficient S-Box Access: Attempt#" 93 | + trialCounter); 94 | seed++; 95 | Collections.shuffle(list, new Random(seed)); 96 | allCoeffSet.clear(); 97 | 98 | for (int i = 0; i <= 15; i++) { 99 | BigInteger[][] mat = new BigInteger[16][17]; 100 | HashSet memberValueSet = new HashSet<>(); 101 | 102 | for (int k = 0; k < mat.length; k++) { 103 | int memberValue = list.get(k + i * 16); 104 | memberValueSet.add(memberValue); 105 | mat[k][16] = BigInteger.ONE; 106 | 107 | // now extract the values that correspond to memberValue 108 | // the method getVariableValues takes the bitCount settings 109 | // into account 110 | BigInteger[] variableValues = getVariableValues(memberValue); 111 | for (int j = 0; j <= 15; j++) { 112 | mat[k][j] = variableValues[j]; 113 | } 114 | } 115 | 116 | new LinearSystemSolver(mat).solveInPlace(); 117 | 118 | if (checkIfProverCanCheat(mat, memberValueSet)) { 119 | System.out.println("Invalid solution"); 120 | for (int ii = 0; ii < 16; ii++) { 121 | if (mat[ii][16].equals(BigInteger.ZERO)) { 122 | System.out 123 | .println("Possibly invalid due to having zero coefficient(s)"); 124 | break; 125 | } 126 | } 127 | 128 | continue loop1; 129 | } 130 | 131 | BigInteger[] coeffs = new BigInteger[16]; 132 | for (int ii = 0; ii < 16; ii++) { 133 | coeffs[ii] = mat[ii][16]; 134 | } 135 | allCoeffSet.add(coeffs); 136 | 137 | } 138 | done = true; 139 | AESSBoxGadgetOptimized2.allCoeffSet = allCoeffSet; 140 | System.out.println("Solution found!"); 141 | } 142 | } 143 | 144 | protected void buildCircuit() { 145 | 146 | output = generator.createProverWitnessWire(); 147 | generator.specifyProverWitnessComputation(new Instruction() { 148 | 149 | @Override 150 | public void evaluate(CircuitEvaluator evaluator) { 151 | // TODO Auto-generated method stub 152 | BigInteger value = evaluator.getWireValue(input); 153 | evaluator.setWireValue(output, 154 | BigInteger.valueOf(SBox[value.intValue()])); 155 | } 156 | }); 157 | 158 | // Although we are getting the bits below anyway (which implicitly 159 | // restricts the bitwidth), it's a safer practice to call 160 | // restrictBitLength() explicitly to avoid some special cases with 161 | // getBitWires(). 162 | // Similar operations get filtered later, so this won't add extra 163 | // constraints. 164 | output.restrictBitLength(8); 165 | input.restrictBitLength(8); 166 | 167 | Wire[] bitsIn = input.getBitWires(8).asArray(); 168 | Wire[] bitsOut = output.getBitWires(8).asArray(); 169 | Wire[] vars = new Wire[16]; 170 | Wire p = input.mul(256).add(output).add(1); 171 | Wire currentProduct = p; 172 | if (bitCount != 0 && bitCount != 16) { 173 | currentProduct = currentProduct.mul(currentProduct); 174 | } 175 | for (int i = 0; i < 16; i++) { 176 | 177 | if (i < bitCount) { 178 | if (i < 8) 179 | vars[i] = bitsOut[i]; 180 | else 181 | vars[i] = bitsIn[i - 8]; 182 | } else { 183 | vars[i] = currentProduct; 184 | if (i != 15) { 185 | currentProduct = currentProduct.mul(p); 186 | } 187 | } 188 | } 189 | 190 | Wire product = generator.getOneWire(); 191 | for (BigInteger[] coeffs : allCoeffSet) { 192 | Wire accum = generator.getZeroWire(); 193 | for (int j = 0; j < vars.length; j++) { 194 | accum = accum.add(vars[j].mul(coeffs[j])); 195 | } 196 | accum = accum.sub(1); 197 | product = product.mul(accum); 198 | } 199 | generator.addZeroAssertion(product); 200 | } 201 | 202 | @Override 203 | public Wire[] getOutputWires() { 204 | return new Wire[] { output }; 205 | } 206 | 207 | private static BigInteger[] getVariableValues(int k) { 208 | 209 | BigInteger[] vars = new BigInteger[16]; 210 | BigInteger v = BigInteger.valueOf(k).add(BigInteger.ONE); 211 | BigInteger product = v; 212 | if (bitCount != 0) { 213 | product = product.multiply(v).mod(Config.FIELD_PRIME); 214 | } 215 | for (int j = 0; j < 16; j++) { 216 | if (j < bitCount) { 217 | vars[j] = ((k >> j) & 0x01) == 1 ? BigInteger.ONE 218 | : BigInteger.ZERO; 219 | } else { 220 | vars[j] = product; 221 | product = product.multiply(v).mod(Config.FIELD_PRIME); 222 | } 223 | } 224 | return vars; 225 | } 226 | 227 | private static boolean checkIfProverCanCheat(BigInteger[][] mat, 228 | HashSet valueSet) { 229 | 230 | BigInteger[] coeffs = new BigInteger[16]; 231 | for (int i = 0; i < 16; i++) { 232 | coeffs[i] = mat[i][16]; 233 | } 234 | 235 | int validResults = 0; 236 | int outsidePermissibleSet = 0; 237 | 238 | // loop over the whole permissible domain (recall that input & output 239 | // are bounded) 240 | 241 | for (int k = 0; k < 256 * 256; k++) { 242 | 243 | BigInteger[] variableValues = getVariableValues(k); 244 | BigInteger result = BigInteger.ZERO; 245 | for (int i = 0; i < 16; i++) { 246 | result = result.add(variableValues[i].multiply(coeffs[i])); 247 | } 248 | result = result.mod(Config.FIELD_PRIME); 249 | if (result.equals(BigInteger.ONE)) { 250 | validResults++; 251 | if (!valueSet.contains(k)) { 252 | outsidePermissibleSet++; 253 | } 254 | } 255 | } 256 | if (validResults != 16 || outsidePermissibleSet != 0) { 257 | System.out.println("Prover can cheat with linear system solution"); 258 | System.out.println("Num of valid values that the prover can use = " 259 | + validResults); 260 | System.out.println("Num of valid values outside permissible set = " 261 | + validResults); 262 | return true; 263 | } else { 264 | return false; 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/blockciphers/sbox/AESSBoxNaiveLookupGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.blockciphers.sbox; 6 | 7 | import circuit.operations.Gadget; 8 | import circuit.structure.Wire; 9 | import examples.gadgets.blockciphers.AES128CipherGadget; 10 | 11 | public class AESSBoxNaiveLookupGadget extends Gadget { 12 | 13 | private static int SBox[] = AES128CipherGadget.SBox; 14 | 15 | private Wire input; 16 | private Wire output; 17 | 18 | public AESSBoxNaiveLookupGadget(Wire input, String... desc) { 19 | super(desc); 20 | this.input = input; 21 | buildCircuit(); 22 | } 23 | 24 | protected void buildCircuit() { 25 | output = generator.getZeroWire(); 26 | for (int i = 0; i < 256; i++) { 27 | output = output.add(input.isEqualTo(i).mul(SBox[i])); 28 | } 29 | } 30 | 31 | @Override 32 | public Wire[] getOutputWires() { 33 | return new Wire[] { output }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/blockciphers/sbox/util/LinearSystemSolver.java: -------------------------------------------------------------------------------- 1 | package examples.gadgets.blockciphers.sbox.util; 2 | 3 | import java.math.BigInteger; 4 | 5 | import circuit.config.Config; 6 | 7 | /** 8 | * Solves a linear system of equations over a finite field. 9 | * 10 | * Used for efficient representation of AES S-box gadget 11 | */ 12 | 13 | public class LinearSystemSolver { 14 | 15 | public static BigInteger prime = Config.FIELD_PRIME; 16 | 17 | private BigInteger[][] mat; 18 | private int numRows, numCols; 19 | 20 | public LinearSystemSolver(BigInteger[][] mat) { 21 | this.mat = mat; 22 | numRows = mat.length; 23 | numCols = mat[0].length; 24 | } 25 | 26 | public void solveInPlace() { 27 | 28 | // https://www.csun.edu/~panferov/math262/262_rref.pdf 29 | // https://www.math.purdue.edu/~shao92/documents/Algorithm%20REF.pdf 30 | guassJordan(); 31 | rref(); 32 | } 33 | 34 | private void guassJordan() { 35 | for (int colIdx = 0, rowIdx = 0; colIdx < numCols; colIdx++, rowIdx++) { 36 | int pivotRowIdx = rowIdx; 37 | while (pivotRowIdx < numRows 38 | && mat[pivotRowIdx][colIdx].equals(BigInteger.ZERO)) { 39 | pivotRowIdx++; 40 | } 41 | if (pivotRowIdx == numRows) 42 | continue; 43 | 44 | // swap 45 | BigInteger[] tmp = mat[pivotRowIdx]; 46 | mat[pivotRowIdx] = mat[rowIdx]; 47 | mat[rowIdx] = tmp; 48 | 49 | pivotRowIdx = rowIdx; 50 | 51 | // dividing by pivot 52 | BigInteger invF = inverse(mat[pivotRowIdx][colIdx]); 53 | for (int j = 0; j < numCols; j++) { 54 | mat[pivotRowIdx][j] = mat[pivotRowIdx][j].multiply(invF).mod( 55 | prime); 56 | } 57 | 58 | for (int k = pivotRowIdx + 1; k < numRows; k++) { 59 | BigInteger f = negate(mat[k][colIdx]); 60 | for (int j = 0; j < numCols; j++) { 61 | mat[k][j] = mat[k][j].add(mat[pivotRowIdx][j].multiply(f)); 62 | mat[k][j] = mat[k][j].mod(prime); 63 | } 64 | } 65 | 66 | } 67 | } 68 | 69 | private void rref() { 70 | for (int rowIdx = numRows - 1; rowIdx >= 0; rowIdx--) { 71 | int pivotColIdx = 0; 72 | while (pivotColIdx < numCols 73 | && mat[rowIdx][pivotColIdx].equals(BigInteger.ZERO)) { 74 | pivotColIdx++; 75 | } 76 | if (pivotColIdx == numCols) 77 | continue; 78 | 79 | for (int k = rowIdx - 1; k >= 0; k--) { 80 | BigInteger f = mat[k][pivotColIdx]; 81 | for (int j = 0; j < numCols; j++) { 82 | mat[k][j] = mat[k][j] 83 | .add(negate(mat[rowIdx][j].multiply(f))); 84 | mat[k][j] = mat[k][j].mod(prime); 85 | } 86 | } 87 | } 88 | } 89 | 90 | private static BigInteger negate(BigInteger x) { 91 | return (prime.subtract(x.mod(prime))).mod(prime); 92 | } 93 | 94 | private static BigInteger inverse(BigInteger x) { 95 | return (x.mod(prime)).modInverse(prime); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/diffieHellmanKeyExchange/FieldExtensionDHKeyExchange.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.diffieHellmanKeyExchange; 6 | 7 | import java.math.BigInteger; 8 | import java.util.Arrays; 9 | 10 | import util.Util; 11 | import circuit.operations.Gadget; 12 | import circuit.structure.Wire; 13 | 14 | /** 15 | * Performs Key Exchange using a field extension F_p[x]/(x^\mu - \omega), where 16 | * the polynomial (x^\mu - \omega) is irreducible. The inputs to this gadget: 17 | * the base g, the other party's input h = g^a, the bits of the secret exponent 18 | * secExpBits and omega. The outputs of this gadget: the derived key h^s to be 19 | * used for symmetric key derivation, and g^s which is sent to the other party. 20 | * 21 | * A sample parameterization that gives low security (~80 bits of security) can 22 | * be found in the Junit tests. A sample usage is in: 23 | * examples/generators/EncryptionCircuitGenerator.java 24 | * 25 | * 26 | */ 27 | public class FieldExtensionDHKeyExchange extends Gadget { 28 | 29 | private Wire[] g; // base 30 | private Wire[] h; // other party's public input (supposedly, h = g^(the 31 | // other party's secret)) 32 | 33 | private Wire[] secretExponentBits; // the bits of the secret exponent of the 34 | // party 35 | // executing this gadget 36 | private long omega; 37 | private int mu; 38 | 39 | // gadget outputs 40 | private Wire[] outputPublicValue; // g^s (to be sent to the other party) 41 | private Wire[] sharedSecret; // the derived secret key h^s 42 | private Wire[][] gPowersTable; 43 | private Wire[][] hPowersTable; 44 | 45 | /** 46 | * Note: In the default mode, the gadget only validates the secret input 47 | * provided by the prover, but it does not validate that the base and public 48 | * input of the other's party are proper elements. Since these values are 49 | * public, they could be checked outside the circuit. 50 | * 51 | * If the validation is needed inside, the method "validateInputs()" should 52 | * be called explicitly. Example is provided in 53 | * FieldExtensionDHKeyExchange_Test 54 | * 55 | */ 56 | public FieldExtensionDHKeyExchange(Wire[] g, Wire[] h, 57 | Wire[] secretExponentBits, long omega, String desc) { 58 | super(desc); 59 | this.g = g; 60 | this.h = h; 61 | this.secretExponentBits = secretExponentBits; 62 | this.omega = omega; 63 | mu = g.length; 64 | if (h.length != g.length) { 65 | throw new IllegalArgumentException( 66 | "g and h must have the same dimension"); 67 | } 68 | 69 | // since this is typically a private input by the prover, 70 | // the check is also done here for safety. No need to remove this if 71 | // done also outside the gadget. The back end takes care of caching 72 | for (Wire w : secretExponentBits) { 73 | generator.addBinaryAssertion(w); 74 | } 75 | 76 | buildCircuit(); 77 | } 78 | 79 | protected void buildCircuit() { 80 | gPowersTable = preparePowersTable(g); 81 | hPowersTable = preparePowersTable(h); 82 | outputPublicValue = exp(g, secretExponentBits, gPowersTable); 83 | sharedSecret = exp(h, secretExponentBits, hPowersTable); 84 | } 85 | 86 | private Wire[] mul(Wire[] a, Wire[] b) { 87 | Wire[] c = new Wire[mu]; 88 | int i, j; 89 | for (i = 0; i < mu; i += 1) { 90 | c[i] = generator.getZeroWire(); 91 | } 92 | for (i = 0; i < mu; i += 1) { 93 | for (j = 0; j < mu; j += 1) { 94 | int k = i + j; 95 | if (k < mu) { 96 | c[k] = c[k].add(a[i].mul(b[j])); 97 | } 98 | k = i + j - mu; 99 | if (k >= 0) { 100 | c[k] = c[k].add(a[i].mul(b[j]).mul(omega)); 101 | } 102 | } 103 | } 104 | return c; 105 | } 106 | 107 | private Wire[][] preparePowersTable(Wire[] base) { 108 | Wire[][] powersTable = new Wire[secretExponentBits.length + 1][mu]; 109 | powersTable[0] = Arrays.copyOf(base, mu); 110 | for (int j = 1; j < secretExponentBits.length + 1; j += 1) { 111 | powersTable[j] = mul(powersTable[j - 1], powersTable[j - 1]); 112 | } 113 | return powersTable; 114 | } 115 | 116 | private Wire[] exp(Wire[] base, Wire[] expBits, Wire[][] powersTable) { 117 | 118 | Wire[] c = new Wire[mu]; 119 | Arrays.fill(c, generator.getZeroWire()); 120 | c[0] = generator.getOneWire(); 121 | for (int j = 0; j < expBits.length; j += 1) { 122 | Wire[] tmp = mul(c, powersTable[j]); 123 | for (int i = 0; i < mu; i++) { 124 | c[i] = c[i].add(expBits[j].mul(tmp[i].sub(c[i]))); 125 | } 126 | } 127 | return c; 128 | } 129 | 130 | // TODO: Test more scenarios 131 | public void validateInputs(BigInteger subGroupOrder) { 132 | 133 | // g and h are not zero and not one 134 | 135 | // checking the first chunk 136 | Wire zeroOrOne1 = g[0].mul(g[0].sub(1)); 137 | Wire zeroOrOne2 = h[0].mul(h[0].sub(1)); 138 | 139 | // checking the rest 140 | Wire allZero1 = generator.getOneWire(); 141 | Wire allZero2 = generator.getOneWire(); 142 | 143 | for (int i = 1; i < mu; i++) { 144 | allZero1 = allZero1.mul(g[i].checkNonZero().invAsBit()); 145 | allZero2 = allZero2.mul(h[i].checkNonZero().invAsBit()); 146 | } 147 | 148 | // assertion 149 | generator.addZeroAssertion(zeroOrOne1.mul(allZero1)); 150 | generator.addZeroAssertion(zeroOrOne2.mul(allZero2)); 151 | 152 | // verify order of points 153 | 154 | int bitLength = subGroupOrder.bitLength(); 155 | Wire[] bits = new Wire[bitLength]; 156 | for (int i = 0; i < bitLength; i++) { 157 | if (subGroupOrder.testBit(i)) 158 | bits[i] = generator.getOneWire(); 159 | else 160 | bits[i] = generator.getZeroWire(); 161 | } 162 | 163 | Wire[] result1 = exp(g, bits, gPowersTable); 164 | Wire[] result2 = exp(h, bits, hPowersTable); 165 | 166 | // both should be one 167 | 168 | generator.addOneAssertion(result1[0]); 169 | generator.addOneAssertion(result2[0]); 170 | for (int i = 1; i < mu; i++) { 171 | generator.addZeroAssertion(result1[i]); 172 | generator.addZeroAssertion(result1[i]); 173 | } 174 | } 175 | 176 | @Override 177 | public Wire[] getOutputWires() { 178 | return Util.concat(outputPublicValue, sharedSecret); 179 | } 180 | 181 | public Wire[] getOutputPublicValue() { 182 | return outputPublicValue; 183 | } 184 | 185 | public Wire[] getSharedSecret() { 186 | return sharedSecret; 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/hash/MerkleTreePathGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.hash; 5 | 6 | import circuit.config.Config; 7 | import circuit.operations.Gadget; 8 | import circuit.structure.Wire; 9 | import circuit.structure.WireArray; 10 | 11 | 12 | /** 13 | * A Merkle tree authentication gadget using the subsetsum hash function 14 | * 15 | */ 16 | 17 | public class MerkleTreePathGadget extends Gadget { 18 | 19 | private static int digestWidth = SubsetSumHashGadget.DIMENSION; 20 | 21 | private int treeHeight; 22 | private Wire directionSelectorWire; 23 | private Wire[] directionSelectorBits; 24 | private Wire[] leafWires; 25 | private Wire[] intermediateHashWires; 26 | private Wire[] outRoot; 27 | 28 | private int leafWordBitWidth; 29 | 30 | public MerkleTreePathGadget(Wire directionSelectorWire, Wire[] leafWires, Wire[] intermediateHasheWires, 31 | int leafWordBitWidth, int treeHeight, String... desc) { 32 | 33 | super(desc); 34 | this.directionSelectorWire = directionSelectorWire; 35 | this.treeHeight = treeHeight; 36 | this.leafWires = leafWires; 37 | this.intermediateHashWires = intermediateHasheWires; 38 | this.leafWordBitWidth = leafWordBitWidth; 39 | 40 | buildCircuit(); 41 | 42 | } 43 | 44 | private void buildCircuit() { 45 | 46 | directionSelectorBits = directionSelectorWire.getBitWires(treeHeight).asArray(); 47 | 48 | // Apply CRH to leaf data 49 | Wire[] leafBits = new WireArray(leafWires).getBits(leafWordBitWidth).asArray(); 50 | SubsetSumHashGadget subsetSumGadget = new SubsetSumHashGadget(leafBits, false); 51 | Wire[] currentHash = subsetSumGadget.getOutputWires(); 52 | 53 | // Apply CRH across tree path guided by the direction bits 54 | for (int i = 0; i < treeHeight; i++) { 55 | Wire[] inHash = new Wire[2 * digestWidth]; 56 | for (int j = 0; j < digestWidth; j++) { 57 | Wire temp = currentHash[j].sub(intermediateHashWires[i * digestWidth + j]); 58 | Wire temp2 = directionSelectorBits[i].mul(temp); 59 | inHash[j] = intermediateHashWires[i * digestWidth + j].add(temp2); 60 | } 61 | for (int j = digestWidth; j < 2 * digestWidth; j++) { 62 | Wire temp = currentHash[j - digestWidth].add(intermediateHashWires[i * digestWidth + j - digestWidth]); 63 | inHash[j] = temp.sub(inHash[j - digestWidth]); 64 | } 65 | 66 | Wire[] nextInputBits = new WireArray(inHash).getBits(Config.LOG2_FIELD_PRIME).asArray(); 67 | subsetSumGadget = new SubsetSumHashGadget(nextInputBits, false); 68 | currentHash = subsetSumGadget.getOutputWires(); 69 | } 70 | outRoot = currentHash; 71 | } 72 | 73 | @Override 74 | public Wire[] getOutputWires() { 75 | return outRoot; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/hash/SHA256Gadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.hash; 5 | 6 | import java.util.Arrays; 7 | 8 | import util.Util; 9 | import circuit.operations.Gadget; 10 | import circuit.structure.Wire; 11 | import circuit.structure.WireArray; 12 | 13 | public class SHA256Gadget extends Gadget { 14 | 15 | private static final long H[] = { 0x6a09e667L, 0xbb67ae85L, 0x3c6ef372L, 0xa54ff53aL, 0x510e527fL, 0x9b05688cL, 16 | 0x1f83d9abL, 0x5be0cd19L }; 17 | 18 | private static final long K[] = { 0x428a2f98L, 0x71374491L, 0xb5c0fbcfL, 0xe9b5dba5L, 0x3956c25bL, 0x59f111f1L, 19 | 0x923f82a4L, 0xab1c5ed5L, 0xd807aa98L, 0x12835b01L, 0x243185beL, 0x550c7dc3L, 0x72be5d74L, 0x80deb1feL, 20 | 0x9bdc06a7L, 0xc19bf174L, 0xe49b69c1L, 0xefbe4786L, 0x0fc19dc6L, 0x240ca1ccL, 0x2de92c6fL, 0x4a7484aaL, 21 | 0x5cb0a9dcL, 0x76f988daL, 0x983e5152L, 0xa831c66dL, 0xb00327c8L, 0xbf597fc7L, 0xc6e00bf3L, 0xd5a79147L, 22 | 0x06ca6351L, 0x14292967L, 0x27b70a85L, 0x2e1b2138L, 0x4d2c6dfcL, 0x53380d13L, 0x650a7354L, 0x766a0abbL, 23 | 0x81c2c92eL, 0x92722c85L, 0xa2bfe8a1L, 0xa81a664bL, 0xc24b8b70L, 0xc76c51a3L, 0xd192e819L, 0xd6990624L, 24 | 0xf40e3585L, 0x106aa070L, 0x19a4c116L, 0x1e376c08L, 0x2748774cL, 0x34b0bcb5L, 0x391c0cb3L, 0x4ed8aa4aL, 25 | 0x5b9cca4fL, 0x682e6ff3L, 0x748f82eeL, 0x78a5636fL, 0x84c87814L, 0x8cc70208L, 0x90befffaL, 0xa4506cebL, 26 | 0xbef9a3f7L, 0xc67178f2L }; 27 | 28 | private Wire[] unpaddedInputs; 29 | 30 | private int bitwidthPerInputElement; 31 | private int totalLengthInBytes; 32 | 33 | private int numBlocks; 34 | private boolean binaryOutput; 35 | private boolean paddingRequired; 36 | 37 | private Wire[] preparedInputBits; 38 | private Wire[] output; 39 | 40 | public SHA256Gadget(Wire[] ins, int bitWidthPerInputElement, int totalLengthInBytes, boolean binaryOutput, 41 | boolean paddingRequired, String... desc) { 42 | 43 | super(desc); 44 | if (totalLengthInBytes * 8 > ins.length * bitWidthPerInputElement 45 | || totalLengthInBytes * 8 < (ins.length - 1) * bitWidthPerInputElement) { 46 | throw new IllegalArgumentException("Inconsistent Length Information"); 47 | } 48 | 49 | if (!paddingRequired && totalLengthInBytes % 64 != 0 50 | && ins.length * bitWidthPerInputElement != totalLengthInBytes) { 51 | throw new IllegalArgumentException("When padding is not forced, totalLengthInBytes % 64 must be zero."); 52 | } 53 | 54 | this.unpaddedInputs = ins; 55 | this.bitwidthPerInputElement = bitWidthPerInputElement; 56 | this.totalLengthInBytes = totalLengthInBytes; 57 | this.binaryOutput = binaryOutput; 58 | this.paddingRequired = paddingRequired; 59 | 60 | buildCircuit(); 61 | 62 | } 63 | 64 | protected void buildCircuit() { 65 | 66 | // pad if needed 67 | prepare(); 68 | 69 | Wire[] outDigest = new Wire[8]; 70 | Wire[] hWires = new Wire[H.length]; 71 | for (int i = 0; i < H.length; i++) { 72 | hWires[i] = generator.createConstantWire(H[i]); 73 | } 74 | 75 | for (int blockNum = 0; blockNum < numBlocks; blockNum++) { 76 | 77 | Wire[][] wsSplitted = new Wire[64][]; 78 | Wire[] w = new Wire[64]; 79 | 80 | for (int i = 0; i < 64; i++) { 81 | if (i < 16) { 82 | wsSplitted[i] = Util.reverseBytes(Arrays.copyOfRange(preparedInputBits, blockNum * 512 + i * 32, 83 | blockNum * 512 + (i + 1) * 32)); 84 | 85 | w[i] = new WireArray(wsSplitted[i]).packAsBits(32); 86 | } else { 87 | Wire t1 = w[i - 15].rotateRight(32, 7); 88 | Wire t2 = w[i - 15].rotateRight(32, 18); 89 | Wire t3 = w[i - 15].shiftRight(32, 3); 90 | Wire s0 = t1.xorBitwise(t2, 32); 91 | s0 = s0.xorBitwise(t3, 32); 92 | 93 | Wire t4 = w[i - 2].rotateRight(32, 17); 94 | Wire t5 = w[i - 2].rotateRight(32, 19); 95 | Wire t6 = w[i - 2].shiftRight(32, 10); 96 | Wire s1 = t4.xorBitwise(t5, 32); 97 | s1 = s1.xorBitwise(t6, 32); 98 | 99 | w[i] = w[i - 16].add(w[i - 7]); 100 | w[i] = w[i].add(s0).add(s1); 101 | w[i] = w[i].trimBits(34, 32); 102 | } 103 | } 104 | 105 | Wire a = hWires[0]; 106 | Wire b = hWires[1]; 107 | Wire c = hWires[2]; 108 | Wire d = hWires[3]; 109 | Wire e = hWires[4]; 110 | Wire f = hWires[5]; 111 | Wire g = hWires[6]; 112 | Wire h = hWires[7]; 113 | 114 | for (int i = 0; i < 64; i++) { 115 | 116 | Wire t1 = e.rotateRight(32, 6); 117 | Wire t2 = e.rotateRight(32, 11); 118 | Wire t3 = e.rotateRight(32, 25); 119 | Wire s1 = t1.xorBitwise(t2, 32); 120 | s1 = s1.xorBitwise(t3, 32); 121 | 122 | Wire ch = computeCh(e, f, g, 32); 123 | 124 | Wire t4 = a.rotateRight(32, 2); 125 | Wire t5 = a.rotateRight(32, 13); 126 | Wire t6 = a.rotateRight(32, 22); 127 | Wire s0 = t4.xorBitwise(t5, 32); 128 | s0 = s0.xorBitwise(t6, 32); 129 | 130 | Wire maj; 131 | // since after each iteration, SHA256 does c = b; and b = a;, we can make use of that to save multiplications in maj computation. 132 | // To do this, we make use of the caching feature, by just changing the order of wires sent to maj(). Caching will take care of the rest. 133 | if(i % 2 == 1){ 134 | maj = computeMaj(c, b, a, 32); 135 | } 136 | else{ 137 | maj = computeMaj(a, b, c, 32); 138 | } 139 | 140 | Wire temp1 = w[i].add(K[i]).add(s1).add(h).add(ch); 141 | 142 | Wire temp2 = maj.add(s0); 143 | 144 | h = g; 145 | g = f; 146 | f = e; 147 | e = temp1.add(d); 148 | e = e.trimBits(35, 32); 149 | 150 | d = c; 151 | c = b; 152 | b = a; 153 | a = temp2.add(temp1); 154 | a = a.trimBits(35, 32); 155 | 156 | } 157 | 158 | hWires[0] = hWires[0].add(a).trimBits(33, 32); 159 | hWires[1] = hWires[1].add(b).trimBits(33, 32); 160 | hWires[2] = hWires[2].add(c).trimBits(33, 32); 161 | hWires[3] = hWires[3].add(d).trimBits(33, 32); 162 | hWires[4] = hWires[4].add(e).trimBits(33, 32); 163 | hWires[5] = hWires[5].add(f).trimBits(33, 32); 164 | hWires[6] = hWires[6].add(g).trimBits(33, 32); 165 | hWires[7] = hWires[7].add(h).trimBits(33, 32); 166 | } 167 | 168 | outDigest[0] = hWires[0]; 169 | outDigest[1] = hWires[1]; 170 | outDigest[2] = hWires[2]; 171 | outDigest[3] = hWires[3]; 172 | outDigest[4] = hWires[4]; 173 | outDigest[5] = hWires[5]; 174 | outDigest[6] = hWires[6]; 175 | outDigest[7] = hWires[7]; 176 | 177 | if (!binaryOutput) { 178 | output = outDigest; 179 | } else { 180 | output = new Wire[8 * 32]; 181 | for (int i = 0; i < 8; i++) { 182 | Wire[] bits = outDigest[i].getBitWires(32).asArray(); 183 | for (int j = 0; j < 32; j++) { 184 | output[j + i * 32] = bits[j]; 185 | } 186 | } 187 | } 188 | } 189 | 190 | private Wire computeMaj(Wire a, Wire b, Wire c, int numBits) { 191 | 192 | Wire[] result = new Wire[numBits]; 193 | Wire[] aBits = a.getBitWires(numBits).asArray(); 194 | Wire[] bBits = b.getBitWires(numBits).asArray(); 195 | Wire[] cBits = c.getBitWires(numBits).asArray(); 196 | 197 | for (int i = 0; i < numBits; i++) { 198 | Wire t1 = aBits[i].mul(bBits[i]); 199 | Wire t2 = aBits[i].add(bBits[i]).add(t1.mul(-2)); 200 | result[i] = t1.add(cBits[i].mul(t2)); 201 | } 202 | return new WireArray(result).packAsBits(); 203 | } 204 | 205 | private Wire computeCh(Wire a, Wire b, Wire c, int numBits) { 206 | Wire[] result = new Wire[numBits]; 207 | 208 | Wire[] aBits = a.getBitWires(numBits).asArray(); 209 | Wire[] bBits = b.getBitWires(numBits).asArray(); 210 | Wire[] cBits = c.getBitWires(numBits).asArray(); 211 | 212 | for (int i = 0; i < numBits; i++) { 213 | Wire t1 = bBits[i].sub(cBits[i]); 214 | Wire t2 = t1.mul(aBits[i]); 215 | result[i] = t2.add(cBits[i]); 216 | } 217 | return new WireArray(result).packAsBits(); 218 | } 219 | 220 | private void prepare() { 221 | 222 | numBlocks = (int) Math.ceil(totalLengthInBytes * 1.0 / 64); 223 | Wire[] bits = new WireArray(unpaddedInputs).getBits(bitwidthPerInputElement).asArray(); 224 | int tailLength = totalLengthInBytes % 64; 225 | if (paddingRequired) { 226 | Wire[] pad; 227 | if ((64 - tailLength >= 9)) { 228 | pad = new Wire[64 - tailLength]; 229 | } else { 230 | pad = new Wire[128 - tailLength]; 231 | } 232 | numBlocks = (totalLengthInBytes + pad.length)/64; 233 | pad[0] = generator.createConstantWire(0x80); 234 | for (int i = 1; i < pad.length - 8; i++) { 235 | pad[i] = generator.getZeroWire(); 236 | } 237 | long lengthInBits = totalLengthInBytes * 8; 238 | Wire[] lengthBits = new Wire[64]; 239 | for (int i = 0; i < 8; i++) { 240 | pad[pad.length - 1 - i] = generator.createConstantWire((lengthInBits >>> (8 * i)) & 0xFFL); 241 | Wire[] tmp = pad[pad.length - 1 - i].getBitWires(8).asArray(); 242 | System.arraycopy(tmp, 0, lengthBits, (7 - i) * 8, 8); 243 | } 244 | int totalNumberOfBits = numBlocks * 512; 245 | preparedInputBits = new Wire[totalNumberOfBits]; 246 | Arrays.fill(preparedInputBits, generator.getZeroWire()); 247 | System.arraycopy(bits, 0, preparedInputBits, 0, totalLengthInBytes * 8); 248 | preparedInputBits[totalLengthInBytes * 8 + 7] = generator.getOneWire(); 249 | System.arraycopy(lengthBits, 0, preparedInputBits, preparedInputBits.length - 64, 64); 250 | } else { 251 | preparedInputBits = bits; 252 | } 253 | } 254 | 255 | /** 256 | * outputs digest as 32-bit words 257 | */ 258 | @Override 259 | public Wire[] getOutputWires() { 260 | return output; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/hash/SubsetSumHashGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.hash; 5 | 6 | import java.math.BigInteger; 7 | import java.util.Arrays; 8 | 9 | import util.Util; 10 | import circuit.config.Config; 11 | import circuit.operations.Gadget; 12 | import circuit.structure.Wire; 13 | 14 | public class SubsetSumHashGadget extends Gadget { 15 | 16 | public static final int DIMENSION = 3; // set to 4 for higher security 17 | public static final int INPUT_LENGTH = 2 * DIMENSION * Config.LOG2_FIELD_PRIME; // length in bits 18 | private static final BigInteger[][] COEFFS; 19 | 20 | private Wire[] inputWires; 21 | private Wire[] outWires; 22 | private boolean binaryOutput; 23 | 24 | static { 25 | COEFFS = new BigInteger[DIMENSION][INPUT_LENGTH]; 26 | for (int i = 0; i < DIMENSION; i++) { 27 | for (int k = 0; k < INPUT_LENGTH; k++) { 28 | COEFFS[i][k] = Util.nextRandomBigInteger(Config.FIELD_PRIME); 29 | } 30 | 31 | } 32 | } 33 | 34 | /** 35 | * @param ins 36 | * The bitwires of the input. 37 | * @param binaryOutput 38 | * Whether the output digest should be splitted into bits or not. 39 | * @param desc 40 | */ 41 | public SubsetSumHashGadget(Wire[] ins, boolean binaryOutput, String... desc) { 42 | 43 | super(desc); 44 | int numBlocks = (int) Math.ceil(ins.length * 1.0 / INPUT_LENGTH); 45 | 46 | if (numBlocks > 1) { 47 | throw new IllegalArgumentException("Only one block is supported at this point"); 48 | } 49 | 50 | int rem = numBlocks * INPUT_LENGTH - ins.length; 51 | 52 | Wire[] pad = new Wire[rem]; 53 | for (int i = 0; i < pad.length; i++) { 54 | pad[i] = generator.getZeroWire(); // TODO: adjust padding 55 | } 56 | inputWires = Util.concat(ins, pad); 57 | this.binaryOutput = binaryOutput; 58 | buildCircuit(); 59 | } 60 | 61 | private void buildCircuit() { 62 | 63 | Wire[] outDigest = new Wire[DIMENSION]; 64 | Arrays.fill(outDigest, generator.getZeroWire()); 65 | 66 | for (int i = 0; i < DIMENSION; i++) { 67 | for (int j = 0; j < INPUT_LENGTH; j++) { 68 | Wire t = inputWires[j].mul(COEFFS[i][j]); 69 | outDigest[i] = outDigest[i].add(t); 70 | } 71 | } 72 | if (!binaryOutput) { 73 | outWires = outDigest; 74 | } else { 75 | outWires = new Wire[DIMENSION * Config.LOG2_FIELD_PRIME]; 76 | for (int i = 0; i < DIMENSION; i++) { 77 | Wire[] bits = outDigest[i].getBitWires(Config.LOG2_FIELD_PRIME).asArray(); 78 | for (int j = 0; j < bits.length; j++) { 79 | outWires[j + i * Config.LOG2_FIELD_PRIME] = bits[j]; 80 | } 81 | } 82 | } 83 | } 84 | 85 | @Override 86 | public Wire[] getOutputWires() { 87 | return outWires; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/math/DotProductGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.math; 5 | 6 | import circuit.operations.Gadget; 7 | import circuit.structure.Wire; 8 | 9 | public class DotProductGadget extends Gadget { 10 | 11 | private Wire[] a; 12 | private Wire[] b; 13 | private Wire output; 14 | 15 | public DotProductGadget(Wire[] a, Wire[] b, String... desc) { 16 | super(desc); 17 | if (a.length != b.length) { 18 | throw new IllegalArgumentException(); 19 | } 20 | this.a = a; 21 | this.b = b; 22 | buildCircuit(); 23 | } 24 | 25 | private void buildCircuit() { 26 | output = generator.getZeroWire(); 27 | for (int i = 0; i < a.length; i++) { 28 | Wire product = a[i].mul(b[i], "Multiply elements # " + i); 29 | output = output.add(product); 30 | } 31 | } 32 | 33 | @Override 34 | public Wire[] getOutputWires() { 35 | return new Wire[] { output }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/math/FieldDivisionGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.math; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.config.Config; 9 | import circuit.eval.CircuitEvaluator; 10 | import circuit.eval.Instruction; 11 | import circuit.operations.Gadget; 12 | import circuit.structure.ConstantWire; 13 | import circuit.structure.Wire; 14 | 15 | // see notes in the end of the code. 16 | 17 | public class FieldDivisionGadget extends Gadget { 18 | 19 | private final Wire a; 20 | private final Wire b; 21 | private Wire c; 22 | 23 | public FieldDivisionGadget(Wire a, Wire b, String... desc) { 24 | super(desc); 25 | this.a = a; 26 | this.b = b; 27 | // if the input values are constant (i.e. known at compilation time), we 28 | // can save one constraint 29 | if (a instanceof ConstantWire && b instanceof ConstantWire) { 30 | BigInteger aConst = ((ConstantWire) a).getConstant(); 31 | BigInteger bInverseConst = ((ConstantWire) b).getConstant().modInverse( 32 | Config.FIELD_PRIME); 33 | c = generator.createConstantWire(aConst.multiply(bInverseConst) 34 | .mod(Config.FIELD_PRIME)); 35 | } else { 36 | c = generator.createProverWitnessWire(debugStr("division result")); 37 | buildCircuit(); 38 | } 39 | } 40 | 41 | private void buildCircuit() { 42 | 43 | // This is an example of computing a value outside the circuit and 44 | // verifying constraints about it in the circuit. See notes below. 45 | 46 | generator.specifyProverWitnessComputation(new Instruction() { 47 | @Override 48 | public void evaluate(CircuitEvaluator evaluator) { 49 | BigInteger aValue = evaluator.getWireValue(a); 50 | BigInteger bValue = evaluator.getWireValue(b); 51 | BigInteger cValue = aValue.multiply( 52 | bValue.modInverse(Config.FIELD_PRIME)).mod( 53 | Config.FIELD_PRIME); 54 | evaluator.setWireValue(c, cValue); 55 | } 56 | 57 | }); 58 | 59 | // to handle the case where a or b can be both zero, see below 60 | generator.addAssertion(b, c, a, 61 | debugStr("Assertion for division result")); 62 | 63 | 64 | /* 65 | * Few notes: 1) The order of the above two statements matters (the 66 | * specification and the assertion). In the current version, it's not 67 | * possible to swap them, as in the evaluation sequence, the assertion 68 | * must happen after the value is assigned. 69 | * 70 | * 2) The instruction defined above relies on the values of wires (a) 71 | * and (b) during runtime. This means that if any point later in the 72 | * program, the references a, and b referred to other wires, these wires 73 | * are going to be used instead in this instruction. Therefore, it will 74 | * be safer to use final references in cases like that to reduce the 75 | * possibility of errors. 76 | * 77 | * 3) The above constraint does not check if a and b are both zeros. In that 78 | * case, the prover will be able to use any value to make the constraint work. 79 | * When this case is problematic, enforce that b cannot have the value of zero. 80 | * 81 | * This can be done by proving that b has an inverse, that satisfies 82 | * b*(invB) = 1; 83 | */ 84 | } 85 | 86 | @Override 87 | public Wire[] getOutputWires() { 88 | return new Wire[] { c }; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/math/LongIntegerModGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.math; 5 | 6 | import java.math.BigInteger; 7 | import java.util.Arrays; 8 | 9 | import util.Util; 10 | import circuit.auxiliary.LongElement; 11 | import circuit.eval.CircuitEvaluator; 12 | import circuit.eval.Instruction; 13 | import circuit.operations.Gadget; 14 | import circuit.structure.Wire; 15 | 16 | /** 17 | * This gadget provides a % b, when both operands are represented as long 18 | * elements. You can check the RSA gadgets/circuit generators for an example. 19 | * Most of the optimizations that reduce the cost of this step are more visible 20 | * in the LongElement class methods called by this gadget. 21 | * 22 | */ 23 | public class LongIntegerModGadget extends Gadget { 24 | 25 | private final LongElement a; 26 | private final LongElement b; 27 | 28 | private LongElement r; 29 | private LongElement q; 30 | private boolean restrictRange; 31 | private int bMinBitwidth; 32 | 33 | /** 34 | * 35 | * 36 | * 37 | * @param a 38 | * @param b 39 | * @param restrictRange if true, the output will be forced to be less than b, 40 | * otherwise the output remainder will only be guaranteed 41 | * to have the same bitwidth as b, but not necessarily less 42 | * than b. The second case is helpful when the purpose is 43 | * just to reduce the range, while having consistent 44 | * output. As an example (in a short integer case for 45 | * simplicity): assume we are interested in this operation 46 | * 3001 % 10. The output should be 1 in normal cases, but 47 | * to save some operations, we might skip checking that the 48 | * result is less than the modulus and just check that it 49 | * has the same bitwidth as the modulus, which we must do 50 | * anyway since the result is provided as a witness. In 51 | * that case, the output of this gadget could be 1 or 11, 52 | * which in some contexts would be ok, e.g. in intermediate 53 | * operations. See the RSA encryption gadget for an 54 | * illustration. 55 | * 56 | * @param desc 57 | */ 58 | 59 | public LongIntegerModGadget(LongElement a, LongElement b, boolean restrictRange, String... desc) { 60 | super(desc); 61 | this.a = a; 62 | this.b = b; 63 | this.restrictRange = restrictRange; 64 | buildCircuit(); 65 | } 66 | 67 | /** 68 | * 69 | * @param a 70 | * @param b 71 | * @param bMinBitwidth The minimum bitwidth of the second operand 72 | * @param restrictRange 73 | * @param desc 74 | */ 75 | public LongIntegerModGadget(LongElement a, LongElement b, int bMinBitwidth, boolean restrictRange, 76 | String... desc) { 77 | super(desc); 78 | this.a = a; 79 | this.b = b; 80 | this.bMinBitwidth = bMinBitwidth; 81 | this.restrictRange = restrictRange; 82 | buildCircuit(); 83 | } 84 | 85 | private void buildCircuit() { 86 | 87 | int aBitwidth = a.getMaxVal(LongElement.CHUNK_BITWIDTH).bitLength(); 88 | int bBitwidth = b.getMaxVal(LongElement.CHUNK_BITWIDTH).bitLength(); 89 | 90 | int rBitwidth = bBitwidth; 91 | int qBitwidth = aBitwidth; 92 | 93 | if (bMinBitwidth > 0) { 94 | qBitwidth = qBitwidth - bMinBitwidth + 1; 95 | } 96 | 97 | 98 | // length in what follows means the number of chunks 99 | int rLength = (int) Math.ceil(rBitwidth * 1.0 / LongElement.CHUNK_BITWIDTH); 100 | int qLength = (int) Math.ceil(qBitwidth * 1.0 / LongElement.CHUNK_BITWIDTH); 101 | 102 | Wire[] rWires = generator.createProverWitnessWireArray(rLength); 103 | Wire[] qWires = generator.createProverWitnessWireArray(qLength); 104 | 105 | int[] rChunkBitwidths = new int[rLength]; 106 | int[] qChunkBitwidths = new int[qLength]; 107 | 108 | Arrays.fill(rChunkBitwidths, LongElement.CHUNK_BITWIDTH); 109 | Arrays.fill(qChunkBitwidths, LongElement.CHUNK_BITWIDTH); 110 | 111 | if (rBitwidth % LongElement.CHUNK_BITWIDTH != 0) { 112 | rChunkBitwidths[rLength - 1] = rBitwidth % LongElement.CHUNK_BITWIDTH; 113 | } 114 | if (qBitwidth % LongElement.CHUNK_BITWIDTH != 0) { 115 | qChunkBitwidths[qLength - 1] = qBitwidth % LongElement.CHUNK_BITWIDTH; 116 | } 117 | 118 | r = new LongElement(rWires, rChunkBitwidths); 119 | q = new LongElement(qWires, qChunkBitwidths); 120 | 121 | 122 | generator.specifyProverWitnessComputation(new Instruction() { 123 | @Override 124 | public void evaluate(CircuitEvaluator evaluator) { 125 | BigInteger aValue = evaluator.getWireValue(a, LongElement.CHUNK_BITWIDTH); 126 | BigInteger bValue = evaluator.getWireValue(b, LongElement.CHUNK_BITWIDTH); 127 | BigInteger rValue = aValue.mod(bValue); 128 | BigInteger qValue = aValue.divide(bValue); 129 | 130 | evaluator.setWireValue(r.getArray(), Util.split(rValue, LongElement.CHUNK_BITWIDTH)); 131 | evaluator.setWireValue(q.getArray(), Util.split(qValue, LongElement.CHUNK_BITWIDTH)); 132 | } 133 | }); 134 | 135 | r.restrictBitwidth(); 136 | q.restrictBitwidth(); 137 | 138 | LongElement res = q.mul(b).add(r); 139 | 140 | // implements the improved long integer equality assertion from xjsnark 141 | res.assertEquality(a); 142 | 143 | if (restrictRange) { 144 | r.assertLessThan(b); 145 | } 146 | 147 | } 148 | 149 | @Override 150 | public Wire[] getOutputWires() { 151 | return r.getArray(); 152 | } 153 | 154 | public LongElement getRemainder() { 155 | return r; 156 | } 157 | 158 | } -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/math/ModConstantGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.math; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.eval.CircuitEvaluator; 9 | import circuit.eval.Instruction; 10 | import circuit.operations.Gadget; 11 | import circuit.structure.Wire; 12 | 13 | /** 14 | * This gadget provides the remainder of a % b, where b is a circuit constant. 15 | * 16 | * 17 | */ 18 | 19 | public class ModConstantGadget extends Gadget { 20 | 21 | private final Wire a; 22 | private final BigInteger b; 23 | private Wire r; 24 | private Wire q; 25 | 26 | private int bitwidth; // a's bitwidth 27 | 28 | public ModConstantGadget(Wire a, int bitwidth, BigInteger b, String...desc) { 29 | super(desc); 30 | this.a = a; 31 | this.b = b; 32 | this.bitwidth = bitwidth; 33 | if(b.signum() != 1){ 34 | throw new IllegalArgumentException("b must be a positive constant. Signed operations not supported yet."); 35 | } 36 | if(bitwidth < b.bitLength()){ 37 | throw new IllegalArgumentException("a's bitwidth < b's bitwidth -- This gadget is not needed."); 38 | } 39 | // TODO: add further checks. 40 | 41 | buildCircuit(); 42 | } 43 | 44 | private void buildCircuit() { 45 | 46 | r = generator.createProverWitnessWire("mod result"); 47 | q = generator.createProverWitnessWire("division result"); 48 | 49 | // notes about how to use this code block can be found in FieldDivisionGadget 50 | generator.specifyProverWitnessComputation(new Instruction() { 51 | @Override 52 | public void evaluate(CircuitEvaluator evaluator) { 53 | BigInteger aValue = evaluator.getWireValue(a); 54 | BigInteger rValue = aValue.mod(b); 55 | evaluator.setWireValue(r, rValue); 56 | BigInteger qValue = aValue.divide(b); 57 | evaluator.setWireValue(q, qValue); 58 | } 59 | 60 | }); 61 | 62 | int bBitwidth = b.bitLength(); 63 | r.restrictBitLength(bBitwidth); 64 | q.restrictBitLength(bitwidth - bBitwidth + 1); 65 | generator.addOneAssertion(r.isLessThan(b, bBitwidth)); 66 | generator.addEqualityAssertion(q.mul(b).add(r), a); 67 | } 68 | 69 | @Override 70 | public Wire[] getOutputWires() { 71 | return new Wire[] { r }; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/math/ModGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.math; 5 | 6 | import java.math.BigInteger; 7 | 8 | import circuit.eval.CircuitEvaluator; 9 | import circuit.eval.Instruction; 10 | import circuit.operations.Gadget; 11 | import circuit.structure.Wire; 12 | 13 | /** 14 | * This gadget provides the remainder of a % b. 15 | * 16 | * 17 | */ 18 | 19 | public class ModGadget extends Gadget { 20 | 21 | private final Wire a; 22 | private final Wire b; 23 | private Wire r; 24 | private Wire q; 25 | 26 | private int bitwidth; // bitwidth for both a, b 27 | 28 | public ModGadget(Wire a, Wire b, int bitwidth, String...desc) { 29 | super(desc); 30 | this.a = a; 31 | this.b = b; 32 | this.bitwidth = bitwidth; 33 | if(bitwidth > 126){ 34 | throw new IllegalArgumentException("Bitwidth not supported yet."); 35 | } 36 | buildCircuit(); 37 | } 38 | 39 | private void buildCircuit() { 40 | 41 | r = generator.createProverWitnessWire("mod result"); 42 | q = generator.createProverWitnessWire("division result"); 43 | 44 | 45 | // notes about how to use this code block can be found in FieldDivisionGadget 46 | generator.specifyProverWitnessComputation(new Instruction() { 47 | @Override 48 | public void evaluate(CircuitEvaluator evaluator) { 49 | BigInteger aValue = evaluator.getWireValue(a); 50 | BigInteger bValue = evaluator.getWireValue(b); 51 | BigInteger rValue = aValue.mod(bValue); 52 | evaluator.setWireValue(r, rValue); 53 | BigInteger qValue = aValue.divide(bValue); 54 | evaluator.setWireValue(q, qValue); 55 | } 56 | 57 | }); 58 | 59 | r.restrictBitLength(bitwidth); 60 | q.restrictBitLength(bitwidth); 61 | generator.addOneAssertion(r.isLessThan(b, bitwidth)); 62 | generator.addEqualityAssertion(q.mul(b).add(r), a); 63 | } 64 | 65 | @Override 66 | public Wire[] getOutputWires() { 67 | return new Wire[] { r }; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/rsa/RSAEncryptionOAEPGadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.rsa; 6 | 7 | import java.math.BigInteger; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | 11 | import util.Util; 12 | import circuit.auxiliary.LongElement; 13 | import circuit.operations.Gadget; 14 | import circuit.structure.Wire; 15 | import circuit.structure.WireArray; 16 | import examples.gadgets.hash.SHA256Gadget; 17 | import examples.gadgets.math.LongIntegerModGadget; 18 | 19 | /** 20 | * A gadget for RSA encryption according to PKCS#1 v2.2. The gadget assumes a 21 | * hardcoded public exponent of 0x10001, and uses SHA256 as the hash function 22 | * for mask generation function (mgf). 23 | * This gadget can accept a hardcoded or a variable RSA modulus. See the 24 | * corresponding generator example. 25 | * 26 | * This gadget is costly in comparison with the PKCS v1.5 RSA encryption gadget 27 | * due to many SHA256 calls during mask generation. 28 | * 29 | * The implementation of this gadget follows the standard specs in: 30 | * https://www.emc.com/collateral/white- 31 | * papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf 32 | */ 33 | 34 | public class RSAEncryptionOAEPGadget extends Gadget { 35 | 36 | private LongElement modulus; 37 | 38 | // every wire represents a byte in the following three arrays 39 | private Wire[] plainText; 40 | private Wire[] seed; 41 | 42 | private Wire[] ciphertext; 43 | 44 | private int rsaKeyBitLength; // in bits (assumed to be divisible by 8) 45 | public static final int SHA256_DIGEST_LENGTH = 32; // in bytes 46 | 47 | public static final byte[] lSHA256_HASH = new byte[] { (byte) 0xe3, 48 | (byte) 0xb0, (byte) 0xc4, 0x42, (byte) 0x98, (byte) 0xfc, 0x1c, 49 | 0x14, (byte) 0x9a, (byte) 0xfb, (byte) 0xf4, (byte) 0xc8, 50 | (byte) 0x99, 0x6f, (byte) 0xb9, 0x24, 0x27, (byte) 0xae, 0x41, 51 | (byte) 0xe4, 0x64, (byte) 0x9b, (byte) 0x93, 0x4c, (byte) 0xa4, 52 | (byte) 0x95, (byte) 0x99, 0x1b, 0x78, 0x52, (byte) 0xb8, 0x55 }; 53 | 54 | public RSAEncryptionOAEPGadget(LongElement modulus, Wire[] plainText, 55 | Wire[] seed, int rsaKeyBitLength, String... desc) { 56 | super(desc); 57 | 58 | if (rsaKeyBitLength % 8 != 0) { 59 | throw new IllegalArgumentException( 60 | "RSA Key bit length is assumed to be a multiple of 8"); 61 | } 62 | 63 | if (plainText.length > rsaKeyBitLength / 8 - 2 * SHA256_DIGEST_LENGTH - 2) { 64 | System.err.println("Message too long"); 65 | throw new IllegalArgumentException( 66 | "Invalid message length for RSA Encryption"); 67 | } 68 | 69 | if (seed.length != SHA256_DIGEST_LENGTH) { 70 | System.err 71 | .println("Seed must have the same length as the hash function output "); 72 | throw new IllegalArgumentException( 73 | "Invalid seed dimension for RSA Encryption"); 74 | } 75 | 76 | this.seed = seed; 77 | this.plainText = plainText; 78 | this.modulus = modulus; 79 | this.rsaKeyBitLength = rsaKeyBitLength; 80 | buildCircuit(); 81 | } 82 | 83 | private void buildCircuit() { 84 | 85 | int mLen = plainText.length; 86 | int hLen = SHA256_DIGEST_LENGTH; 87 | int keyLen = rsaKeyBitLength / 8; // in bytes 88 | Wire[] paddingString = new Wire[keyLen - mLen - 2 * hLen - 2]; 89 | Arrays.fill(paddingString, generator.getZeroWire()); 90 | 91 | Wire[] db = new Wire[keyLen - hLen - 1]; 92 | for (int i = 0; i < keyLen - hLen - 1; i++) { 93 | if (i < hLen) { 94 | db[i] = generator 95 | .createConstantWire((lSHA256_HASH[i] + 256) % 256); 96 | } else if (i < hLen + paddingString.length) { 97 | db[i] = paddingString[i - hLen]; 98 | } else if (i < hLen + paddingString.length + 1) { 99 | db[i] = generator.getOneWire(); 100 | } else { 101 | db[i] = plainText[i - (hLen + paddingString.length + 1)]; 102 | } 103 | } 104 | 105 | Wire[] dbMask = mgf1(seed, keyLen - hLen - 1); 106 | Wire[] maskedDb = new Wire[keyLen - hLen - 1]; 107 | for (int i = 0; i < keyLen - hLen - 1; i++) { 108 | maskedDb[i] = dbMask[i].xorBitwise(db[i], 8); 109 | } 110 | 111 | Wire[] seededMask = mgf1(maskedDb, hLen); 112 | Wire[] maskedSeed = new Wire[hLen]; 113 | for (int i = 0; i < hLen; i++) { 114 | maskedSeed[i] = seededMask[i].xorBitwise(seed[i], 8); 115 | } 116 | 117 | Wire[] paddedByteArray = Util.concat(maskedSeed, maskedDb); // Big-Endian 118 | 119 | // The LongElement implementation is LittleEndian, so we will process the array in reverse order 120 | 121 | LongElement paddedMsg = new LongElement( 122 | new BigInteger[] { BigInteger.ZERO }); 123 | for (int i = 0; i < paddedByteArray.length; i++) { 124 | LongElement e = new LongElement(paddedByteArray[paddedByteArray.length-i-1], 8); 125 | LongElement c = new LongElement(Util.split( 126 | BigInteger.ONE.shiftLeft(8 * i), 127 | LongElement.CHUNK_BITWIDTH)); 128 | paddedMsg = paddedMsg.add(e.mul(c)); 129 | } 130 | 131 | // do modular exponentiation 132 | LongElement s = paddedMsg; 133 | for (int i = 0; i < 16; i++) { 134 | s = s.mul(s); 135 | s = new LongIntegerModGadget(s, modulus, rsaKeyBitLength, false).getRemainder(); 136 | } 137 | s = s.mul(paddedMsg); 138 | s = new LongIntegerModGadget(s, modulus, rsaKeyBitLength, true).getRemainder(); 139 | 140 | // return the cipher text as byte array 141 | ciphertext = s.getBits(rsaKeyBitLength).packBitsIntoWords(8); 142 | } 143 | 144 | public void checkSeedCompliance() { 145 | for (int i = 0; i < seed.length; i++) { 146 | // Verify that the seed wires are bytes 147 | // This is also checked already by the sha256 gadget in the mgf1 calls, but added here for clarity 148 | seed[i].restrictBitLength(8); 149 | } 150 | } 151 | 152 | private Wire[] mgf1(Wire[] in, int length) { 153 | 154 | ArrayList mgfOutputList = new ArrayList(); 155 | for (int i = 0; i <= ((int) Math.ceil(length * 1.0 156 | / SHA256_DIGEST_LENGTH)) - 1; i++) { 157 | 158 | // the standard follows a Big Endian format 159 | Wire[] counter = generator.createConstantWireArray(new long[] { 160 | (byte) (i >>> 24), (byte) (i >>> 16), (byte) (i >>> 8), 161 | (byte) i }); 162 | 163 | Wire[] inputToHash = Util.concat(in, counter); 164 | SHA256Gadget shaGadget = new SHA256Gadget(inputToHash, 8, 165 | inputToHash.length, false, true); 166 | Wire[] digest = shaGadget.getOutputWires(); 167 | 168 | Wire[] msgHashBytes = new WireArray(digest).getBits(32) 169 | .packBitsIntoWords(8); 170 | // reverse the byte array representation of each word of the digest 171 | // to 172 | // be compatible with the endianess 173 | for (int j = 0; j < 8; j++) { 174 | Wire tmp = msgHashBytes[4 * j]; 175 | msgHashBytes[4 * j] = msgHashBytes[(4 * j + 3)]; 176 | msgHashBytes[4 * j + 3] = tmp; 177 | tmp = msgHashBytes[4 * j + 1]; 178 | msgHashBytes[4 * j + 1] = msgHashBytes[4 * j + 2]; 179 | msgHashBytes[4 * j + 2] = tmp; 180 | } 181 | for (int j = 0; j < msgHashBytes.length; j++) { 182 | mgfOutputList.add(msgHashBytes[j]); 183 | } 184 | } 185 | Wire[] out = mgfOutputList.toArray(new Wire[] {}); 186 | return Arrays.copyOf(out, length); 187 | } 188 | 189 | @Override 190 | public Wire[] getOutputWires() { 191 | return ciphertext; 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/rsa/RSAEncryptionV1_5_Gadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.gadgets.rsa; 6 | 7 | import java.math.BigInteger; 8 | 9 | import util.Util; 10 | import circuit.auxiliary.LongElement; 11 | import circuit.operations.Gadget; 12 | import circuit.structure.Wire; 13 | import examples.gadgets.math.FieldDivisionGadget; 14 | import examples.gadgets.math.LongIntegerModGadget; 15 | 16 | /** 17 | * A gadget for RSA encryption according to PKCS#1 v1.5. A future version will 18 | * have the RSA-OAEP method according to PKCS#1 v2.x. The gadget assumes a 19 | * hardcoded public exponent of 0x10001. 20 | * This gadget can accept a hardcoded or a variable RSA modulus. See the 21 | * corresponding generator example. 22 | * 23 | * Implemented according to the standard specs here: 24 | * https://www.emc.com/collateral/white- 25 | * papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf 26 | * 27 | */ 28 | 29 | public class RSAEncryptionV1_5_Gadget extends Gadget { 30 | 31 | private LongElement modulus; 32 | 33 | // every wire represents a byte in the following three arrays 34 | private Wire[] plainText; 35 | private Wire[] randomness; // (rsaKeyBitLength / 8 - 3 - plainTextLength) 36 | // non-zero bytes 37 | private Wire[] ciphertext; 38 | 39 | private int rsaKeyBitLength; // in bits (assumed to be divisible by 8) 40 | 41 | public RSAEncryptionV1_5_Gadget(LongElement modulus, Wire[] plainText, 42 | Wire[] randomness, int rsaKeyBitLength, String... desc) { 43 | super(desc); 44 | 45 | if (rsaKeyBitLength % 8 != 0) { 46 | throw new IllegalArgumentException( 47 | "RSA Key bit length is assumed to be a multiple of 8"); 48 | } 49 | 50 | if (plainText.length > rsaKeyBitLength / 8 - 11 51 | || plainText.length + randomness.length != rsaKeyBitLength / 8 - 3) { 52 | System.err.println("Check Message & Padding length"); 53 | throw new IllegalArgumentException( 54 | "Invalid Argument Dimensions for RSA Encryption"); 55 | } 56 | 57 | this.randomness = randomness; 58 | this.plainText = plainText; 59 | this.modulus = modulus; 60 | this.rsaKeyBitLength = rsaKeyBitLength; 61 | buildCircuit(); 62 | } 63 | 64 | public static int getExpectedRandomnessLength(int rsaKeyBitLength, 65 | int plainTextLength) { 66 | if (rsaKeyBitLength % 8 != 0) { 67 | throw new IllegalArgumentException( 68 | "RSA Key bit length is assumed to be a multiple of 8"); 69 | 70 | } 71 | return rsaKeyBitLength / 8 - 3 - plainTextLength; 72 | } 73 | 74 | private void buildCircuit() { 75 | 76 | int lengthInBytes = rsaKeyBitLength / 8; 77 | Wire[] paddedPlainText = new Wire[lengthInBytes]; 78 | for (int i = 0; i < plainText.length; i++) { 79 | paddedPlainText[plainText.length - i - 1] = plainText[i]; 80 | } 81 | paddedPlainText[plainText.length] = generator.getZeroWire(); 82 | for (int i = 0; i < randomness.length; i++) { 83 | paddedPlainText[plainText.length + 1 + (randomness.length - 1) - i] = randomness[i]; 84 | } 85 | paddedPlainText[lengthInBytes - 2] = generator.createConstantWire(2); 86 | paddedPlainText[lengthInBytes - 1] = generator.getZeroWire(); 87 | 88 | /* 89 | * To proceed with the RSA operations, we need to convert the 90 | * padddedPlainText array to a long element. Two ways to do that. 91 | */ 92 | // 1. safest method: 93 | // WireArray allBits = new WireArray(paddedPlainText).getBits(8); 94 | // LongElement paddedMsg = new LongElement(allBits); 95 | 96 | 97 | // 2. Make multiple long integer constant multiplications (need to be 98 | // done carefully) 99 | LongElement paddedMsg = new LongElement( 100 | new BigInteger[] { BigInteger.ZERO }); 101 | for (int i = 0; i < paddedPlainText.length; i++) { 102 | LongElement e = new LongElement(paddedPlainText[i], 8); 103 | LongElement c = new LongElement(Util.split( 104 | BigInteger.ONE.shiftLeft(8 * i), 105 | LongElement.CHUNK_BITWIDTH)); 106 | paddedMsg = paddedMsg.add(e.mul(c)); 107 | } 108 | 109 | LongElement s = paddedMsg; 110 | for (int i = 0; i < 16; i++) { 111 | s = s.mul(s); 112 | s = new LongIntegerModGadget(s, modulus, rsaKeyBitLength, false).getRemainder(); 113 | } 114 | s = s.mul(paddedMsg); 115 | s = new LongIntegerModGadget(s, modulus, rsaKeyBitLength, true).getRemainder(); 116 | 117 | // return the cipher text as byte array 118 | ciphertext = s.getBits(rsaKeyBitLength).packBitsIntoWords(8); 119 | } 120 | 121 | 122 | public void checkRandomnessCompliance(){ 123 | // assert the randomness vector has non-zero bytes 124 | for (int i = 0; i < randomness.length; i++) { 125 | randomness[i].restrictBitLength(8); 126 | // verify that each element has a multiplicative inverse 127 | new FieldDivisionGadget(generator.getOneWire(), randomness[i]); 128 | } 129 | } 130 | 131 | @Override 132 | public Wire[] getOutputWires() { 133 | return ciphertext; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/gadgets/rsa/RSASigVerificationV1_5_Gadget.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.gadgets.rsa; 5 | 6 | import circuit.auxiliary.LongElement; 7 | import circuit.operations.Gadget; 8 | import circuit.structure.Wire; 9 | import circuit.structure.WireArray; 10 | import examples.gadgets.math.LongIntegerModGadget; 11 | 12 | /** 13 | * A gadget to check if an RSA signature is valid according to PKCS 1 v1.5 (A 14 | * gadget based on the latest standard (PSS) will be added in the future). 15 | * This gadget assumes SHA256 for the message hash, and a public exponent of 16 | * 0x10001. 17 | * This gadget can accept a hardcoded or a variable RSA modulus. See the 18 | * corresponding generator example. 19 | * 20 | * Implemented according to the standard specs here: 21 | * https://www.emc.com/collateral/white- 22 | * papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf 23 | * 24 | * 25 | * 26 | * 27 | */ 28 | public class RSASigVerificationV1_5_Gadget extends Gadget { 29 | 30 | private LongElement modulus; 31 | private LongElement signature; 32 | private Wire[] msgHash; // 32-bit wires (the output of SHA256 gadget) 33 | private Wire isValidSignature; 34 | private int rsaKeyBitLength; // in bits 35 | 36 | public static final byte[] SHA256_IDENTIFIER = new byte[] { 0x30, 0x31, 37 | 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 38 | 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; 39 | 40 | public static final int SHA256_DIGEST_LENGTH = 32; // in bytes 41 | 42 | public RSASigVerificationV1_5_Gadget(LongElement modulus, Wire[] msgHash, 43 | LongElement signature, int rsaKeyBitLength, String... desc) { 44 | super(desc); 45 | this.modulus = modulus; 46 | this.msgHash = msgHash; 47 | this.signature = signature; 48 | this.rsaKeyBitLength = rsaKeyBitLength; 49 | buildCircuit(); 50 | } 51 | 52 | private void buildCircuit() { 53 | 54 | LongElement s = signature; 55 | 56 | for (int i = 0; i < 16; i++) { 57 | s = s.mul(s); 58 | s = new LongIntegerModGadget(s, modulus, rsaKeyBitLength, false).getRemainder(); 59 | } 60 | s = s.mul(signature); 61 | s = new LongIntegerModGadget(s, modulus, rsaKeyBitLength, true).getRemainder(); 62 | Wire[] sChunks = s.getArray(); 63 | 64 | // note that the following can be improved, but for simplicity we 65 | // are going to compare byte by byte 66 | 67 | // get byte arrays 68 | Wire[] sBytes = new WireArray(sChunks).getBits( 69 | LongElement.CHUNK_BITWIDTH).packBitsIntoWords(8); 70 | Wire[] msgHashBytes = new WireArray(msgHash).getBits(32) 71 | .packBitsIntoWords(8); 72 | 73 | // reverse the byte array representation of each word of the digest to 74 | // be compatiable with the endianess 75 | for (int i = 0; i < 8; i++) { 76 | Wire tmp = msgHashBytes[4 * i]; 77 | msgHashBytes[4 * i] = msgHashBytes[(4 * i + 3)]; 78 | msgHashBytes[4 * i + 3] = tmp; 79 | tmp = msgHashBytes[4 * i + 1]; 80 | msgHashBytes[4 * i + 1] = msgHashBytes[4 * i + 2]; 81 | msgHashBytes[4 * i + 2] = tmp; 82 | } 83 | 84 | int lengthInBytes = (int) (Math.ceil(rsaKeyBitLength * 1.0 / 8)); 85 | Wire sumChecks = generator.getZeroWire(); 86 | sumChecks = sumChecks.add(sBytes[lengthInBytes - 1].isEqualTo(0)); 87 | sumChecks = sumChecks.add(sBytes[lengthInBytes - 2].isEqualTo(1)); 88 | for (int i = 3; i < lengthInBytes - SHA256_DIGEST_LENGTH 89 | - SHA256_IDENTIFIER.length; i++) { 90 | sumChecks = sumChecks 91 | .add(sBytes[lengthInBytes - i].isEqualTo(0xff)); 92 | } 93 | sumChecks = sumChecks.add(sBytes[SHA256_DIGEST_LENGTH 94 | + SHA256_IDENTIFIER.length].isEqualTo(0)); 95 | 96 | for (int i = 0; i < SHA256_IDENTIFIER.length; i++) { 97 | sumChecks = sumChecks.add(sBytes[SHA256_IDENTIFIER.length 98 | + SHA256_DIGEST_LENGTH - 1 - i] 99 | .isEqualTo((int) (SHA256_IDENTIFIER[i] + 256) % 256)); 100 | } 101 | for (int i = SHA256_DIGEST_LENGTH - 1; i >= 0; i--) { 102 | sumChecks = sumChecks.add(sBytes[SHA256_DIGEST_LENGTH - 1 - i] 103 | .isEqualTo(msgHashBytes[i])); 104 | } 105 | 106 | isValidSignature = sumChecks.isEqualTo(lengthInBytes); 107 | 108 | } 109 | 110 | @Override 111 | public Wire[] getOutputWires() { 112 | return new Wire[] { isValidSignature }; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/SimpleCircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.generators; 5 | 6 | import circuit.eval.CircuitEvaluator; 7 | import circuit.structure.CircuitGenerator; 8 | import circuit.structure.Wire; 9 | 10 | public class SimpleCircuitGenerator extends CircuitGenerator { 11 | 12 | private Wire[] inputs; 13 | 14 | public SimpleCircuitGenerator(String circuitName) { 15 | super(circuitName); 16 | } 17 | 18 | @Override 19 | protected void buildCircuit() { 20 | 21 | // declare input array of length 4. 22 | inputs = createInputWireArray(4); 23 | 24 | // r1 = in0 * in1 25 | Wire r1 = inputs[0].mul(inputs[1]); 26 | 27 | // r2 = in2 + in3 28 | Wire r2 = inputs[2].add(inputs[3]); 29 | 30 | // result = (r1+5)*(6*r2) 31 | Wire result = r1.add(5).mul(r2.mul(6)); 32 | 33 | // mark the wire as output 34 | makeOutput(result); 35 | 36 | } 37 | 38 | @Override 39 | public void generateSampleInput(CircuitEvaluator circuitEvaluator) { 40 | for (int i = 0; i < 4; i++) { 41 | circuitEvaluator.setWireValue(inputs[i], i + 1); 42 | } 43 | } 44 | 45 | public static void main(String[] args) throws Exception { 46 | 47 | SimpleCircuitGenerator generator = new SimpleCircuitGenerator("simple_example"); 48 | generator.generateCircuit(); 49 | generator.evalCircuit(); 50 | generator.prepFiles(); 51 | generator.runLibsnark(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/augmenter/AugmentedAuctionCircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.generators.augmenter; 5 | 6 | import java.util.Arrays; 7 | 8 | import util.Util; 9 | import circuit.eval.CircuitEvaluator; 10 | import circuit.structure.CircuitGenerator; 11 | import circuit.structure.Wire; 12 | import examples.gadgets.augmenter.PinocchioGadget; 13 | import examples.gadgets.hash.SHA256Gadget; 14 | 15 | /** 16 | * This circuit generator augments a second-price auction circuit (produced by Pinocchio's compiler) 17 | * with SHA-256 gadgets on each input and output value. 18 | * 19 | */ 20 | 21 | public class AugmentedAuctionCircuitGenerator extends CircuitGenerator { 22 | 23 | // each value is assumed to be a 64-bit value 24 | private Wire[] secretInputValues; 25 | private Wire[] secretOutputValues; 26 | 27 | // randomness vectors for each participant (each random vector is 7 64-bit words) 28 | private Wire[][] secretInputRandomness; 29 | private Wire[][] secretOutputRandomness; 30 | 31 | private String pathToCompiledCircuit; 32 | private int numParties; // includes the auction manager + the participants 33 | 34 | public AugmentedAuctionCircuitGenerator(String circuitName, String pathToCompiledCircuit, int numParticipants) { 35 | super(circuitName); 36 | this.pathToCompiledCircuit = pathToCompiledCircuit; 37 | this.numParties = numParticipants + 1; 38 | } 39 | 40 | @Override 41 | protected void buildCircuit() { 42 | 43 | secretInputValues = createProverWitnessWireArray(numParties - 1); // the manager has a zero input (no need to commit to it) 44 | secretInputRandomness = new Wire[numParties - 1][]; 45 | secretOutputRandomness = new Wire[numParties][]; 46 | for(int i = 0; i < numParties - 1; i++){ 47 | secretInputRandomness[i] = createProverWitnessWireArray(7); 48 | secretOutputRandomness[i] = createProverWitnessWireArray(7); 49 | } 50 | secretOutputRandomness[numParties-1] = createProverWitnessWireArray(7); 51 | 52 | // instantiate a Pinocchio gadget for the auction circuit 53 | PinocchioGadget auctionGagdet = new PinocchioGadget(Util.concat(zeroWire, secretInputValues), pathToCompiledCircuit); 54 | Wire[] outputs = auctionGagdet.getOutputWires(); 55 | 56 | // ignore the last output for this circuit which carries the index of the winner (not needed for this example) 57 | secretOutputValues = Arrays.copyOfRange(outputs, 0, outputs.length - 1); 58 | 59 | // augment the input side 60 | for(int i = 0; i < numParties - 1; i++){ 61 | SHA256Gadget g = new SHA256Gadget(Util.concat(secretInputValues[i], secretInputRandomness[i]), 64, 64, false, false); 62 | makeOutputArray(g.getOutputWires(), "Commitment for party # " + i + "'s input balance."); 63 | } 64 | 65 | // augment the output side 66 | for(int i = 0; i < numParties; i++){ 67 | // adapt the output values to 64-bit values (adaptation is needed due to the way Pinocchio's compiler handles subtractions) 68 | secretOutputValues[i] = secretOutputValues[i].getBitWires(64*2).packAsBits(64); 69 | SHA256Gadget g = new SHA256Gadget(Util.concat(secretOutputValues[i], secretOutputRandomness[i]), 64, 64, false, false); 70 | makeOutputArray(g.getOutputWires(), "Commitment for party # " + i + "'s output balance."); 71 | } 72 | } 73 | 74 | @Override 75 | public void generateSampleInput(CircuitEvaluator evaluator) { 76 | 77 | for(int i = 0; i < numParties - 1; i++){ 78 | evaluator.setWireValue(secretInputValues[i], Util.nextRandomBigInteger(63)); 79 | } 80 | 81 | for(int i = 0; i < numParties - 1; i++){ 82 | for(Wire w:secretInputRandomness[i]){ 83 | evaluator.setWireValue(w, Util.nextRandomBigInteger(64)); 84 | } 85 | } 86 | for(int i = 0; i < numParties; i++){ 87 | for(Wire w:secretOutputRandomness[i]){ 88 | evaluator.setWireValue(w, Util.nextRandomBigInteger(64)); 89 | } 90 | } 91 | } 92 | 93 | 94 | public static void main(String[] args) throws Exception { 95 | AugmentedAuctionCircuitGenerator generator = new AugmentedAuctionCircuitGenerator("augmented_auction_10", "auction_10.arith", 10); 96 | generator.generateCircuit(); 97 | generator.evalCircuit(); 98 | generator.prepFiles(); 99 | generator.runLibsnark(); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/blockciphers/AES128CipherCircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.generators.blockciphers; 6 | 7 | import java.math.BigInteger; 8 | import java.util.Arrays; 9 | 10 | import circuit.config.Config; 11 | import circuit.eval.CircuitEvaluator; 12 | import circuit.structure.CircuitGenerator; 13 | import circuit.structure.Wire; 14 | import examples.gadgets.blockciphers.AES128CipherGadget; 15 | 16 | 17 | // A sample usage of the AES128 block cipher gadget 18 | public class AES128CipherCircuitGenerator extends CircuitGenerator { 19 | 20 | private Wire[] inputs; 21 | private Wire[] key; 22 | private Wire[] outputs; 23 | private AES128CipherGadget gadget; 24 | 25 | public AES128CipherCircuitGenerator(String circuitName) { 26 | super(circuitName); 27 | } 28 | 29 | @Override 30 | protected void buildCircuit() { 31 | inputs = createInputWireArray(16); // in bytes 32 | key = createInputWireArray(16); // in bytes 33 | 34 | Wire[] expandedKey = AES128CipherGadget.expandKey(key); 35 | gadget = new AES128CipherGadget(inputs, expandedKey, ""); 36 | outputs = gadget.getOutputWires(); 37 | for (Wire o : outputs) { 38 | makeOutput(o); 39 | } 40 | 41 | } 42 | 43 | @Override 44 | public void generateSampleInput(CircuitEvaluator circuitEvaluator) { 45 | 46 | BigInteger keyV = new BigInteger("2b7e151628aed2a6abf7158809cf4f3c", 16); 47 | BigInteger msgV = new BigInteger("ae2d8a571e03ac9c9eb76fac45af8e51", 16); 48 | 49 | // expected output:0xf5d3d58503b9699de785895a96fdbaaf 50 | 51 | byte[] keyArray = keyV.toByteArray(); 52 | byte[] msgArray = msgV.toByteArray(); 53 | msgArray = Arrays.copyOfRange(msgArray, msgArray.length - 16, 54 | msgArray.length); 55 | keyArray = Arrays.copyOfRange(keyArray, keyArray.length - 16, 56 | keyArray.length); 57 | 58 | for (int i = 0; i < msgArray.length; i++) { 59 | circuitEvaluator.setWireValue(inputs[i], (msgArray[i] & 0xff)); 60 | } 61 | 62 | for (int i = 0; i < keyArray.length; i++) { 63 | circuitEvaluator.setWireValue(key[i], (keyArray[i] & 0xff)); 64 | } 65 | } 66 | 67 | public static void main(String[] args) throws Exception { 68 | 69 | Config.hexOutputEnabled = true; 70 | AES128CipherCircuitGenerator generator = new AES128CipherCircuitGenerator( 71 | "AES_Circuit"); 72 | generator.generateCircuit(); 73 | generator.evalCircuit(); 74 | generator.prepFiles(); 75 | generator.runLibsnark(); 76 | 77 | } 78 | } -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/hash/MerkleTreeMembershipCircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.generators.hash; 5 | 6 | import util.Util; 7 | import circuit.config.Config; 8 | import circuit.eval.CircuitEvaluator; 9 | import circuit.structure.CircuitGenerator; 10 | import circuit.structure.Wire; 11 | import examples.gadgets.hash.MerkleTreePathGadget; 12 | import examples.gadgets.hash.SubsetSumHashGadget; 13 | 14 | public class MerkleTreeMembershipCircuitGenerator extends CircuitGenerator { 15 | 16 | private Wire[] publicRootWires; 17 | private Wire[] intermediateHasheWires; 18 | private Wire directionSelector; 19 | private Wire[] leafWires; 20 | private int leafNumOfWords = 10; 21 | private int leafWordBitWidth = 32; 22 | private int treeHeight; 23 | private int hashDigestDimension = SubsetSumHashGadget.DIMENSION; 24 | 25 | private MerkleTreePathGadget merkleTreeGadget; 26 | 27 | public MerkleTreeMembershipCircuitGenerator(String circuitName, int treeHeight) { 28 | super(circuitName); 29 | this.treeHeight = treeHeight; 30 | } 31 | 32 | @Override 33 | protected void buildCircuit() { 34 | 35 | /** declare inputs **/ 36 | 37 | publicRootWires = createInputWireArray(hashDigestDimension, "Input Merkle Tree Root"); 38 | intermediateHasheWires = createProverWitnessWireArray(hashDigestDimension * treeHeight, "Intermediate Hashes"); 39 | directionSelector = createProverWitnessWire("Direction selector"); 40 | leafWires = createProverWitnessWireArray(leafNumOfWords, "Secret Leaf"); 41 | 42 | /** connect gadget **/ 43 | 44 | merkleTreeGadget = new MerkleTreePathGadget( 45 | directionSelector, leafWires, intermediateHasheWires, leafWordBitWidth, treeHeight); 46 | Wire[] actualRoot = merkleTreeGadget.getOutputWires(); 47 | 48 | /** Now compare the actual root with the public known root **/ 49 | Wire errorAccumulator = getZeroWire(); 50 | for(int i = 0; i < hashDigestDimension; i++){ 51 | Wire diff = actualRoot[i].sub(publicRootWires[i]); 52 | Wire check = diff.checkNonZero(); 53 | errorAccumulator = errorAccumulator.add(check); 54 | } 55 | 56 | makeOutputArray(actualRoot, "Computed Root"); 57 | 58 | /** Expected mismatch here if the sample input below is tried**/ 59 | makeOutput(errorAccumulator.checkNonZero(), "Error if NON-zero"); 60 | 61 | } 62 | 63 | @Override 64 | public void generateSampleInput(CircuitEvaluator circuitEvaluator) { 65 | 66 | for (int i = 0; i < hashDigestDimension; i++) { 67 | circuitEvaluator.setWireValue(publicRootWires[i], Util.nextRandomBigInteger(Config.FIELD_PRIME)); 68 | } 69 | 70 | circuitEvaluator.setWireValue(directionSelector, Util.nextRandomBigInteger(treeHeight)); 71 | for (int i = 0; i < hashDigestDimension*treeHeight; i++) { 72 | circuitEvaluator.setWireValue(intermediateHasheWires[i], Util.nextRandomBigInteger(Config.FIELD_PRIME)); 73 | } 74 | 75 | for(int i = 0; i < leafNumOfWords; i++){ 76 | circuitEvaluator.setWireValue(leafWires[i], Integer.MAX_VALUE); 77 | } 78 | 79 | } 80 | 81 | 82 | public static void main(String[] args) throws Exception { 83 | 84 | MerkleTreeMembershipCircuitGenerator generator = new MerkleTreeMembershipCircuitGenerator("tree_64", 64); 85 | generator.generateCircuit(); 86 | generator.evalCircuit(); 87 | generator.prepFiles(); 88 | generator.runLibsnark(); 89 | } 90 | 91 | 92 | } 93 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/hash/SHA2CircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | s * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.generators.hash; 5 | 6 | import circuit.eval.CircuitEvaluator; 7 | import circuit.structure.CircuitGenerator; 8 | import circuit.structure.Wire; 9 | import examples.gadgets.hash.SHA256Gadget; 10 | 11 | public class SHA2CircuitGenerator extends CircuitGenerator { 12 | 13 | private Wire[] inputWires; 14 | private SHA256Gadget sha2Gadget; 15 | 16 | public SHA2CircuitGenerator(String circuitName) { 17 | super(circuitName); 18 | } 19 | 20 | @Override 21 | protected void buildCircuit() { 22 | 23 | // assuming the circuit input will be 64 bytes 24 | inputWires = createInputWireArray(64); 25 | // this gadget is not applying any padding. 26 | sha2Gadget = new SHA256Gadget(inputWires, 8, 64, false, false); 27 | Wire[] digest = sha2Gadget.getOutputWires(); 28 | makeOutputArray(digest, "digest"); 29 | 30 | // ====================================================================== 31 | // To see how padding can be done, and see how the gadget library will save constraints automatically, 32 | // try the snippet below instead. 33 | /* 34 | inputWires = createInputWireArray(3); // 3-byte input 35 | sha2Gadget = new SHA256Gadget(inputWires, 8, 3, false, true); 36 | Wire[] digest = sha2Gadget.getOutputWires(); 37 | makeOutputArray(digest, "digest"); 38 | */ 39 | 40 | } 41 | 42 | @Override 43 | public void generateSampleInput(CircuitEvaluator evaluator) { 44 | String inputStr = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"; 45 | for (int i = 0; i < inputWires.length; i++) { 46 | evaluator.setWireValue(inputWires[i], inputStr.charAt(i)); 47 | } 48 | } 49 | 50 | public static void main(String[] args) throws Exception { 51 | SHA2CircuitGenerator generator = new SHA2CircuitGenerator("sha_256"); 52 | generator.generateCircuit(); 53 | generator.evalCircuit(); 54 | generator.prepFiles(); 55 | generator.runLibsnark(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/hybridEncryption/HybridEncryptionCircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.generators.hybridEncryption; 5 | 6 | import java.math.BigInteger; 7 | import java.util.Arrays; 8 | 9 | import util.Util; 10 | import circuit.eval.CircuitEvaluator; 11 | import circuit.structure.CircuitGenerator; 12 | import circuit.structure.Wire; 13 | import circuit.structure.WireArray; 14 | import examples.gadgets.blockciphers.SymmetricEncryptionCBCGadget; 15 | import examples.gadgets.diffieHellmanKeyExchange.FieldExtensionDHKeyExchange; 16 | import examples.gadgets.hash.SHA256Gadget; 17 | 18 | // This gadget shows a simple example of hybrid encryption for illustration purposes 19 | // It currently uses the field extension key exchange gadget with the speck cipher 20 | 21 | public class HybridEncryptionCircuitGenerator extends CircuitGenerator { 22 | 23 | private Wire[] plaintext; // as 64-bit words 24 | private int plaintextSize; // number of 64-bit words 25 | private Wire[] ciphertext; // as 64-bit words 26 | 27 | private String ciphername; 28 | private Wire[] secExpBits; 29 | 30 | // Will assume the parameterization used in the test files ~ 80-bits 31 | // security 32 | public static final int EXPONENT_BITWIDTH = 397; // in bits 33 | public static final int MU = 4; 34 | public static final int OMEGA = 7; 35 | 36 | public HybridEncryptionCircuitGenerator(String circuitName, int plaintextSize, 37 | String ciphername) { 38 | super(circuitName); 39 | this.ciphername = ciphername; 40 | this.plaintextSize = plaintextSize; 41 | } 42 | 43 | @Override 44 | protected void buildCircuit() { 45 | 46 | plaintext = createInputWireArray(plaintextSize, "plaint text"); 47 | 48 | 49 | // Part I: Exchange a key: 50 | 51 | // The secret exponent is a private input by the prover 52 | secExpBits = createProverWitnessWireArray(EXPONENT_BITWIDTH, "SecretExponent"); 53 | for(int i = 0; i < EXPONENT_BITWIDTH; i++){ 54 | addBinaryAssertion(secExpBits[i]); // verify all bits are binary 55 | } 56 | 57 | Wire[] g = new Wire[MU]; 58 | Wire[] h = new Wire[MU]; 59 | 60 | // Hardcode the base and the other party's key (suitable when keys are not expected to change) 61 | g[0] = createConstantWire(new BigInteger("16377448892084713529161739182205318095580119111576802375181616547062197291263")); 62 | g[1] = createConstantWire(new BigInteger("13687683608888423916085091250849188813359145430644908352977567823030408967189")); 63 | g[2] = createConstantWire(new BigInteger("12629166084120705167185476169390021031074363183264910102253898080559854363106")); 64 | g[3] = createConstantWire(new BigInteger("19441276922979928804860196077335093208498949640381586557241379549605420212272")); 65 | 66 | h[0] = createConstantWire(new BigInteger("8252578783913909531884765397785803733246236629821369091076513527284845891757")); 67 | h[1] = createConstantWire(new BigInteger("20829599225781884356477513064431048695774529855095864514701692089787151865093")); 68 | h[2] = createConstantWire(new BigInteger("1540379511125324102377803754608881114249455137236500477169164628692514244862")); 69 | h[3] = createConstantWire(new BigInteger("1294177986177175279602421915789749270823809536595962994745244158374705688266")); 70 | 71 | // To make g and h variable inputs to the circuit, simply do the following 72 | // instead, and supply the above values using the generateSampleInput() 73 | // method instead. 74 | /* 75 | * Wire[] g = createInputWireArray(mu); 76 | * Wire[] h = createInputWireArray(mu); 77 | */ 78 | 79 | // Exchange keys 80 | FieldExtensionDHKeyExchange exchange = new FieldExtensionDHKeyExchange(g, h, secExpBits, 81 | OMEGA, ""); 82 | 83 | // Output g^s 84 | Wire[] g_to_s = exchange.getOutputPublicValue(); 85 | makeOutputArray(g_to_s, "DH Key Exchange Output"); 86 | 87 | // Use h^s to generate a symmetric secret key and an initialization 88 | // vector. Apply a Hash-based KDF. 89 | Wire[] h_to_s = exchange.getSharedSecret(); 90 | SHA256Gadget hashGadget = new SHA256Gadget(h_to_s, 256, 128, true, 91 | false); 92 | Wire[] secret = hashGadget.getOutputWires(); 93 | Wire[] key = Arrays.copyOfRange(secret, 0, 128); 94 | Wire[] iv = Arrays.copyOfRange(secret, 128, 256); 95 | 96 | 97 | // Part II: Apply symmetric Encryption 98 | 99 | Wire[] plaintextBits = new WireArray(plaintext).getBits(64).asArray(); 100 | SymmetricEncryptionCBCGadget symEncGagdet = new SymmetricEncryptionCBCGadget( 101 | plaintextBits, key, iv, ciphername); 102 | ciphertext = symEncGagdet.getOutputWires(); 103 | makeOutputArray(ciphertext, "Cipher Text"); 104 | } 105 | 106 | @Override 107 | public void generateSampleInput(CircuitEvaluator evaluator) { 108 | // TODO Auto-generated method stub 109 | for(int i = 0; i < plaintextSize; i++){ 110 | evaluator.setWireValue(plaintext[i], Util.nextRandomBigInteger(64)); 111 | } 112 | for(int i = 0; i < EXPONENT_BITWIDTH; i++){ 113 | evaluator.setWireValue(secExpBits[i], Util.nextRandomBigInteger(1)); 114 | } 115 | 116 | } 117 | 118 | public static void main(String[] args) throws Exception { 119 | HybridEncryptionCircuitGenerator generator = new HybridEncryptionCircuitGenerator( 120 | "enc_example", 16, "speck128"); 121 | generator.generateCircuit(); 122 | generator.evalCircuit(); 123 | generator.prepFiles(); 124 | generator.runLibsnark(); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/math/DotProductCircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.generators.math; 5 | 6 | import circuit.eval.CircuitEvaluator; 7 | import circuit.structure.CircuitGenerator; 8 | import circuit.structure.Wire; 9 | import examples.gadgets.math.DotProductGadget; 10 | 11 | public class DotProductCircuitGenerator extends CircuitGenerator { 12 | 13 | private Wire[] a; 14 | private Wire[] b; 15 | private int dimension; 16 | 17 | public DotProductCircuitGenerator(String circuitName, int dimension) { 18 | super(circuitName); 19 | this.dimension = dimension; 20 | } 21 | 22 | @Override 23 | protected void buildCircuit() { 24 | 25 | a = createInputWireArray(dimension, "Input a"); 26 | b = createInputWireArray(dimension, "Input b"); 27 | 28 | DotProductGadget dotProductGadget = new DotProductGadget(a, b); 29 | Wire[] result = dotProductGadget.getOutputWires(); 30 | makeOutput(result[0], "output of dot product a, b"); 31 | } 32 | 33 | @Override 34 | public void generateSampleInput(CircuitEvaluator circuitEvaluator) { 35 | 36 | for (int i = 0; i < dimension; i++) { 37 | circuitEvaluator.setWireValue(a[i], 10 + i); 38 | circuitEvaluator.setWireValue(b[i], 20 + i); 39 | } 40 | } 41 | 42 | public static void main(String[] args) throws Exception { 43 | 44 | DotProductCircuitGenerator generator = new DotProductCircuitGenerator("dot_product", 3); 45 | generator.generateCircuit(); 46 | generator.evalCircuit(); 47 | generator.prepFiles(); 48 | generator.runLibsnark(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/rsa/RSAEncryptionCircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.generators.rsa; 5 | 6 | import java.math.BigInteger; 7 | import java.security.Key; 8 | import java.security.KeyPair; 9 | import java.security.KeyPairGenerator; 10 | import java.security.SecureRandom; 11 | import java.security.interfaces.RSAPrivateKey; 12 | import java.security.interfaces.RSAPublicKey; 13 | import java.util.Arrays; 14 | 15 | import javax.crypto.Cipher; 16 | 17 | import circuit.auxiliary.LongElement; 18 | import circuit.eval.CircuitEvaluator; 19 | import circuit.structure.CircuitGenerator; 20 | import circuit.structure.Wire; 21 | import circuit.structure.WireArray; 22 | import examples.gadgets.rsa.RSAEncryptionV1_5_Gadget; 23 | 24 | 25 | // a demo for RSA Encryption PKCS #1, V1.5 26 | public class RSAEncryptionCircuitGenerator extends CircuitGenerator { 27 | 28 | private int rsaKeyLength; 29 | private int plainTextLength; 30 | private Wire[] inputMessage; 31 | private Wire[] randomness; 32 | private Wire[] cipherText; 33 | private LongElement rsaModulus; 34 | 35 | private RSAEncryptionV1_5_Gadget rsaEncryptionV1_5_Gadget; 36 | 37 | public RSAEncryptionCircuitGenerator(String circuitName, int rsaKeyLength, 38 | int plainTextLength) { 39 | super(circuitName); 40 | this.rsaKeyLength = rsaKeyLength; 41 | this.plainTextLength = plainTextLength; 42 | // constraints on the plaintext length will be checked by the gadget 43 | } 44 | 45 | @Override 46 | protected void buildCircuit() { 47 | 48 | inputMessage = createProverWitnessWireArray(plainTextLength); // in bytes 49 | for(int i = 0; i < plainTextLength;i++){ 50 | inputMessage[i].restrictBitLength(8); 51 | } 52 | 53 | randomness = createProverWitnessWireArray(RSAEncryptionV1_5_Gadget 54 | .getExpectedRandomnessLength(rsaKeyLength, plainTextLength)); 55 | // constraints on the randomness vector are checked later. 56 | 57 | 58 | /** 59 | * Since an RSA modulus take many wires to present, it could increase 60 | * the size of verification key if we divide it into very small chunks, 61 | * e.g. 32-bits (which happens by default in this version to minimize 62 | * the number of gates later in the circuit). In case the verification 63 | * key size is important, e.g. going to be stored in a smart contract, a 64 | * possible workaround could be by either assuming the largest possible 65 | * bitwidths for the chunks, and then converting them into smaller 66 | * chunks, or let the prover provide the key as a witness to the 67 | * circuit, and compute its hash, which will be part of the statement. 68 | * This way of doing this increases the number of gates a bit, but 69 | * reduces the VK size when crucial. 70 | * 71 | **/ 72 | 73 | rsaModulus = createLongElementInput(rsaKeyLength); 74 | 75 | // The modulus can also be hardcoded by changing the statement above to the following 76 | 77 | // rsaModulus = new LongElement(Util.split(new 78 | // BigInteger("f0dac4df56945ec31a037c5b736b64192f14baf27f2036feb85dfe45dc99d8d3c024e226e6fd7cabb56f780f9289c000a873ce32c66f4c1b2970ae6b7a3ceb2d7167fbbfe41f7b0ed7a07e3c32f14c3940176d280ceb25ed0bf830745a9425e1518f27de822b17b2b599e0aea7d72a2a6efe37160e46bf7c78b0573c9014380ab7ec12ce272a83aaa464f814c08a0b0328e191538fefaadd236ae10ba9cbb525df89da59118c7a7b861ec1c05e09976742fc2d08bd806d3715e702d9faa3491a3e4cf76b5546f927e067b281c25ddc1a21b1fb12788d39b27ca0052144ab0aad7410dc316bd7e9d2fe5e0c7a1028102454be9c26c3c347dd93ee044b680c93cb", 79 | // 16), LongElement.CHUNK_BITWIDTH)); 80 | 81 | // In case of hardcoding the modulus, comment the line that sets the modulus value in generateSampleInput() to avoid an exception 82 | 83 | rsaEncryptionV1_5_Gadget = new RSAEncryptionV1_5_Gadget(rsaModulus, inputMessage, 84 | randomness, rsaKeyLength); 85 | 86 | // since the randomness vector is a witness in this example, verify any needed constraints 87 | rsaEncryptionV1_5_Gadget.checkRandomnessCompliance(); 88 | 89 | Wire[] cipherTextInBytes = rsaEncryptionV1_5_Gadget.getOutputWires(); // in bytes 90 | 91 | // do some grouping to reduce VK Size 92 | cipherText = new WireArray(cipherTextInBytes).packWordsIntoLargerWords(8, 30); 93 | makeOutputArray(cipherText, 94 | "Output cipher text"); 95 | 96 | } 97 | 98 | @Override 99 | public void generateSampleInput(CircuitEvaluator evaluator) { 100 | 101 | String msg = ""; 102 | for (int i = 0; i < inputMessage.length; i++) { 103 | 104 | evaluator.setWireValue(inputMessage[i], (int) ('a' + i)); 105 | msg = msg + (char) ('a' + i); 106 | } 107 | System.out.println("PlainText:" + msg); 108 | 109 | try { 110 | 111 | // to make sure that the implementation is working fine, 112 | // encrypt with the underlying java implementation for RSA 113 | // Encryption in a sample run, 114 | // extract the randomness (after decryption manually), then run the 115 | // circuit with the extracted randomness 116 | 117 | SecureRandom random = new SecureRandom(); 118 | KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); 119 | generator.initialize(rsaKeyLength, random); 120 | KeyPair pair = generator.generateKeyPair(); 121 | Key pubKey = pair.getPublic(); 122 | BigInteger modulus = ((RSAPublicKey) pubKey).getModulus(); 123 | 124 | Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 125 | evaluator.setWireValue(this.rsaModulus, modulus, 126 | LongElement.CHUNK_BITWIDTH); 127 | 128 | Key privKey = pair.getPrivate(); 129 | 130 | cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); 131 | byte[] cipherText = cipher.doFinal(msg.getBytes()); 132 | // System.out.println("ciphertext : " + new String(cipherText)); 133 | byte[] cipherTextPadded = new byte[cipherText.length + 1]; 134 | System.arraycopy(cipherText, 0, cipherTextPadded, 1, cipherText.length); 135 | cipherTextPadded[0] = 0; 136 | 137 | byte[][] result = RSAUtil.extractRSARandomness1_5(cipherText, 138 | (RSAPrivateKey) privKey); 139 | // result[0] contains the plaintext (after decryption) 140 | // result[1] contains the randomness 141 | 142 | boolean check = Arrays.equals(result[0], msg.getBytes()); 143 | if (!check) { 144 | throw new RuntimeException( 145 | "Randomness Extraction did not decrypt right"); 146 | } 147 | 148 | byte[] sampleRandomness = result[1]; 149 | for (int i = 0; i < sampleRandomness.length; i++) { 150 | evaluator.setWireValue(randomness[i], (sampleRandomness[i]+256)%256); 151 | } 152 | 153 | } catch (Exception e) { 154 | System.err 155 | .println("Error while generating sample input for circuit"); 156 | e.printStackTrace(); 157 | } 158 | 159 | } 160 | 161 | public static void main(String[] args) throws Exception { 162 | int keyLength = 2048; 163 | int msgLength = 3; 164 | RSAEncryptionCircuitGenerator generator = new RSAEncryptionCircuitGenerator( 165 | "rsa" + keyLength + "_encryption", keyLength, msgLength); 166 | generator.generateCircuit(); 167 | generator.evalCircuit(); 168 | generator.prepFiles(); 169 | generator.runLibsnark(); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/rsa/RSAEncryptionOAEPCircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.generators.rsa; 5 | 6 | import java.math.BigInteger; 7 | import java.security.Key; 8 | import java.security.KeyPair; 9 | import java.security.KeyPairGenerator; 10 | import java.security.SecureRandom; 11 | import java.security.Security; 12 | import java.security.interfaces.RSAPrivateKey; 13 | import java.security.interfaces.RSAPublicKey; 14 | import java.util.Arrays; 15 | 16 | import javax.crypto.Cipher; 17 | 18 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 19 | 20 | import circuit.auxiliary.LongElement; 21 | import circuit.eval.CircuitEvaluator; 22 | import circuit.structure.CircuitGenerator; 23 | import circuit.structure.Wire; 24 | import circuit.structure.WireArray; 25 | import examples.gadgets.rsa.RSAEncryptionOAEPGadget; 26 | 27 | public class RSAEncryptionOAEPCircuitGenerator extends CircuitGenerator { 28 | 29 | private int rsaKeyLength; 30 | private int plainTextLength; 31 | private Wire[] inputMessage; 32 | private Wire[] seed; 33 | private Wire[] cipherText; 34 | private LongElement rsaModulus; 35 | 36 | private RSAEncryptionOAEPGadget rsaEncryptionOAEPGadget; 37 | 38 | public RSAEncryptionOAEPCircuitGenerator(String circuitName, int rsaKeyLength, 39 | int plainTextLength) { 40 | super(circuitName); 41 | this.rsaKeyLength = rsaKeyLength; 42 | this.plainTextLength = plainTextLength; 43 | // constraints on the plaintext length will be checked by the gadget 44 | } 45 | 46 | @Override 47 | protected void buildCircuit() { 48 | 49 | inputMessage = createProverWitnessWireArray(plainTextLength); // in bytes 50 | for(int i = 0; i < plainTextLength;i++){ 51 | inputMessage[i].restrictBitLength(8); 52 | } 53 | 54 | seed = createProverWitnessWireArray(RSAEncryptionOAEPGadget.SHA256_DIGEST_LENGTH); 55 | // constraints on the seed are checked later. 56 | 57 | rsaModulus = createLongElementInput(rsaKeyLength); 58 | 59 | // The modulus can also be hardcoded by changing the statement above to the following 60 | 61 | // rsaModulus = new LongElement(Util.split(new 62 | // BigInteger("f0dac4df56945ec31a037c5b736b64192f14baf27f2036feb85dfe45dc99d8d3c024e226e6fd7cabb56f780f9289c000a873ce32c66f4c1b2970ae6b7a3ceb2d7167fbbfe41f7b0ed7a07e3c32f14c3940176d280ceb25ed0bf830745a9425e1518f27de822b17b2b599e0aea7d72a2a6efe37160e46bf7c78b0573c9014380ab7ec12ce272a83aaa464f814c08a0b0328e191538fefaadd236ae10ba9cbb525df89da59118c7a7b861ec1c05e09976742fc2d08bd806d3715e702d9faa3491a3e4cf76b5546f927e067b281c25ddc1a21b1fb12788d39b27ca0052144ab0aad7410dc316bd7e9d2fe5e0c7a1028102454be9c26c3c347dd93ee044b680c93cb", 63 | // 16), LongElement.CHUNK_BITWIDTH)); 64 | 65 | // In case of hardcoding, comment the line that sets the modulus value in generateSampleInput() 66 | 67 | 68 | rsaEncryptionOAEPGadget = new RSAEncryptionOAEPGadget(rsaModulus, inputMessage, 69 | seed, rsaKeyLength); 70 | 71 | // since seed is a witness in this example, verify any needed constraints 72 | // If the key or the msg are witnesses, similar constraints are needed 73 | rsaEncryptionOAEPGadget.checkSeedCompliance(); 74 | 75 | Wire[] cipherTextInBytes = rsaEncryptionOAEPGadget.getOutputWires(); // in bytes 76 | 77 | // do some grouping to reduce VK Size 78 | cipherText = new WireArray(cipherTextInBytes).packWordsIntoLargerWords(8, 30); 79 | makeOutputArray(cipherText, 80 | "Output cipher text"); 81 | 82 | } 83 | 84 | @Override 85 | public void generateSampleInput(CircuitEvaluator evaluator) { 86 | 87 | String msg = ""; 88 | for (int i = 0; i < inputMessage.length; i++) { 89 | 90 | evaluator.setWireValue(inputMessage[i], (int) ('a' + i)); 91 | msg = msg + (char) ('a' + i); 92 | } 93 | System.out.println("PlainText:" + msg); 94 | 95 | try { 96 | 97 | // to make sure that the implementation is working fine, 98 | // encrypt with the BouncyCastle RSA OAEP encryption in a sample run, 99 | // extract the seed (after decryption manually), then run the 100 | // circuit with the extracted seed 101 | 102 | // The BouncyCastle implementation is used at is supports SHA-256 for the MGF, while the native Java implementation uses SHA-1 by default. 103 | 104 | Security.addProvider(new BouncyCastleProvider()); 105 | Cipher cipher = Cipher 106 | .getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "BC"); 107 | 108 | SecureRandom random = new SecureRandom(); 109 | KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); 110 | generator.initialize(rsaKeyLength, random); 111 | KeyPair pair = generator.generateKeyPair(); 112 | Key pubKey = pair.getPublic(); 113 | BigInteger modulus = ((RSAPublicKey) pubKey).getModulus(); 114 | 115 | evaluator.setWireValue(this.rsaModulus, modulus, 116 | LongElement.CHUNK_BITWIDTH); 117 | 118 | Key privKey = pair.getPrivate(); 119 | 120 | cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); 121 | byte[] cipherText = cipher.doFinal(msg.getBytes()); 122 | // System.out.println("ciphertext : " + new String(cipherText)); 123 | byte[] cipherTextPadded = new byte[cipherText.length + 1]; 124 | System.arraycopy(cipherText, 0, cipherTextPadded, 1, cipherText.length); 125 | cipherTextPadded[0] = 0; 126 | 127 | 128 | byte[][] result = RSAUtil.extractRSAOAEPSeed(cipherText, 129 | (RSAPrivateKey) privKey); 130 | // result[0] contains the plaintext (after decryption) 131 | // result[1] contains the randomness 132 | 133 | boolean check = Arrays.equals(result[0], msg.getBytes()); 134 | if (!check) { 135 | throw new RuntimeException( 136 | "Randomness Extraction did not decrypt right"); 137 | } 138 | 139 | byte[] sampleRandomness = result[1]; 140 | for (int i = 0; i < sampleRandomness.length; i++) { 141 | evaluator.setWireValue(seed[i], (sampleRandomness[i]+256)%256); 142 | } 143 | 144 | } catch (Exception e) { 145 | System.err 146 | .println("Error while generating sample input for circuit"); 147 | e.printStackTrace(); 148 | } 149 | 150 | } 151 | 152 | public static void main(String[] args) throws Exception { 153 | int keyLength = 2048; 154 | int msgLength = 3; 155 | RSAEncryptionOAEPCircuitGenerator generator = new RSAEncryptionOAEPCircuitGenerator( 156 | "rsa" + keyLength + "_oaep_encryption", keyLength, msgLength); 157 | generator.generateCircuit(); 158 | generator.evalCircuit(); 159 | generator.prepFiles(); 160 | generator.runLibsnark(); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/rsa/RSASigVerCircuitGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.generators.rsa; 5 | 6 | import java.math.BigInteger; 7 | import java.security.KeyPair; 8 | import java.security.KeyPairGenerator; 9 | import java.security.SecureRandom; 10 | import java.security.Signature; 11 | import java.security.interfaces.RSAPublicKey; 12 | 13 | import circuit.auxiliary.LongElement; 14 | import circuit.eval.CircuitEvaluator; 15 | import circuit.structure.CircuitGenerator; 16 | import circuit.structure.Wire; 17 | import examples.gadgets.hash.SHA256Gadget; 18 | import examples.gadgets.rsa.RSASigVerificationV1_5_Gadget; 19 | 20 | //a demo for RSA Signatures PKCS #1, V1.5 21 | public class RSASigVerCircuitGenerator extends CircuitGenerator { 22 | 23 | private int rsaKeyLength; 24 | private Wire[] inputMessage; 25 | private LongElement signature; 26 | private LongElement rsaModulus; 27 | 28 | private SHA256Gadget sha2Gadget; 29 | private RSASigVerificationV1_5_Gadget rsaSigVerificationV1_5_Gadget; 30 | 31 | public RSASigVerCircuitGenerator(String circuitName, int rsaKeyLength) { 32 | super(circuitName); 33 | this.rsaKeyLength = rsaKeyLength; 34 | } 35 | 36 | @Override 37 | protected void buildCircuit() { 38 | 39 | // a sample input message of 3 byte 40 | inputMessage = createInputWireArray(3); 41 | sha2Gadget = new SHA256Gadget(inputMessage, 8, inputMessage.length, 42 | false, true); 43 | Wire[] digest = sha2Gadget.getOutputWires(); 44 | 45 | /** 46 | * Since an RSA modulus take many wires to present, it could increase 47 | * the size of verification key if we divide it into very small chunks, 48 | * e.g. 32-bits (which happens by default in this version to minimize 49 | * the number of gates later in the circuit). In case the verification 50 | * key size is important, e.g. going to be stored in a smart contract, 51 | * there is a workaround, by first assuming the largest possible 52 | * bitwidths for the chunks, and then converting them into smaller 53 | * chunks. Even better, let the prover provide the key as a witness to 54 | * the circuit, and compute their hash, which will be part of the 55 | * statement. This way of doing this increases the number of gates a 56 | * bit, but reduces the VK size when needed. 57 | * 58 | **/ 59 | 60 | rsaModulus = createLongElementInput(rsaKeyLength); 61 | 62 | // The modulus can also be hardcoded by changing the statement above to the following 63 | 64 | // rsaModulus = new LongElement(Util.split(new 65 | // BigInteger("f0dac4df56945ec31a037c5b736b64192f14baf27f2036feb85dfe45dc99d8d3c024e226e6fd7cabb56f780f9289c000a873ce32c66f4c1b2970ae6b7a3ceb2d7167fbbfe41f7b0ed7a07e3c32f14c3940176d280ceb25ed0bf830745a9425e1518f27de822b17b2b599e0aea7d72a2a6efe37160e46bf7c78b0573c9014380ab7ec12ce272a83aaa464f814c08a0b0328e191538fefaadd236ae10ba9cbb525df89da59118c7a7b861ec1c05e09976742fc2d08bd806d3715e702d9faa3491a3e4cf76b5546f927e067b281c25ddc1a21b1fb12788d39b27ca0052144ab0aad7410dc316bd7e9d2fe5e0c7a1028102454be9c26c3c347dd93ee044b680c93cb", 66 | // 16), LongElement.CHUNK_BITWIDTH)); 67 | 68 | // In case of hardcoding the modulus, comment the line that sets the modulus value in generateSampleInput() to avoid an exception 69 | 70 | signature = createLongElementProverWitness(rsaKeyLength); 71 | 72 | // since the signature is provided as a witness, verify some properties 73 | // about it 74 | signature.restrictBitwidth(); 75 | signature.assertLessThan(rsaModulus); // might not be really necessary in that 76 | // case 77 | 78 | rsaSigVerificationV1_5_Gadget = new RSASigVerificationV1_5_Gadget( 79 | rsaModulus, digest, signature, rsaKeyLength); 80 | makeOutput(rsaSigVerificationV1_5_Gadget.getOutputWires()[0], 81 | "Is Signature valid?"); 82 | 83 | } 84 | 85 | @Override 86 | public void generateSampleInput(CircuitEvaluator evaluator) { 87 | String inputStr = "abc"; 88 | for (int i = 0; i < inputMessage.length; i++) { 89 | evaluator.setWireValue(inputMessage[i], inputStr.charAt(i)); 90 | } 91 | 92 | try { 93 | KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); 94 | keyGen.initialize(rsaKeyLength, new SecureRandom()); 95 | KeyPair keyPair = keyGen.generateKeyPair(); 96 | 97 | Signature signature = Signature.getInstance("SHA256withRSA"); 98 | signature.initSign(keyPair.getPrivate()); 99 | 100 | byte[] message = inputStr.getBytes(); 101 | signature.update(message); 102 | 103 | byte[] sigBytes = signature.sign(); 104 | byte[] signaturePadded = new byte[sigBytes.length + 1]; 105 | System.arraycopy(sigBytes, 0, signaturePadded, 1, sigBytes.length); 106 | signaturePadded[0] = 0; 107 | BigInteger modulus = ((RSAPublicKey) keyPair.getPublic()) 108 | .getModulus(); 109 | // System.out.println(modulus.toString(16)); 110 | BigInteger sig = new BigInteger(signaturePadded); 111 | 112 | // if (!minimizeVerificationKey) { 113 | evaluator.setWireValue(this.rsaModulus, modulus, 114 | LongElement.CHUNK_BITWIDTH); 115 | evaluator.setWireValue(this.signature, sig, 116 | LongElement.CHUNK_BITWIDTH); 117 | // } else { 118 | // evaluator.setWireValue(this.rsaModulusWires, 119 | // Util.split(modulus, Config.LOG2_FIELD_PRIME - 1)); 120 | // evaluator.setWireValue(this.signatureWires, 121 | // Util.split(sig, Config.LOG2_FIELD_PRIME - 1)); 122 | // } 123 | } catch (Exception e) { 124 | System.err 125 | .println("Error while generating sample input for circuit"); 126 | e.printStackTrace(); 127 | } 128 | 129 | } 130 | 131 | public static void main(String[] args) throws Exception { 132 | int keyLength = 2048; 133 | RSASigVerCircuitGenerator generator = new RSASigVerCircuitGenerator( 134 | "rsa" + keyLength + "_sha256_sig_verify", keyLength); 135 | generator.generateCircuit(); 136 | generator.evalCircuit(); 137 | generator.prepFiles(); 138 | generator.runLibsnark(); 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/generators/rsa/RSAUtil.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | 5 | package examples.generators.rsa; 6 | 7 | import java.math.BigInteger; 8 | import java.security.MessageDigest; 9 | import java.security.interfaces.RSAPrivateKey; 10 | import java.util.Arrays; 11 | 12 | /** 13 | * Utility methods to extract sample randomness used by standard implementations 14 | * for RSA Encryption. In absence of test vectors, the extracted randomness is 15 | * used to test our RSA gadgets to make sure the RSA circuits match the standard 16 | * implementations. 17 | * 18 | */ 19 | 20 | public class RSAUtil { 21 | 22 | public static byte[][] extractRSARandomness1_5(byte[] cipherText, 23 | RSAPrivateKey privateKey) { 24 | 25 | BigInteger modulus = privateKey.getModulus(); 26 | int keySize = modulus.bitLength(); 27 | BigInteger d = privateKey.getPrivateExponent(); 28 | 29 | byte[] cipherTextPadded = new byte[cipherText.length + 1]; 30 | System.arraycopy(cipherText, 0, cipherTextPadded, 1, cipherText.length); 31 | cipherTextPadded[0] = 0; 32 | 33 | BigInteger c = new BigInteger(cipherText); 34 | c = new BigInteger(cipherTextPadded); 35 | BigInteger product = BigInteger.ONE; 36 | for (int i = keySize - 1; i >= 0; i--) { 37 | product = product.multiply(product).mod(modulus); 38 | boolean bit = d.testBit(i); 39 | if (bit) 40 | product = product.multiply(c).mod(modulus); 41 | } 42 | 43 | // System.out.println("After decryption manually = " 44 | // + product.toString(16)); 45 | 46 | byte[] paddedPlaintext = product.toByteArray(); 47 | if (paddedPlaintext.length != keySize / 8 - 1) { 48 | System.out.println("Error"); 49 | return null; 50 | } 51 | byte[] plaintext = null; 52 | byte[] randomness = null; 53 | 54 | if (paddedPlaintext[0] != 2) { 55 | System.out.println("Error"); 56 | } else { 57 | for (int i = 1; i < keySize / 8 - 2; i++) { 58 | if (paddedPlaintext[i] != 0) { 59 | continue; 60 | } else { 61 | plaintext = new byte[(keySize / 8 - 2) - i]; 62 | randomness = new byte[i - 1]; 63 | System.arraycopy(paddedPlaintext, i + 1, plaintext, 0, 64 | plaintext.length); 65 | System.arraycopy(paddedPlaintext, 1, randomness, 0, 66 | randomness.length); 67 | 68 | break; 69 | } 70 | } 71 | } 72 | byte[][] result = new byte[][] { plaintext, randomness }; 73 | return result; 74 | } 75 | 76 | private static final byte[] intToByteArray(int value) { 77 | return new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), 78 | (byte) (value >>> 8), (byte) value }; 79 | } 80 | 81 | private static byte[] mgf(byte[] array, int maskLen, int hlen) { 82 | 83 | byte[] v = new byte[0]; 84 | for (int i = 0; i <= ((int) Math.ceil(maskLen * 1.0 / hlen)) - 1; i++) { 85 | byte[] c = intToByteArray(i); 86 | MessageDigest hash = null; 87 | try { 88 | hash = MessageDigest.getInstance("SHA-256"); 89 | } catch (Exception e) { 90 | e.printStackTrace(); 91 | } 92 | hash.update(concat(array, c)); 93 | byte[] digest = hash.digest(); 94 | hash.reset(); 95 | v = concat(v, digest); 96 | } 97 | return v; 98 | } 99 | 100 | private static byte[] concat(byte[] a1, byte[] a2) { 101 | int l = a1.length + a2.length; 102 | byte[] result = new byte[l]; 103 | for (int i = 0; i < a1.length; i++) { 104 | result[i] = a1[i]; 105 | } 106 | for (int i = 0; i < a2.length; i++) { 107 | result[i + a1.length] = a2[i]; 108 | } 109 | return result; 110 | } 111 | 112 | public static byte[][] extractRSAOAEPSeed(byte[] cipherText, 113 | RSAPrivateKey privateKey) { 114 | 115 | BigInteger modulus = privateKey.getModulus(); 116 | int keySize = modulus.bitLength(); 117 | BigInteger d = privateKey.getPrivateExponent(); 118 | 119 | byte[] cipherTextPadded = new byte[cipherText.length + 1]; 120 | System.arraycopy(cipherText, 0, cipherTextPadded, 1, cipherText.length); 121 | cipherTextPadded[0] = 0; 122 | 123 | BigInteger c = new BigInteger(cipherText); 124 | c = new BigInteger(cipherTextPadded); 125 | 126 | BigInteger product = BigInteger.ONE; 127 | for (int i = keySize - 1; i >= 0; i--) { 128 | product = product.multiply(product).mod(modulus); 129 | boolean bit = d.testBit(i); 130 | if (bit) 131 | product = product.multiply(c).mod(modulus); 132 | } 133 | 134 | int hlen = 32; 135 | int maskedDBLength = keySize / 8 - hlen - 1; 136 | 137 | byte[] encodedMessageBytes = product.toByteArray(); 138 | 139 | if (encodedMessageBytes.length > keySize / 8) { 140 | encodedMessageBytes = Arrays.copyOfRange(encodedMessageBytes, 1, 141 | encodedMessageBytes.length); 142 | } else { 143 | while (encodedMessageBytes.length < keySize / 8) { 144 | encodedMessageBytes = concat(new byte[] { 0 }, 145 | encodedMessageBytes); 146 | } 147 | } 148 | 149 | byte[] maskedSeed = Arrays 150 | .copyOfRange(encodedMessageBytes, 1, hlen + 1); 151 | byte[] maskedDb = Arrays.copyOfRange(encodedMessageBytes, hlen + 1, 152 | encodedMessageBytes.length); 153 | 154 | byte[] seedMask = mgf(maskedDb, hlen, hlen); 155 | byte[] seed = Arrays.copyOf(seedMask, hlen); 156 | for (int i = 0; i < hlen; i++) { 157 | seed[i] ^= maskedSeed[i]; 158 | } 159 | 160 | byte[] dbMask = mgf(seed, keySize / 8 - hlen - 1, hlen); 161 | dbMask= Arrays.copyOf(dbMask, keySize/8-hlen-1); 162 | 163 | byte[] DB = new byte[dbMask.length + 1]; // appending a zero to the left, to avoid sign issues in the BigInteger 164 | System.arraycopy(maskedDb, 0, DB, 1, maskedDBLength); 165 | for (int i = 0; i < maskedDBLength; i++) { 166 | DB[i + 1] ^= dbMask[i]; 167 | } 168 | // BigInteger dbInt = new BigInteger(DB); 169 | 170 | int shift1 = 0; 171 | while (DB[shift1] == 0) { 172 | shift1++; 173 | } 174 | int idx = 32 + shift1; 175 | while (DB[idx] == 0) { 176 | idx++; 177 | } 178 | byte[] plaintext = Arrays.copyOfRange(DB, idx + 1, DB.length); 179 | byte[][] result = new byte[][] { plaintext, seed }; 180 | return result; 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/tests/blockciphers/Chaskey128_Test.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.tests.blockciphers; 5 | 6 | import java.math.BigInteger; 7 | import java.util.ArrayList; 8 | 9 | import junit.framework.TestCase; 10 | 11 | import org.junit.Test; 12 | 13 | import circuit.eval.CircuitEvaluator; 14 | import circuit.structure.CircuitGenerator; 15 | import circuit.structure.Wire; 16 | import examples.gadgets.blockciphers.ChaskeyLTS128CipherGadget; 17 | 18 | 19 | // test case from: https://www.cryptolux.org/index.php/FELICS 20 | 21 | public class Chaskey128_Test extends TestCase { 22 | 23 | @Test 24 | public void testCase1() { 25 | 26 | CircuitGenerator generator = new CircuitGenerator("Chaskey_Test1") { 27 | 28 | private Wire[] plaintext; // 4 32-bit words 29 | private Wire[] key; // 4 32-bit words 30 | private Wire[] ciphertext; // 4 32-bit words 31 | 32 | @Override 33 | protected void buildCircuit() { 34 | plaintext = createInputWireArray(4); 35 | key = createInputWireArray(4); 36 | ciphertext = new ChaskeyLTS128CipherGadget(plaintext, key) 37 | .getOutputWires(); 38 | makeOutputArray(ciphertext); 39 | } 40 | 41 | @Override 42 | public void generateSampleInput(CircuitEvaluator evaluator) { 43 | 44 | BigInteger[] keyV = { BigInteger.valueOf(0x68e90956L), 45 | BigInteger.valueOf(0x29e3585fL), 46 | BigInteger.valueOf(0x98ecec40L), 47 | BigInteger.valueOf(0x2f9822c5L) }; 48 | 49 | BigInteger[] msgV = { BigInteger.valueOf(0x262823b8L), 50 | BigInteger.valueOf(0x5e405efdL), 51 | BigInteger.valueOf(0xa901a369L), 52 | BigInteger.valueOf(0xd87aea78L) }; 53 | 54 | for (int i = 0; i < plaintext.length; i++) { 55 | evaluator.setWireValue(plaintext[i], msgV[i]); 56 | } 57 | for (int i = 0; i < key.length; i++) { 58 | evaluator.setWireValue(key[i], keyV[i]); 59 | } 60 | } 61 | }; 62 | 63 | generator.generateCircuit(); 64 | generator.evalCircuit(); 65 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 66 | ArrayList cipherText = generator.getOutWires(); 67 | 68 | BigInteger[] expeectedCiphertext = { BigInteger.valueOf(0x4d8d60d5L), 69 | BigInteger.valueOf(0x7b34bfa2L), 70 | BigInteger.valueOf(0x2f77f8abL), 71 | BigInteger.valueOf(0x07deeddfL) }; 72 | 73 | for (int i = 0; i < 4; i++) { 74 | assertEquals(evaluator.getWireValue(cipherText.get(i)), 75 | expeectedCiphertext[i]); 76 | } 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/tests/blockciphers/Speck128_Test.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.tests.blockciphers; 5 | 6 | import java.math.BigInteger; 7 | import java.util.ArrayList; 8 | 9 | import junit.framework.TestCase; 10 | 11 | import org.junit.Test; 12 | 13 | import circuit.eval.CircuitEvaluator; 14 | import circuit.structure.CircuitGenerator; 15 | import circuit.structure.Wire; 16 | import examples.gadgets.blockciphers.Speck128CipherGadget; 17 | 18 | /** 19 | * Tests SPECK block cipher @ keysize = 128, blocksize = 128. 20 | * Test vector obtained from: https://github.com/inmcm/Simon_Speck_Ciphers/blob/master/Python/SimonSpeckCiphers/tests/test_simonspeck.py 21 | */ 22 | 23 | public class Speck128_Test extends TestCase { 24 | 25 | @Test 26 | public void testCase1() { 27 | 28 | CircuitGenerator generator = new CircuitGenerator("Speck128_Test") { 29 | 30 | Wire[] plaintext; // 2 64-bit words 31 | Wire[] key; // 2 64-bit words 32 | Wire[] ciphertext; // 2 64-bit words 33 | 34 | @Override 35 | protected void buildCircuit() { 36 | plaintext = createInputWireArray(2); 37 | key = createInputWireArray(2); 38 | Wire[] expandedKey = Speck128CipherGadget.expandKey(key); 39 | ciphertext = new Speck128CipherGadget(plaintext, expandedKey).getOutputWires(); 40 | makeOutputArray(ciphertext); 41 | } 42 | 43 | @Override 44 | public void generateSampleInput(CircuitEvaluator evaluator) { 45 | evaluator.setWireValue(key[0], new BigInteger("0706050403020100", 16)); 46 | evaluator.setWireValue(key[1], new BigInteger("0f0e0d0c0b0a0908", 16)); 47 | evaluator.setWireValue(plaintext[0], new BigInteger("7469206564616d20", 16)); 48 | evaluator.setWireValue(plaintext[1], new BigInteger("6c61766975716520", 16)); 49 | } 50 | }; 51 | 52 | generator.generateCircuit(); 53 | generator.evalCircuit(); 54 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 55 | ArrayList cipherText= generator.getOutWires(); 56 | assertEquals(evaluator.getWireValue(cipherText.get(0)), new BigInteger("7860fedf5c570d18", 16)); 57 | assertEquals(evaluator.getWireValue(cipherText.get(1)), new BigInteger("a65d985179783265", 16)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/tests/diffieHellmanKeyExchange/ECDHKeyExchange_Test.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.tests.diffieHellmanKeyExchange; 5 | 6 | import java.math.BigInteger; 7 | import java.util.ArrayList; 8 | 9 | import junit.framework.TestCase; 10 | 11 | import org.junit.Test; 12 | 13 | import circuit.eval.CircuitEvaluator; 14 | import circuit.structure.CircuitGenerator; 15 | import circuit.structure.Wire; 16 | import examples.gadgets.diffieHellmanKeyExchange.ECDHKeyExchangeGadget; 17 | 18 | /** 19 | * Tests Key Exchange via Elliptic curve Gadget (ECDHKeyExchangeGadget.java) 20 | 21 | */ 22 | 23 | public class ECDHKeyExchange_Test extends TestCase { 24 | 25 | 26 | // The sage script to compute the sample case is commented in the end of the file. 27 | // TODO: Add more test cases 28 | 29 | @Test 30 | public void testVariableInputCase() { 31 | 32 | CircuitGenerator generator = new CircuitGenerator("ECDH_Test") { 33 | 34 | int exponentBitlength = ECDHKeyExchangeGadget.SECRET_BITWIDTH; 35 | private Wire[] secretBits; 36 | private Wire baseX; 37 | private Wire hX; 38 | 39 | @Override 40 | protected void buildCircuit() { 41 | 42 | secretBits = createInputWireArray(exponentBitlength, "exponent"); 43 | baseX = createInputWire(); 44 | hX = createInputWire(); 45 | 46 | 47 | ECDHKeyExchangeGadget keyExchangeGadget = 48 | new ECDHKeyExchangeGadget(baseX, hX, secretBits); 49 | 50 | makeOutput(keyExchangeGadget.getOutputPublicValue()); 51 | 52 | // Just for testing. In real scenarios, this should not be made public 53 | makeOutput(keyExchangeGadget.getSharedSecret()); 54 | 55 | } 56 | 57 | @Override 58 | public void generateSampleInput(CircuitEvaluator evaluator) { 59 | 60 | evaluator.setWireValue(baseX, new BigInteger("4")); 61 | evaluator.setWireValue(hX, new BigInteger("21766081959050939664800904742925354518084319102596785077490863571049214729748")); 62 | 63 | BigInteger exponent = new BigInteger("13867691842196510828352345865165018381161315605899394650350519162543016860992"); 64 | for(int i = 0; i < exponentBitlength; i++){ 65 | evaluator.setWireValue(secretBits[i], exponent.testBit(i)?1:0); 66 | } 67 | } 68 | }; 69 | 70 | generator.generateCircuit(); 71 | generator.evalCircuit(); 72 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 73 | ArrayList output = generator.getOutWires(); 74 | 75 | assertEquals(evaluator.getWireValue(output.get(0)), new BigInteger("13458082339735734368462130456283583571822918321676509705348825437102113182254")); 76 | assertEquals(evaluator.getWireValue(output.get(1)), new BigInteger("4167917227796707610764894996898236918915412447839980711033808347811701875717")); 77 | } 78 | 79 | 80 | @Test 81 | public void testHardcodedInputCase() { 82 | 83 | CircuitGenerator generator = new CircuitGenerator("ECDH_Test2") { 84 | 85 | 86 | int exponentBitlength = ECDHKeyExchangeGadget.SECRET_BITWIDTH; 87 | private Wire[] secretBits; 88 | private Wire baseX; 89 | private Wire hX; 90 | 91 | @Override 92 | protected void buildCircuit() { 93 | 94 | secretBits = createInputWireArray(exponentBitlength, "exponent"); 95 | baseX = createConstantWire(new BigInteger("4")); 96 | hX = createConstantWire(new BigInteger("21766081959050939664800904742925354518084319102596785077490863571049214729748")); 97 | 98 | ECDHKeyExchangeGadget keyExchangeGadget = 99 | new ECDHKeyExchangeGadget(baseX, hX, secretBits); 100 | 101 | makeOutput(keyExchangeGadget.getOutputPublicValue()); 102 | 103 | // Just for testing. In real scenarios, this should not be made public 104 | makeOutput(keyExchangeGadget.getSharedSecret()); 105 | 106 | } 107 | 108 | @Override 109 | public void generateSampleInput(CircuitEvaluator evaluator) { 110 | 111 | 112 | BigInteger exponent = new BigInteger("13867691842196510828352345865165018381161315605899394650350519162543016860992"); 113 | for(int i = 0; i < exponentBitlength; i++){ 114 | evaluator.setWireValue(secretBits[i], exponent.testBit(i)?1:0); 115 | } 116 | } 117 | }; 118 | 119 | generator.generateCircuit(); 120 | generator.evalCircuit(); 121 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 122 | ArrayList output = generator.getOutWires(); 123 | 124 | assertEquals(evaluator.getWireValue(output.get(0)), new BigInteger("13458082339735734368462130456283583571822918321676509705348825437102113182254")); 125 | assertEquals(evaluator.getWireValue(output.get(1)), new BigInteger("4167917227796707610764894996898236918915412447839980711033808347811701875717")); 126 | } 127 | 128 | 129 | @Test 130 | public void testInputValidation1() { 131 | 132 | CircuitGenerator generator = new CircuitGenerator("ECDH_Test_InputValidation") { 133 | 134 | 135 | int exponentBitlength = ECDHKeyExchangeGadget.SECRET_BITWIDTH; 136 | private Wire[] secretBits; 137 | private Wire baseX; 138 | private Wire hX; 139 | 140 | @Override 141 | protected void buildCircuit() { 142 | 143 | secretBits = createInputWireArray(exponentBitlength, "exponent"); 144 | baseX = createInputWire(); 145 | hX = createInputWire(); 146 | 147 | 148 | ECDHKeyExchangeGadget keyExchangeGadget = 149 | new ECDHKeyExchangeGadget(baseX, hX, secretBits); 150 | 151 | keyExchangeGadget.validateInputs(); 152 | } 153 | 154 | @Override 155 | public void generateSampleInput(CircuitEvaluator evaluator) { 156 | 157 | evaluator.setWireValue(baseX, new BigInteger("4")); 158 | evaluator.setWireValue(hX, new BigInteger("21766081959050939664800904742925354518084319102596785077490863571049214729748")); 159 | 160 | BigInteger exponent = new BigInteger("13867691842196510828352345865165018381161315605899394650350519162543016860992"); 161 | for(int i = 0; i < exponentBitlength; i++){ 162 | evaluator.setWireValue(secretBits[i], exponent.testBit(i)?1:0); 163 | } 164 | } 165 | }; 166 | 167 | generator.generateCircuit(); 168 | generator.evalCircuit(); 169 | 170 | // if no exception get thrown we are ok 171 | } 172 | 173 | 174 | 175 | public void testInputValidation2() { 176 | 177 | 178 | // try invalid input 179 | CircuitGenerator generator = new CircuitGenerator("ECDH_Test_InputValidation2") { 180 | 181 | 182 | int exponentBitlength = ECDHKeyExchangeGadget.SECRET_BITWIDTH; 183 | private Wire[] secretBits; 184 | private Wire baseX; 185 | private Wire hX; 186 | 187 | @Override 188 | protected void buildCircuit() { 189 | 190 | secretBits = createInputWireArray(exponentBitlength, "exponent"); 191 | baseX = createInputWire(); 192 | hX = createInputWire(); 193 | 194 | 195 | ECDHKeyExchangeGadget keyExchangeGadget = 196 | new ECDHKeyExchangeGadget(baseX, baseX, hX, hX, secretBits); 197 | 198 | keyExchangeGadget.validateInputs(); 199 | } 200 | 201 | @Override 202 | public void generateSampleInput(CircuitEvaluator evaluator) { 203 | 204 | // invalid 205 | evaluator.setWireValue(baseX, new BigInteger("14")); 206 | evaluator.setWireValue(hX, new BigInteger("21766081959050939664800904742925354518084319102596785077490863571049214729748")); 207 | 208 | BigInteger exponent = new BigInteger("13867691842196510828352345865165018381161315605899394650350519162543016860992"); 209 | for(int i = 0; i < exponentBitlength; i++){ 210 | evaluator.setWireValue(secretBits[i], exponent.testBit(i)?1:0); 211 | } 212 | } 213 | }; 214 | 215 | generator.generateCircuit(); 216 | 217 | // we expect an exception somewhere 218 | try{ 219 | generator.evalCircuit(); 220 | assertTrue(false); 221 | } catch(Exception e){ 222 | System.out.println("Exception Expected!"); 223 | assertTrue(true); 224 | } 225 | 226 | // TODO: test more error conditions 227 | } 228 | 229 | 230 | 231 | // Sage Script generating the above values: 232 | // 233 | // p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 234 | // K. = NumberField(x-1) 235 | // aa = 126932 236 | // E = EllipticCurve(GF(p),[0,aa,0,1,0]) 237 | // print(E.order()) 238 | // print(n(log(E.order(),2))) 239 | // print(n(log(2736030358979909402780800718157159386074658810754251464600343418943805806723,2))) 240 | // 241 | // secret = 13867691842196510828352345865165018381161315605899394650350519162543016860992 242 | // 243 | // base = E(4, 5854969154019084038134685408453962516899849177257040453511959087213437462470) 244 | // print(base*secret) 245 | // print(h*secret) 246 | } 247 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/tests/hash/SHA256_Test.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.tests.hash; 5 | 6 | import junit.framework.TestCase; 7 | 8 | import org.junit.Test; 9 | 10 | import util.Util; 11 | import circuit.config.Config; 12 | import circuit.eval.CircuitEvaluator; 13 | import circuit.structure.CircuitGenerator; 14 | import circuit.structure.Wire; 15 | import examples.gadgets.hash.SHA256Gadget; 16 | import java.math.BigInteger; 17 | 18 | /** 19 | * Tests SHA256 standard cases. 20 | * 21 | */ 22 | 23 | public class SHA256_Test extends TestCase { 24 | 25 | @Test 26 | public void testCase1() { 27 | 28 | String inputStr = ""; 29 | String expectedDigest = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; 30 | 31 | CircuitGenerator generator = new CircuitGenerator("SHA2_Test1") { 32 | 33 | Wire[] inputWires; 34 | 35 | @Override 36 | protected void buildCircuit() { 37 | inputWires = createInputWireArray(inputStr.length()); 38 | Wire[] digest = new SHA256Gadget(inputWires, 8, inputStr.length(), false, true, "").getOutputWires(); 39 | makeOutputArray(digest); 40 | } 41 | 42 | @Override 43 | public void generateSampleInput(CircuitEvaluator e) { 44 | // no input needed 45 | } 46 | }; 47 | 48 | generator.generateCircuit(); 49 | generator.evalCircuit(); 50 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 51 | 52 | String outDigest = ""; 53 | for (Wire w : generator.getOutWires()) { 54 | outDigest += Util.padZeros(evaluator.getWireValue(w).toString(16), 8); 55 | } 56 | assertEquals(outDigest, expectedDigest); 57 | 58 | } 59 | 60 | @Test 61 | public void testCase2() { 62 | 63 | String inputStr = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; 64 | String expectedDigest = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"; 65 | 66 | CircuitGenerator generator = new CircuitGenerator("SHA2_Test2") { 67 | 68 | Wire[] inputWires; 69 | 70 | @Override 71 | protected void buildCircuit() { 72 | inputWires = createInputWireArray(inputStr.length()); 73 | Wire[] digest = new SHA256Gadget(inputWires, 8, inputStr.length(), false, true, "").getOutputWires(); 74 | makeOutputArray(digest); 75 | } 76 | 77 | @Override 78 | public void generateSampleInput(CircuitEvaluator e) { 79 | for (int i = 0; i < inputStr.length(); i++) { 80 | e.setWireValue(inputWires[i], inputStr.charAt(i)); 81 | } 82 | } 83 | }; 84 | 85 | generator.generateCircuit(); 86 | generator.evalCircuit(); 87 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 88 | 89 | String outDigest = ""; 90 | for (Wire w : generator.getOutWires()) { 91 | outDigest += Util.padZeros(evaluator.getWireValue(w).toString(16), 8); 92 | } 93 | assertEquals(outDigest, expectedDigest); 94 | 95 | } 96 | 97 | @Test 98 | public void testCase3() { 99 | 100 | String inputStr = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"; 101 | String expectedDigest = "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"; 102 | 103 | CircuitGenerator generator = new CircuitGenerator("SHA2_Test3") { 104 | 105 | Wire[] inputWires; 106 | 107 | @Override 108 | protected void buildCircuit() { 109 | inputWires = createInputWireArray(inputStr.length()); 110 | Wire[] digest = new SHA256Gadget(inputWires, 8, inputStr.length(), false, true, "").getOutputWires(); 111 | makeOutputArray(digest); 112 | } 113 | 114 | @Override 115 | public void generateSampleInput(CircuitEvaluator e) { 116 | for (int i = 0; i < inputStr.length(); i++) { 117 | e.setWireValue(inputWires[i], inputStr.charAt(i)); 118 | } 119 | } 120 | }; 121 | 122 | generator.generateCircuit(); 123 | generator.evalCircuit(); 124 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 125 | 126 | String outDigest = ""; 127 | for (Wire w : generator.getOutWires()) { 128 | outDigest += Util.padZeros(evaluator.getWireValue(w).toString(16), 8); 129 | } 130 | assertEquals(outDigest, expectedDigest); 131 | 132 | } 133 | 134 | @Test 135 | public void testCase4() { 136 | 137 | String inputStr = "abc"; 138 | String expectedDigest = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; 139 | 140 | CircuitGenerator generator = new CircuitGenerator("SHA2_Test4") { 141 | 142 | Wire[] inputWires; 143 | 144 | @Override 145 | protected void buildCircuit() { 146 | inputWires = createInputWireArray(inputStr.length()); 147 | Wire[] digest = new SHA256Gadget(inputWires, 8, inputStr.length(), false, true, "").getOutputWires(); 148 | makeOutputArray(digest); 149 | } 150 | 151 | @Override 152 | public void generateSampleInput(CircuitEvaluator e) { 153 | for (int i = 0; i < inputStr.length(); i++) { 154 | e.setWireValue(inputWires[i], inputStr.charAt(i)); 155 | } 156 | } 157 | }; 158 | 159 | generator.generateCircuit(); 160 | generator.evalCircuit(); 161 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 162 | 163 | String outDigest = ""; 164 | for (Wire w : generator.getOutWires()) { 165 | outDigest += Util.padZeros(evaluator.getWireValue(w).toString(16), 8); 166 | } 167 | assertEquals(outDigest, expectedDigest); 168 | } 169 | 170 | 171 | 172 | @Test 173 | public void testCase5() { 174 | 175 | String inputStr = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; 176 | String expectedDigest = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"; 177 | 178 | // Testing different settings of the bitWidthPerInputElement parameter 179 | // wordSize = # of bytes per input wire 180 | 181 | for (int wordSize = 1; wordSize <= Config.LOG2_FIELD_PRIME / 8 - 1; wordSize++) { 182 | 183 | final int numBytesPerInputWire = wordSize; 184 | 185 | CircuitGenerator generator = new CircuitGenerator("SHA2_Test5") { 186 | 187 | Wire[] inputWires; 188 | @Override 189 | protected void buildCircuit() { 190 | inputWires = createInputWireArray(inputStr.length() 191 | / numBytesPerInputWire 192 | + (inputStr.length() % numBytesPerInputWire != 0 ? 1 : 0)); 193 | Wire[] digest = new SHA256Gadget(inputWires, 8 * numBytesPerInputWire, 194 | inputStr.length(), false, true, "") 195 | .getOutputWires(); 196 | makeOutputArray(digest); 197 | } 198 | 199 | @Override 200 | public void generateSampleInput(CircuitEvaluator e) { 201 | for (int i = 0; i < inputWires.length; i++) { 202 | BigInteger sum = BigInteger.ZERO; 203 | for (int j = i * numBytesPerInputWire; j < (i + 1) * numBytesPerInputWire 204 | && j < inputStr.length(); j++) { 205 | BigInteger v = BigInteger.valueOf(inputStr 206 | .charAt(j)); 207 | sum = sum.add(v.shiftLeft((j % numBytesPerInputWire) * 8)); 208 | } 209 | e.setWireValue(inputWires[i], sum); 210 | } 211 | } 212 | }; 213 | 214 | generator.generateCircuit(); 215 | generator.evalCircuit(); 216 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 217 | 218 | String outDigest = ""; 219 | for (Wire w : generator.getOutWires()) { 220 | outDigest += Util.padZeros( 221 | evaluator.getWireValue(w).toString(16), 8); 222 | } 223 | assertEquals(outDigest, expectedDigest); 224 | 225 | } 226 | 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/tests/math/Mod_Test.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.tests.math; 5 | 6 | import java.math.BigInteger; 7 | 8 | import junit.framework.TestCase; 9 | 10 | import org.junit.Test; 11 | 12 | import circuit.eval.CircuitEvaluator; 13 | import circuit.structure.CircuitGenerator; 14 | import circuit.structure.Wire; 15 | import examples.gadgets.math.ModConstantGadget; 16 | import examples.gadgets.math.ModGadget; 17 | 18 | 19 | public class Mod_Test extends TestCase { 20 | 21 | // TODO; add more tests 22 | @Test 23 | public void testCase1() { 24 | 25 | int a = 1262178522; 26 | int b = 257; // b will be an input to the circuit 27 | 28 | CircuitGenerator generator = new CircuitGenerator("Mod_Test1") { 29 | 30 | Wire[] inputWires; 31 | 32 | @Override 33 | protected void buildCircuit() { 34 | 35 | inputWires = createInputWireArray(2); 36 | // Wire r = new ModGadget(inputWires[0], (int) Math.ceil(Math.log10(a) / Math.log10(2)), inputWires[1], 37 | // (int) Math.ceil(Math.log10(b) / Math.log10(2))).getOutputWires()[0]; 38 | 39 | Wire r = new ModGadget(inputWires[0], inputWires[1], 32).getOutputWires()[0]; 40 | makeOutput(r); 41 | } 42 | 43 | @Override 44 | public void generateSampleInput(CircuitEvaluator e) { 45 | e.setWireValue(inputWires[0], a); 46 | e.setWireValue(inputWires[1], b); 47 | 48 | } 49 | }; 50 | 51 | generator.generateCircuit(); 52 | CircuitEvaluator evaluator = new CircuitEvaluator(generator); 53 | generator.generateSampleInput(evaluator); 54 | evaluator.evaluate(); 55 | Wire rWire = generator.getOutWires().get(0); 56 | assertEquals(evaluator.getWireValue(rWire), BigInteger.valueOf(a % b)); 57 | } 58 | 59 | @Test 60 | public void testCase2() { 61 | 62 | int a = 1262178522; 63 | int b = 257; // b will be a constant 64 | 65 | CircuitGenerator generator = new CircuitGenerator("Mod_Test2") { 66 | 67 | Wire[] inputWires; 68 | 69 | @Override 70 | protected void buildCircuit() { 71 | 72 | inputWires = createInputWireArray(1); 73 | Wire r = new ModConstantGadget(inputWires[0], 32, BigInteger.valueOf(b)).getOutputWires()[0]; 74 | makeOutput(r); 75 | } 76 | 77 | @Override 78 | public void generateSampleInput(CircuitEvaluator e) { 79 | e.setWireValue(inputWires[0], a); 80 | } 81 | }; 82 | 83 | generator.generateCircuit(); 84 | CircuitEvaluator evaluator = new CircuitEvaluator(generator); 85 | generator.generateSampleInput(evaluator); 86 | evaluator.evaluate(); 87 | Wire rWire = generator.getOutWires().get(0); 88 | assertEquals(evaluator.getWireValue(rWire), BigInteger.valueOf(a % b)); 89 | } 90 | 91 | 92 | } 93 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/tests/rsa/RSAEncryptionOAEP_Test.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.tests.rsa; 5 | 6 | import java.math.BigInteger; 7 | import java.security.Key; 8 | import java.security.KeyPair; 9 | import java.security.KeyPairGenerator; 10 | import java.security.SecureRandom; 11 | import java.security.Security; 12 | import java.security.interfaces.RSAPrivateKey; 13 | import java.security.interfaces.RSAPublicKey; 14 | import java.util.ArrayList; 15 | import java.util.Arrays; 16 | 17 | import javax.crypto.Cipher; 18 | 19 | import junit.framework.TestCase; 20 | 21 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 22 | import org.junit.Test; 23 | 24 | import circuit.auxiliary.LongElement; 25 | import circuit.eval.CircuitEvaluator; 26 | import circuit.structure.CircuitGenerator; 27 | import circuit.structure.Wire; 28 | import circuit.structure.WireArray; 29 | import examples.gadgets.rsa.RSAEncryptionOAEPGadget; 30 | import examples.generators.rsa.RSAUtil; 31 | 32 | public class RSAEncryptionOAEP_Test extends TestCase { 33 | 34 | @Test 35 | public void testEncryptionDifferentKeyLengths() throws Exception { 36 | 37 | String plainText = "abc"; 38 | 39 | // testing commonly used rsa key lengths 40 | 41 | // might need to increase memory heap to run this test on some platforms 42 | 43 | int[] keySizeArray = new int[] { 1024, 2048, 3072 }; 44 | 45 | for (int keySize : keySizeArray) { 46 | 47 | final byte[] cipherTextBytes = new byte[keySize / 8]; 48 | 49 | SecureRandom random = new SecureRandom(); 50 | KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); 51 | keyGen.initialize(keySize, random); 52 | KeyPair keyPair = keyGen.generateKeyPair(); 53 | Key pubKey = keyPair.getPublic(); 54 | BigInteger rsaModulusValue = ((RSAPublicKey) pubKey).getModulus(); 55 | 56 | CircuitGenerator generator = new CircuitGenerator("RSA" + keySize 57 | + "_OAEP_Enc_TestEncryption") { 58 | 59 | int rsaKeyLength = keySize; 60 | int plainTextLength = plainText.length(); 61 | Wire[] inputMessage; 62 | Wire[] seed; 63 | Wire[] cipherText; 64 | LongElement rsaModulus; 65 | 66 | RSAEncryptionOAEPGadget rsaEncryptionOAEPGadget; 67 | 68 | @Override 69 | protected void buildCircuit() { 70 | inputMessage = createProverWitnessWireArray(plainTextLength); // in bytes 71 | for(int i = 0; i < plainTextLength;i++){ 72 | inputMessage[i].restrictBitLength(8); 73 | } 74 | 75 | rsaModulus = createLongElementInput(rsaKeyLength); 76 | seed = createProverWitnessWireArray(RSAEncryptionOAEPGadget.SHA256_DIGEST_LENGTH); 77 | rsaEncryptionOAEPGadget = new RSAEncryptionOAEPGadget( 78 | rsaModulus, inputMessage, seed, rsaKeyLength); 79 | 80 | // since seed is a witness 81 | rsaEncryptionOAEPGadget.checkSeedCompliance(); 82 | 83 | Wire[] cipherTextInBytes = rsaEncryptionOAEPGadget 84 | .getOutputWires(); // in bytes 85 | 86 | // group every 8 bytes together 87 | cipherText = new WireArray(cipherTextInBytes) 88 | .packWordsIntoLargerWords(8, 8); 89 | makeOutputArray(cipherText, "Output cipher text"); 90 | } 91 | 92 | @Override 93 | public void generateSampleInput(CircuitEvaluator evaluator) { 94 | 95 | for (int i = 0; i < inputMessage.length; i++) { 96 | evaluator.setWireValue(inputMessage[i], 97 | plainText.charAt(i)); 98 | } 99 | try { 100 | 101 | Security.addProvider(new BouncyCastleProvider()); 102 | Cipher cipher = Cipher.getInstance( 103 | "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "BC"); 104 | 105 | evaluator 106 | .setWireValue(this.rsaModulus, rsaModulusValue, 107 | LongElement.CHUNK_BITWIDTH); 108 | 109 | Key privKey = keyPair.getPrivate(); 110 | 111 | cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); 112 | byte[] tmp = cipher.doFinal(plainText.getBytes()); 113 | System.arraycopy(tmp, 0, cipherTextBytes, 0, 114 | keySize / 8); 115 | 116 | byte[] cipherTextPadded = new byte[cipherTextBytes.length + 1]; 117 | System.arraycopy(cipherTextBytes, 0, cipherTextPadded, 118 | 1, cipherTextBytes.length); 119 | cipherTextPadded[0] = 0; 120 | 121 | byte[][] result = RSAUtil.extractRSAOAEPSeed( 122 | cipherTextBytes, (RSAPrivateKey) privKey); 123 | 124 | boolean check = Arrays.equals(result[0], 125 | plainText.getBytes()); 126 | if (!check) { 127 | throw new RuntimeException( 128 | "Randomness Extraction did not decrypt right"); 129 | } 130 | 131 | byte[] sampleRandomness = result[1]; 132 | for (int i = 0; i < sampleRandomness.length; i++) { 133 | evaluator.setWireValue(seed[i], 134 | (sampleRandomness[i] + 256) % 256); 135 | } 136 | 137 | } catch (Exception e) { 138 | System.err 139 | .println("Error while generating sample input for circuit"); 140 | e.printStackTrace(); 141 | } 142 | } 143 | }; 144 | 145 | generator.generateCircuit(); 146 | generator.evalCircuit(); 147 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 148 | 149 | // retrieve the ciphertext from the circuit, and verify that it 150 | // matches the expected ciphertext and that it decrypts correctly 151 | // (using the BouncyCastle RSA decryptor) 152 | ArrayList cipherTextList = generator.getOutWires(); 153 | BigInteger t = BigInteger.ZERO; 154 | int i = 0; 155 | for (Wire w : cipherTextList) { 156 | BigInteger val = evaluator.getWireValue(w); 157 | t = t.add(val.shiftLeft(i * 64)); 158 | i++; 159 | } 160 | 161 | // extract the bytes 162 | byte[] cipherTextBytesFromCircuit = t.toByteArray(); 163 | 164 | // ignore the sign byte if any was added 165 | if (t.bitLength() == keySize 166 | && cipherTextBytesFromCircuit.length == keySize / 8 + 1) { 167 | cipherTextBytesFromCircuit = Arrays.copyOfRange( 168 | cipherTextBytesFromCircuit, 1, 169 | cipherTextBytesFromCircuit.length); 170 | } 171 | 172 | for (int k = 0; k < cipherTextBytesFromCircuit.length; k++) { 173 | assertEquals(cipherTextBytes[k], cipherTextBytesFromCircuit[k]); 174 | 175 | } 176 | 177 | Cipher cipher = Cipher.getInstance( 178 | "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "BC"); 179 | cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); 180 | byte[] cipherTextDecrypted = cipher 181 | .doFinal(cipherTextBytesFromCircuit); 182 | assertTrue(Arrays.equals(plainText.getBytes(), cipherTextDecrypted)); 183 | } 184 | 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/examples/tests/rsa/RSAEncryption_Test.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package examples.tests.rsa; 5 | 6 | import java.math.BigInteger; 7 | import java.security.Key; 8 | import java.security.KeyPair; 9 | import java.security.KeyPairGenerator; 10 | import java.security.SecureRandom; 11 | import java.security.interfaces.RSAPrivateKey; 12 | import java.security.interfaces.RSAPublicKey; 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | 16 | import javax.crypto.Cipher; 17 | 18 | import junit.framework.TestCase; 19 | 20 | import org.junit.Test; 21 | 22 | import circuit.auxiliary.LongElement; 23 | import circuit.eval.CircuitEvaluator; 24 | import circuit.structure.CircuitGenerator; 25 | import circuit.structure.Wire; 26 | import circuit.structure.WireArray; 27 | import examples.gadgets.rsa.RSAEncryptionV1_5_Gadget; 28 | import examples.generators.rsa.RSAUtil; 29 | 30 | 31 | // Tests RSA PKCS #1, V1.5 32 | 33 | public class RSAEncryption_Test extends TestCase { 34 | 35 | 36 | @Test 37 | public void testEncryptionDifferentKeyLengths() throws Exception{ 38 | 39 | 40 | String plainText = "abc"; 41 | 42 | // testing commonly used rsa key lengths 43 | 44 | // might need to increase memory heap to run this test on some platforms 45 | 46 | int[] keySizeArray = new int[] { 1024, 2048, 3072, 4096}; 47 | 48 | for (int keySize : keySizeArray) { 49 | 50 | final byte[] cipherTextBytes = new byte[keySize/8]; 51 | SecureRandom random = new SecureRandom(); 52 | KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); 53 | keyGen.initialize(keySize, random); 54 | KeyPair keyPair = keyGen.generateKeyPair(); 55 | Key pubKey = keyPair.getPublic(); 56 | BigInteger rsaModulusValue = ((RSAPublicKey) pubKey).getModulus(); 57 | 58 | CircuitGenerator generator = new CircuitGenerator("RSA" + keySize 59 | + "_Enc_TestEncryption") { 60 | 61 | int rsaKeyLength = keySize; 62 | int plainTextLength = plainText.length(); 63 | Wire[] inputMessage; 64 | Wire[] randomness; 65 | Wire[] cipherText; 66 | LongElement rsaModulus; 67 | 68 | RSAEncryptionV1_5_Gadget rsaEncryptionV1_5_Gadget; 69 | 70 | @Override 71 | protected void buildCircuit() { 72 | inputMessage = createProverWitnessWireArray(plainTextLength); // in bytes 73 | for(int i = 0; i < plainTextLength;i++){ 74 | inputMessage[i].restrictBitLength(8); 75 | } 76 | 77 | rsaModulus = createLongElementInput(rsaKeyLength); 78 | randomness = createProverWitnessWireArray(RSAEncryptionV1_5_Gadget 79 | .getExpectedRandomnessLength(rsaKeyLength, plainTextLength)); 80 | rsaEncryptionV1_5_Gadget = new RSAEncryptionV1_5_Gadget(rsaModulus, inputMessage, 81 | randomness, rsaKeyLength); 82 | 83 | // since randomness is a witness 84 | rsaEncryptionV1_5_Gadget.checkRandomnessCompliance(); 85 | Wire[] cipherTextInBytes = rsaEncryptionV1_5_Gadget.getOutputWires(); // in bytes 86 | 87 | // group every 8 bytes together 88 | cipherText = new WireArray(cipherTextInBytes).packWordsIntoLargerWords(8, 8); 89 | makeOutputArray(cipherText, 90 | "Output cipher text"); 91 | } 92 | 93 | @Override 94 | public void generateSampleInput(CircuitEvaluator evaluator) { 95 | 96 | for (int i = 0; i < inputMessage.length; i++) { 97 | evaluator.setWireValue(inputMessage[i], 98 | plainText.charAt(i)); 99 | } 100 | try { 101 | Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 102 | evaluator.setWireValue(this.rsaModulus, rsaModulusValue, 103 | LongElement.CHUNK_BITWIDTH); 104 | 105 | Key privKey = keyPair.getPrivate(); 106 | 107 | cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); 108 | byte[] tmp = cipher.doFinal(plainText.getBytes()); 109 | System.arraycopy(tmp, 0, cipherTextBytes, 0, keySize/8); 110 | 111 | byte[] cipherTextPadded = new byte[cipherTextBytes.length + 1]; 112 | System.arraycopy(cipherTextBytes, 0, cipherTextPadded, 1, cipherTextBytes.length); 113 | cipherTextPadded[0] = 0; 114 | 115 | byte[][] result = RSAUtil.extractRSARandomness1_5(cipherTextBytes, 116 | (RSAPrivateKey) privKey); 117 | 118 | boolean check = Arrays.equals(result[0], plainText.getBytes()); 119 | if (!check) { 120 | throw new RuntimeException( 121 | "Randomness Extraction did not decrypt right"); 122 | } 123 | 124 | byte[] sampleRandomness = result[1]; 125 | for (int i = 0; i < sampleRandomness.length; i++) { 126 | evaluator.setWireValue(randomness[i], (sampleRandomness[i]+256)%256); 127 | } 128 | 129 | } catch (Exception e) { 130 | System.err 131 | .println("Error while generating sample input for circuit"); 132 | e.printStackTrace(); 133 | } 134 | } 135 | }; 136 | 137 | generator.generateCircuit(); 138 | generator.evalCircuit(); 139 | CircuitEvaluator evaluator = generator.getCircuitEvaluator(); 140 | 141 | // retrieve the ciphertext from the circuit, and verify that it matches the expected ciphertext and that it decrypts correctly (using the Java built-in RSA decryptor) 142 | ArrayList cipherTextList = generator.getOutWires(); 143 | BigInteger t = BigInteger.ZERO; 144 | int i = 0; 145 | for(Wire w:cipherTextList){ 146 | BigInteger val = evaluator.getWireValue(w); 147 | t = t.add(val.shiftLeft(i*64)); 148 | i++; 149 | } 150 | 151 | // extract the bytes 152 | byte[] cipherTextBytesFromCircuit = t.toByteArray(); 153 | 154 | // ignore the sign byte if any was added 155 | if(t.bitLength() == keySize && cipherTextBytesFromCircuit.length == keySize/8+1){ 156 | cipherTextBytesFromCircuit=Arrays.copyOfRange(cipherTextBytesFromCircuit, 1, cipherTextBytesFromCircuit.length); 157 | } 158 | 159 | for(int k = 0; k < cipherTextBytesFromCircuit.length;k++){ 160 | assertEquals(cipherTextBytes[k], cipherTextBytesFromCircuit[k]); 161 | 162 | } 163 | 164 | Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 165 | cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); 166 | byte[] cipherTextDecrypted = cipher.doFinal(cipherTextBytesFromCircuit); 167 | assertTrue(Arrays.equals(plainText.getBytes(), cipherTextDecrypted)); 168 | } 169 | 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/util/BigIntStorage.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.math.BigInteger; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.concurrent.ConcurrentMap; 6 | 7 | /** 8 | * shares big integer constants 9 | * 10 | */ 11 | public class BigIntStorage { 12 | 13 | private ConcurrentMap bigIntegerSet; 14 | private static BigIntStorage instance; 15 | 16 | private BigIntStorage(){ 17 | bigIntegerSet = new ConcurrentHashMap(); 18 | } 19 | 20 | public static BigIntStorage getInstance(){ 21 | if(instance == null){ 22 | instance = new BigIntStorage(); 23 | } 24 | return instance; 25 | } 26 | 27 | public BigInteger getBigInteger(BigInteger x){ 28 | bigIntegerSet.putIfAbsent(x, x); 29 | return bigIntegerSet.get(x); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /JsnarkCircuitBuilder/src/util/Util.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Author: Ahmed Kosba 3 | *******************************************************************************/ 4 | package util; 5 | 6 | import java.math.BigInteger; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Random; 10 | 11 | import circuit.structure.Wire; 12 | 13 | public class Util { 14 | 15 | // seeded by 1 for testing purposes 16 | static Random rand = new Random(1); 17 | 18 | public static BigInteger[] split(BigInteger x, int numchunks, int chunksize) { 19 | BigInteger[] chunks = new BigInteger[numchunks]; 20 | BigInteger mask = new BigInteger("2").pow(chunksize).subtract(BigInteger.ONE); 21 | for (int i = 0; i < numchunks; i++) { 22 | chunks[i] = x.shiftRight(chunksize * i).and(mask); 23 | } 24 | return chunks; 25 | } 26 | 27 | public static BigInteger combine(BigInteger[] table, Wire[] blocks, int bitwidth) { 28 | BigInteger sum = BigInteger.ZERO; 29 | for (int i = 0; i < blocks.length; i++) { 30 | if (table[blocks[i].getWireId()] == null) { 31 | continue; 32 | } 33 | sum = sum.add(table[blocks[i].getWireId()].multiply(new BigInteger("2").pow(bitwidth * i))); 34 | } 35 | return sum; 36 | } 37 | 38 | public static BigInteger group(BigInteger[] list, int width) { 39 | BigInteger x = BigInteger.ZERO; 40 | for (int i = 0; i < list.length; i++) { 41 | x = x.add(list[i].shiftLeft(width * i)); 42 | } 43 | return x; 44 | } 45 | 46 | public static int[] concat(int[] a1, int[] a2) { 47 | int[] all = new int[a1.length + a2.length]; 48 | for (int i = 0; i < all.length; i++) { 49 | all[i] = i < a1.length ? a1[i] : a2[i - a1.length]; 50 | } 51 | return all; 52 | } 53 | 54 | public static Wire[] concat(Wire[] a1, Wire[] a2) { 55 | Wire[] all = new Wire[a1.length + a2.length]; 56 | for (int i = 0; i < all.length; i++) { 57 | all[i] = i < a1.length ? a1[i] : a2[i - a1.length]; 58 | } 59 | return all; 60 | } 61 | 62 | public static Wire[] concat(Wire w, Wire[] a) { 63 | Wire[] all = new Wire[1 + a.length]; 64 | for (int i = 0; i < all.length; i++) { 65 | all[i] = i < 1 ? w : a[i - 1]; 66 | } 67 | return all; 68 | } 69 | 70 | public static int[] concat(int[][] arrays) { 71 | int sum = 0; 72 | for (int i = 0; i < arrays.length; i++) { 73 | sum += arrays[i].length; 74 | } 75 | int[] all = new int[sum]; 76 | int idx = 0; 77 | for (int i = 0; i < arrays.length; i++) { 78 | for (int j = 0; j < arrays[i].length; j++) { 79 | all[idx++] = arrays[i][j]; 80 | } 81 | } 82 | return all; 83 | } 84 | 85 | public static BigInteger[] randomBigIntegerArray(int num, BigInteger n) { 86 | 87 | BigInteger[] result = new BigInteger[num]; 88 | for (int i = 0; i < num; i++) { 89 | result[i] = nextRandomBigInteger(n); 90 | } 91 | return result; 92 | } 93 | 94 | public static BigInteger nextRandomBigInteger(BigInteger n) { 95 | 96 | BigInteger result = new BigInteger(n.bitLength(), rand); 97 | while (result.compareTo(n) >= 0) { 98 | result = new BigInteger(n.bitLength(), rand); 99 | } 100 | return result; 101 | } 102 | 103 | public static BigInteger[] randomBigIntegerArray(int num, int numBits) { 104 | 105 | BigInteger[] result = new BigInteger[num]; 106 | for (int i = 0; i < num; i++) { 107 | result[i] = nextRandomBigInteger(numBits); 108 | } 109 | return result; 110 | } 111 | 112 | public static BigInteger nextRandomBigInteger(int numBits) { 113 | 114 | BigInteger result = new BigInteger(numBits, rand); 115 | return result; 116 | } 117 | 118 | public static String getDesc(String... desc) { 119 | if (desc.length == 0) { 120 | return ""; 121 | } else { 122 | return desc[0]; 123 | } 124 | 125 | } 126 | 127 | public static ArrayList parseSequenceLists(String s) { 128 | 129 | ArrayList list = new ArrayList(); 130 | String[] chunks = s.split(","); 131 | for (String chunk : chunks) { 132 | if (chunk.equals("")) 133 | continue; 134 | int lower = Integer.parseInt(chunk.split(":")[0]); 135 | int upper = Integer.parseInt(chunk.split(":")[1]); 136 | for (int i = lower; i <= upper; i++) { 137 | list.add(i); 138 | } 139 | } 140 | return list; 141 | } 142 | 143 | public static Wire[] reverseBytes(Wire[] inBitWires) { 144 | Wire[] outs = Arrays.copyOf(inBitWires, inBitWires.length); 145 | int numBytes = inBitWires.length / 8; 146 | for (int i = 0; i < numBytes / 2; i++) { 147 | int other = numBytes - i - 1; 148 | for (int j = 0; j < 8; j++) { 149 | Wire temp = outs[i * 8 + j]; 150 | outs[i * 8 + j] = outs[other * 8 + j]; 151 | outs[other * 8 + j] = temp; 152 | } 153 | } 154 | return outs; 155 | } 156 | 157 | public static String arrayToString(int[] a, String separator) { 158 | StringBuilder s = new StringBuilder(); 159 | for (int i = 0; i < a.length - 1; i++) { 160 | s.append(a[i] + separator); 161 | } 162 | s.append(a[a.length - 1]); 163 | return s.toString(); 164 | } 165 | 166 | public static String arrayToString(Wire[] a, String separator) { 167 | StringBuilder s = new StringBuilder(); 168 | for (int i = 0; i < a.length - 1; i++) { 169 | s.append(a[i] + separator); 170 | } 171 | s.append(a[a.length - 1]); 172 | return s.toString(); 173 | } 174 | 175 | public static boolean isBinary(BigInteger v) { 176 | return v.equals(BigInteger.ZERO) || v.equals(BigInteger.ONE); 177 | } 178 | 179 | public static String padZeros(String s, int l) { 180 | return String.format("%" + l + "s",s).replace(' ', '0'); 181 | } 182 | 183 | public static BigInteger computeMaxValue(int numBits){ 184 | return BigIntStorage.getInstance().getBigInteger( 185 | new BigInteger("2").pow(numBits).subtract( 186 | BigInteger.ONE)); 187 | } 188 | 189 | public static BigInteger computeBound(int numBits){ 190 | return BigIntStorage.getInstance().getBigInteger( 191 | new BigInteger("2").pow(numBits)); 192 | } 193 | 194 | public static BigInteger[] split(BigInteger x, int chunksize) { 195 | int numChunks = (int)Math.ceil(x.bitLength()*1.0/chunksize); 196 | BigInteger[] chunks = new BigInteger[numChunks]; 197 | BigInteger mask = new BigInteger("2").pow(chunksize).subtract(BigInteger.ONE); 198 | for (int i = 0; i < numChunks; i++) { 199 | chunks[i] = x.shiftRight(chunksize * i).and(mask); 200 | } 201 | return chunks; 202 | } 203 | 204 | public static Wire[] padWireArray(Wire[] a, int length, Wire p) { 205 | if (a.length == length) { 206 | return a; 207 | } else if (a.length > length) { 208 | System.err.println("No padding needed!"); 209 | return a; 210 | } else { 211 | Wire[] newArray = new Wire[length]; 212 | System.arraycopy(a, 0, newArray, 0, a.length); 213 | for (int k = a.length; k < length; k++) { 214 | newArray[k] = p; 215 | } 216 | return newArray; 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015 Ahmed Kosba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | --------------------------------------------------------------------------------