I am implementing support for TLS 1.2 for the IBM mainframe operating system z/VSE in Assembler language, but am having difficultly getting the PRF to work. I am validating with the values described in: https://www.ietf.org/mail-archive/web/tls/current/msg03416.html The first 32 bytes of my output are correct using this test case. After the first 32 bytes I place the HMAC-256 32 bytes into a buffer and call the HMAC-256 again with the same secret. Is that correct or should I be concatenating the label and/or original seed date onto the first HMAC output?
1 Answer
Your description is vague but there are two levels of HMAC'ing required: you compute a series of intermediate values A0, A1, A2 ... where A0 is the 'seed' (see next) and each following value is HMAC_hash of the previous, and HMAC_hash each A1, A2 ... concatenated with 'seed' (see next) and concatenate those to produce P_hash and truncate to the final result.
Note an important change of scope: 'seed' in the P_hash and Ai formulas is the concatenation of 'label' and 'seed' from the PRF call. For clarity I use 'lbsd' as the name for this 'combined' version of 'seed'.
I find Java convenient for test/prototype code like this because you don't need to fiddle with low-level pointers, offsets, and such like C much less assembler, leaving it much easier to see (and verify) the logic. I display the series of 'A' blocks (A0, A1, A2 etc) and the corresponding pieces of P_hash which I notate P1, P2 etc.
So with this code
//nopackage
import java.nio.ByteBuffer;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class Cry46549 {
public static void main (String[] args) throws Exception {
// java Cry46549 hmacname <file_containing_secret_seed_output # label is constant
byte[] secret = new byte[16], seed = new byte[16], label = "test label".getBytes("ASCII");
System.in.read(secret); System.in.read(seed); int want = System.in.available();
// WARNING only in test code, stream.available() isn't reliable
byte[] lbsd = Arrays.copyOf(label, label.length+seed.length);
System.arraycopy(seed,0, lbsd,label.length, seed.length);
Mac mac = Mac.getInstance(args[0]); mac.init(new SecretKeySpec(secret,""));
byte[] A = lbsd; dump("A0:", A); ByteBuffer bb = ByteBuffer.allocate(want+64);
for( int i = 1; bb.position() < want; i++ ){
A = mac.doFinal (A); dump("A"+i+":",A);
mac.update(A); byte[] P = mac.doFinal(lbsd);
dump("P"+i+":",P); bb.put(P);
}
byte[] R = new byte[want]; bb.flip(); bb.get(R); dump("result:",R);
}
public static void dump (String heading, byte[] a){
System.out.println (heading);
for( int i = 0; i < a.length; i++ ){
if( i%8==0 ) System.out.printf("%04x", i);
System.out.printf(" %02x", a[i]);
if( i%8==7 ) System.out.println();
}
if( a.length%8!=0 ) System.out.println();
}
}
and the data for the P_SHA256 case from your link, lightly edited and xxd-r'ed into convenient binary, I get the following output, with desired final result as you can see:
A0:
0000 74 65 73 74 20 6c 61 62
0008 65 6c a0 ba 9f 93 6c da
0010 31 18 27 a6 f7 96 ff d5
0018 19 8c
A1:
0000 9f 5b b1 29 e2 f8 0a a4
0008 7b 67 05 b3 3e 21 4c c5
0010 e5 09 f2 83 e8 7b 3b e5
0018 e1 da a7 44 92 27 7d a1
P1:
0000 e3 f2 29 ba 72 7b e1 7b
0008 8d 12 26 20 55 7c d4 53
0010 c2 aa b2 1d 07 c3 d4 95
0018 32 9b 52 d4 e6 1e db 5a
A2:
0000 fc 16 6a 08 80 91 f7 6f
0008 af 99 35 9a 69 93 8f 75
0010 c1 54 24 63 8f 61 38 46
0018 34 24 37 32 b6 28 72 7b
P2:
0000 6b 30 17 91 e9 0d 35 c9
0008 c9 a4 6b 4e 14 ba f9 af
0010 0f a0 22 f7 07 7d ef 17
0018 ab fd 37 97 c0 56 4b ab
A3:
0000 fe 5f 7d 49 75 e1 23 c0
0008 d4 33 a3 a2 61 b6 42 c0
0010 58 04 46 46 1a f0 88 f0
0018 c7 9f 43 45 72 a1 a1 a6
P3:
0000 4f bc 91 66 6e 9d ef 9b
0008 97 fc e3 4f 79 67 89 ba
0010 a4 80 82 d1 22 ee 42 c5
0018 a7 2e 5a 51 10 ff f7 01
A4:
0000 3e fb 20 52 e4 76 f9 6c
0008 64 8d ca 6d 2d 00 8f b9
0010 75 ef b6 a4 83 60 be ec
0018 fd 47 a8 51 2b e7 3e eb
P4:
0000 87 34 7b 66 71 5a 92 bf
0008 c6 21 f0 17 10 81 1a b3
0010 8f 71 0e 7b 33 2f 44 10
0018 9e 42 fd 63 e4 7f 62 c0
result:
0000 e3 f2 29 ba 72 7b e1 7b
0008 8d 12 26 20 55 7c d4 53
0010 c2 aa b2 1d 07 c3 d4 95
0018 32 9b 52 d4 e6 1e db 5a
0020 6b 30 17 91 e9 0d 35 c9
0028 c9 a4 6b 4e 14 ba f9 af
0030 0f a0 22 f7 07 7d ef 17
0038 ab fd 37 97 c0 56 4b ab
0040 4f bc 91 66 6e 9d ef 9b
0048 97 fc e3 4f 79 67 89 ba
0050 a4 80 82 d1 22 ee 42 c5
0058 a7 2e 5a 51 10 ff f7 01
0060 87 34 7b 66
-
$\begingroup$ Ends with
E3 F2 ...
etc as in the post mentioned, cannot be wrong. $\endgroup$– Maarten Bodewes ♦Commented Apr 17, 2017 at 19:28 -
$\begingroup$ I now have it working! The intermediate values for A0, A1, P1, A2, P2, etc. were very helpful and allowed me to correct my assembler error. Thank you! $\endgroup$– DonCSICommented Apr 18, 2017 at 11:08
-
$\begingroup$ @DonCSI By far the best way of saying thanks on stackexchange is to accept the answer (V mark left hand side) $\endgroup$– Maarten Bodewes ♦Commented Apr 20, 2017 at 15:34
HMAC_hash(secret, A(1) + seed)
; you can see thatA(1) = HMAC_hash(secret, A(1-1=0))
so you can see that the first block isHMAC_hash(secret, HMAC_hash(secret, seed + seed)
. Then you can replace the HMAC seed with the input seed which is label + original seed etc. $\endgroup$