Example #1
0
  /**
   * Flush the file, computing the hash if necessary. After this method completes the hash obtained
   * in {@link #getHeader()} will be valid, but the file will not be closed. If you want the file to
   * be closed, use {@link #close()} instead, which invokes this method.
   *
   * <p><b>Warning</b>: Because of the hash computation this method can be costly! After calling
   * this the internal hash computation must be completely reset, so a subsequent write will cause
   * the hash to be updated from scratch. Use caution with this method. In fact, this is the reason
   * this method is not named {@code flush}.
   *
   * @throws IOException An error occurred writing the file.
   */
  public void finish() throws IOException {
    if (!_open) return;

    // Save the current position so we can restore it later.
    long pos = _backing.getFilePointer();

    // If the hash is not valid, compute it now.
    if (!_hashvalid) {
      // The hash is not valid. Complete the computation now and store
      // the resulting hash.
      _backing.seek(_backing.length());
      _updateDigest();
      _head.hash = _digest.digest();
      _hashvalid = true;

      // Reset the digest now to force it to be re-computed next time.
      // This must be done since we just "used up" the existing digest
      // instance.
      _resetDigest();
    }

    // Write the header to the backing store.
    _backing.seek(PicoStructure.HEAD_START);
    _backing.write(_head.putHeader());

    // Restore the file position.
    _backing.seek(pos);
  }
Example #2
0
  /**
   * Finish the stream, writing the header and the encrypted data to the stream. The underlying
   * stream is not closed. If you want to cause the underlying stream to be closed, too, use {@code
   * close()}, which invokes this method and then closes the stream.
   *
   * @throws IOException An error occurred writing the file.
   */
  public void finish() throws IOException {
    // Finish the hash and store it in the header.
    _head.hash = _hash.digest();

    // Write the header to the backing store.
    _backing.write(_head.putHeader());

    // Write the encrypted data to the backing store.
    _encrypted.flush();
    _encrypted.close();

    // Now we perform that transfer from temporary file to final file.
    InputStream fis = new FileInputStream(_tmpfile);
    byte[] buffer = new byte[1024];
    while (fis.available() > 0) {
      int length = fis.read(buffer);
      if (length >= 0) {
        _backing.write(buffer, 0, length);
      }
    } // Copy all encrypted data.
    fis.close();
    _backing.flush();
    _tmpfile.delete();
    _closed = true;
  }
Example #3
0
 /**
  * Get the header for this Pico file. The returned header is a copy of the actual header.
  *
  * <p>Note that the returned hash may be {@code null} if it has not been computed. If the file has
  * been opened, but not written, then the hash will be available via this method.
  *
  * @return The header of this file.
  * @throws IOException The file length cannot be read.
  */
 public PicoHeader getHeader() throws IOException {
   PicoHeader head = _head.clone();
   if (!_hashvalid) {
     head.hash = null;
   }
   return head;
 }
Example #4
0
  /*
   * (non-Javadoc)
   *
   * @see java.nio.channels.SeekableByteChannel#read(java.nio.ByteBuffer)
   */
  @Override
  public int read(ByteBuffer dst) throws IOException {
    if (dst == null) {
      throw new NullPointerException("The destination buffer is null.");
    }
    if (dst.limit() == 0) {
      throw new IllegalArgumentException("The destination buffer has zero length.");
    }
    if (!_open) return -1;

    // Make an attempt to read up to r bytes from the channel, where
    // r is the number of bytes remaining in the buffer, that is,
    // dst.remaining(), at the moment this method is invoked.
    long _here = position();

    // Build temporary storage.
    int remain = dst.remaining();
    byte[] data = new byte[remain];

    int length = _backing.read(data, 0, remain);

    if (length > 0) {

      // Iterate thru each byte of temporary storage and decrypt those
      // bytes back
      // into the buffer
      for (int index = 0; index < length; index++) {
        data[index] = _head.crypt(data[index], index + _here);
      } // Decrypt all bytes.
      dst.put(data, 0, length);
    }
    return length;
  }
Example #5
0
  @Override
  public void write(byte[] arr, int off, int len) throws IOException {
    if (_closed) return;

    // first make copy of relevant part of array
    // TODO : add bounds checking
    // JMC: Why not just new byte[len]??
    byte[] encodedArr = new byte[len - off];
    System.arraycopy(arr, off, encodedArr, 0, len);

    for (int i = off; i < (off + len); i++) {
      byte b = arr[i];

      // Add to the message digest.
      _hash.update((byte) b);

      // Encode.
      b = _head.crypt((byte) b, _position);
      _position++;

      // Add encoded byte to b
      encodedArr[i] = b;
    }

    // Write.
    _encrypted.write(encodedArr);
    //        System.out.println("SBJHBKJDBH");
  }
Example #6
0
  /**
   * Make a new Pico output stream, wrapping the provided stream. Use the given key to encrypt the
   * data.
   *
   * @param key The key to use to encrypt.
   * @param os The stream to get the output.
   * @throws IOException An error occurred creating the temporary file.
   */
  public PicoOutputStream(byte[] key, OutputStream os) throws IOException {
    super(os);

    if (key == null) {
      throw new NullPointerException("The key is null.");
    }
    if (os == null) {
      throw new NullPointerException("The output stream is null.");
    }
    _backing = os;

    try {
      _hash = MessageDigest.getInstance(PicoStructure.HASH);
    } catch (NoSuchAlgorithmException nsae) {
      throw new RuntimeException("Failed to create hash.", nsae);
    }

    // Construct a temporary file to get the encrypted data.
    _tmpfile = File.createTempFile("pico", "pico");
    _tmpfile.deleteOnExit();
    _encrypted = new FileOutputStream(_tmpfile);
    // Build the header.
    _head = new PicoHeader();
    _head.setKey(key);
  }
Example #7
0
 /**
  * Read the next byte at the current position, and return it. Return -1 if the end of the file is
  * read.
  *
  * @return The next byte.
  * @throws IOException The next byte cannot be read.
  */
 public int read() throws IOException {
   if (!_open) return -1;
   long _here = position();
   int val = _backing.read();
   if (val >= 0) {
     val = _head.crypt((byte) val, _here);
   }
   return val;
 }
Example #8
0
  /**
   * Read the header. This reads the header from an existing file. After this method completes the
   * header reflects what it stored in the file, including the hash and key.
   *
   * @throws PicoException The header structure is incorrect.
   * @throws IOException The header cannot be read.
   */
  private void _readHeader() throws PicoException, IOException {
    if (!_open) return;

    // Save the current position and move to the start of the header.
    long pos = _backing.getFilePointer();
    _backing.seek(PicoStructure.HEAD_START);

    // Read the header from the file. We read the fixed length portion
    // here, up through the start of the key.
    byte[] _fixedhdr = new byte[(int) PicoStructure.FIXED_HEADER_LENGTH];
    int length = _backing.read(_fixedhdr);

    // Make sure we have enough bytes for the magic string.
    if (length < PicoStructure.MAGIC_LENGTH - PicoStructure.MAGIC_OFFSET) {
      // The magic string was not present. This cannot be a Pico wrapper
      // file.
      throw new PicoException("File too short; missing magic string.");
    }

    // Process the fixed portion of the header. After calling this the
    // key is allocated, but not populated.
    _head = PicoHeader.getHeader(_fixedhdr);

    // The hash is valid because we just read it from the file and nothing
    // has yet been written. All write methods must invalidate the hash.
    _hashvalid = true;

    // Go and read the key, now that we know its length. Note that we read
    // it directly into the array returned by getKey.
    length = _backing.read(_head.getKey());

    // Make sure we have the complete key. The only bad case is that the
    // file ends before the key is complete.
    if (length != _head.getKey().length) {
      throw new PicoException("File too short; incomplete key.");
    }

    // Move to the original position in the file.
    _backing.seek(pos);

    // Ka-presto! The header has been read. Life is good.
  }
Example #9
0
  /**
   * Write a byte at the current position.
   *
   * @param datum The byte.
   * @throws IOException The byte cannot be written.
   */
  public void write(int datum) throws IOException {
    if (!_open) return;
    datum &= 0xff;
    long _here = position();

    // Are we synced up?
    if (_here == _digestvalidto) {

      // Yes, digest the byte and move the valid to pointer.
      _digest.update((byte) datum);
      _digestvalidto++; // JMC: Was missing.
    } // Otherwise, advancing of the position will destroy the digest sync.

    datum = _head.crypt((byte) datum, _here);
    _backing.write(datum);
  }
Example #10
0
  /* (non-Javadoc)
   * @see java.io.OutputStream#write(int)
   */
  @Override
  public void write(int datum) throws IOException {
    if (_closed) return;

    // Byteify this.
    datum &= 0xff;

    // Add to the message digest.
    _hash.update((byte) datum);

    // Encode.
    datum = _head.crypt((byte) datum, _position);

    // Write.
    _encrypted.write(datum);
    _position += 1;
  }
Example #11
0
  /**
   * Create a new Pico file instance from the given random access file. If the file exists and it is
   * not empty then it is truncated. If it does not exist then it is created.
   *
   * @param backing The random access file.
   * @param key The key to use to encrypt the file.
   * @throws IOException The file cannot be read.
   */
  protected PicoFile(RandomAccessFile backing, byte[] key) throws IOException {
    assert backing != null : "Backing is null.";
    assert key != null : "Key is null.";
    assert key.length > 0 : "Key is missing.";
    _backing = backing;
    _open = true;
    _resetDigest();
    // We are creating a new file, so truncate any existing file and
    // generate a new header.
    _backing.setLength(0L);
    _head = new PicoHeader();
    _head.setKey(key);

    // Now the Header size is fixed since we have the key and know the size
    // of the hash
    // we will write later.

    // This actually positions us to _head.offset + 0
    position(0L);
  }
Example #12
0
  /*
   * (non-Javadoc)
   *
   * @see java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
   *
   * NOTE: Changes the state of src in the normal case, i.e., position =
   * limit.
   */
  @Override
  public int write(ByteBuffer src) throws IOException {
    if (src == null) {
      throw new NullPointerException("The source buffer is null.");
    }

    // Is there something to actually write in source.
    if (src.limit() == 0 || src.remaining() == 0) {
      throw new IllegalArgumentException(
          "The source buffer has zero length or zero remaining bytes.");
    }

    if (!_open) return -1;

    // based on the specification _hear is p.
    long _here = position();

    // Make an attempt to write up to r bytes to the channel, where
    // r is the number of bytes remaining in the buffer, that is,
    // src.remaining(), at the moment this method is invoked.
    // NOTE: not good to use the src.array() since that is the backing array
    // which
    // reflects the entire buffer and not from p to remaining.
    byte[] encr = new byte[src.remaining()];

    // Update the digest to the start of the write.
    _updateDigest();

    // Have we reached source's limit?
    for (int index = 0; src.hasRemaining(); index++) {
      // The unencrypted data.
      byte b = src.get();
      _digest.update(b);
      _digestvalidto++;
      // encrypt it into the buffer.
      encr[index] = _head.crypt(b, index + _here);
    }
    _backing.write(encr);
    return encr.length;
  }