/** * Copyright (c) 2007-2009 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags * * This file is part of SMaRt. * * SMaRt is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SMaRt is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with SMaRt. If not, see . */ package navigators.smart.paxosatwar.executionmanager; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignedObject; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import navigators.smart.paxosatwar.messages.CollectProof; import navigators.smart.paxosatwar.messages.FreezeProof; import navigators.smart.reconfiguration.ReconfigurationManager; import navigators.smart.tom.core.timer.messages.RTCollect; import navigators.smart.tom.leaderchange.CollectData; /** * This class is used to process data relacioned with freezed rounds. * Generate proposes - good values, verify the proposed values and so on... */ public class ProofVerifier { //******* EDUARDO BEGIN: tudo isso deve ser acessado a partir do ReconvigurationManager **************// //private int numberOfNonces; // Ammount of nonces that have to be delivered to the application //private PublicKey[] publickeys; // public keys of the replicas //******* EDUARDO END **************// private PrivateKey prk = null; // private key for this replica private Signature engine; // Signature engine private ReconfigurationManager manager; /** * Creates a new instance of ProofVerifier * @param conf TOM configuration */ public ProofVerifier(ReconfigurationManager manager) { //******* EDUARDO BEGIN **************// //this.quorumF = conf.getF(); //this.quorumStrong = (int) ((conf.getN() + quorumF) / 2); //this.numberOfNonces = manager.getStaticConf().getNumberOfNonces(); this.manager = manager; this.prk = manager.getStaticConf().getRSAPrivateKey(); //******* EDUARDO END **************// try { this.engine = Signature.getInstance("SHA1withRSA"); } catch (Exception e) { e.printStackTrace(); } } /** * Signs a collect data object * @param cp a collect data object * @return Signed collect data object */ public SignedObject sign(CollectData collects) { try { return new SignedObject(collects, prk, engine); } catch (Exception e) { e.printStackTrace(); return null; } } /** * Signs proofs of a freezed consensus * @param cp Proofs of a freezed consensus * @return Signed proofs */ public SignedObject sign(CollectProof cp) { try { return new SignedObject(cp, prk, engine); } catch (Exception e) { e.printStackTrace(); return null; } } /** * TODO: Nao sei para que serve nem o q e um RTCollect. Mas deve ser da difusao atomica * @param cp * @return Signed */ public SignedObject sign(RTCollect trc) { try { return new SignedObject(trc, prk, engine); } catch (Exception e) { e.printStackTrace(); return null; } } /** * Counts how many proofs are in the given array (how many are diferent from null) * @param proofs Array of proofs, which might have indexes pointing to null * @return Number of proofs in the array */ public int countProofs(SignedObject[] proofs) { int validProofs = 0; for (int i = 0; i < proofs.length; i++) { if (proofs[i] != null) { validProofs++; } } return validProofs; } /** * Obtains the value that is considered to be good, as is specified by the PaW algorithm * @param proofs Signed proofs to be evaluated * @param in True if the proofs to be evaluated are from the freezed consensus, false for the proofs from the next consensus * @return The value considered to be good, if any. If such value can't be found, null is returned */ public byte[] getGoodValue(SignedObject[] proofs, boolean in) { try { CollectProof[] cps = new CollectProof[proofs.length]; for (int i = 0; i < proofs.length; i++) { if (proofs[i] != null) { cps[i] = (CollectProof) proofs[i].getObject(); } } return getGoodValue(cps, in); } catch (Exception e) { e.printStackTrace(); return null; } } /** * Checks if an specified array of bytes is contained in a given linked list (whose values are arrays of bytes) * @param l Linked list containing arrays of bytes * @param e Array of bytes that is to be search in the linked list * @return True if 'e' is contained in 'l', false otherwise */ private boolean containsArray(LinkedList l, byte[] e) { for (Iterator i = l.iterator(); i.hasNext();) { byte[] value = i.next(); if (Arrays.equals(value, e)) { return true; } } return false; } /** * Obtains the value that is considered to be good, as is specified by the PaW algorithm * @param proofs Proofs to be evaluated * @param in True if the proofs to be evaluated are from the freezed consensus, false for the proofs from the next consensus * @return The value considered to be good, if any. If such value can't be found, null is returned */ public byte[] getGoodValue(CollectProof[] proofs, boolean in) { LinkedList poss = buildPoss(proofs, in); LinkedList acc = buildAcc(proofs, in); for (Iterator i = acc.iterator(); i.hasNext();) { byte[] value = i.next(); if (containsArray(poss, value)) { return value; } } return null; } /** * Called by acceptors to verify if some proposal is good, as specified by the PaW algorithm * @param value The proposed value * @param proofs The proofs to check the value agaisnt * @param in True if the proofs to be evaluated are from the freezed consensus, false for the proofs from the next consensus * @return True if the value is good, false otherwise */ public boolean good(byte[] value, CollectProof[] proofs, boolean in) { LinkedList poss = buildPoss(proofs, in); LinkedList acc = buildAcc(proofs, in); //condition G2 if (containsArray(acc, value) && (containsArray(poss, value) || poss.isEmpty())) { return true; } //condition G1 if (poss.isEmpty()) { //alysson: ainda nao estou bem certo q isso esta certo return true; } return false; } /** * Returns the round number of the next consensus's execution from an array of proofs * @param proof Array of proofs which gives out the round number of next consensus's execution * @return The number of the round, or -1 if there is not one executing */ public int getNextExecRound(CollectProof[] proof) { for (int i = 0; i < proof.length; i++) { if (proof[i].getProofs(false) != null) { int r = proof[i].getProofs(false).getRound(); int c = 1; for (int j = i + 1; j < proof.length; j++) { if (proof[j].getProofs(false) != null) { if (r == proof[j].getProofs(false).getRound()) { c++; } } } //******* EDUARDO BEGIN: nova forma de acessar o f **************// if (c > this.manager.getQuorumF()) { //******* EDUARDO END **************// return r; } } } return -1; } /** * Checks if this is a valid proof * @param eid Execution ID to match against the proof * @param round round number to match against the proof * @param proof Proof to be verified * @return True if valid, false otherwise */ public boolean validProof(int eid, int round, FreezeProof proof) { // TODO: nao devia ser 'proof.getRound() <= round'? return (proof != null) && (proof.getEid() == eid) && (proof.getRound() == round); } /** * Returns the valid proofs * @param eid Execution ID to match against the proofs * @param round round number to match against the proofs * @param proof Proofs to be verified * @return Array the the valid proofs */ public CollectProof[] checkValid(int eid, int round, SignedObject[] proof) { if (proof == null) { return null; } Collection valid = new HashSet(); try { for (int i = 0; i < proof.length; i++) { if (proof[i] != null && validSignature(proof[i], i)) { CollectProof cp = (CollectProof) proof[i].getObject(); if (validProof(eid, round, cp.getProofs(true))) { valid.add(cp); } } } } catch (Exception e) { e.printStackTrace(); } return valid.toArray(new CollectProof[0]); } /** * Checks if a signature is valid * @param so Signed object * @param sender Replica that sent the signed object * @return True if the signature is valid, false otherwise */ public boolean validSignature(SignedObject so, int sender) { try { //return so.verify(this.publickeys[sender], engine); //******* EDUARDO BEGIN **************// return so.verify(this.manager.getStaticConf().getRSAPublicKey(sender), engine); //******* EDUARDO END **************// } catch (Exception e) { e.printStackTrace(); } return false; } /** * Checks if a replica is the leader, given an array of proofs. * @param l Replica to be checked * @param proof Proofs to verify the leader against * @return True if 'l' is the leader, false otherwise */ public boolean isTheLeader(int l, CollectProof[] proof) { int c = 0; // A replica is considered to really be the leader, if more than F // proofs have 'getLeader()' set to be 'l' for (int i = 0; i < proof.length; i++) { if (proof[i] != null && proof[i].getLeader() == l) { c++; } } //******* EDUARDO BEGIN **************// return c > this.manager.getCurrentViewF(); //******* EDUARDO END **************// } /** * Builds a Poss set as defined in the PaW algorithm * @param proofs Proofs to be used to create the set * @param in True if the proofs to be used are from the freezed consensus, false for the proofs from the next consensus * @return A linked list which stands for the Poss set */ private LinkedList buildPoss(CollectProof[] proofs, boolean in) { LinkedList poss = new LinkedList(); for (int i = 0; i < proofs.length; i++) { byte[] w = null; if (proofs[i] != null && proofs[i].getProofs(in) != null) { w = proofs[i].getProofs(in).getWeak(); } if (w != null) { int countW = 0; int countS = 0; for (int j = 0; j < proofs.length; j++) { if (proofs[j] != null && proofs[j].getProofs(in) != null) { if (Arrays.equals(w, proofs[j].getProofs(in).getWeak())) { countW++; } if (Arrays.equals(w, proofs[j].getProofs(in).getStrong())) { countS++; } } } //******* EDUARDO BEGIN **************// if ((countW > manager.getQuorumStrong() || countS > manager.getQuorumF()) //******* EDUARDO END **************// && !poss.contains(w)) { poss.add(w); } } } return poss; } /** * Builds a Acc set as defined in the PaW algorithm * @param proofs Proofs to be used to create the set * @param in True if the proofs to be used are from the freezed consensus, false for the proofs from the next consensus * @return A linked list which stands for the Acc set */ private LinkedList buildAcc(CollectProof[] proofs, boolean in) { LinkedList acc = new LinkedList(); for (int i = 0; i < proofs.length; i++) { byte[] w = null; if (proofs[i] != null && proofs[i].getProofs(in) != null) { w = proofs[i].getProofs(in).getWeak(); } if (w != null) { int count = 0; for (int j = 0; j < proofs.length; j++) { if (proofs[j] != null && proofs[j].getProofs(in) != null && Arrays.equals(w, proofs[j].getProofs(in).getWeak())) { count++; } } //******* EDUARDO BEGIN **************// if (count > manager.getQuorumF() && !acc.contains(w)) { //******* EDUARDO END **************// acc.add(w); } } } return acc; } }