• 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.subtle;
18 
19 import java.io.FilterOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.nio.ByteBuffer;
23 import java.security.GeneralSecurityException;
24 
25 /**
26  * An instance of {@link FilterOutputStream} that encrypts the input using a nonce based online
27  * authentication scheme.
28  */
29 class StreamingAeadEncryptingStream extends FilterOutputStream {
30   private StreamSegmentEncrypter encrypter;
31   private int plaintextSegmentSize;
32   ByteBuffer ptBuffer; // contains plaintext that has not yet been encrypted.
33   ByteBuffer ctBuffer; // used for the ciphertext (does not buffer anything).
34   boolean open;
35 
StreamingAeadEncryptingStream( NonceBasedStreamingAead streamAead, OutputStream ciphertextChannel, byte[] associatedData)36   public StreamingAeadEncryptingStream(
37       NonceBasedStreamingAead streamAead, OutputStream ciphertextChannel, byte[] associatedData)
38       throws GeneralSecurityException, IOException {
39     super(ciphertextChannel);
40     encrypter = streamAead.newStreamSegmentEncrypter(associatedData);
41     plaintextSegmentSize = streamAead.getPlaintextSegmentSize();
42     ptBuffer = ByteBuffer.allocate(plaintextSegmentSize);
43     ctBuffer = ByteBuffer.allocate(streamAead.getCiphertextSegmentSize());
44     ptBuffer.limit(plaintextSegmentSize - streamAead.getCiphertextOffset());
45     ByteBuffer header = encrypter.getHeader();
46     byte[] headerBytes = new byte[header.remaining()];
47     header.get(headerBytes);
48     out.write(headerBytes);
49     open = true;
50   }
51 
52   @Override
write(int b)53   public void write(int b) throws IOException {
54     write(new byte[] {(byte) b});
55   }
56 
57   @Override
write(byte[] b)58   public void write(byte[] b) throws IOException {
59     write(b, 0, b.length);
60   }
61 
62   // TODO(bleichen): Mabye implement write(ByteBuffer) so that
63   //   there are no surprises if the underlying class is extended.
64 
65   @Override
write(byte[] pt, int offset, int length)66   public synchronized void write(byte[] pt, int offset, int length) throws IOException {
67     if (!open) {
68       throw new IOException("Trying to write to closed stream");
69     }
70     int startPosition = offset;
71     int remaining = length;
72     while (remaining > ptBuffer.remaining()) {
73       int sliceSize = ptBuffer.remaining();
74       ByteBuffer slice = ByteBuffer.wrap(pt, startPosition, sliceSize);
75       startPosition += sliceSize;
76       remaining -= sliceSize;
77       try {
78         ptBuffer.flip();
79         ctBuffer.clear();
80         encrypter.encryptSegment(ptBuffer, slice, false, ctBuffer);
81       } catch (GeneralSecurityException ex) {
82         throw new IOException(ex);
83       }
84       ctBuffer.flip();
85       out.write(ctBuffer.array(), ctBuffer.position(), ctBuffer.remaining());
86       ptBuffer.clear();
87       ptBuffer.limit(plaintextSegmentSize);
88     }
89     ptBuffer.put(pt, startPosition, remaining);
90   }
91 
92   @Override
close()93   public synchronized void close() throws IOException {
94     if (!open) {
95       return;
96     }
97     try {
98       ptBuffer.flip();
99       ctBuffer.clear();
100       encrypter.encryptSegment(ptBuffer, true, ctBuffer);
101     } catch (GeneralSecurityException ex) {
102       // TODO(bleichen): define the state of this. E.g. open = false;
103       throw new IOException(
104           "ptBuffer.remaining():"
105               + ptBuffer.remaining()
106               + " ctBuffer.remaining():"
107               + ctBuffer.remaining(),
108           ex);
109     }
110     ctBuffer.flip();
111     out.write(ctBuffer.array(), ctBuffer.position(), ctBuffer.remaining());
112     open = false;
113     super.close();
114   }
115 }
116