• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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 com.google.crypto.tink.aead.internal.InsecureNonceXChaCha20;
20 import java.nio.ByteBuffer;
21 import java.security.GeneralSecurityException;
22 import java.security.InvalidKeyException;
23 import java.util.Arrays;
24 
25 /**
26  * {@link XChaCha20} stream cipher based on
27  * https://download.libsodium.org/doc/advanced/xchacha20.html and
28  * https://tools.ietf.org/html/draft-arciszewski-xchacha-01.
29  *
30  * <p>This cipher is meant to be used to construct an AEAD with Poly1305.
31  */
32 class XChaCha20 implements IndCpaCipher {
33   static final int NONCE_LENGTH_IN_BYTES = 24;
34 
35   private final InsecureNonceXChaCha20 cipher;
36 
37   /**
38    * Constructs a new XChaCha20 cipher with the supplied {@code key}.
39    *
40    * @throws IllegalArgumentException when {@code key} length is not {@link
41    *     com.google.crypto.tink.aead.internal.ChaCha20Util#KEY_SIZE_IN_BYTES}.
42    */
XChaCha20(byte[] key, int initialCounter)43   XChaCha20(byte[] key, int initialCounter) throws InvalidKeyException {
44     cipher = new InsecureNonceXChaCha20(key, initialCounter);
45   }
46 
47   @Override
encrypt(final byte[] plaintext)48   public byte[] encrypt(final byte[] plaintext) throws GeneralSecurityException {
49     ByteBuffer output = ByteBuffer.allocate(NONCE_LENGTH_IN_BYTES + plaintext.length);
50     byte[] nonce = Random.randBytes(NONCE_LENGTH_IN_BYTES);
51     output.put(nonce); // Prepend nonce to ciphertext output.
52     cipher.encrypt(output, nonce, plaintext);
53     return output.array();
54   }
55 
56   @Override
decrypt(final byte[] ciphertext)57   public byte[] decrypt(final byte[] ciphertext) throws GeneralSecurityException {
58     if (ciphertext.length < NONCE_LENGTH_IN_BYTES) {
59       throw new GeneralSecurityException("ciphertext too short");
60     }
61     byte[] nonce = Arrays.copyOf(ciphertext, NONCE_LENGTH_IN_BYTES);
62     ByteBuffer rawCiphertext =
63         ByteBuffer.wrap(
64             ciphertext, NONCE_LENGTH_IN_BYTES, ciphertext.length - NONCE_LENGTH_IN_BYTES);
65     return cipher.decrypt(nonce, rawCiphertext);
66   }
67 }
68