FEMessage* FEInitiator::barter(const vector<EncBuffer*>& ctextR, const vector<hash_t>& ptHashR, const vector<hash_t>& ptHashI) { if (ctextR.empty()) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::barter] No responder ciphertext given"); if (ptHashR.empty()) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::barter] No initiator plaintext hash given"); ctextB = ctextR; // create contract createContract(); // compute hashes hash_t ptHashMerkleI = Hash::hash(ptHashI, verifiablePK->hashAlg, verifiablePK->hashKey, Hash::TYPE_MERKLE); hash_t ptHashMerkleR = Hash::hash(ptHashR, verifiablePK->hashAlg, verifiablePK->hashKey, Hash::TYPE_MERKLE); hash_t ctHashMerkleI = Hash::hash(ctextA, verifiablePK->hashAlg, verifiablePK->hashKey, Hash::TYPE_MERKLE); hash_t ctHashMerkleR = Hash::hash(ctextB, verifiablePK->hashAlg, verifiablePK->hashKey, Hash::TYPE_MERKLE); // set the contract contract->setPTHashA(ptHashMerkleI); contract->setCTHashA(ctHashMerkleI); contract->setPTHashB(ptHashMerkleR); contract->setCTHashB(ctHashMerkleR); contract->setEncAlgA(ctextA[0]->encAlg); contract->setEncAlgB(ctextB[0]->encAlg); contract->setPTHashBlocksB(ptHashR.size()); contract->setCTHashBlocksB(ctextR.size()); // optimization: if all ciphertexts have the same key, just output one key // shortcut: if two ciphertexts have the same key, assume all have // the same key vector<ZZ> keys; if (ctextA.size() > 1 && ctextA[0]->key == ctextA[1]->key) keys.push_back(ZZFromBytes(ctextA[0]->key)); else { for (unsigned i = 0; i < ctextA.size(); i++) { keys.push_back(ZZFromBytes(ctextA[i]->key)); } } // now set up signature and escrow VEProver prover(regularPK); // label is the multicontract string label = saveString(*contract); vector<ZZ> escrow = prover.encrypt(keys, label, regularPK->hashAlg, stat); // need to sign on the escrow using our signature key string escrowStr = CommonFunctions::vecToString(escrow); /* TODO: When we use RSA enc as escrow, we should also sign the contract */ string sig = Signature::sign(*signKey, escrowStr, regularPK->hashAlg); // now output the escrow, signature, and contract (label) return new FEMessage(escrow, sig, *contract); }
bool Buyer::checkKey(const vector<string>& keys) { if (!inProgress) throw CashException(CashException::CE_FE_ERROR, "[Buyer::pay] Pay called on a buyer not working"); if (keys.empty()) throw CashException(CashException::CE_FE_ERROR, "[Buyer::pay] No key given"); for (unsigned i = 0; i < ctext.size(); i++) { // decrypt the ciphertext using key unsigned index = (keys.size() == 1) ? 0 : i; Buffer* plaintext = ctext[i]->decrypt(keys[index], contract->getEncAlgB()); ptext.push_back(plaintext); } // compute hashes hash_t ptHash = Hash::hash(ptext, contract->getPTHashB().alg, contract->getPTHashB().key, contract->getPTHashB().type); if (ptHash != contract->getPTHashB()) throw CashException(CashException::CE_FE_ERROR, "[Buyer::pay] The decrypted file was not the promised file"); return true; }
ProofMessage* CLBlindIssuer::getPartialSignature(const ZZ &C, const vector<ZZ>& publics, const ProofMessage &pm, int stat, const hashalg_t &hashAlg) { // at this point, verifier will know which program it is working with variable_map proverPublics = pm.proof.getCommitments(); variable_map pv = pm.publics; verifier.compute(v, proverPublics, pv); SigmaProof proof = pm.proof; startTimer(); bool verified = verifier.verify(proof, stat); printTimer("[CLBlindIssuer] verified recipient proof"); if(verified){ // XXX: should we be checking that public values are in the // proper range? same goes for recipient side of things too... if((int)publics.size() != numPublics) throw CashException(CashException::CE_SIZE_ERROR, "[CLBlindIssuer::getPartialSignature] Number of public inputs " "does not match the number the issuer was constructed with"); startTimer(); InterpreterProver prover; prover.check("ZKP/examples/cl-issue.txt", inputs, g); // want to keep the same inputs and groups, but need a new variable // map for doing issue program ZZ lx = v.at("l_x"); v.clear(); const GroupRSA* grp = (GroupRSA*) g.at("pkGroup"); v["l_x"] = lx; v["stat"] = grp->getStat(); v["modSize"] = grp->getModulusLength(); v["C"] = C; for(unsigned i = 0; i < publics.size(); i++) v["x_"+lexical_cast<string>(i+numPrivates+1)] = publics[i]; prover.compute(v); // set up partial signature to include in message for recipient variable_map partialSig; variable_map pVars = prover.getEnvironment().variables; partialSig["A"] = pVars.at("A"); partialSig["e"] = pVars.at("e"); partialSig["vdoubleprime"] = pVars.at("vdoubleprime"); variable_map p = prover.getPublicVariables(); SigmaProof pr = prover.computeProof(hashAlg); printTimer("[CLBlindIssuer] computed issuer proof"); return new ProofMessage(partialSig, p, pr); } else { throw CashException(CashException::CE_PARSE_ERROR, "[CLBlindIssuer::getPartialSignature] Proof did not verify"); } }
vector<ZZ> FEInitiator::pay(const vector<string>& keys) { if (TYPE_BUY != exchangeType) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::pay] Pay called on an FEInitiator not buying"); if (keys.empty()) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::pay] No key given"); decryptCheck(keys); return endorsement; }
ProofMessage* CLBlindRecipient::getC(const vector<SecretValue>& privates, const hashalg_t &hashAlg) { if((int)privates.size() != numPrivates) throw CashException(CashException::CE_SIZE_ERROR, "[CLBlindRecipient::getC] Number of private inputs does not match " "the number the recipient was constructed with"); // add the private messages to the variable map, as well as the // randomness used to form their commitments for(unsigned i = 0; i < privates.size(); i++){ string index = lexical_cast<string>(i+1); v["x_"+index] = privates[i].first; v["r_"+index] = privates[i].second; } // XXX: what about public messages? startTimer(); prover.compute(v); variable_map publics = prover.getPublicVariables(); SigmaProof proof = prover.computeProof(hashAlg); printTimer("[CLBlindRecipient] created proof"); variable_map vals; variable_map pVars = prover.getEnvironment().variables; vals["C"] = pVars.at("C"); vals["vprime"] = pVars.at("vprime"); return new ProofMessage(vals, publics, proof); }
string ProgramMaker::makeCLObtain(const string &grpPart, const string &comPart, const string &comRelPart) { string program = "computation: " "given: " + grpPart + " " "integers: x[1:l], stat, modSize " "compute: " "random integer in [0,2^(modSize+stat)): vprime " "C := h^vprime * for(i, 1:l, *, g_i^x_i) " "proof: " "given:" + grpPart + " " "element in pkGroup: C " + comPart + " " "integer: l_x " "prove knowledge of: " "integers: x[1:l], r[1:l] " "exponent in pkGroup: vprime " "such that: " "for(i, 1:l, range: (-(2^l_x-1)) <= x_i < 2^l_x) " "C = h^vprime * for(i, 1:l, *, g_i^x_i) "; program += comRelPart; // now write this to file and return the file name ofstream writer; string fname = "ZKP/examples/cl-obtain-temp.txt"; writer.open(fname.c_str(), ios::out); if (!writer) throw CashException(CashException::CE_PARSE_ERROR, "[ProgramMaker::makeCLObtain]: Could not write program to file"); writer << program; writer.close(); return fname; }
int Wallet::nextCoinIndex() { if(numCoinsUsed >= walletSize) { throw CashException(CashException::CE_UNKNOWN_ERROR, "Tried to get the next coin from a wallet from which every " "coin had already been spent"); } return spendOrder[numCoinsUsed++]; }
hashalg_t Hash::get_algbyname(const string &name) { for (int i=0; i < MAXALG; i++) { if (name == alg_names[i]) return (hashalg_t)i; } throw CashException(CashException::CE_UNKNOWN_ERROR, "[Hash::get_algbyname] Can't find digest for %s", name.c_str()); }
BuyMessage* Buyer::buy(const vector<EncBuffer*>& ct, const vector<hash_t>& ptHash) { if (inProgress) throw CashException(CashException::CE_FE_ERROR, "[Buyer::buy] Buy called on an already working buyer"); if (ct.empty()) throw CashException(CashException::CE_FE_ERROR, "[Buyer::buy] No ciphertext given"); if (ptHash.empty()) throw CashException(CashException::CE_FE_ERROR, "[Buyer::buy] No plaintext hash given"); // store ciphertexts ctext = ct; // compute hashes // XXX temporary fix: use regular hashes (size-1 hashes aren't matching) startTimer(); hash_t ptHashMerkle = Hash::hash(ptHash, pk->hashAlg, pk->hashKey, Hash::TYPE_MERKLE); hash_t ctHashMerkle = Hash::hash(ctext, pk->hashAlg, pk->hashKey, Hash::TYPE_MERKLE); // create contract createContract(); // set up the contract contract->setPTHashB(ptHashMerkle); contract->setCTHashB(ctHashMerkle); contract->setEncAlgB(ctext[0]->encAlg); contract->setPTHashBlocksB(ptHash.size()); contract->setCTHashBlocksB(ctext.size()); printTimer("[Buyer::buy] created contract"); startTimer(); // set up the escrow VECiphertext* escrow = new VECiphertext(makeEscrow()); printTimer("[Buyer::buy] created escrow"); // set inProgress inProgress = true; return new BuyMessage(coin, contract, escrow); }
bool FEReceiver::checkSignature(string sig, string label){ //The function verify is given by the cashlib library if (Signature::verify(*initiatorSignPK, sig, label, regularPK->hashAlg)){ return true; } else{ throw CashException(CashException::CE_FE_ERROR, "The signature does not verify"); } return false; }
FEMessage* FEInitiator::buy(const vector</*const*/ EncBuffer*>& ctextR, const vector</*const*/ hash_t>& ptHashR) { if (TYPE_NONE != exchangeType) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::buy] Buy called on an already working FEInitiator"); if (ctextR.empty()) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::buy] No ciphertext given"); if (ptHashR.empty()) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::buy] No plaintext hash given"); // now decide that we are running a buy protocol exchangeType = TYPE_BUY; // store ciphertexts ctextB = ctextR; // compute hashes hash_t ptHashMerkle = Hash::hash(ptHashR, verifiablePK->hashAlg, verifiablePK->hashKey, Hash::TYPE_MERKLE); hash_t ctHashMerkle = Hash::hash(ctextB, verifiablePK->hashAlg, verifiablePK->hashKey, Hash::TYPE_MERKLE); // create contract if (NULL == contract) createContract(); // set up the contract contract->setPTHashB(ptHashMerkle); contract->setCTHashB(ctHashMerkle); contract->setEncAlgB(ctextB[0]->encAlg); contract->setPTHashBlocksB(ptHashR.size()); contract->setCTHashBlocksB(ctextR.size()); string signature = signContract(); return new FEMessage(signature, *contract); }
long long elapsedTime() { timeval now; gettimeofday(&now, NULL); if (brownieTimerValue.size() == 0) throw CashException(CashException::CE_TIMER_ERROR, "[Timer::printTimer] No running timer found. " "Maybe you forgot to start one?"); long long elapsed = TV_DIFF_US(brownieTimerValue.back(), now); brownieTimerValue.pop_back(); return elapsed; }
vector<EncBuffer*> FEInitiator::continueRound(const vector<const Buffer*>& ptextI, const cipher_t& encAlgI) { if (TYPE_NONE != exchangeType) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::continueRound] Barter called on an " "already working FEInitiator"); setInitiatorFiles(ptextI, encrypt(ptextI,encAlgI)); // decide we are running barter exchangeType = TYPE_BARTER; return ctextA; }
vector<EncBuffer*> FEInitiator::encrypt(const vector<const Buffer*>& ptextI, const cipher_t& encAlgI) const { if (ptextI.empty()) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::encrypt] No initiator plaintext given"); string key = Ciphertext::generateKey(encAlgI); vector<EncBuffer*> ctexts; for (unsigned i = 0; i < ptextI.size(); i++) { ctexts.push_back(ptextI[i]->encrypt(encAlgI, key)); } return ctexts; }
void UndefinedVariables::apply(ASTNodePtr n){ // need to do two passes over the tree because things can be declared // in 'prove knowledge of' block and used in 'given' block and vice // versa n->visit(*this); n->visit(*this); for (def_map::iterator it = definedVars.begin(); it != definedVars.end(); ++it) { if (!it->second) throw CashException(CashException::CE_PARSE_ERROR, "%s has not been defined", it->first.c_str()); } }
bool BankTool::isCoinDoubleSpent(const Coin &coin1, const Coin &coin2) const { // throw an exception if these coins do not share the same S value if(coin1.getSPrime() != coin2.getSPrime()) { throw CashException(CashException::CE_UNKNOWN_ERROR, "[BankTool::checkIfCoinDoubleSpent] Coins do not share same serial " "coin point and are not valid candidates for checking double " "spending."); } // if contract hashes are same, then both coins were spent in the same // transaction so the coin was not double spent return (coin1.getR() != coin2.getR()); }
hash_t Hash::hash(const char* data, size_t len, const alg_t alg, const string &key, const int hashType) { if (hashType == Hash::TYPE_PLAIN) { return hash(data, len, alg, key); } else if (hashType == Hash::TYPE_MERKLE) { MerkleContract contract(key, alg); MerkleTree tree(data, len, contract); return tree.getRoot(); } else { throw CashException(CashException::CE_HASH_ERROR, "[Hash::hash] Unknown hash type used"); } }
vector<string> FEInitiator::giveKeys(const vector<string>& keysR) { // first check that we really are in barter if (TYPE_BARTER != exchangeType) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::giveKeys] GiveKeys called on an " "FEInitiator not bartering"); if (keysR.empty()) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::giveKeys] No key given"); decryptCheck(keysR); vector<string> keys; if (ctextA.size() > 1 && ctextA[0]->key == ctextA[1]->key) keys.push_back(ctextA[0]->key); else { for (unsigned i = 0; i < ctextA.size(); i++) { keys.push_back(ctextA[i]->key); } } return keys; }
VarInfo ASTBinaryOp::getExprType(Environment &env) { VarInfo leftInfo; VarInfo rightInfo; // don't recompute types if we don't have to if (env.exprTypes.count(lhs->toString()) == 0) leftInfo = lhs->getExprType(env); else leftInfo = env.exprTypes.at(lhs->toString()); if (env.exprTypes.count(rhs->toString()) == 0) rightInfo = rhs->getExprType(env); else rightInfo = env.exprTypes.at(rhs->toString()); Type left = leftInfo.type; Type right = rightInfo.type; string lGrp = leftInfo.group; string rGrp = rightInfo.group; // integers can be mixed with anything without changing type // so can moduli (which are just integers) if (left == VarInfo::INTEGER || left == VarInfo::MODULUS) { VarInfo info(rGrp, right); env.exprTypes[toString()] = info; return info; } else if (right == VarInfo::INTEGER || right == VarInfo::MODULUS) { VarInfo info(lGrp, left); env.exprTypes[toString()] = info; return info; } // exponents can be mixed in the same group else if (left == right && lGrp == rGrp && left == VarInfo::EXPONENT) { VarInfo info(lGrp, left); env.exprTypes[toString()] = info; return info; } // elements in the same group are okay else if (left == right && lGrp == rGrp && left == VarInfo::ELEMENT) { VarInfo info(lGrp, VarInfo::ELEMENT); env.exprTypes[toString()] = info; return info; } else { throw CashException(CashException::CE_PARSE_ERROR, "Cannot mix different types of variables"); } }
bool FEInitiator::decryptCheck(const vector<string>& keysR) { for (unsigned i = 0; i < ctextB.size(); i++) { // decrypt the ciphertext using key unsigned index = (keysR.size() == 1) ? 0 : i; Buffer* ptext = ctextB[i]->decrypt(keysR[index], contract->getEncAlgB()); ptextB.push_back(ptext); } // compute hashes const hash_t& pt = contract->getPTHashB(); hash_t ptHash = Hash::hash(ptextB, pt.alg, pt.key, pt.type); if (ptHash != pt) throw CashException(CashException::CE_FE_ERROR, "[FEInitiator::decryptCheck] The decrypted file was " "not the promised file"); return true; }
vector<ZZ> CommonFunctions::subvector(const vector<ZZ> &vec, unsigned start, unsigned length) { if(length < 1) { vector<ZZ> blank; return blank; } if(start >= vec.size() || start+length-1 >= vec.size()) { // the 1st check is actually redundant throw CashException(CashException::CE_SIZE_ERROR, "[CommonFunctions::subvector] Attempt to access elements " "beyond the end of the input vector"); } vector<ZZ> result; for(unsigned i = 0; i < length; i++) { result.push_back(vec[start+i]); } return result; }
VarInfo ASTPow::getExprType(Environment &env) { // can raise an element to an exponent, an integer to an exponent, // and an element to an integer, and an integer to an integer // also for square stuff can raise an exponent to an integer pair<VarInfo,VarInfo> p = getTypes(env); VarInfo leftInfo = p.first; VarInfo rightInfo = p.second; Type left = leftInfo.type; Type right = rightInfo.type; string lGrp = leftInfo.group; string rGrp = rightInfo.group; if ((left == VarInfo::ELEMENT && right == VarInfo::EXPONENT) || (left == VarInfo::ELEMENT && right == VarInfo::MODULUS)){ VarInfo info(lGrp, left); env.exprTypes[toString()] = info; return info; } else if (left == VarInfo::EXPONENT && right == VarInfo::INTEGER) { VarInfo info(lGrp, left); env.exprTypes[toString()] = info; return info; } else if (left == VarInfo::ELEMENT && right == VarInfo::INTEGER) { VarInfo info(lGrp, left); env.exprTypes[toString()] = info; return info; } else if (left == VarInfo::INTEGER && right == VarInfo::EXPONENT) { VarInfo info(rGrp, VarInfo::ELEMENT); env.exprTypes[toString()] = info; return info; } else if (left == right && left == VarInfo::INTEGER) { VarInfo info(lGrp, left); env.exprTypes[toString()] = info; return info; } else { throw CashException(CashException::CE_PARSE_ERROR, "Cannot perform exponentiation with those variables"); } }
hash_t Hash::hmac(const char* data, size_t len, const alg_t alg, const string& key) { const EVP_MD *m; char digest[EVP_MAX_MD_SIZE]; unsigned int dlen; m = get_MD(alg); if(!m) throw CashException(CashException::CE_UNKNOWN_ERROR, "[Hash::hmac] Can't find digest %s", alg); HMAC(m, key.data(), key.length(), (unsigned char *)data, len, (unsigned char *)digest, &dlen); // return C++ string container of digest hash_t ret(digest, dlen, alg, TYPE_PLAIN, key); return ret; }
ZZ ASTDiv::eval(Environment &env) { pair<VarInfo,VarInfo> p = getTypes(env); VarInfo leftInfo = p.first; VarInfo rightInfo = p.second; // XXX: this will cause a problem if we ever try to use // it during constant propagation/substitution const Group* lGroup = env.groups.at(leftInfo.group); const Group* rGroup = env.groups.at(rightInfo.group); const Group* retGroup = (lGroup != 0 ? lGroup : rGroup); // integers can just be divided if ((lGroup == 0 && rGroup == 0) || (leftInfo.type == VarInfo::MODULUS || rightInfo.type == VarInfo::MODULUS)) { return lhs->eval(env) / rhs->eval(env); } else if (leftInfo.type == VarInfo::EXPONENT || rightInfo.type == VarInfo::EXPONENT) { // want to get LHS * inv(RHS) // this only works if we are in a group with known order if (retGroup->getType() == Group::TYPE_PRIME) { ZZ ord = retGroup->getOrder(); ZZ right = InvMod(rhs->eval(env), ord); return lhs->eval(env) * right; // in an RSA group, only permit computing 1/x if the caller // knows the order of the group } else if(retGroup->getType() == Group::TYPE_RSA && (retGroup->getOrder() != 0)) { ZZ ord = retGroup->getOrder(); ZZ right = InvMod(rhs->eval(env), ord); return lhs->eval(env) * right; } else { throw CashException(CashException::CE_PARSE_ERROR, "That operation is not permitted in an RSA group"); } } else { assert(retGroup); ZZ mod = retGroup->getModulus(); return MulMod(lhs->eval(env), InvMod(rhs->eval(env), mod), mod); } }
hash_t Hash::hash(const char *buf, int len, const alg_t alg) { if (_brownie_hash_ctx.get() == 0) _brownie_hash_ctx.reset(EVP_MD_CTX_create()); EVP_MD_CTX *ctx = _brownie_hash_ctx.get(); const EVP_MD *m; char digest[EVP_MAX_MD_SIZE]; unsigned int dlen; m = get_MD(alg); if(!m) throw CashException(CashException::CE_UNKNOWN_ERROR, "[Hash::hash] Can't find digest %s", alg); EVP_DigestInit_ex(ctx, m, NULL); EVP_DigestUpdate(ctx, buf, len); EVP_DigestFinal_ex(ctx, (unsigned char *)digest, &dlen); // return HashVal container of digest hash_t ret(digest, dlen, alg, TYPE_PLAIN); return ret; }
void TypeChecker::applyASTDeclRandExponents(ASTDeclRandExponentsPtr n) { ASTListDeclPtr exps = n->getExponents(); string grpname = n->getGroup()->getName(); ASTDeclIdentifierSubPtr exp; ASTDeclIDRangePtr expRange; VarInfo info(grpname, VarInfo::EXPONENT); for (int i = 0; i < exps->size(); i++) { ASTNodePtr ith = exps->get(i); if ((exp = dynamic_pointer_cast<ASTDeclIdentifierSub>(ith)) != 0){ // associate exponent with its group env.varTypes[exp->getName()] = info; } else if((expRange=dynamic_pointer_cast<ASTDeclIDRange>(ith))!=0){ for(int j = expRange->getLBound(); j <= expRange->getUBound(); j++){ env.varTypes[expRange->getName(j)] = info; } } else { throw CashException(CashException::CE_PARSE_ERROR, "Variable %d in list was not a valid exponent name", i); } } }
string ProgramMaker::makeCLProve(const string &grpPart, const string &comPart, const string &comRelPart) { string program = "computation: " "given:" + grpPart + " " "element in pkGroup: A " "exponents in pkGroup: e, v " "integers: x[1:l], modSize, stat " "compute: " "random integers in [0,2^(modSize+stat)): r, r_C " "vprime := v + r*e " "Aprime := A * h^r " "C := h^r_C * for(i, 1:l, *, g_i^x_i) " "D := for(i, l+1:l+k, *, g_i^x_i) " "proof: " "given:" + grpPart + " " "elements in pkGroup: C, D, Aprime " + comPart + " " "integers: l_x, x[l+1:l+k] " "prove knowledge of: " "integers: x[1:l], r[1:l] " "exponents in pkGroup: e, vprime, r_C " "such that: " "for(i, 1:l, range: (-(2^l_x-1)) <= x_i < 2^l_x) " "C = h^r_C * for(i, 1:l, *, g_i^x_i) " "f = C^(-1) * D^(-1) * (Aprime^e) * h^(r_C-vprime)"; program += " " + comRelPart; // again, write to file and return filename ofstream writer; string fname = "ZKP/examples/cl-prove-temp.txt"; writer.open(fname.c_str(), ios::out); if (!writer) throw CashException(CashException::CE_PARSE_ERROR, "[ProgramMaker::makeCLProve]: Could not write program to file"); writer << program; writer.close(); return fname; }