All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.crypto.tink.subtle.StreamingAeadEncryptingChannel Maven / Gradle / Ivy

// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package com.google.crypto.tink.subtle;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.WritableByteChannel;
import java.security.GeneralSecurityException;

/**
 * An instance of {@link WritableByteChannel} that encrypts the input using a nonce based online
 * authentication scheme.
 */
class StreamingAeadEncryptingChannel implements WritableByteChannel {
  private WritableByteChannel ciphertextChannel;
  private StreamSegmentEncrypter encrypter;
  ByteBuffer ptBuffer; // contains plaintext that has not yet been encrypted.
  ByteBuffer ctBuffer; // contains ciphertext that has not been written to ciphertextChannel.
  private int plaintextSegmentSize;
  boolean open = true;

  public StreamingAeadEncryptingChannel(
      NonceBasedStreamingAead streamAead,
      WritableByteChannel ciphertextChannel,
      byte[] associatedData) throws GeneralSecurityException, IOException {
    this.ciphertextChannel = ciphertextChannel;
    encrypter = streamAead.newStreamSegmentEncrypter(associatedData);
    plaintextSegmentSize = streamAead.getPlaintextSegmentSize();
    ptBuffer = ByteBuffer.allocate(plaintextSegmentSize);
    ptBuffer.limit(plaintextSegmentSize - streamAead.getCiphertextOffset());
    ctBuffer = ByteBuffer.allocate(streamAead.getCiphertextSegmentSize());
    // At this point, ciphertextChannel might not yet be ready to receive bytes.
    // Buffering the header in ctBuffer ensures that the header will be written when writing to
    // ciphertextChannel is possible.
    ctBuffer.put(encrypter.getHeader());
    ctBuffer.flip();
    ciphertextChannel.write(ctBuffer);
  }

  @Override
  public synchronized int write(ByteBuffer pt) throws IOException {
    if (!open) {
      throw new ClosedChannelException();
    }
    if (ctBuffer.remaining() > 0) {
      ciphertextChannel.write(ctBuffer);
    }
    int startPosition = pt.position();
    while (pt.remaining() > ptBuffer.remaining()) {
      if (ctBuffer.remaining() > 0) {
        return pt.position() - startPosition;
      }
      int sliceSize = ptBuffer.remaining();
      ByteBuffer slice = pt.slice();
      slice.limit(sliceSize);
      pt.position(pt.position() + sliceSize);
      try {
        ptBuffer.flip();
        ctBuffer.clear();
        if (slice.remaining() != 0) {
          encrypter.encryptSegment(ptBuffer, slice, false, ctBuffer);
        } else {
          encrypter.encryptSegment(ptBuffer, false, ctBuffer);
        }
      } catch (GeneralSecurityException ex) {
        throw new IOException(ex);
      }
      ctBuffer.flip();
      ciphertextChannel.write(ctBuffer);
      ptBuffer.clear();
      ptBuffer.limit(plaintextSegmentSize);
    }
    ptBuffer.put(pt);
    return pt.position() - startPosition;
  }

  @Override
  public synchronized void close() throws IOException {
    if (!open) {
      return;
    }
    // TODO(bleichen): Is there a way to fully write the remaining ciphertext?
    //   The following is the strategy from java.nio.channels.Channels.writeFullyImpl
    //   I.e. try writing as long as at least one byte is written.
    while (ctBuffer.remaining() > 0) {
      int n = ciphertextChannel.write(ctBuffer);
      if (n <= 0) {
        throw new IOException("Failed to write ciphertext before closing");
      }
    }
    try {
      ctBuffer.clear();
      ptBuffer.flip();
      encrypter.encryptSegment(ptBuffer, true, ctBuffer);
    } catch (GeneralSecurityException ex) {
      // TODO(bleichen): define the state of this. E.g. open = false;
      throw new IOException(ex);
    }
    ctBuffer.flip();
    while (ctBuffer.remaining() > 0) {
      int n = ciphertextChannel.write(ctBuffer);
      if (n <= 0) {
        throw new IOException("Failed to write ciphertext before closing");
      }
    }
    ciphertextChannel.close();
    open = false;
  }

  @Override
  public boolean isOpen() {
    return open;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy