Skip to content

Commit cbba912

Browse files
committed
RFC 8446: Implement HKDF functionality in TlsCrypto
1 parent 4b133f1 commit cbba912

File tree

14 files changed

+418
-58
lines changed

14 files changed

+418
-58
lines changed

tls/src/main/java/org/bouncycastle/tls/crypto/TlsCrypto.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@ public interface TlsCrypto
167167
*/
168168
TlsHash createHash(short hashAlgorithm);
169169

170+
/**
171+
* Create a suitable HMAC using the hash algorithm identifier passed in.
172+
* <p>
173+
* See enumeration class {@link HashAlgorithm} for appropriate argument values.
174+
* </p>
175+
* @param hashAlgorithm the hash algorithm the HMAC should use.
176+
* @return a {@link TlsHMAC}.
177+
*/
178+
TlsHMAC createHMAC(short hashAlgorithm);
179+
170180
/**
171181
* Create a suitable HMAC for the MAC algorithm identifier passed in.
172182
* <p>
@@ -212,4 +222,11 @@ public interface TlsCrypto
212222
* @return an initialized SRP6 verifier generator,
213223
*/
214224
TlsSRP6VerifierGenerator createSRP6VerifierGenerator(TlsSRPConfig srpConfig);
225+
226+
/**
227+
* Setup an initial "secret" for a chain of HKDF calls (RFC 5869), containing a string of HashLen zeroes.
228+
*
229+
* @param hashAlgorithm the hash algorithm to instantiate HMAC with. See {@link HashAlgorithm} for values.
230+
*/
231+
TlsSecret hkdfInit(short hashAlgorithm);
215232
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.bouncycastle.tls.crypto;
2+
3+
import java.io.IOException;
4+
5+
import org.bouncycastle.tls.TlsUtils;
6+
import org.bouncycastle.util.Strings;
7+
8+
public abstract class TlsCryptoUtils
9+
{
10+
public static TlsSecret hkdfExpandLabel(TlsSecret secret, short hashAlgorithm, String label, byte[] context, int length)
11+
throws IOException
12+
{
13+
byte[] expandedLabel = Strings.toByteArray("tls13 " + label);
14+
15+
byte[] hkdfLabel = new byte[2 + (1 + expandedLabel.length) + (1 + context.length)];
16+
17+
TlsUtils.checkUint16(length);
18+
TlsUtils.writeUint16(length, hkdfLabel, 0);
19+
20+
TlsUtils.writeOpaque8(expandedLabel, hkdfLabel, 2);
21+
22+
TlsUtils.writeOpaque8(context, hkdfLabel, 2 + (1 + expandedLabel.length));
23+
24+
return secret.hkdfExpand(hashAlgorithm, hkdfLabel, length);
25+
}
26+
}

tls/src/main/java/org/bouncycastle/tls/crypto/TlsMAC.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ public interface TlsMAC
3030
*/
3131
byte[] calculateMAC();
3232

33+
/**
34+
* Write the calculated MAC to an output buffer.
35+
*
36+
* @param output output array to write the MAC to.
37+
* @param outOff offset into the output array to write the MAC to.
38+
*/
39+
void calculateMAC(byte[] output, int outOff);
40+
3341
/**
3442
* Return the length of the MAC generated by this service.
3543
*

tls/src/main/java/org/bouncycastle/tls/crypto/TlsSecret.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.IOException;
44

55
import org.bouncycastle.tls.EncryptionAlgorithm;
6+
import org.bouncycastle.tls.HashAlgorithm;
67
import org.bouncycastle.tls.MACAlgorithm;
78

89
/**
@@ -56,4 +57,23 @@ public interface TlsSecret
5657
* @return the secret's internal data.
5758
*/
5859
byte[] extract();
60+
61+
/**
62+
* RFC 5869 HKDF-Expand function, with this secret's data as the pseudo-random key ('prk').
63+
*
64+
* @param hashAlgorithm the hash algorithm to instantiate HMAC with. See {@link HashAlgorithm} for values.
65+
* @param info optional context and application specific information (can be zero-length).
66+
* @param length length of output keying material in octets.
67+
* @return output keying material (of 'length' octets).
68+
*/
69+
TlsSecret hkdfExpand(short hashAlgorithm, byte[] info, int length);
70+
71+
/**
72+
* RFC 5869 HKDF-Extract function, with this secret's data as the 'salt'.
73+
*
74+
* @param hashAlgorithm the hash algorithm to instantiate HMAC with. See {@link HashAlgorithm} for values.
75+
* @param ikm input keying material.
76+
* @return a pseudo-random key (of HashLen octets).
77+
*/
78+
TlsSecret hkdfExtract(short hashAlgorithm, byte[] ikm);
5979
}

tls/src/main/java/org/bouncycastle/tls/crypto/impl/AbstractTlsSecret.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
import org.bouncycastle.tls.crypto.TlsCertificate;
66
import org.bouncycastle.tls.crypto.TlsCipher;
7+
import org.bouncycastle.tls.crypto.TlsCrypto;
78
import org.bouncycastle.tls.crypto.TlsCryptoParameters;
9+
import org.bouncycastle.tls.crypto.TlsHMAC;
810
import org.bouncycastle.tls.crypto.TlsSecret;
911
import org.bouncycastle.util.Arrays;
1012

@@ -26,6 +28,8 @@ protected AbstractTlsSecret(byte[] data)
2628
this.data = data;
2729
}
2830

31+
protected abstract TlsSecret adoptLocalSecret(byte[] data);
32+
2933
protected void checkAlive()
3034
{
3135
if (data == null)
@@ -68,6 +72,60 @@ public synchronized byte[] extract()
6872
return result;
6973
}
7074

75+
public synchronized TlsSecret hkdfExpand(short hashAlgorithm, byte[] info, int length)
76+
{
77+
checkAlive();
78+
79+
byte[] prk = data;
80+
81+
TlsCrypto crypto = getCrypto();
82+
TlsHMAC hmac = crypto.createHMAC(hashAlgorithm);
83+
84+
hmac.setKey(prk, 0, prk.length);
85+
86+
byte[] okm = new byte[length];
87+
88+
int hashLen = hmac.getMacLength();
89+
byte[] t = new byte[hashLen];
90+
byte counter = 0x00;
91+
92+
int pos = 0;
93+
while (pos < length)
94+
{
95+
if (counter != 0x00)
96+
{
97+
hmac.update(t, 0, t.length);
98+
}
99+
hmac.update(info, 0, info.length);
100+
hmac.update(new byte[]{ ++counter }, 0, 1);
101+
102+
hmac.calculateMAC(t, 0);
103+
104+
int copyLength = Math.min(hashLen, length - pos);
105+
System.arraycopy(t, 0, okm, pos, copyLength);
106+
pos += copyLength;
107+
}
108+
109+
return adoptLocalSecret(okm);
110+
}
111+
112+
public synchronized TlsSecret hkdfExtract(short hashAlgorithm, byte[] ikm)
113+
{
114+
checkAlive();
115+
116+
byte[] salt = data;
117+
118+
TlsCrypto crypto = getCrypto();
119+
TlsHMAC hmac = crypto.createHMAC(hashAlgorithm);
120+
121+
hmac.setKey(salt, 0, salt.length);
122+
hmac.update(ikm, 0, ikm.length);
123+
124+
byte[] prk = hmac.calculateMAC();
125+
126+
return adoptLocalSecret(prk);
127+
}
128+
71129
synchronized byte[] copyData()
72130
{
73131
return Arrays.clone(data);

tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,9 +597,14 @@ protected BlockCipher createSEEDBlockCipher()
597597
return new CBCBlockCipher(new SEEDEngine());
598598
}
599599

600+
public TlsHMAC createHMAC(short hashAlgorithm)
601+
{
602+
return new HMacOperator(createDigest(hashAlgorithm));
603+
}
604+
600605
public TlsHMAC createHMAC(int macAlgorithm)
601606
{
602-
return new HMacOperator(createDigest(TlsUtils.getHashAlgorithmForHMACAlgorithm(macAlgorithm)));
607+
return createHMAC(TlsUtils.getHashAlgorithmForHMACAlgorithm(macAlgorithm));
603608
}
604609

605610
public TlsSRP6Client createSRP6Client(TlsSRPConfig srpConfig)
@@ -676,6 +681,11 @@ public BigInteger generateVerifier(byte[] salt, byte[] identity, byte[] password
676681
};
677682
}
678683

684+
public TlsSecret hkdfInit(short hashAlgorithm)
685+
{
686+
return adoptLocalSecret(new byte[HashAlgorithm.getOutputSize(hashAlgorithm)]);
687+
}
688+
679689
private class BlockOperator
680690
implements TlsBlockCipherImpl
681691
{
@@ -792,6 +802,11 @@ public byte[] calculateMAC()
792802
return rv;
793803
}
794804

805+
public void calculateMAC(byte[] output, int outOff)
806+
{
807+
hmac.doFinal(output, outOff);
808+
}
809+
795810
public int getInternalBlockSize()
796811
{
797812
return ((ExtendedDigest)hmac.getUnderlyingDigest()).getByteLength();

tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSecret.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ public synchronized TlsSecret deriveUsingPRF(int prfAlgorithm, String label, byt
4040
return crypto.adoptLocalSecret(result);
4141
}
4242

43+
protected TlsSecret adoptLocalSecret(byte[] data)
44+
{
45+
return crypto.adoptLocalSecret(data);
46+
}
47+
4348
protected AbstractTlsCrypto getCrypto()
4449
{
4550
return crypto;

tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,13 @@ protected TlsCipher createCipher(TlsCryptoParameters cryptoParams, int encryptio
216216
}
217217
}
218218

219+
public TlsHMAC createHMAC(short hashAlgorithm)
220+
{
221+
String digestName = getDigestName(hashAlgorithm).replaceAll("-", "");
222+
String hmacName = "Hmac" + digestName;
223+
return createHMAC(hmacName);
224+
}
225+
219226
public TlsHMAC createHMAC(int macAlgorithm)
220227
{
221228
switch (macAlgorithm)
@@ -619,6 +626,11 @@ public byte[] encrypt(byte[] input, int inOff, int length)
619626
};
620627
}
621628

629+
public TlsSecret hkdfInit(short hashAlgorithm)
630+
{
631+
return adoptLocalSecret(new byte[HashAlgorithm.getOutputSize(hashAlgorithm)]);
632+
}
633+
622634
/**
623635
* If you want to create your own versions of the AEAD ciphers required, override this method.
624636
*

tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsHMAC.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Hashtable;
55

66
import javax.crypto.Mac;
7+
import javax.crypto.ShortBufferException;
78
import javax.crypto.spec.SecretKeySpec;
89

910
import org.bouncycastle.tls.crypto.TlsHMAC;
@@ -87,6 +88,18 @@ public byte[] calculateMAC()
8788
return hmac.doFinal();
8889
}
8990

91+
public void calculateMAC(byte[] output, int outOff)
92+
{
93+
try
94+
{
95+
hmac.doFinal(output, outOff);
96+
}
97+
catch (ShortBufferException e)
98+
{
99+
throw new IllegalArgumentException(e.getMessage());
100+
}
101+
}
102+
90103
public int getInternalBlockSize()
91104
{
92105
return internalBlockSize.intValue();

tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMAC.java

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)