1 // Copyright 2017 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink; 18 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.io.OutputStream; 22 import java.nio.channels.ReadableByteChannel; 23 import java.nio.channels.SeekableByteChannel; 24 import java.nio.channels.WritableByteChannel; 25 import java.security.GeneralSecurityException; 26 27 /** 28 * An interface for streaming authenticated encryption with associated data. 29 * 30 * <p>Streaming encryption is typically used for encrypting large plaintexts such as large files. 31 * Tink may eventually contain multiple interfaces for streaming encryption depending on the 32 * supported properties. This interface supports a streaming interface for symmetric encryption with 33 * authentication. The underlying encryption modes are selected so that partial plaintext can be 34 * obtained fast by decrypting and authenticating just a part of the ciphertext. 35 * 36 * <h3>Security guarantees</h3> 37 * 38 * <p>Instances of StreamingAead must follow the nOAE definition as proposed in the paper "Online 39 * Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance" by Hoang, Reyhanitabar, Rogaway 40 * and Vizár https://eprint.iacr.org/2015/189.pdf 41 * 42 * <h3>Restrictions</h3> 43 * 44 * <p>Encryption must be done in one session. There is no possibility to modify an existing 45 * ciphertext or append to it (other than reencrypt the whole file again). One reason for this 46 * restriction is the use of AES-GCM as one cipher to implement this interface. If single segments 47 * are modified then this is equivalent to reusing the same IV twice, but reusing an IV twice leaks 48 * an AES-GCM key. Another reason is that implementations of this interface have no protection 49 * against roll-back attacks: an attacker can always try to restore a previous version of the file 50 * without detection. 51 * 52 * <h3>Blocking vs non-blocking I/O</h3> 53 * 54 * <p>A channel can be in a blocking mode (i.e. always waits until the requested number of bytes 55 * have been processed) or non-blocking mode (i.e. I/O operation will never block and may transfer 56 * fewer bytes than were requested or possibly no bytes at all). 57 * 58 * <p>If the channel provided to the streaming encryption is in blocking mode then encryption and 59 * decryption have the same property. That is, encryption always processes all the plaintext passed 60 * in, and waits until complete segments have been written to the ciphertext channel (incomplete 61 * segment, if any, is buffered). Similarly, decryption blocks until sufficiently many bytes have 62 * been read from the ciphertext channel so that all the requested plaintext can be decrypted and 63 * authenticated, or until the end of the plaintext has been reached, or an IOException occurred. 64 * 65 * <p>If the channel provided to the streaming encryption is in non-blocking mode, then encryption 66 * and decryption are also non-blocking. Since encryption and decryption is done in segments it is 67 * possible that for example a call attempting to read() returns no plaintext at all even if partial 68 * ciphertext was read from the underlying channel. 69 * 70 * <h3>Sample encryption</h3> 71 * 72 * <pre>{@code 73 * StreamingAead s = ... 74 * java.nio.channels.FileChannel ciphertextDestination = 75 * new FileOutputStream(ciphertextFile).getChannel(); 76 * byte[] aad = ... 77 * WritableByteChannel encryptingChannel = s.newEncryptingChannel(ciphertextDestination, aad); 78 * while ( ... ) { 79 * int r = encryptingChannel.write(buffer); 80 * ... 81 * } 82 * encryptingChannel.close(); 83 * }</pre> 84 * 85 * <h3>Sample full decryption</h3> 86 * 87 * <pre>{@code 88 * StreamingAead s = ... 89 * java.nio.channels.FileChannel ciphertextSource = 90 * new FileInputStream(ciphertextFile).getChannel(); 91 * byte[] aad = ... 92 * ReadableByteChannel decryptingChannel = s.newDecryptingChannel(ciphertextSource, aad); 93 * int chunkSize = ... 94 * ByteBuffer buffer = ByteBuffer.allocate(chunkSize); 95 * do { 96 * buffer.clear(); 97 * int cnt = decryptingChannel.read(buffer); 98 * if (cnt > 0) { 99 * // Process cnt bytes of plaintext. 100 * } else if (read == -1) { 101 * // End of plaintext detected. 102 * break; 103 * } else if (read == 0) { 104 * // No ciphertext is available at the moment. 105 * } 106 * } 107 * }</pre> 108 * 109 * @since 1.1.0 110 */ 111 public interface StreamingAead { 112 113 /** 114 * Returns a WritableByteChannel for plaintext. Any data written to the returned 115 * channel will be encrypted and the resulting ciphertext written to the provided 116 * {@code ciphertextDestination} 117 * 118 * @param ciphertextDestination the channel to which the ciphertext is written. 119 * @param associatedData data associated with the plaintext. This data is authenticated 120 * but not encrypted. It must be passed into the decryption. 121 */ newEncryptingChannel( WritableByteChannel ciphertextDestination, byte[] associatedData)122 WritableByteChannel newEncryptingChannel( 123 WritableByteChannel ciphertextDestination, byte[] associatedData) 124 throws GeneralSecurityException, IOException; 125 126 /** 127 * Returns a SeekableByteChannel that allows to access the plaintext. 128 * 129 * <p>This method does not work on Android Marshmallow (API level 23) or older because these 130 * Android versions don't have the java.nio.channels.SeekableByteChannel interface. 131 * 132 * @param ciphertextSource the ciphertext 133 * @param associatedData the data associated with the ciphertext. 134 * @return {@link SeekableByteChannel} that allows random read access to the plaintext. The 135 * following methods of SeekableByteChannel are implemented: 136 * <ul> 137 * <li>{@code long position()} Returns the channel's position in the plaintext. 138 * <li>{@code SeekableByteChannel position(long newPosition)} Sets the channel's position. 139 * Setting the position to a value greater than the plaintext size is legal. A later 140 * attempt to read byte will immediately return an end-of-file indication. 141 * <li>{@code int read(ByteBuffer dst)} Bytes are read starting at the channel's position, 142 * and then the position is updated with the number of bytes actually read. All bytes 143 * returned have been authenticated. If the end of the stream has been reached -1 is 144 * returned. A result of -1 is authenticated (e.g. by checking the MAC of the last 145 * ciphertext chunk.) A call to this function attempts to fill dst, but it may return 146 * fewer bytes than requested, e.g. if the underlying ciphertextSource does not provide 147 * the requested number of bytes or if the plaintext ended. 148 * <p>Throws {@link IOException} if a MAC verification failed. TODO: Should we extend 149 * the interface with read(ByteBuffer dst, long position) to avoid race conditions? 150 * <li>{@code long size()} Returns the size of the plaintext. TODO: Decide whether the 151 * result should be authenticated) 152 * <li>{@code SeekableByteChannel truncate(long size)} throws {@link 153 * java.nio.channels.NonWritableChannelException } because the channel is read-only. 154 * <li>{@code int write(ByteBuffer src)} throws {@link 155 * java.nio.channels.NonWritableChannelException } because the channel is read-only. 156 * <li>{@code close()} closes the channel 157 * <li>{@code isOpen()} 158 * </ul> 159 * 160 * @throws GeneralSecurityException if the header of the ciphertext is corrupt or if 161 * associatedData is not correct. 162 * @throws IOException if an IOException occurred while reading from ciphertextDestination. 163 */ newSeekableDecryptingChannel( SeekableByteChannel ciphertextSource, byte[] associatedData)164 SeekableByteChannel newSeekableDecryptingChannel( 165 SeekableByteChannel ciphertextSource, byte[] associatedData) 166 throws GeneralSecurityException, IOException; 167 newDecryptingChannel( ReadableByteChannel ciphertextSource, byte[] associatedData)168 ReadableByteChannel newDecryptingChannel( 169 ReadableByteChannel ciphertextSource, byte[] associatedData) 170 throws GeneralSecurityException, IOException; 171 172 /** 173 * Returns a wrapper around {@code ciphertextDestination}, such that any write-operation via 174 * the wrapper results in AEAD-encryption of the written data, using {@code associatedData} 175 * as associated authenticated data. The associated data is not included in the ciphertext 176 * and has to be passed in as parameter for decryption. 177 */ newEncryptingStream(OutputStream ciphertextDestination, byte[] associatedData)178 OutputStream newEncryptingStream(OutputStream ciphertextDestination, byte[] associatedData) 179 throws GeneralSecurityException, IOException; 180 181 /** 182 * Returns a wrapper around {@code ciphertextSource}, such that any read-operation 183 * via the wrapper results in AEAD-decryption of the underlying ciphertext, 184 * using {@code associatedData} as associated authenticated data. 185 * 186 * <p>The returned InputStream may support {@code mark()}/{@code reset()}, 187 * but does not have to do it -- {@code markSupported()} provides the corresponding info. 188 * 189 * <p>The returned InputStream supports {@code skip()}, yet possibly in an inefficient way, 190 * i.e. by reading a sequence of blocks until the desired position. If a more efficient 191 * {@code skip()}-functionality is needed, the Channel-based API can be used. 192 */ newDecryptingStream(InputStream ciphertextSource, byte[] associatedData)193 InputStream newDecryptingStream(InputStream ciphertextSource, byte[] associatedData) 194 throws GeneralSecurityException, IOException; 195 } 196