Files
circomlib-ml/test/modules/maci-domainobjs.js
2022-11-25 20:52:22 +08:00

297 lines
11 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrivKey = exports.PubKey = exports.Keypair = exports.Message = exports.Command = exports.StateLeaf = void 0;
const assert = require('assert');
const maci_crypto_1 = require("./maci-crypto");
const SERIALIZED_PRIV_KEY_PREFIX = 'macisk.';
class PrivKey {
constructor(rawPrivKey) {
this.copy = () => {
return new PrivKey(BigInt(this.rawPrivKey.toString()));
};
this.asCircuitInputs = () => {
return (0, maci_crypto_1.formatPrivKeyForBabyJub)(this.rawPrivKey).toString();
};
this.serialize = () => {
return SERIALIZED_PRIV_KEY_PREFIX + this.rawPrivKey.toString(16);
};
this.rawPrivKey = rawPrivKey;
}
}
exports.PrivKey = PrivKey;
PrivKey.unserialize = (s) => {
const x = s.slice(SERIALIZED_PRIV_KEY_PREFIX.length);
return new PrivKey(BigInt('0x' + x));
};
PrivKey.isValidSerializedPrivKey = (s) => {
const correctPrefix = s.startsWith(SERIALIZED_PRIV_KEY_PREFIX);
const x = s.slice(SERIALIZED_PRIV_KEY_PREFIX.length);
let validValue = false;
try {
const value = BigInt('0x' + x);
validValue = value < maci_crypto_1.SNARK_FIELD_SIZE;
}
catch {
// comment to make linter happy
}
return correctPrefix && validValue;
};
const SERIALIZED_PUB_KEY_PREFIX = 'macipk.';
class PubKey {
constructor(rawPubKey) {
this.copy = () => {
return new PubKey([
BigInt(this.rawPubKey[0].toString()),
BigInt(this.rawPubKey[1].toString()),
]);
};
this.asContractParam = () => {
return {
x: this.rawPubKey[0].toString(),
y: this.rawPubKey[1].toString(),
};
};
this.asCircuitInputs = () => {
return this.rawPubKey.map((x) => x.toString());
};
this.asArray = () => {
return [
this.rawPubKey[0],
this.rawPubKey[1],
];
};
this.serialize = () => {
// Blank leaves have pubkey [0, 0], which packPubKey does not support
if (BigInt(this.rawPubKey[0]) === BigInt(0) &&
BigInt(this.rawPubKey[1]) === BigInt(0)) {
return SERIALIZED_PUB_KEY_PREFIX + 'z';
}
const packed = (0, maci_crypto_1.packPubKey)(this.rawPubKey).toString('hex');
return SERIALIZED_PUB_KEY_PREFIX + packed.toString();
};
assert(rawPubKey.length === 2);
assert(rawPubKey[0] < maci_crypto_1.SNARK_FIELD_SIZE);
assert(rawPubKey[1] < maci_crypto_1.SNARK_FIELD_SIZE);
this.rawPubKey = rawPubKey;
}
}
exports.PubKey = PubKey;
PubKey.unserialize = (s) => {
// Blank leaves have pubkey [0, 0], which packPubKey does not support
if (s === SERIALIZED_PUB_KEY_PREFIX + 'z') {
return new PubKey([BigInt(0), BigInt(0)]);
}
const len = SERIALIZED_PUB_KEY_PREFIX.length;
const packed = Buffer.from(s.slice(len), 'hex');
return new PubKey((0, maci_crypto_1.unpackPubKey)(packed));
};
PubKey.isValidSerializedPubKey = (s) => {
const correctPrefix = s.startsWith(SERIALIZED_PUB_KEY_PREFIX);
let validValue = false;
try {
PubKey.unserialize(s);
validValue = true;
}
catch {
// comment to make linter happy
}
return correctPrefix && validValue;
};
class Keypair {
constructor(privKey) {
this.copy = () => {
return new Keypair(this.privKey.copy());
};
if (privKey) {
this.privKey = privKey;
this.pubKey = new PubKey((0, maci_crypto_1.genPubKey)(privKey.rawPrivKey));
}
else {
const rawKeyPair = (0, maci_crypto_1.genKeypair)();
this.privKey = new PrivKey(rawKeyPair.privKey);
this.pubKey = new PubKey(rawKeyPair.pubKey);
}
}
static genEcdhSharedKey(privKey, pubKey) {
return (0, maci_crypto_1.genEcdhSharedKey)(privKey.rawPrivKey, pubKey.rawPubKey);
}
equals(keypair) {
const equalPrivKey = this.privKey.rawPrivKey === keypair.privKey.rawPrivKey;
const equalPubKey = this.pubKey.rawPubKey[0] === keypair.pubKey.rawPubKey[0] &&
this.pubKey.rawPubKey[1] === keypair.pubKey.rawPubKey[1];
// If this assertion fails, something is very wrong and this function
// should not return anything
// XOR is equivalent to: (x && !y) || (!x && y )
const x = (equalPrivKey && equalPubKey);
const y = (!equalPrivKey && !equalPubKey);
assert((x && !y) || (!x && y));
return equalPrivKey;
}
}
exports.Keypair = Keypair;
/*
* An encrypted command and signature.
*/
class Message {
constructor(iv, data) {
this.asArray = () => {
return [
this.iv,
...this.data,
];
};
this.asContractParam = () => {
return {
iv: this.iv.toString(),
data: this.data.map((x) => x.toString()),
};
};
this.asCircuitInputs = () => {
return this.asArray();
};
this.hash = () => {
return (0, maci_crypto_1.hash11)(this.asArray());
};
this.copy = () => {
return new Message(BigInt(this.iv.toString()), this.data.map((x) => BigInt(x.toString())));
};
assert(data.length === 10);
this.iv = iv;
this.data = data;
}
}
exports.Message = Message;
/*
* A leaf in the state tree, which maps public keys to votes
*/
class StateLeaf {
constructor(pubKey, voteOptionTreeRoot, voiceCreditBalance, nonce) {
this.asArray = () => {
return [
...this.pubKey.asArray(),
this.voteOptionTreeRoot,
this.voiceCreditBalance,
this.nonce,
];
};
this.asCircuitInputs = () => {
return this.asArray();
};
this.hash = () => {
return (0, maci_crypto_1.hash5)(this.asArray());
};
this.serialize = () => {
const j = {
pubKey: this.pubKey.serialize(),
voteOptionTreeRoot: this.voteOptionTreeRoot.toString(16),
voiceCreditBalance: this.voiceCreditBalance.toString(16),
nonce: this.nonce.toString(16),
};
return Buffer.from(JSON.stringify(j, null, 0), 'utf8').toString('base64');
};
this.pubKey = pubKey;
this.voteOptionTreeRoot = voteOptionTreeRoot;
this.voiceCreditBalance = voiceCreditBalance;
// The this is the current nonce. i.e. a user who has published 0 valid
// command should have this value at 0, and the first command should
// have a nonce of 1
this.nonce = nonce;
}
copy() {
return new StateLeaf(this.pubKey.copy(), BigInt(this.voteOptionTreeRoot.toString()), BigInt(this.voiceCreditBalance.toString()), BigInt(this.nonce.toString()));
}
static genBlankLeaf(emptyVoteOptionTreeRoot) {
return new StateLeaf(new PubKey(maci_crypto_1.NOTHING_UP_MY_SLEEVE_PUBKEY), emptyVoteOptionTreeRoot, BigInt(0), BigInt(0));
}
static genRandomLeaf() {
return new StateLeaf(new PubKey(maci_crypto_1.NOTHING_UP_MY_SLEEVE_PUBKEY), (0, maci_crypto_1.genRandomSalt)(), (0, maci_crypto_1.genRandomSalt)(), (0, maci_crypto_1.genRandomSalt)());
}
}
exports.StateLeaf = StateLeaf;
StateLeaf.unserialize = (serialized) => {
const j = JSON.parse(Buffer.from(serialized, 'base64').toString('utf8'));
return new StateLeaf(PubKey.unserialize(j.pubKey), BigInt('0x' + j.voteOptionTreeRoot), BigInt('0x' + j.voiceCreditBalance), BigInt('0x' + j.nonce));
};
/*
* Unencrypted data whose fields include the user's public key, vote etc.
*/
class Command {
constructor(stateIndex, newPubKey, voteOptionIndex, newVoteWeight, nonce, salt = (0, maci_crypto_1.genRandomSalt)()) {
this.copy = () => {
return new Command(BigInt(this.stateIndex.toString()), this.newPubKey.copy(), BigInt(this.voteOptionIndex.toString()), BigInt(this.newVoteWeight.toString()), BigInt(this.nonce.toString()), BigInt(this.salt.toString()));
};
this.asArray = () => {
return [
this.stateIndex,
...this.newPubKey.asArray(),
this.voteOptionIndex,
this.newVoteWeight,
this.nonce,
this.salt,
];
};
/*
* Check whether this command has deep equivalence to another command
*/
this.equals = (command) => {
return this.stateIndex == command.stateIndex &&
this.newPubKey[0] == command.newPubKey[0] &&
this.newPubKey[1] == command.newPubKey[1] &&
this.voteOptionIndex == command.voteOptionIndex &&
this.newVoteWeight == command.newVoteWeight &&
this.nonce == command.nonce &&
this.salt == command.salt;
};
this.hash = () => {
return (0, maci_crypto_1.hash11)(this.asArray());
};
/*
* Signs this command and returns a Signature.
*/
this.sign = (privKey) => {
return (0, maci_crypto_1.sign)(privKey.rawPrivKey, this.hash());
};
/*
* Returns true if the given signature is a correct signature of this
* command and signed by the private key associated with the given public
* key.
*/
this.verifySignature = (signature, pubKey) => {
return (0, maci_crypto_1.verifySignature)(this.hash(), signature, pubKey.rawPubKey);
};
/*
* Encrypts this command along with a signature to produce a Message.
*/
this.encrypt = (signature, sharedKey) => {
const plaintext = [
...this.asArray(),
signature.R8[0],
signature.R8[1],
signature.S,
];
const ciphertext = (0, maci_crypto_1.encrypt)(plaintext, sharedKey);
const message = new Message(ciphertext.iv, ciphertext.data);
return message;
};
this.stateIndex = stateIndex;
this.newPubKey = newPubKey;
this.voteOptionIndex = voteOptionIndex;
this.newVoteWeight = newVoteWeight;
this.nonce = nonce;
this.salt = salt;
}
}
exports.Command = Command;
/*
* Decrypts a Message to produce a Command.
*/
Command.decrypt = (message, sharedKey) => {
const decrypted = (0, maci_crypto_1.decrypt)(message, sharedKey);
const command = new Command(decrypted[0], new PubKey([decrypted[1], decrypted[2]]), decrypted[3], decrypted[4], decrypted[5], decrypted[6]);
const signature = {
R8: [decrypted[7], decrypted[8]],
S: decrypted[9],
};
return { command, signature };
};