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

com.google.crypto.tink.subtle.Kwp Maven / Gradle / Ivy

Go to download

Tink is a small cryptographic library that provides a safe, simple, agile and fast way to accomplish some common cryptographic tasks.

There is a newer version: 1.16.0
Show newest version
// Copyright 2018 Google Inc.
//
// 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.subtle;

import com.google.crypto.tink.KeyWrap;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Implements the key wrapping primitive KWP defined in NIST SP 800 38f.
 * The same encryption mode is also defined in RFC 5649. The NIST document is used here
 * as a primary reference, since it contains a security analysis and further
 * recommendations. In particular, Section 8 of NIST SP 800 38f suggests that the
 * allowed key sizes may be restricted. The implementation in this class
 * requires that the key sizes are in the range MIN_WRAP_KEY_SIZE and MAX_WRAP_KEY_SIZE.
 *
 * 

The minimum of 16 bytes has been chosen, because 128 bit keys are the smallest * key sizes used in tink. Additionally, wrapping short keys with KWP does not use * the function W and hence prevents using security arguments based on the assumption * that W is strong pseudorandom. (I.e. one consequence of using a strong pseudorandom * permutation as an underlying function is that leaking partial information about * decrypted bytes is not useful for an attack.) * *

The upper bound for the key size is somewhat arbitrary. Setting an upper bound is * motivated by the analysis in section A.4 of NIST SP 800 38f: forgeries of long * messages is simpler than forgeries of short message. * * @since 1.?.? */ public class Kwp implements KeyWrap { private final SecretKey aesKey; static final int MIN_WRAP_KEY_SIZE = 16; static final int MAX_WRAP_KEY_SIZE = 4096; static final int ROUNDS = 6; static final byte[] PREFIX = new byte[]{(byte) 0xa6, (byte) 0x59, (byte) 0x59, (byte) 0xa6}; /** * Construct a new Instance for KWP. * @param key the wrapping key. This is an AES key. * Supported key sizes are 128 and 256 bits. */ public Kwp(final byte[] key) throws GeneralSecurityException { if (key.length != 16 && key.length != 32) { throw new GeneralSecurityException("Unsupported key length"); } aesKey = new SecretKeySpec(key, "AES"); } /** * Returns the size of a wrapped key for a given input size. * @param inputSize the size of the key to wrap in bytes. */ private int wrappingSize(int inputSize) { int paddingSize = 7 - (inputSize + 7) % 8; return inputSize + paddingSize + 8; } /** * Computes the pseudorandom permutation W over the IV * concatenated with zero padded key material. * @param iv an IV of size 8. * @param key the key to wrap. * The pseudorandom permutation W is only defined for * inputs with a size that is a multiple of 8 bytes and * that is at least 24 bytes long. Hence computeW is undefined * for keys of size 8 bytes or shorter. */ private byte[] computeW(final byte[] iv, final byte[] key) throws GeneralSecurityException { // Checks the parameter sizes for which W is defined. // Note, that the caller ensures stricter limits. if (key.length <= 8 || key.length > Integer.MAX_VALUE - 16 || iv.length != 8) { throw new GeneralSecurityException("computeW called with invalid parameters"); } byte[] data = new byte[wrappingSize(key.length)]; System.arraycopy(iv, 0, data, 0, iv.length); System.arraycopy(key, 0, data, 8, key.length); int blocks = data.length / 8 - 1; Cipher aes = EngineFactory.CIPHER.getInstance("AES/ECB/NoPadding"); aes.init(Cipher.ENCRYPT_MODE, aesKey); byte[] block = new byte[16]; System.arraycopy(data, 0, block, 0, 8); for (int i = 0; i < ROUNDS; i++) { for (int j = 0; j < blocks; j++) { System.arraycopy(data, 8 * (j + 1), block, 8, 8); int length = aes.doFinal(block, 0, 16, block); assert length == 16; // xor the round constant in bigendian order to the left half of block. int roundConst = i * blocks + j + 1; for (int b = 0; b < 4; b++) { block[7 - b] ^= (byte) (roundConst & 0xff); roundConst >>>= 8; } System.arraycopy(block, 8, data, 8 * (j + 1), 8); } } System.arraycopy(block, 0, data, 0, 8); return data; } /** * Compute the inverse of the pseudorandom permutation W. * @param wrapped the input data to invert. This is the wrapped key. * @return the concatenation of the IV followed by a potentially * zero padded key. * invertW does not perform an integrity check. */ private byte[] invertW(final byte[] wrapped) throws GeneralSecurityException { // Checks the input size for which invertW is defined. // The caller ensures stricter limits if (wrapped.length < 24 || wrapped.length % 8 != 0) { throw new GeneralSecurityException("Incorrect data size"); } byte[] data = Arrays.copyOf(wrapped, wrapped.length); int blocks = data.length / 8 - 1; Cipher aes = EngineFactory.CIPHER.getInstance("AES/ECB/NoPadding"); aes.init(Cipher.DECRYPT_MODE, aesKey); byte[] block = new byte[16]; System.arraycopy(data, 0, block, 0, 8); for (int i = ROUNDS - 1; i >= 0; i--) { for (int j = blocks - 1; j >= 0; j--) { System.arraycopy(data, 8 * (j + 1), block, 8, 8); // xor the round constant in bigendian order to the left half of block. int roundConst = i * blocks + j + 1; for (int b = 0; b < 4; b++) { block[7 - b] ^= (byte) (roundConst & 0xff); roundConst >>>= 8; } int length = aes.doFinal(block, 0, 16, block); assert length == 16; System.arraycopy(block, 8, data, 8 * (j + 1), 8); } } System.arraycopy(block, 0, data, 0, 8); return data; } /** * Wraps some key material {@code data}. * * @param data the key to wrap. * @return the wrapped key */ @Override public byte[] wrap(final byte[] data) throws GeneralSecurityException { if (data.length < MIN_WRAP_KEY_SIZE) { throw new GeneralSecurityException("Key size of key to wrap too small"); } if (data.length > MAX_WRAP_KEY_SIZE) { throw new GeneralSecurityException("Key size of key to wrap too large"); } byte[] iv = new byte[8]; System.arraycopy(PREFIX, 0, iv, 0, PREFIX.length); for (int i = 0; i < 4; i++) { iv[4 + i] = (byte) ((data.length >> (8 * (3 - i))) & 0xff); } return computeW(iv, data); } /** * Unwraps a wrapped key. * * @throws GeneralSecurityException if {@code data} fails the integrity check. */ @Override public byte[] unwrap(final byte[] data) throws GeneralSecurityException { if (data.length < wrappingSize(MIN_WRAP_KEY_SIZE)) { throw new GeneralSecurityException("Wrapped key size is too small"); } if (data.length > wrappingSize(MAX_WRAP_KEY_SIZE)) { throw new GeneralSecurityException("Wrapped key size is too large"); } if (data.length % 8 != 0) { throw new GeneralSecurityException( "Wrapped key size must be a multiple of 8 bytes"); } byte[] unwrapped = invertW(data); // Check the padding. // W has been designed to be a strong pseudorandom permutation. // Hence leaking any amount of information about improperly padded keys // would not be a vulnerability. This means that here we don't have to go to // some extra length to assure that the code is constant time. boolean ok = true; for (int i = 0; i < 4; i++) { if (PREFIX[i] != unwrapped[i]) { ok = false; } } int encodedSize = 0; for (int i = 4; i < 8; i++) { encodedSize = (encodedSize << 8) + (unwrapped[i] & 0xff); } if (wrappingSize(encodedSize) != unwrapped.length) { ok = false; } else { for (int j = 8 + encodedSize; j < unwrapped.length; j++) { if (unwrapped[j] != 0) { ok = false; } } } if (ok) { return Arrays.copyOfRange(unwrapped, 8, 8 + encodedSize); } else { throw new BadPaddingException("Invalid padding"); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy