• 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 static com.google.crypto.tink.internal.Util.isPrefix;
20 
21 import com.google.crypto.tink.AccessesPartialKey;
22 import com.google.crypto.tink.Aead;
23 import com.google.crypto.tink.InsecureSecretKeyAccess;
24 import com.google.crypto.tink.aead.ChaCha20Poly1305Key;
25 import com.google.crypto.tink.aead.internal.InsecureNonceChaCha20Poly1305;
26 import com.google.crypto.tink.aead.internal.Poly1305;
27 import java.nio.ByteBuffer;
28 import java.security.GeneralSecurityException;
29 import java.util.Arrays;
30 
31 /**
32  * ChaCha20Poly1305 AEAD construction, as described in <a
33  * href="https://tools.ietf.org/html/rfc8439#section-2.8">RFC 8439, section 2.8</a>.
34  *
35  * @since 1.1.0
36  */
37 public final class ChaCha20Poly1305 implements Aead {
38   private final InsecureNonceChaCha20Poly1305 cipher;
39   private final byte[] outputPrefix;
40 
ChaCha20Poly1305(final byte[] key, final byte[] outputPrefix)41   private ChaCha20Poly1305(final byte[] key, final byte[] outputPrefix)
42       throws GeneralSecurityException {
43     cipher = new InsecureNonceChaCha20Poly1305(key);
44     this.outputPrefix = outputPrefix;
45   }
46 
ChaCha20Poly1305(final byte[] key)47   public ChaCha20Poly1305(final byte[] key) throws GeneralSecurityException {
48     this(key, new byte[0]);
49   }
50 
51   @AccessesPartialKey
create(ChaCha20Poly1305Key key)52   public static Aead create(ChaCha20Poly1305Key key) throws GeneralSecurityException {
53     return new ChaCha20Poly1305(
54         key.getKeyBytes().toByteArray(InsecureSecretKeyAccess.get()),
55         key.getOutputPrefix().toByteArray());
56   }
57 
rawEncrypt(final byte[] plaintext, final byte[] associatedData)58   private byte[] rawEncrypt(final byte[] plaintext, final byte[] associatedData)
59       throws GeneralSecurityException {
60     ByteBuffer output =
61         ByteBuffer.allocate(
62             ChaCha20.NONCE_LENGTH_IN_BYTES + plaintext.length + Poly1305.MAC_TAG_SIZE_IN_BYTES);
63     byte[] nonce = Random.randBytes(ChaCha20.NONCE_LENGTH_IN_BYTES);
64     output.put(nonce); // Prepend nonce to ciphertext output.
65     cipher.encrypt(output, nonce, plaintext, associatedData);
66     return output.array();
67   }
68 
69   @Override
encrypt(final byte[] plaintext, final byte[] associatedData)70   public byte[] encrypt(final byte[] plaintext, final byte[] associatedData)
71       throws GeneralSecurityException {
72     byte[] ciphertext = rawEncrypt(plaintext, associatedData);
73     if (outputPrefix.length == 0) {
74       return ciphertext;
75     }
76     return Bytes.concat(outputPrefix, ciphertext);
77   }
78 
rawDecrypt(final byte[] ciphertext, final byte[] associatedData)79   private byte[] rawDecrypt(final byte[] ciphertext, final byte[] associatedData)
80       throws GeneralSecurityException {
81     if (ciphertext.length < ChaCha20.NONCE_LENGTH_IN_BYTES + Poly1305.MAC_TAG_SIZE_IN_BYTES) {
82       throw new GeneralSecurityException("ciphertext too short");
83     }
84     byte[] nonce = Arrays.copyOf(ciphertext, ChaCha20.NONCE_LENGTH_IN_BYTES);
85     ByteBuffer rawCiphertext =
86         ByteBuffer.wrap(
87             ciphertext,
88             ChaCha20.NONCE_LENGTH_IN_BYTES,
89             ciphertext.length - ChaCha20.NONCE_LENGTH_IN_BYTES);
90     return cipher.decrypt(rawCiphertext, nonce, associatedData);
91   }
92 
93   @Override
decrypt(final byte[] ciphertext, final byte[] associatedData)94   public byte[] decrypt(final byte[] ciphertext, final byte[] associatedData)
95       throws GeneralSecurityException {
96     if (outputPrefix.length == 0) {
97       return rawDecrypt(ciphertext, associatedData);
98     }
99     if (!isPrefix(outputPrefix, ciphertext)) {
100       throw new GeneralSecurityException("Decryption failed (OutputPrefix mismatch).");
101     }
102     byte[] copiedCiphertext =
103         Arrays.copyOfRange(ciphertext, outputPrefix.length, ciphertext.length);
104     return rawDecrypt(copiedCiphertext, associatedData);
105   }
106 }
107