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

com.google.crypto.tink.aead.internal.ChaCha20Util 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 java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.Arrays;

/** Internal utility methods for {X}ChaCha20 implementations. */
final class ChaCha20Util {
  static final int BLOCK_SIZE_IN_INTS = 16;
  static final int KEY_SIZE_IN_INTS = 8;
  static final int BLOCK_SIZE_IN_BYTES = BLOCK_SIZE_IN_INTS * 4;
  static final int KEY_SIZE_IN_BYTES = KEY_SIZE_IN_INTS * 4;

  // First four words of the initial state (in little-endian order):
  // 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574.
  // See also https://datatracker.ietf.org/doc/html/rfc7539#section-2.3.
  private static final int[] SIGMA =
      toIntArray(
          new byte[] {
            'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'
          });

  /**
   * Sets the first 12 words of the initial state as described in
   * https://datatracker.ietf.org/doc/html/rfc7539#section-2.3.
   */
  static void setSigmaAndKey(int[] state, final int[] key) {
    System.arraycopy(SIGMA, 0, state, 0, SIGMA.length); // 4 words
    System.arraycopy(key, 0, state, SIGMA.length, KEY_SIZE_IN_INTS); // 8 words
  }

  /**
   * Computes the 20 ChaCha rounds as described in
   * https://datatracker.ietf.org/doc/html/rfc7539#section-2.3.
   */
  static void shuffleState(final int[] state) {
    for (int i = 0; i < 10; i++) {
      quarterRound(state, 0, 4, 8, 12);
      quarterRound(state, 1, 5, 9, 13);
      quarterRound(state, 2, 6, 10, 14);
      quarterRound(state, 3, 7, 11, 15);
      quarterRound(state, 0, 5, 10, 15);
      quarterRound(state, 1, 6, 11, 12);
      quarterRound(state, 2, 7, 8, 13);
      quarterRound(state, 3, 4, 9, 14);
    }
  }

  /**
   * Computes the ChaCha quarter round as described in
   * https://datatracker.ietf.org/doc/html/rfc7539#section-2.1.
   */
  static void quarterRound(int[] x, int a, int b, int c, int d) {
    x[a] += x[b];
    x[d] = rotateLeft(x[d] ^ x[a], 16);
    x[c] += x[d];
    x[b] = rotateLeft(x[b] ^ x[c], 12);
    x[a] += x[b];
    x[d] = rotateLeft(x[d] ^ x[a], 8);
    x[c] += x[d];
    x[b] = rotateLeft(x[b] ^ x[c], 7);
  }

  /** Converts {@code input} byte array to an int array */
  static int[] toIntArray(final byte[] input) {
    if (input.length % 4 != 0) {
      throw new IllegalArgumentException("invalid input length");
    }
    IntBuffer intBuffer = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
    int[] ret = new int[intBuffer.remaining()];
    intBuffer.get(ret);
    return ret;
  }

  static byte[] toByteArray(final int[] input) {
    ByteBuffer byteBuffer = ByteBuffer.allocate(input.length * 4).order(ByteOrder.LITTLE_ENDIAN);
    byteBuffer.asIntBuffer().put(input);
    return byteBuffer.array();
  }

  // See https://tools.ietf.org/html/draft-arciszewski-xchacha-01#section-2.2.
  static int[] hChaCha20(final int[] key, final int[] nonce) {
    int[] state = new int[BLOCK_SIZE_IN_INTS];
    setSigmaAndKey(state, key);
    state[12] = nonce[0];
    state[13] = nonce[1];
    state[14] = nonce[2];
    state[15] = nonce[3];
    shuffleState(state);
    // state[0] = state[0], state[1] = state[1], state[2] = state[2], state[3] = state[3]
    state[4] = state[12];
    state[5] = state[13];
    state[6] = state[14];
    state[7] = state[15];
    return Arrays.copyOf(state, ChaCha20Util.KEY_SIZE_IN_INTS);
  }

  static byte[] hChaCha20(final byte[] key, final byte[] nonce) {
    return toByteArray(hChaCha20(toIntArray(key), toIntArray(nonce)));
  }

  private static int rotateLeft(int x, int y) {
    return (x << y) | (x >>> -y);
  }

  private ChaCha20Util() {}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy