Welcome to mirror list, hosted at ThFree Co, Russian Federation.

HKDFBytesGenerator.java « generators « crypto « spongycastle « org « java « main « src « core - gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: fbeeca61d5e67f1aea663197a1443fba5053fd13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package org.spongycastle.crypto.generators;

import org.spongycastle.crypto.DataLengthException;
import org.spongycastle.crypto.DerivationFunction;
import org.spongycastle.crypto.DerivationParameters;
import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.macs.HMac;
import org.spongycastle.crypto.params.HKDFParameters;
import org.spongycastle.crypto.params.KeyParameter;

/**
 * HMAC-based Extract-and-Expand Key Derivation Function (HKDF) implemented
 * according to IETF RFC 5869, May 2010 as specified by H. Krawczyk, IBM
 * Research & P. Eronen, Nokia. It uses a HMac internally to compute de OKM
 * (output keying material) and is likely to have better security properties
 * than KDF's based on just a hash function.
 */
public class HKDFBytesGenerator
    implements DerivationFunction
{

    private HMac hMacHash;
    private int hashLen;

    private byte[] info;
    private byte[] currentT;

    private int generatedBytes;

    /**
     * Creates a HKDFBytesGenerator based on the given hash function.
     *
     * @param hash the digest to be used as the source of generatedBytes bytes
     */
    public HKDFBytesGenerator(Digest hash)
    {
        this.hMacHash = new HMac(hash);
        this.hashLen = hash.getDigestSize();
    }

    public void init(DerivationParameters param)
    {
        if (!(param instanceof HKDFParameters))
        {
            throw new IllegalArgumentException(
                "HKDF parameters required for HKDFBytesGenerator");
        }

        HKDFParameters params = (HKDFParameters)param;
        if (params.skipExtract())
        {
            // use IKM directly as PRK
            hMacHash.init(new KeyParameter(params.getIKM()));
        }
        else
        {
            hMacHash.init(extract(params.getSalt(), params.getIKM()));
        }

        info = params.getInfo();

        generatedBytes = 0;
        currentT = new byte[hashLen];
    }

    /**
     * Performs the extract part of the key derivation function.
     *
     * @param salt the salt to use
     * @param ikm  the input keying material
     * @return the PRK as KeyParameter
     */
    private KeyParameter extract(byte[] salt, byte[] ikm)
    {
        hMacHash.init(new KeyParameter(ikm));
        if (salt == null)
        {
            // TODO check if hashLen is indeed same as HMAC size
            hMacHash.init(new KeyParameter(new byte[hashLen]));
        }
        else
        {
            hMacHash.init(new KeyParameter(salt));
        }

        hMacHash.update(ikm, 0, ikm.length);

        byte[] prk = new byte[hashLen];
        hMacHash.doFinal(prk, 0);
        return new KeyParameter(prk);
    }

    /**
     * Performs the expand part of the key derivation function, using currentT
     * as input and output buffer.
     *
     * @throws DataLengthException if the total number of bytes generated is larger than the one
     * specified by RFC 5869 (255 * HashLen)
     */
    private void expandNext()
        throws DataLengthException
    {
        int n = generatedBytes / hashLen + 1;
        if (n >= 256)
        {
            throw new DataLengthException(
                "HKDF cannot generate more than 255 blocks of HashLen size");
        }
        // special case for T(0): T(0) is empty, so no update
        if (generatedBytes != 0)
        {
            hMacHash.update(currentT, 0, hashLen);
        }
        hMacHash.update(info, 0, info.length);
        hMacHash.update((byte)n);
        hMacHash.doFinal(currentT, 0);
    }

    public Digest getDigest()
    {
        return hMacHash.getUnderlyingDigest();
    }

    public int generateBytes(byte[] out, int outOff, int len)
        throws DataLengthException, IllegalArgumentException
    {

        if (generatedBytes + len > 255 * hashLen)
        {
            throw new DataLengthException(
                "HKDF may only be used for 255 * HashLen bytes of output");
        }

        if (generatedBytes % hashLen == 0)
        {
            expandNext();
        }

        // copy what is left in the currentT (1..hash
        int toGenerate = len;
        int posInT = generatedBytes % hashLen;
        int leftInT = hashLen - generatedBytes % hashLen;
        int toCopy = Math.min(leftInT, toGenerate);
        System.arraycopy(currentT, posInT, out, outOff, toCopy);
        generatedBytes += toCopy;
        toGenerate -= toCopy;
        outOff += toCopy;

        while (toGenerate > 0)
        {
            expandNext();
            toCopy = Math.min(hashLen, toGenerate);
            System.arraycopy(currentT, 0, out, outOff, toCopy);
            generatedBytes += toCopy;
            toGenerate -= toCopy;
            outOff += toCopy;
        }

        return len;
    }
}