1package org.bouncycastle.crypto.generators;
2
3import org.bouncycastle.crypto.DataLengthException;
4import org.bouncycastle.crypto.DerivationFunction;
5import org.bouncycastle.crypto.DerivationParameters;
6import org.bouncycastle.crypto.Digest;
7import org.bouncycastle.crypto.params.ISO18033KDFParameters;
8import org.bouncycastle.crypto.params.KDFParameters;
9
10/**
11 * Basic KDF generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
12 * <br>
13 * This implementation is based on ISO 18033/P1363a.
14 */
15public class BaseKDFBytesGenerator
16    implements DerivationFunction
17{
18    private int     counterStart;
19    private Digest  digest;
20    private byte[]  shared;
21    private byte[]  iv;
22
23    /**
24     * Construct a KDF Parameters generator.
25     * <p>
26     * @param counterStart value of counter.
27     * @param digest the digest to be used as the source of derived keys.
28     */
29    protected BaseKDFBytesGenerator(
30        int     counterStart,
31        Digest  digest)
32    {
33        this.counterStart = counterStart;
34        this.digest = digest;
35    }
36
37    public void init(
38        DerivationParameters    param)
39    {
40        if (param instanceof KDFParameters)
41        {
42            KDFParameters   p = (KDFParameters)param;
43
44            shared = p.getSharedSecret();
45            iv = p.getIV();
46        }
47        else if (param instanceof ISO18033KDFParameters)
48        {
49            ISO18033KDFParameters p = (ISO18033KDFParameters)param;
50
51            shared = p.getSeed();
52            iv = null;
53        }
54        else
55        {
56            throw new IllegalArgumentException("KDF parameters required for KDF2Generator");
57        }
58    }
59
60    /**
61     * return the underlying digest.
62     */
63    public Digest getDigest()
64    {
65        return digest;
66    }
67
68    /**
69     * fill len bytes of the output buffer with bytes generated from
70     * the derivation function.
71     *
72     * @throws IllegalArgumentException if the size of the request will cause an overflow.
73     * @throws DataLengthException if the out buffer is too small.
74     */
75    public int generateBytes(
76        byte[]  out,
77        int     outOff,
78        int     len)
79        throws DataLengthException, IllegalArgumentException
80    {
81        if ((out.length - len) < outOff)
82        {
83            throw new DataLengthException("output buffer too small");
84        }
85
86        long    oBytes = len;
87        int     outLen = digest.getDigestSize();
88
89        //
90        // this is at odds with the standard implementation, the
91        // maximum value should be hBits * (2^32 - 1) where hBits
92        // is the digest output size in bits. We can't have an
93        // array with a long index at the moment...
94        //
95        if (oBytes > ((2L << 32) - 1))
96        {
97            throw new IllegalArgumentException("Output length too large");
98        }
99
100        int cThreshold = (int)((oBytes + outLen - 1) / outLen);
101
102        byte[] dig = null;
103
104        dig = new byte[digest.getDigestSize()];
105
106        int counter = counterStart;
107
108        for (int i = 0; i < cThreshold; i++)
109        {
110            digest.update(shared, 0, shared.length);
111
112            digest.update((byte)(counter >> 24));
113            digest.update((byte)(counter >> 16));
114            digest.update((byte)(counter >> 8));
115            digest.update((byte)counter);
116
117            if (iv != null)
118            {
119                digest.update(iv, 0, iv.length);
120            }
121
122            digest.doFinal(dig, 0);
123
124            if (len > outLen)
125            {
126                System.arraycopy(dig, 0, out, outOff, outLen);
127                outOff += outLen;
128                len -= outLen;
129            }
130            else
131            {
132                System.arraycopy(dig, 0, out, outOff, len);
133            }
134
135            counter++;
136        }
137
138        digest.reset();
139
140        return len;
141    }
142}
143