Package freenet.keys

Source Code of freenet.keys.USK

/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.keys;

import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.regex.Pattern;

import freenet.support.Fields;
import freenet.support.Logger;

/**
* Updatable Subspace Key.
* Not really a ClientKey as it cannot be directly requested.
*
* Contains:
* - Enough information to produce a real SSK.
* - Site name.
* - Site edition number.
*
* WARNING: Changing non-transient members on classes that are Serializable can result in
* restarting downloads or losing uploads.
*/
public class USK extends BaseClientKey implements Comparable<USK>, Serializable {

    private static final long serialVersionUID = 1L;
    /* The character to separate the site name from the edition number in its SSK form.
   * I chose "-", because it makes it ludicrously easy to go from the USK form to the
   * SSK form, and we don't need to go vice versa.
   */
  static protected final String SEPARATOR = "-";
  /** Encryption type */
  public final byte cryptoAlgorithm;
  /** Public key hash */
  protected final byte[] pubKeyHash;
  /** Encryption key */
  protected final byte[] cryptoKey;
  // Extra must be verified on creation, and is fixed for now. FIXME if it becomes changeable, need to keep values here.
 
  public final String siteName;
  public final long suggestedEdition;
 
  private final int hashCode;

  public USK(byte[] pubKeyHash, byte[] cryptoKey, byte[] extra, String siteName, long suggestedEdition) throws MalformedURLException {
    this.pubKeyHash = pubKeyHash;
    this.cryptoKey = cryptoKey;
    this.siteName = siteName;
    this.suggestedEdition = suggestedEdition;
    if(extra == null)
      throw new MalformedURLException("No extra bytes (third bit) in USK");
    if(pubKeyHash == null)
      throw new MalformedURLException("No pubkey hash (first bit) in USK");
    if(cryptoKey == null)
      throw new MalformedURLException("No crypto key (second bit) in USK");
    // Verify extra bytes, get cryptoAlgorithm - FIXME this should be a static method or something?
    ClientSSK tmp = new ClientSSK(siteName, pubKeyHash, extra, null, cryptoKey);
    cryptoAlgorithm = tmp.cryptoAlgorithm;
    if(pubKeyHash.length != NodeSSK.PUBKEY_HASH_SIZE)
      throw new MalformedURLException("Pubkey hash wrong length: "+pubKeyHash.length+" should be "+NodeSSK.PUBKEY_HASH_SIZE);
    if(cryptoKey.length != ClientSSK.CRYPTO_KEY_LENGTH)
      throw new MalformedURLException("Decryption key wrong length: "+cryptoKey.length+" should be "+ClientSSK.CRYPTO_KEY_LENGTH);
    hashCode = Fields.hashCode(pubKeyHash) ^ Fields.hashCode(cryptoKey) ^
      siteName.hashCode() ^ (int)suggestedEdition ^ (int)(suggestedEdition >> 32);
  }

  public static USK create(FreenetURI uri) throws MalformedURLException {
    if(!uri.isUSK()) throw new MalformedURLException("Not a USK");
    return new USK(uri.getRoutingKey(), uri.getCryptoKey(), uri.getExtra(), uri.getDocName(), uri.getSuggestedEdition());
  }
 
  protected USK(byte[] pubKeyHash2, byte[] cryptoKey2, String siteName2, long suggestedEdition2, byte cryptoAlgorithm) {
    this.pubKeyHash = pubKeyHash2;
    this.cryptoKey = cryptoKey2;
    this.siteName = siteName2;
    this.suggestedEdition = suggestedEdition2;
    this.cryptoAlgorithm = cryptoAlgorithm;
    hashCode = Fields.hashCode(pubKeyHash) ^ Fields.hashCode(cryptoKey) ^
      siteName.hashCode() ^ (int)suggestedEdition ^ (int)(suggestedEdition >> 32);
  }
 
  protected USK() {
      // For serialization.
        pubKeyHash = null;
        cryptoKey = null;
        siteName = null;
      suggestedEdition = 0;
      cryptoAlgorithm = 0;
      hashCode = 0;
  }

  private static final Pattern badDocNamePattern;
  static {
    badDocNamePattern = Pattern.compile(".*\\-[0-9]+(\\/.*)?$");
  }

  // FIXME: Be careful with this constructor! There must not be an edition in the ClientSSK!
  public USK(ClientSSK ssk, long myARKNumber) {
    this.pubKeyHash = ssk.pubKeyHash;
    this.cryptoKey = ssk.cryptoKey;
    this.siteName = ssk.docName;
    this.suggestedEdition = myARKNumber;
    this.cryptoAlgorithm = ssk.cryptoAlgorithm;

    if (badDocNamePattern.matcher(siteName).matches())  // not error -- just "possible" bug
      Logger.normal(this, "POSSIBLE BUG: edition in ClientSSK " + ssk, new Exception("debug"));

    hashCode = Fields.hashCode(pubKeyHash) ^ Fields.hashCode(cryptoKey) ^
      siteName.hashCode() ^ (int)suggestedEdition ^ (int)(suggestedEdition >> 32);
  }

  public USK(USK usk) {
    // FIXME can we not copy pubKeyHash?
    // If we can guarantee that neither USK nor anything getting it without copying will change it?
    // db4o treats byte[] as individual byte members, so there are no issues with deactivation.
    this.pubKeyHash = usk.pubKeyHash.clone();
    this.cryptoAlgorithm = usk.cryptoAlgorithm;
    // FIXME should we copy cryptoKey?
    this.cryptoKey = usk.cryptoKey;
    this.siteName = usk.siteName;
    this.suggestedEdition = usk.suggestedEdition;
    hashCode = Fields.hashCode(pubKeyHash) ^ Fields.hashCode(cryptoKey) ^
      siteName.hashCode() ^ (int)suggestedEdition ^ (int)(suggestedEdition >> 32);
  }

  @Override
  public FreenetURI getURI() {
    return new FreenetURI(pubKeyHash, cryptoKey, ClientSSK.getExtraBytes(cryptoAlgorithm), siteName, suggestedEdition);
  }

  public ClientSSK getSSK(long ver) {
    return getSSK(getName(ver));
  }
 
  public ClientSSK getSSK(String string) {
    try {
      return new ClientSSK(string, pubKeyHash, ClientSSK.getExtraBytes(cryptoAlgorithm), null, cryptoKey);
    } catch (MalformedURLException e) {
      Logger.error(this, "Caught "+e+" should not be possible in USK.getSSK", e);
      throw new Error(e);
    }
  }
 
  public String getName(long ver) {
    return siteName + SEPARATOR + ver;
  }

  public ClientKey getSSK() {
    return getSSK(suggestedEdition);
  }
 
  public USK copy(long edition) {
    if(suggestedEdition == edition) return this;
    return new USK(pubKeyHash, cryptoKey, siteName, edition, cryptoAlgorithm);
  }

  public USK clearCopy() {
    return copy(0);
  }
 
  public final USK copy() {
    // We need our own constructor to make sure we copy pubKeyHash.
    // So clone() doesn't work for this.
    // FIXME when we are sure we don't need to copy the byte[]'s we might be able to switch back to clone().
    return new USK(this);
  }
 
  @Override
  public boolean equals(Object o) {
    if(o == null || !(o instanceof USK)) return false;
    return equals(o, true);
  }
 
  public boolean equals(Object o, boolean includeVersion) {
    if(o instanceof USK) {
      USK u = (USK)o;
      if(!Arrays.equals(pubKeyHash, u.pubKeyHash)) return false;
      if(!Arrays.equals(cryptoKey, u.cryptoKey)) return false;
      if(!siteName.equals(u.siteName)) return false;
      if(includeVersion && (suggestedEdition != u.suggestedEdition)) return false;
      return true;
    }
    return false;
  }

  @Override
  public int hashCode() {
    return hashCode;
  }

  public FreenetURI getBaseSSK() {
    return new FreenetURI("SSK", siteName, pubKeyHash, cryptoKey, ClientSSK.getExtraBytes(cryptoAlgorithm));
  }
 
  @Override
  public String toString() {
    return super.toString()+ ':' +getURI();
  }

  public FreenetURI turnMySSKIntoUSK(FreenetURI uri) {
    if(uri.getKeyType().equals("SSK") &&
        Arrays.equals(uri.getRoutingKey(), pubKeyHash) &&
        Arrays.equals(uri.getCryptoKey(), cryptoKey) &&
        Arrays.equals(uri.getExtra(), ClientSSK.getExtraBytes(cryptoAlgorithm)) &&
        uri.getDocName() != null &&
        uri.getDocName().startsWith(siteName)) {
      String doc = uri.getDocName();
      doc = doc.substring(siteName.length());
      if(doc.length() < 2 || doc.charAt(0) != '-') return uri;
      doc = doc.substring(1);
      long edition;
      try {
        edition = Long.parseLong(doc);
      } catch (NumberFormatException e) {
        Logger.normal(this, "Trying to turn SSK back into USK: "+uri+" doc="+doc+" caught "+e, e);
        return uri;
      }
      if(!doc.equals(Long.toString(edition))) return uri;
      return new FreenetURI("USK", siteName, uri.getAllMetaStrings(), pubKeyHash, cryptoKey, ClientSSK.getExtraBytes(cryptoAlgorithm), edition);
    }
    return uri;
  }

  @Override
  public int compareTo(USK o) {
    if(this == o) return 0;
    if(cryptoAlgorithm < o.cryptoAlgorithm) return -1;
    if(cryptoAlgorithm > o.cryptoAlgorithm) return 1;
    int cmp = Fields.compareBytes(pubKeyHash, o.pubKeyHash);
    if(cmp != 0) return cmp;
    cmp = Fields.compareBytes(cryptoKey, o.cryptoKey);
    if(cmp != 0) return cmp;
    cmp = siteName.compareTo(o.siteName);
    if(cmp != 0) return cmp;
    if(suggestedEdition > o.suggestedEdition) return 1;
    if(suggestedEdition < o.suggestedEdition) return -1;
    return 0;
  }
 
  public static final Comparator<USK> FAST_COMPARATOR = new Comparator<USK>() {

    @Override
    public int compare(USK o1, USK o2) {
      if(o1.hashCode > o2.hashCode) return 1;
      else if(o1.hashCode < o2.hashCode) return -1;
      return o1.compareTo(o2);
    }
   
  };

  public byte[] getPubKeyHash() {
    return Arrays.copyOf(pubKeyHash, pubKeyHash.length);
  }

  public boolean samePubKeyHash(NodeSSK k) {
    return Arrays.equals(k.getPubKeyHash(), pubKeyHash);
  }
}
TOP

Related Classes of freenet.keys.USK

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.