• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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