// 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.prf;

import com.google.crypto.tink.KeyTemplate;
import com.google.crypto.tink.KeyTypeManager;
import com.google.crypto.tink.Registry;
import com.google.crypto.tink.proto.HashType;
import com.google.crypto.tink.proto.HkdfPrfKey;
import com.google.crypto.tink.proto.HkdfPrfKeyFormat;
import com.google.crypto.tink.proto.HkdfPrfParams;
import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
import com.google.crypto.tink.subtle.Random;
import com.google.crypto.tink.subtle.Validators;
import com.google.crypto.tink.subtle.prf.HkdfStreamingPrf;
import com.google.crypto.tink.subtle.prf.PrfImpl;
import com.google.crypto.tink.subtle.prf.StreamingPrf;
import com.google.protobuf.ByteString;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.InvalidProtocolBufferException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.Map;

This key manager generates new HkdfPrfKey keys and produces new instances of HkdfStreamingPrf and HkdfPrf.
/** * This key manager generates new {@code HkdfPrfKey} keys and produces new instances of {@code * HkdfStreamingPrf} and {@code HkdfPrf}. */
public class HkdfPrfKeyManager extends KeyTypeManager<HkdfPrfKey> { private static com.google.crypto.tink.subtle.Enums.HashType convertHash(HashType hashType) throws GeneralSecurityException { switch (hashType) { case SHA1: return com.google.crypto.tink.subtle.Enums.HashType.SHA1; case SHA256: return com.google.crypto.tink.subtle.Enums.HashType.SHA256; case SHA384: return com.google.crypto.tink.subtle.Enums.HashType.SHA384; case SHA512: return com.google.crypto.tink.subtle.Enums.HashType.SHA512; default: throw new GeneralSecurityException("HashType " + hashType.name() + " not known in"); } } HkdfPrfKeyManager() { super( HkdfPrfKey.class, new PrimitiveFactory<StreamingPrf, HkdfPrfKey>(StreamingPrf.class) { @Override public StreamingPrf getPrimitive(HkdfPrfKey key) throws GeneralSecurityException { return new HkdfStreamingPrf( convertHash(key.getParams().getHash()), key.getKeyValue().toByteArray(), key.getParams().getSalt().toByteArray()); } }, // PrfSet can be constructed using a KeyManager, but it is not really usable until it // is wrapped with a KeySet, since the KeyManager does not know about key IDs. new PrimitiveFactory<PrfSet, HkdfPrfKey>(PrfSet.class) { @Override public PrfSet getPrimitive(HkdfPrfKey key) throws GeneralSecurityException { // We don't know the key ID here. Use 0 as a placeholder. PrfSetWrapper will fix this // for us. final int unknownKeyId = 0; final Prf prf = PrfImpl.wrap( new HkdfStreamingPrf( convertHash(key.getParams().getHash()), key.getKeyValue().toByteArray(), key.getParams().getSalt().toByteArray())); return new PrfSet() { @Override public int getPrimaryId() { return unknownKeyId; } @Override public Map<Integer, Prf> getPrfs() { return Collections.singletonMap(unknownKeyId, prf); } }; } }); } @Override public String getKeyType() { return "type.googleapis.com/google.crypto.tink.HkdfPrfKey"; } @Override public int getVersion() { return 0; } @Override public KeyMaterialType keyMaterialType() { return KeyMaterialType.SYMMETRIC; } @Override public void validateKey(HkdfPrfKey key) throws GeneralSecurityException { Validators.validateVersion(key.getVersion(), getVersion()); validateKeySize(key.getKeyValue().size()); validateParams(key.getParams()); } @Override public HkdfPrfKey parseKey(ByteString byteString) throws InvalidProtocolBufferException { return HkdfPrfKey.parseFrom(byteString, ExtensionRegistryLite.getEmptyRegistry()); } @Override public KeyFactory<HkdfPrfKeyFormat, HkdfPrfKey> keyFactory() { return new KeyFactory<HkdfPrfKeyFormat, HkdfPrfKey>(HkdfPrfKeyFormat.class) { @Override public void validateKeyFormat(HkdfPrfKeyFormat format) throws GeneralSecurityException { validateKeySize(format.getKeySize()); validateParams(format.getParams()); } @Override public HkdfPrfKeyFormat parseKeyFormat(ByteString byteString) throws InvalidProtocolBufferException { return HkdfPrfKeyFormat.parseFrom(byteString, ExtensionRegistryLite.getEmptyRegistry()); } @Override public HkdfPrfKey createKey(HkdfPrfKeyFormat format) throws GeneralSecurityException { return HkdfPrfKey.newBuilder() .setKeyValue(ByteString.copyFrom(Random.randBytes(format.getKeySize()))) .setVersion(getVersion()) .setParams(format.getParams()) .build(); } }; } // We use a somewhat larger minimum key size than usual, because PRFs might be used by many users, // in which case the security can degrade by a factor depending on the number of users. (Discussed // for example in https://eprint.iacr.org/2012/159) private static final int MIN_KEY_SIZE = 32; private static void validateKeySize(int keySize) throws GeneralSecurityException { if (keySize < MIN_KEY_SIZE) { throw new GeneralSecurityException("Invalid HkdfPrfKey/HkdfPrfKeyFormat: Key size too short"); } } private static void validateParams(HkdfPrfParams params) throws GeneralSecurityException { // Omitting SHA1 for the moment; there seems to be no reason to allow it. if (params.getHash() != HashType.SHA256 && params.getHash() != HashType.SHA512) { throw new GeneralSecurityException("Invalid HkdfPrfKey/HkdfPrfKeyFormat: Unsupported hash"); } } public static void register(boolean newKeyAllowed) throws GeneralSecurityException { Registry.registerKeyManager(new HkdfPrfKeyManager(), newKeyAllowed); } public static String staticKeyType() { return new HkdfPrfKeyManager().getKeyType(); }
Generates a KeyTemplate for HKDF-PRF keys with the following parameters.
  • Hash function: SHA256
  • HMAC key size: 32 bytes
  • Salt: empty
/** * Generates a {@link KeyTemplate} for HKDF-PRF keys with the following parameters. * * <ul> * <li>Hash function: SHA256 * <li>HMAC key size: 32 bytes * <li>Salt: empty * </ul> */
public static final KeyTemplate hkdfSha256Template() { HkdfPrfKeyFormat format = HkdfPrfKeyFormat.newBuilder() .setKeySize(32) // the size in bytes of the HKDF key .setParams(HkdfPrfParams.newBuilder().setHash(HashType.SHA256)) .build(); return KeyTemplate.create( HkdfPrfKeyManager.staticKeyType(), format.toByteArray(), KeyTemplate.OutputPrefixType.RAW); } }