All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.crypto.tink.aead.internal.InsecureNonceChaCha20Base Maven / Gradle / Ivy

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package com.google.crypto.tink.aead.internal;

import com.google.crypto.tink.subtle.Bytes;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;

/**
 * Abstract base class for {@link InsecureNonceChaCha20}.
 *
 * 

ChaCha20 and XChaCha20 have two differences: the size of the nonce and the initial state of * the block function that produces a key stream block from a key, a nonce, and a counter. * *

Concrete implementations of this class are meant to be used to construct an {@link * com.google.crypto.tink.Aead} with {@link com.google.crypto.tink.subtle.Poly1305}. * *

Since this class supports user-supplied nonces, which would be insecure if the nonce ever * repeates, most users should not use this class directly. */ abstract class InsecureNonceChaCha20Base { int[] key; private final int initialCounter; public InsecureNonceChaCha20Base(final byte[] key, int initialCounter) throws InvalidKeyException { if (key.length != ChaCha20Util.KEY_SIZE_IN_BYTES) { throw new InvalidKeyException("The key length in bytes must be 32."); } this.key = ChaCha20Util.toIntArray(key); this.initialCounter = initialCounter; } /** Returns the initial state from {@code nonce} and {@code counter}. */ abstract int[] createInitialState(final int[] nonce, int counter); /** * The size of the randomly generated nonces. * *

ChaCha20 uses 12-byte nonces, but XChaCha20 use 24-byte nonces. */ abstract int nonceSizeInBytes(); /** Encrypts {@code plaintext} using {@code nonce}. */ public byte[] encrypt(final byte[] nonce, final byte[] plaintext) throws GeneralSecurityException { ByteBuffer ciphertext = ByteBuffer.allocate(plaintext.length); encrypt(ciphertext, nonce, plaintext); return ciphertext.array(); } /** Encrypts {@code plaintext} using {@code nonce} and writes result to {@code output}. */ public void encrypt(ByteBuffer output, final byte[] nonce, final byte[] plaintext) throws GeneralSecurityException { if (output.remaining() < plaintext.length) { throw new IllegalArgumentException("Given ByteBuffer output is too small"); } process(nonce, output, ByteBuffer.wrap(plaintext)); } /** Decrypts {@code ciphertext} using {@code nonce}. */ public byte[] decrypt(final byte[] nonce, final byte[] ciphertext) throws GeneralSecurityException { return decrypt(nonce, ByteBuffer.wrap(ciphertext)); } /** Decrypts {@code ciphertext} using {@code nonce}. */ public byte[] decrypt(final byte[] nonce, ByteBuffer ciphertext) throws GeneralSecurityException { ByteBuffer plaintext = ByteBuffer.allocate(ciphertext.remaining()); process(nonce, plaintext, ciphertext); return plaintext.array(); } private void process(final byte[] nonce, ByteBuffer output, ByteBuffer input) throws GeneralSecurityException { if (nonce.length != nonceSizeInBytes()) { throw new GeneralSecurityException( "The nonce length (in bytes) must be " + nonceSizeInBytes()); } int length = input.remaining(); int numBlocks = (length / ChaCha20Util.BLOCK_SIZE_IN_BYTES) + 1; for (int i = 0; i < numBlocks; i++) { ByteBuffer keyStreamBlock = chacha20Block(nonce, i + initialCounter); if (i == numBlocks - 1) { // last block Bytes.xor(output, input, keyStreamBlock, length % ChaCha20Util.BLOCK_SIZE_IN_BYTES); } else { Bytes.xor(output, input, keyStreamBlock, ChaCha20Util.BLOCK_SIZE_IN_BYTES); } } } // https://tools.ietf.org/html/rfc8439#section-2.3. ByteBuffer chacha20Block(final byte[] nonce, int counter) { int[] state = createInitialState(ChaCha20Util.toIntArray(nonce), counter); int[] workingState = state.clone(); ChaCha20Util.shuffleState(workingState); for (int i = 0; i < state.length; i++) { state[i] += workingState[i]; } ByteBuffer out = ByteBuffer.allocate(ChaCha20Util.BLOCK_SIZE_IN_BYTES).order(ByteOrder.LITTLE_ENDIAN); out.asIntBuffer().put(state, 0, ChaCha20Util.BLOCK_SIZE_IN_INTS); return out; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy