Package freenet.keys

Source Code of freenet.keys.InsertableClientSSK

/* 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.IOException;
import java.net.MalformedURLException;
import java.security.MessageDigest;
import java.util.Arrays;

import net.i2p.util.NativeBigInteger;

import freenet.support.Logger;
import freenet.support.math.MersenneTwister;

import freenet.crypt.DSA;
import freenet.crypt.DSAGroup;
import freenet.crypt.DSAPrivateKey;
import freenet.crypt.DSAPublicKey;
import freenet.crypt.DSASignature;
import freenet.crypt.Global;
import freenet.crypt.PCFBMode;
import freenet.crypt.RandomSource;
import freenet.crypt.SHA256;
import freenet.crypt.UnsupportedCipherException;
import freenet.crypt.Util;
import freenet.crypt.ciphers.Rijndael;
import freenet.keys.Key.Compressed;
import freenet.support.api.Bucket;
import freenet.support.compress.InvalidCompressionCodecException;

/** A ClientSSK that has a private key and therefore can be inserted. */
public class InsertableClientSSK extends ClientSSK {

    private static final long serialVersionUID = 1L;

    public final DSAPrivateKey privKey;
 
  private static boolean logMINOR;
  static {
      Logger.registerClass(InsertableClientSSK.class);
  }
 
  public InsertableClientSSK(String docName, byte[] pubKeyHash, DSAPublicKey pubKey, DSAPrivateKey privKey, byte[] cryptoKey, byte cryptoAlgorithm) throws MalformedURLException {
    super(docName, pubKeyHash, getExtraBytes(cryptoAlgorithm), pubKey, cryptoKey);
    if(pubKey == null) throw new NullPointerException();
    this.privKey = privKey;
  }
 
  protected InsertableClientSSK() {
      // For serialization.
      privKey = null;
  }
 
  public static InsertableClientSSK create(FreenetURI uri) throws MalformedURLException {
    if(uri.getKeyType().equalsIgnoreCase("KSK"))
      return ClientKSK.create(uri);

    if(uri.getRoutingKey() == null)
      throw new MalformedURLException("Insertable SSK URIs must have a private key!: "+uri);
    if(uri.getCryptoKey() == null)
      throw new MalformedURLException("Insertable SSK URIs must have a private key!: "+uri);
   
    byte keyType;

    byte[] extra = uri.getExtra();
    if(uri.getKeyType().equals("SSK")) {
      if(extra == null)
        throw new MalformedURLException("Inserting pre-1010 keys not supported");
      // Formatted exactly as ,extra on fetching
      if(extra.length < 5)
        throw new MalformedURLException("SSK private key ,extra too short");
      if(extra[1] != 1) {
        throw new MalformedURLException("SSK not a private key");
      }
      keyType = extra[2];
      if(keyType != Key.ALGO_AES_PCFB_256_SHA256)
        throw new MalformedURLException("Unrecognized crypto type in SSK private key");
    }
    else {
      throw new MalformedURLException("Not a valid SSK insert URI type: "+uri.getKeyType());
    }
   
    // Allow docName="" for SSKs. E.g. GenerateSSK returns these; we want to be consistent.
    // However, we recommend that you not use this, especially not for a freesite, as
    // SSK@blah,blah,blah//filename is confusing for clients, browsers etc.
    if(uri.getDocName() == null)
      throw new MalformedURLException("SSK URIs must have a document name (to avoid ambiguity)");
    DSAGroup g = Global.DSAgroupBigA;
    DSAPrivateKey privKey;
    try {
      privKey = new DSAPrivateKey(new NativeBigInteger(1, uri.getRoutingKey()), g);
    } catch(IllegalArgumentException e) {
      // DSAPrivateKey is invalid
      Logger.error(InsertableClientSSK.class, "Caught "+e, e);
      throw new MalformedURLException("SSK private key (routing key) is invalid: " + e);
    }
    DSAPublicKey pubKey = new DSAPublicKey(g, privKey);
    byte[] pkHash = pubKey.asBytesHash();
    return new InsertableClientSSK(uri.getDocName(), pkHash, pubKey, privKey, uri.getCryptoKey(), keyType);
  }
 
  public ClientSSKBlock encode(Bucket sourceData, boolean asMetadata, boolean dontCompress, short alreadyCompressedCodec, long sourceLength, RandomSource r, String compressordescriptor, boolean pre1254) throws SSKEncodeException, IOException, InvalidCompressionCodecException {
    byte[] compressedData;
    short compressionAlgo;
    try {
      Compressed comp = Key.compress(sourceData, dontCompress, alreadyCompressedCodec, sourceLength, ClientSSKBlock.MAX_DECOMPRESSED_DATA_LENGTH, SSKBlock.DATA_LENGTH, true, compressordescriptor, pre1254);
      compressedData = comp.compressedData;
      compressionAlgo = comp.compressionAlgorithm;
    } catch (KeyEncodeException e) {
      throw new SSKEncodeException(e.getMessage(), e);
    }
    // Pad it
    MessageDigest md256 = SHA256.getMessageDigest();
    try {
      byte[] data;
      // First pad it
      if (compressedData.length != SSKBlock.DATA_LENGTH) {
        // Hash the data
        if (compressedData.length != 0)
          md256.update(compressedData);
        byte[] digest = md256.digest();
        MersenneTwister mt = new MersenneTwister(digest);
        data = Arrays.copyOf(compressedData, SSKBlock.DATA_LENGTH);
        if (compressedData.length > data.length) {
          throw new RuntimeException("compressedData.length = " + compressedData.length + " but data.length="
              + data.length);
        }
        Util.randomBytes(mt, data, compressedData.length, SSKBlock.DATA_LENGTH - compressedData.length);
      } else {
        data = compressedData;
      }

      // Implicit hash of data
      byte[] origDataHash = md256.digest(data);

      Rijndael aes;
      try {
        aes = new Rijndael(256, 256);
      } catch (UnsupportedCipherException e) {
        throw new Error("256/256 Rijndael not supported!");
      }

      // Encrypt data. Data encryption key = H(plaintext data).

      aes.initialize(origDataHash);
      PCFBMode pcfb = PCFBMode.create(aes, origDataHash);

      pcfb.blockEncipher(data, 0, data.length);

      byte[] encryptedDataHash = md256.digest(data);

      // Create headers

      byte[] headers = new byte[SSKBlock.TOTAL_HEADERS_LENGTH];
      // First two bytes = hash ID
      int x = 0;
      headers[x++] = (byte) (KeyBlock.HASH_SHA256 >> 8);
      headers[x++] = (byte) (KeyBlock.HASH_SHA256);
      // Then crypto ID
      headers[x++] = (byte) (Key.ALGO_AES_PCFB_256_SHA256 >> 8);
      headers[x++] = Key.ALGO_AES_PCFB_256_SHA256;
      // Then E(H(docname))
      // Copy to headers
      System.arraycopy(ehDocname, 0, headers, x, ehDocname.length);
      x += ehDocname.length;
      // Now the encrypted headers
      byte[] encryptedHeaders = Arrays.copyOf(origDataHash, SSKBlock.ENCRYPTED_HEADERS_LENGTH);
      int y = origDataHash.length;
      short len = (short) compressedData.length;
      if (asMetadata)
        len |= 32768;
      encryptedHeaders[y++] = (byte) (len >> 8);
      encryptedHeaders[y++] = (byte) len;
      encryptedHeaders[y++] = (byte) (compressionAlgo >> 8);
      encryptedHeaders[y++] = (byte) compressionAlgo;
      if (encryptedHeaders.length != y)
        throw new IllegalStateException("Have more bytes to generate encoding SSK");
      aes.initialize(cryptoKey);
      pcfb.reset(ehDocname);
      pcfb.blockEncipher(encryptedHeaders, 0, encryptedHeaders.length);
      System.arraycopy(encryptedHeaders, 0, headers, x, encryptedHeaders.length);
      x += encryptedHeaders.length;
      // Generate implicit overall hash.
      md256.update(headers, 0, x);
      md256.update(encryptedDataHash);
      byte[] overallHash = md256.digest();
      // Now sign it
      DSASignature sig = DSA.sign(pubKey.getGroup(), privKey, new NativeBigInteger(1, overallHash), r);
      // Pack R and S into 32 bytes each, and copy to headers.

      // Then create and return the ClientSSKBlock.
      byte[] rBuf = truncate(sig.getR().toByteArray(), SSKBlock.SIG_R_LENGTH);
      byte[] sBuf = truncate(sig.getS().toByteArray(), SSKBlock.SIG_S_LENGTH);
      System.arraycopy(rBuf, 0, headers, x, rBuf.length);
      x += rBuf.length;
      System.arraycopy(sBuf, 0, headers, x, sBuf.length);
      x += sBuf.length;
      if (x != SSKBlock.TOTAL_HEADERS_LENGTH)
        throw new IllegalStateException("Too long");
      try {
        return new ClientSSKBlock(data, headers, this, !logMINOR);
      } catch (SSKVerifyException e) {
        throw (AssertionError)new AssertionError("Impossible encoding error").initCause(e);
      }
    } finally {
      SHA256.returnMessageDigest(md256);
    }
  }

  private byte[] truncate(byte[] bs, int len) {
    if(bs.length == len)
      return bs;
    else if (bs.length < len) {
      byte[] buf = new byte[len];
      System.arraycopy(bs, 0, buf, len - bs.length, bs.length);
      return buf;
    } else { // if (bs.length > len) {
      for(int i=0;i<(bs.length-len);i++) {
        if(bs[i] != 0)
          throw new IllegalStateException("Cannot truncate");
      }
      return Arrays.copyOfRange(bs, bs.length-len, bs.length);
    }
  }

  public static InsertableClientSSK createRandom(RandomSource r, String docName) {
    byte[] ckey = new byte[CRYPTO_KEY_LENGTH];
    r.nextBytes(ckey);
    DSAGroup g = Global.DSAgroupBigA;
    DSAPrivateKey privKey = new DSAPrivateKey(g, r);
    DSAPublicKey pubKey = new DSAPublicKey(g, privKey);
    try {
      byte[] pkHash = SHA256.digest(pubKey.asBytes());
      return new InsertableClientSSK(docName, pkHash, pubKey, privKey, ckey,
          Key.ALGO_AES_PCFB_256_SHA256);
    } catch (MalformedURLException e) {
      throw new Error(e);
    }
  }

  public FreenetURI getInsertURI() {
    return new FreenetURI("SSK", docName, privKey.getX().toByteArray(), cryptoKey, getInsertExtraBytes());
  }

  private byte[] getInsertExtraBytes() {
    byte[] extra = getExtraBytes();
    extra[1] = 1; // insert
    return extra;
  }

  public DSAGroup getCryptoGroup() {
    return Global.DSAgroupBigA;
  }
 
}
TOP

Related Classes of freenet.keys.InsertableClientSSK

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.