| package org.bouncycastle.crypto.generators; |
| |
| import java.math.BigInteger; |
| |
| import org.bouncycastle.crypto.DataLengthException; |
| import org.bouncycastle.crypto.DerivationParameters; |
| import org.bouncycastle.crypto.Mac; |
| import org.bouncycastle.crypto.MacDerivationFunction; |
| import org.bouncycastle.crypto.params.KDFCounterParameters; |
| import org.bouncycastle.crypto.params.KeyParameter; |
| |
| /** |
| * This KDF has been defined by the publicly available NIST SP 800-108 specification. |
| * NIST SP800-108 allows for alternative orderings of the input fields, meaning that the input can be formated in multiple ways. |
| * There are 3 supported formats: - Below [i]_2 is a counter of r-bits length concatenated to the fixedInputData. |
| * <ul> |
| * <li>1: K(i) := PRF( KI, [i]_2 || Label || 0x00 || Context || [L]_2 ) with the counter at the very beginning of the fixedInputData (The default implementation has this format)</li> |
| * <li>2: K(i) := PRF( KI, Label || 0x00 || Context || [L]_2 || [i]_2 ) with the counter at the very end of the fixedInputData</li> |
| * <li>3a: K(i) := PRF( KI, Label || 0x00 || [i]_2 || Context || [L]_2 ) OR:</li> |
| * <li>3b: K(i) := PRF( KI, Label || 0x00 || [i]_2 || [L]_2 || Context ) OR:</li> |
| * <li>3c: K(i) := PRF( KI, Label || [i]_2 || 0x00 || Context || [L]_2 ) etc... with the counter somewhere in the 'middle' of the fixedInputData.</li> |
| * </ul> |
| * This function must be called with the following KDFCounterParameters(): |
| * <ul> |
| * <li>KI</li> |
| * <li>The part of the fixedInputData that comes BEFORE the counter OR null</li> |
| * <li>the part of the fixedInputData that comes AFTER the counter OR null </li> |
| * <li>the length of the counter in bits (not bytes)</li> |
| * </ul> |
| * Resulting function calls assuming an 8 bit counter. |
| * <ul> |
| * <li>1. KDFCounterParameters(ki, null, "Label || 0x00 || Context || [L]_2]", 8);</li> |
| * <li>2. KDFCounterParameters(ki, "Label || 0x00 || Context || [L]_2]", null, 8);</li> |
| * <li>3a. KDFCounterParameters(ki, "Label || 0x00", "Context || [L]_2]", 8);</li> |
| * <li>3b. KDFCounterParameters(ki, "Label || 0x00", "[L]_2] || Context", 8);</li> |
| * <li>3c. KDFCounterParameters(ki, "Label", "0x00 || Context || [L]_2]", 8);</li> |
| * </ul> |
| */ |
| public class KDFCounterBytesGenerator |
| implements MacDerivationFunction |
| { |
| |
| private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE); |
| private static final BigInteger TWO = BigInteger.valueOf(2); |
| |
| // please refer to the standard for the meaning of the variable names |
| // all field lengths are in bytes, not in bits as specified by the standard |
| |
| // fields set by the constructor |
| private final Mac prf; |
| private final int h; |
| |
| // fields set by init |
| private byte[] fixedInputDataCtrPrefix; |
| private byte[] fixedInputData_afterCtr; |
| private int maxSizeExcl; |
| // ios is i defined as an octet string (the binary representation) |
| private byte[] ios; |
| |
| // operational |
| private int generatedBytes; |
| // k is used as buffer for all K(i) values |
| private byte[] k; |
| |
| |
| public KDFCounterBytesGenerator(Mac prf) |
| { |
| this.prf = prf; |
| this.h = prf.getMacSize(); |
| this.k = new byte[h]; |
| } |
| |
| |
| public void init(DerivationParameters param) |
| { |
| if (!(param instanceof KDFCounterParameters)) |
| { |
| throw new IllegalArgumentException("Wrong type of arguments given"); |
| } |
| |
| KDFCounterParameters kdfParams = (KDFCounterParameters)param; |
| |
| // --- init mac based PRF --- |
| |
| this.prf.init(new KeyParameter(kdfParams.getKI())); |
| |
| // --- set arguments --- |
| |
| this.fixedInputDataCtrPrefix = kdfParams.getFixedInputDataCounterPrefix(); |
| this.fixedInputData_afterCtr = kdfParams.getFixedInputDataCounterSuffix(); |
| |
| int r = kdfParams.getR(); |
| this.ios = new byte[r / 8]; |
| |
| BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h)); |
| this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ? |
| Integer.MAX_VALUE : maxSize.intValue(); |
| |
| // --- set operational state --- |
| |
| generatedBytes = 0; |
| } |
| |
| |
| public Mac getMac() |
| { |
| return prf; |
| } |
| |
| public int generateBytes(byte[] out, int outOff, int len) |
| throws DataLengthException, IllegalArgumentException |
| { |
| |
| int generatedBytesAfter = generatedBytes + len; |
| if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl) |
| { |
| throw new DataLengthException( |
| "Current KDFCTR may only be used for " + maxSizeExcl + " bytes"); |
| } |
| |
| if (generatedBytes % h == 0) |
| { |
| generateNext(); |
| } |
| |
| // copy what is left in the currentT (1..hash |
| int toGenerate = len; |
| int posInK = generatedBytes % h; |
| int leftInK = h - generatedBytes % h; |
| int toCopy = Math.min(leftInK, toGenerate); |
| System.arraycopy(k, posInK, out, outOff, toCopy); |
| generatedBytes += toCopy; |
| toGenerate -= toCopy; |
| outOff += toCopy; |
| |
| while (toGenerate > 0) |
| { |
| generateNext(); |
| toCopy = Math.min(h, toGenerate); |
| System.arraycopy(k, 0, out, outOff, toCopy); |
| generatedBytes += toCopy; |
| toGenerate -= toCopy; |
| outOff += toCopy; |
| } |
| |
| return len; |
| } |
| |
| private void generateNext() |
| { |
| int i = generatedBytes / h + 1; |
| |
| // encode i into counter buffer |
| switch (ios.length) |
| { |
| case 4: |
| ios[0] = (byte)(i >>> 24); |
| // fall through |
| case 3: |
| ios[ios.length - 3] = (byte)(i >>> 16); |
| // fall through |
| case 2: |
| ios[ios.length - 2] = (byte)(i >>> 8); |
| // fall through |
| case 1: |
| ios[ios.length - 1] = (byte)i; |
| break; |
| default: |
| throw new IllegalStateException("Unsupported size of counter i"); |
| } |
| |
| |
| // special case for K(0): K(0) is empty, so no update |
| prf.update(fixedInputDataCtrPrefix, 0, fixedInputDataCtrPrefix.length); |
| prf.update(ios, 0, ios.length); |
| prf.update(fixedInputData_afterCtr, 0, fixedInputData_afterCtr.length); |
| prf.doFinal(k, 0); |
| } |
| } |