Package freenet.keys

Source Code of freenet.keys.ClientSSKBlock

/* 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.util.Arrays;

import freenet.crypt.PCFBMode;
import freenet.crypt.UnsupportedCipherException;
import freenet.crypt.ciphers.Rijndael;
import freenet.support.Logger;
import freenet.support.api.Bucket;
import freenet.support.api.BucketFactory;
import freenet.support.io.ArrayBucket;
import freenet.support.io.ArrayBucketFactory;
import freenet.support.io.BucketTools;

public class ClientSSKBlock implements ClientKeyBlock {
 
  static final int DATA_DECRYPT_KEY_LENGTH = 32;
 
  static public final int MAX_DECOMPRESSED_DATA_LENGTH = 32768;
 
  private final SSKBlock block;
  /** Is metadata. Set on decode. */
  private boolean isMetadata;
  /** Has decoded? */
  private boolean decoded;
  /** Client-key. This contains the decryption key etc. */
  private final ClientSSK key;

  /** Compression algorithm from last time tried to decompress. */
  private short compressionAlgorithm = -1;
 
  public ClientSSKBlock(byte[] data, byte[] headers, ClientSSK key, boolean dontVerify) throws SSKVerifyException {
    block = new SSKBlock(data, headers, (NodeSSK) key.getNodeKey(true), dontVerify);
    this.key = key;
  }
 
  public static ClientSSKBlock construct(SSKBlock block, ClientSSK key) throws SSKVerifyException {
    // Constructor expects clientkey to have the pubkey.
    // In the case of binary blobs, the block may have it instead.
    if(key.getPubKey() == null)
      key.setPublicKey(block.getPubKey());
    return new ClientSSKBlock(block.data, block.headers, key, false);
  }
 
  /**
   * Decode the data.
   */
  @Override
  public Bucket decode(BucketFactory factory, int maxLength, boolean dontDecompress) throws KeyDecodeException, IOException {
    /* We know the signature is valid because it is checked in the constructor. */
    /* We also know e(h(docname)) is valid */
    byte[] decryptedHeaders = new byte[SSKBlock.ENCRYPTED_HEADERS_LENGTH];
    System.arraycopy(block.headers, block.headersOffset, decryptedHeaders, 0, SSKBlock.ENCRYPTED_HEADERS_LENGTH);
    Rijndael aes;
    try {
      Logger.minor(this, "cryptoAlgorithm="+key.cryptoAlgorithm+" for "+getClientKey().getURI());
      aes = new Rijndael(256,256);
    } catch (UnsupportedCipherException e) {
      throw new Error(e);
    }
    aes.initialize(key.cryptoKey);
    // ECB-encrypted E(H(docname)) serves as IV.
    PCFBMode pcfb = PCFBMode.create(aes, key.ehDocname);
    pcfb.blockDecipher(decryptedHeaders, 0, decryptedHeaders.length);
    // First 32 bytes are the key
    byte[] dataDecryptKey = Arrays.copyOf(decryptedHeaders, DATA_DECRYPT_KEY_LENGTH);
    aes.initialize(dataDecryptKey);
    byte[] dataOutput = block.data.clone();
    // Data decrypt key should be unique, so use it as IV
    pcfb.reset(dataDecryptKey);
    pcfb.blockDecipher(dataOutput, 0, dataOutput.length);
    // 2 bytes - data length
    int dataLength = ((decryptedHeaders[DATA_DECRYPT_KEY_LENGTH] & 0xff) << 8) +
      (decryptedHeaders[DATA_DECRYPT_KEY_LENGTH+1] & 0xff);
    // Metadata flag is top bit
    if((dataLength & 32768) != 0) {
      dataLength = dataLength & ~32768;
      isMetadata = true;
    }
    if(dataLength > dataOutput.length) {
      throw new SSKDecodeException("Data length: "+dataLength+" but data.length="+dataOutput.length);
    }
   
        compressionAlgorithm = (short)(((decryptedHeaders[DATA_DECRYPT_KEY_LENGTH+2] & 0xff) << 8) + (decryptedHeaders[DATA_DECRYPT_KEY_LENGTH+3] & 0xff));
        decoded = true;
       
        if(dontDecompress) {
          if(compressionAlgorithm == (short)-1)
            return BucketTools.makeImmutableBucket(factory, dataOutput, dataLength);
          else if(dataLength < 2)
            throw new SSKDecodeException("Data length is less than 2 yet compressed!");
          else
            return BucketTools.makeImmutableBucket(factory, dataOutput, 2, dataLength - 2);
        }

        Bucket b = Key.decompress(compressionAlgorithm >= 0, dataOutput, dataLength, factory, Math.min(MAX_DECOMPRESSED_DATA_LENGTH, maxLength), compressionAlgorithm, true);
        return b;
  }

  @Override
  public boolean isMetadata() {
    if(!decoded)
      throw new IllegalStateException("Cannot read isMetadata before decoded");
    return isMetadata;
  }

  @Override
  public ClientSSK getClientKey() {
    return key;
  }

  public short getCompressionCodec() {
    return compressionAlgorithm;
  }
 
  @Override
  public byte[] memoryDecode() throws KeyDecodeException {
    return memoryDecode(false);
  }
 
    /**
     * Decode into RAM, if short.
     * @throws KeyDecodeException
     */
  public byte[] memoryDecode(boolean dontDecompress) throws KeyDecodeException {
    try {
      ArrayBucket a = (ArrayBucket) decode(new ArrayBucketFactory(), 32*1024, dontDecompress);
      return BucketTools.toByteArray(a); // FIXME
    } catch (IOException e) {
      throw new Error(e);
    }
  }

  @Override
  public int hashCode() {
    return block.hashCode() ^ key.hashCode();
  }

  /** Return true if this is the same block as the other ClientSSKBlock, *and* it is the same key */
  @Override
  public boolean equals(Object o) {
    if(!(o instanceof ClientSSKBlock)) return false;
    ClientSSKBlock block = (ClientSSKBlock) o;
    if(!key.equals(block.key)) return false;
    return this.block.equals(block.block);
  }

  @Override
  public KeyBlock getBlock() {
    return block;
  }

  @Override
  public Key getKey() {
    return block.getKey();
  }
 
}
TOP

Related Classes of freenet.keys.ClientSSKBlock

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.