Package com.subgraph.orchid.directory.consensus

Source Code of com.subgraph.orchid.directory.consensus.ConsensusDocumentImpl

package com.subgraph.orchid.directory.consensus;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import com.subgraph.orchid.ConsensusDocument;
import com.subgraph.orchid.DirectoryServer;
import com.subgraph.orchid.KeyCertificate;
import com.subgraph.orchid.RouterStatus;
import com.subgraph.orchid.Tor;
import com.subgraph.orchid.VoteAuthorityEntry;
import com.subgraph.orchid.crypto.TorPublicKey;
import com.subgraph.orchid.crypto.TorSignature.DigestAlgorithm;
import com.subgraph.orchid.data.HexDigest;
import com.subgraph.orchid.data.Timestamp;
import com.subgraph.orchid.directory.TrustedAuthorities;

public class ConsensusDocumentImpl implements ConsensusDocument {
 
  enum SignatureVerifyStatus { STATUS_UNVERIFIED, STATUS_NEED_CERTS, STATUS_VERIFIED };
 
  private final static Logger logger = Logger.getLogger(ConsensusDocumentImpl.class.getName());
 
  private final static String BW_WEIGHT_SCALE_PARAM = "bwweightscale";
  private final static int BW_WEIGHT_SCALE_DEFAULT = 10000;
  private final static int BW_WEIGHT_SCALE_MIN = 1;
  private final static int BW_WEIGHT_SCALE_MAX = Integer.MAX_VALUE;
 
  private final static String CIRCWINDOW_PARAM = "circwindow";
  private final static int CIRCWINDOW_DEFAULT = 1000;
  private final static int CIRCWINDOW_MIN = 100;
  private final static int CIRCWINDOW_MAX = 1000;
 
  private final static String USE_NTOR_HANDSHAKE_PARAM = "UseNTorHandshake";
 
  private Set<RequiredCertificate> requiredCertificates = new HashSet<RequiredCertificate>();
 
 
  private int consensusMethod;
  private ConsensusFlavor flavor;
  private Timestamp validAfter;
  private Timestamp freshUntil;
  private Timestamp validUntil;
  private int distDelaySeconds;
  private int voteDelaySeconds;
  private Set<String> clientVersions;
  private Set<String> serverVersions;
  private Set<String> knownFlags;
  private HexDigest signingHash;
  private HexDigest signingHash256;
  private Map<HexDigest, VoteAuthorityEntry> voteAuthorityEntries;
  private List<RouterStatus> routerStatusEntries;
  private Map<String, Integer> bandwidthWeights;
  private Map<String, Integer> parameters;
  private int signatureCount;
  private boolean isFirstCallToVerifySignatures = true;
  private String rawDocumentData;
 
  void setConsensusFlavor(ConsensusFlavor flavor) { this.flavor = flavor; }
  void setConsensusMethod(int method) { consensusMethod = method; }
  void setValidAfter(Timestamp ts) { validAfter = ts; }
  void setFreshUntil(Timestamp ts) { freshUntil = ts; }
  void setValidUntil(Timestamp ts) { validUntil = ts; }
  void setDistDelaySeconds(int seconds) { distDelaySeconds = seconds; }
  void setVoteDelaySeconds(int seconds) { voteDelaySeconds = seconds; }
  void addClientVersion(String version) { clientVersions.add(version); }
  void addServerVersion(String version) { serverVersions.add(version); }
  void addParameter(String name, int value) { parameters.put(name, value); }
  void addBandwidthWeight(String name, int value) { bandwidthWeights.put(name, value); }
   
  void addSignature(DirectorySignature signature) {
    final VoteAuthorityEntry voteAuthority = voteAuthorityEntries.get(signature.getIdentityDigest());
    if(voteAuthority == null) {
      logger.warning("Consensus contains signature for source not declared in authority section: "+ signature.getIdentityDigest());
      return;
    }
    final List<DirectorySignature> signatures = voteAuthority.getSignatures();
    final DigestAlgorithm newSignatureAlgorithm = signature.getSignature().getDigestAlgorithm();
    for(DirectorySignature sig: signatures) {
      DigestAlgorithm algo = sig.getSignature().getDigestAlgorithm();
      if(algo.equals(newSignatureAlgorithm)) {
        logger.warning("Consensus contains two or more signatures for same source with same algorithm");
        return;
      }
    }
    signatureCount += 1;
    signatures.add(signature);
  }

  void setSigningHash(HexDigest hash) { signingHash = hash; }
  void setSigningHash256(HexDigest hash) { signingHash256 = hash; }
  void setRawDocumentData(String rawData) { rawDocumentData = rawData; }
 
  ConsensusDocumentImpl() {
    clientVersions = new HashSet<String>();
    serverVersions = new HashSet<String>();
    knownFlags = new HashSet<String>();
    voteAuthorityEntries = new HashMap<HexDigest, VoteAuthorityEntry>();
    routerStatusEntries = new ArrayList<RouterStatus>();
    bandwidthWeights = new HashMap<String, Integer>();
    parameters = new HashMap<String, Integer>();
  }
 
  void addKnownFlag(String flag) {
    knownFlags.add(flag);
  }
 
  void addVoteAuthorityEntry(VoteAuthorityEntry entry) {
    voteAuthorityEntries.put(entry.getIdentity(), entry);
  }
 
  void addRouterStatusEntry(RouterStatusImpl entry) {
    routerStatusEntries.add(entry);
  }
 
  public ConsensusFlavor getFlavor() {
    return flavor;
  }

  public Timestamp getValidAfterTime() {
    return validAfter;
  }
 
  public Timestamp getFreshUntilTime() {
    return freshUntil;
  }
 
  public Timestamp getValidUntilTime() {
    return validUntil;
  }
 
  public int getConsensusMethod() {
    return consensusMethod;
  }
 
  public int getVoteSeconds() {
    return voteDelaySeconds;
  }
 
  public int getDistSeconds() {
    return distDelaySeconds;
  }
 
  public Set<String> getClientVersions() {
    return clientVersions;
  }
 
  public Set<String> getServerVersions() {
    return serverVersions;
  }
 
  public boolean isLive() {
    if(validUntil == null) {
      return false;
    } else {
      return !validUntil.hasPassed();
    }
  }
 
  public List<RouterStatus> getRouterStatusEntries() {
    return Collections.unmodifiableList(routerStatusEntries);
  }
 
  public String getRawDocumentData() {
    return rawDocumentData;
  }
 
  public ByteBuffer getRawDocumentBytes() {
    if(getRawDocumentData() == null) {
      return ByteBuffer.allocate(0);
    } else {
      return ByteBuffer.wrap(getRawDocumentData().getBytes(Tor.getDefaultCharset()));
    }
  }

  public boolean isValidDocument() {
    return (validAfter != null) && (freshUntil != null) && (validUntil != null) &&
    (voteDelaySeconds > 0) && (distDelaySeconds > 0) && (signingHash != null) &&
    (signatureCount > 0);
  }
 
  public HexDigest getSigningHash() {
    return signingHash;
  }
 
  public HexDigest getSigningHash256() {
    return signingHash256;
  }
 
  public synchronized SignatureStatus verifySignatures() {
    boolean firstCall = isFirstCallToVerifySignatures;
    isFirstCallToVerifySignatures = false;
    requiredCertificates.clear();
    int verifiedCount = 0;
    int certsNeededCount = 0;
    final int v3Count = TrustedAuthorities.getInstance().getV3AuthorityServerCount();
    final int required = (v3Count / 2) + 1;
   
    for(VoteAuthorityEntry entry: voteAuthorityEntries.values()) {
      switch(verifySingleAuthority(entry)) {
      case STATUS_FAILED:
        break;
      case STATUS_NEED_CERTS:
        certsNeededCount += 1;
        break;
      case STATUS_VERIFIED:
        verifiedCount += 1;
        break;
      }
    }
   
    if(verifiedCount >= required) {
      return SignatureStatus.STATUS_VERIFIED;
    } else if(verifiedCount + certsNeededCount >= required) {
      if(firstCall) {
        logger.info("Certificates need to be retrieved to verify consensus");
      }
      return SignatureStatus.STATUS_NEED_CERTS;
    } else {
      return SignatureStatus.STATUS_FAILED;
    }
  }

  private SignatureStatus verifySingleAuthority(VoteAuthorityEntry authority) {
   
    boolean certsNeeded = false;
    boolean validSignature = false;
   
    for(DirectorySignature s: authority.getSignatures()) {
      DirectoryServer trusted = TrustedAuthorities.getInstance().getAuthorityServerByIdentity(s.getIdentityDigest());
      if(trusted == null) {
        logger.warning("Consensus signed by unrecognized directory authority: "+ s.getIdentityDigest());
        return SignatureStatus.STATUS_FAILED;
      } else {
        switch(verifySignatureForTrustedAuthority(trusted, s)) {
        case STATUS_NEED_CERTS:
          certsNeeded = true;
          break;
        case STATUS_VERIFIED:
          validSignature = true;
          break;
        default:
          break;
        }
      }
    }
   
    if(validSignature) {
      return SignatureStatus.STATUS_VERIFIED;
    } else if(certsNeeded) {
      return SignatureStatus.STATUS_NEED_CERTS;
    } else {
      return SignatureStatus.STATUS_FAILED;
    }
  }
 
  private SignatureStatus verifySignatureForTrustedAuthority(DirectoryServer trustedAuthority, DirectorySignature signature) {
    final KeyCertificate certificate = trustedAuthority.getCertificateByFingerprint(signature.getSigningKeyDigest());
    if(certificate == null) {
      logger.fine("Missing certificate for signing key: "+ signature.getSigningKeyDigest());
      addRequiredCertificateForSignature(signature);
      return SignatureStatus.STATUS_NEED_CERTS;
    }
    if(certificate.isExpired()) {
      return SignatureStatus.STATUS_FAILED;
    }
   
    final TorPublicKey signingKey = certificate.getAuthoritySigningKey();
    final HexDigest d = (signature.useSha256()) ? signingHash256 : signingHash;
    if(!signingKey.verifySignature(signature.getSignature(), d)) {
      logger.warning("Signature failed on consensus for signing key: "+ signature.getSigningKeyDigest());
      return SignatureStatus.STATUS_FAILED;
    }
    return SignatureStatus.STATUS_VERIFIED;
  }

  public Set<RequiredCertificate> getRequiredCertificates() {
    return requiredCertificates;
  }

  private void addRequiredCertificateForSignature(DirectorySignature signature) {
    requiredCertificates.add(new RequiredCertificateImpl(signature.getIdentityDigest(), signature.getSigningKeyDigest()));
  }

  public boolean equals(Object o) {
    if(!(o instanceof ConsensusDocumentImpl))
      return false;
    final ConsensusDocumentImpl other = (ConsensusDocumentImpl) o;
    return other.getSigningHash().equals(signingHash);
  }
 
  public int hashCode() {
    return (signingHash == null) ? 0 : signingHash.hashCode();
  }
 
  private int getParameterValue(String name, int defaultValue, int minValue, int maxValue) {
    if(!parameters.containsKey(name)) {
      return defaultValue;
    }
    final int value = parameters.get(name);
    if(value < minValue) {
      return minValue;
    } else if(value > maxValue) {
      return maxValue;
    } else {
      return value;
    }
  }
 
  private boolean getBooleanParameterValue(String name, boolean defaultValue) {
    if(!parameters.containsKey(name)) {
      return defaultValue;
    }
    final int value = parameters.get(name);
    return value != 0;
  }
 
  public int getCircWindowParameter() {
    return getParameterValue(CIRCWINDOW_PARAM, CIRCWINDOW_DEFAULT, CIRCWINDOW_MIN, CIRCWINDOW_MAX);
  }
 
  public int getWeightScaleParameter() {
    return getParameterValue(BW_WEIGHT_SCALE_PARAM, BW_WEIGHT_SCALE_DEFAULT, BW_WEIGHT_SCALE_MIN, BW_WEIGHT_SCALE_MAX);
  }
 
  public int getBandwidthWeight(String tag) {
    if(bandwidthWeights.containsKey(tag)) {
      return bandwidthWeights.get(tag);
    } else {
      return -1;
    }
  }
 
  public boolean getUseNTorHandshake() {
    return getBooleanParameterValue(USE_NTOR_HANDSHAKE_PARAM, false);
  }
}
TOP

Related Classes of com.subgraph.orchid.directory.consensus.ConsensusDocumentImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.