RFC 9529

Traces of Ephemeral Diffie-Hellman Over COSE (EDHOC)

๐Ÿ“… published March 2024, from ietf

Traces of Ephemeral Diffie-Hellman Over COSE (EDHOC) ๐Ÿฆ‹

okay bestie, this one is RFC 9529 ๐Ÿ’– and it's giving "example traces for EDHOC" energy. It came from the IETF (Internet Engineering Task Force) and was written by G. Selander, J. PreuรŸ Mattsson (Ericsson), M. Serafin (ASSA ABLOY), M. Tiloca (RISE AB), and M. Vuฤiniฤ‡ (Inria). It's dated March 2024, its category is Informational, and its ISSN is 2070-1721 โœจ. The title block doesn't list anything it obsoletes or updates, so there's nothing to report there ๐Ÿ™ˆ.


Abstract ๐Ÿ’…

so the whole point is right here: this document contains example traces of Ephemeral Diffie-Hellman Over COSE (EDHOC) ๐ŸŽ€.

Status of This Memo ๐Ÿ“œ

this one is not an Internet Standards Track specification. it's published for informational purposes ๐Ÿ’œ

this is a product of the Internet Engineering Task Force (IETF), so it represents the consensus of the IETF community. it got public review and was approved for publication by the Internet Engineering Steering Group (IESG). not every document the IESG approves is a candidate for any level of Internet Standard, so check Section 2 of RFC 7841 if you're curious ๐Ÿงš

info about the current status of this doc, any errata, and how to give feedback is at https://www.rfc-editor.org/info/rfc9529 ๐ŸŒธ

Copyright (c) 2024 IETF Trust and the persons identified as the document authors. All rights reserved.

this document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as in effect on the date of publication. please read these carefully, bestie, because they describe your rights and restrictions for this document ๐Ÿ“–. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions, and they're provided without warranty as described in the Revised BSD License โš ๏ธ.

Table of Contents ๐ŸŽ€

1.  Introduction
  1.1.  Setup
  1.2.  Requirements Language
2.  Authentication with Signatures, X.509 Identified by 'x5t'
  2.1.  message_1
  2.2.  message_2
  2.3.  message_3
  2.4.  message_4
  2.5.  PRK_out and PRK_exporter
  2.6.  OSCORE Parameters
  2.7.  Key Update
  2.8.  Certificates
3.  Authentication with Static DH, CCS Identified by 'kid'
  3.1.  message_1 (First Time)
  3.2.  error
  3.3.  message_1 (Second Time)
  3.4.  message_2
  3.5.  message_3
  3.6.  message_4
  3.7.  PRK_out and PRK_exporter
  3.8.  OSCORE Parameters
  3.9.  Key Update
4.  Invalid Traces
  4.1.  Encoding Errors
  4.2.  Cryptography-Related Errors
  4.3.  Non-deterministic CBOR
5.  Security Considerations
6.  IANA Considerations
7.  References
  7.1.  Normative References
  7.2.  Informative References
Acknowledgments
Authors' Addresses

1. Introduction โœจ

EDHOC [RFC9528] is a lightweight authenticated key exchange protocol designed for highly constrained settings ๐Ÿฆ‹. this document has annotated traces of EDHOC sessions, with input, output, and intermediate processing results, so testing your implementation is way less painful ๐Ÿ’–. the traces have been verified by two independent implementations ๐Ÿ‘‘.

1.1. Setup ๐ŸŒธ

EDHOC runs between an Initiator (I) and a Responder (R) ๐Ÿซถ. the private/public key pairs and credentials of the Initiator and the Responder needed to produce the protocol messages are shown in the traces whenever they're needed for the calculations.

EDHOC messages and intermediate results are encoded in Concise Binary Object Representation (CBOR) [RFC8949]. that means you can display them in CBOR diagnostic notation using, e.g., the CBOR playground [CborMe], which makes them easy for humans to parse ๐Ÿ“. credentials can also be encoded in CBOR, e.g., CBOR Web Tokens (CWTs) [RFC8392] โœจ.

the document contains two traces:

  • Section 2 - Authentication with signature keys identified by the hash value of the X.509 certificates (provided in Section 2.8). the endpoints use Edwards-curve Digital Signature Algorithm (EdDSA) [RFC8032] for authentication and X25519 [RFC7748] for ephemeral-ephemeral Diffie-Hellman (DH) key exchange ๐Ÿ’….
  • Section 3 - Authentication with static Diffie-Hellman keys identified by short key identifiers labeling CWT Claims Sets (CCSs) [RFC8392]. the endpoints use NIST P-256 [SP-800-186] for both ephemeral-ephemeral and ephemeral-static DH key exchange. this trace also shows the cipher suite negotiation and is an example of low protocol overhead, with message sizes of 39, 45, and 19 bytes ๐Ÿ”ฅ.

examples of invalid EDHOC messages are in Section 4 ๐Ÿ™ˆ.

Note 1. The same name is used for hexadecimal byte strings and their CBOR encodings. The traces contain both the raw byte strings and the corresponding CBOR-encoded data items ๐Ÿ’œ.

Note 2. If it's not clear from the context, remember that CBOR sequences and CBOR arrays assume CBOR-encoded data items as elements ๐Ÿงš.

Note 3. When the protocol transporting EDHOC messages doesn't inherently provide correlation across all messages, some messages are typically prepended with connection identifiers and potentially a message_1 indicator (see Section 3.4.1 and Appendix A.2 of [RFC9528]). those bytes are not included in the traces in this document ๐ŸŽ€.

1.2. Requirements Language ๐Ÿ“œ

the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here ๐Ÿ’–.

2. Authentication with Signatures, X.509 Identified by 'x5t' ๐Ÿฆ‹

in this example, the Initiator (I) and Responder (R) are authenticated with digital signatures (METHOD = 0) ๐Ÿ’…. both of them support cipher suite 0, which determines these algorithms:

  • EDHOC AEAD algorithm = AES-CCM-16-64-128
  • EDHOC hash algorithm = SHA-256
  • EDHOC Message Authentication Code (MAC) length in bytes (Static DH) = 8
  • EDHOC key exchange algorithm (ECDH curve) = X25519
  • EDHOC signature algorithm = EdDSA
  • application AEAD algorithm = AES-CCM-16-64-128
  • application hash algorithm = SHA-256

the public keys are represented with X.509 certificates identified by the CBOR Object Signing and Encryption (COSE) header parameter 'x5t' ๐ŸŒˆ.

2.1. message_1 ๐Ÿ’œ

both endpoints are authenticated with signatures, i.e., METHOD = 0 โœจ:

METHOD (CBOR Data Item) (1 byte)
00

the Initiator selects cipher suite 0. a single cipher suite is encoded as an int ๐Ÿ“:

SUITES_I (CBOR Data Item) (1 byte)
00

the Initiator creates an ephemeral key pair for use with the EDHOC key exchange algorithm ๐Ÿงš:

Initiator's ephemeral private key
X (Raw Value) (32 bytes)
89 2e c2 8e 5c b6 66 91 08 47 05 39 50 0b 70 5e 60 d0 08 d3 47 c5 81
7e e9 f3 32 7c 8a 87 bb 03
Initiator's ephemeral public key
G_X (Raw Value) (32 bytes)
31 f8 2c 7b 5b 9c bb f0 f1 94 d9 13 cc 12 ef 15 32 d3 28 ef 32 63 2a
48 81 a1 c0 70 1e 23 7f 04
Initiator's ephemeral public key
G_X (CBOR Data Item) (34 bytes)
58 20 31 f8 2c 7b 5b 9c bb f0 f1 94 d9 13 cc 12 ef 15 32 d3 28 ef 32
63 2a 48 81 a1 c0 70 1e 23 7f 04

the Initiator picks its connection identifier C_I to be the byte string 0x2d. it's encoded as 0x2d since it's represented by the 1-byte CBOR int -14 ๐ŸŒธ:

Connection identifier chosen by the Initiator
C_I (Raw Value) (1 byte)
2d
Connection identifier chosen by the Initiator
C_I (CBOR Data Item) (1 byte)
2d

no external authorization data here ๐Ÿ™ˆ:

EAD_1 (CBOR Sequence) (0 bytes)

the Initiator constructs message_1 ๐ŸŽ€:

message_1 =
(
  0,
  0,
  h'31f82c7b5b9cbbf0f194d913cc12ef1532d328ef32632a48
    81a1c0701e237f04',
  -14
)
message_1 (CBOR Sequence) (37 bytes)
00 00 58 20 31 f8 2c 7b 5b 9c bb f0 f1 94 d9 13 cc 12 ef 15 32 d3 28
ef 32 63 2a 48 81 a1 c0 70 1e 23 7f 04 2d

2.2. message_2 ๐ŸŒท

the Responder supports the most preferred and selected cipher suite 0, so SUITES_I is acceptable ๐Ÿ‘‘.

the Responder creates an ephemeral key pair for use with the EDHOC key exchange algorithm ๐Ÿ’ซ:

Responder's ephemeral private key
Y (Raw Value) (32 bytes)
e6 9c 23 fb f8 1b c4 35 94 24 46 83 7f e8 27 bf 20 6c 8f a1 0a 39 db
47 44 9e 5a 81 34 21 e1 e8
Responder's ephemeral public key
G_Y (Raw Value) (32 bytes)
dc 88 d2 d5 1d a5 ed 67 fc 46 16 35 6b c8 ca 74 ef 9e be 8b 38 7e 62
3a 36 0b a4 80 b9 b2 9d 1c
Responder's ephemeral public key
G_Y (CBOR Data Item) (34 bytes)
58 20 dc 88 d2 d5 1d a5 ed 67 fc 46 16 35 6b c8 ca 74 ef 9e be 8b 38
7e 62 3a 36 0b a4 80 b9 b2 9d 1c

the Responder picks its connection identifier C_R to be the byte string 0x18. it's encoded as h'18' = 0x4118 since it's not represented by a 1-byte CBOR int ๐Ÿซง:

Connection identifier chosen by the Responder
C_R (Raw Value) (1 byte)
18
Connection identifier chosen by the Responder
C_R (CBOR Data Item) (2 bytes)
41 18

the transcript hash TH_2 is calculated with the EDHOC hash algorithm ๐Ÿ”ฎ:

TH_2 = H( G_Y, H(message_1) )
H(message_1) (Raw Value) (32 bytes)
c1 65 d6 a9 9d 1b ca fa ac 8d bf 2b 35 2a 6f 7d 71 a3 0b 43 9c 9d 64
d3 49 a2 38 48 03 8e d1 6b
H(message_1) (CBOR Data Item) (34 bytes)
58 20 c1 65 d6 a9 9d 1b ca fa ac 8d bf 2b 35 2a 6f 7d 71 a3 0b 43 9c
9d 64 d3 49 a2 38 48 03 8e d1 6b

the input to calculate TH_2 is the CBOR sequence ๐Ÿญ:

G_Y, H(message_1)
Input to calculate TH_2 (CBOR Sequence) (68 bytes)
58 20 dc 88 d2 d5 1d a5 ed 67 fc 46 16 35 6b c8 ca 74 ef 9e be 8b 38
7e 62 3a 36 0b a4 80 b9 b2 9d 1c 58 20 c1 65 d6 a9 9d 1b ca fa ac 8d
bf 2b 35 2a 6f 7d 71 a3 0b 43 9c 9d 64 d3 49 a2 38 48 03 8e d1 6b
TH_2 (Raw Value) (32 bytes)
c6 40 5c 15 4c 56 74 66 ab 1d f2 03 69 50 0e 54 0e 9f 14 bd 3a 79 6a
06 52 ca e6 6c 90 61 68 8d
TH_2 (CBOR Data Item) (34 bytes)
58 20 c6 40 5c 15 4c 56 74 66 ab 1d f2 03 69 50 0e 54 0e 9f 14 bd 3a
79 6a 06 52 ca e6 6c 90 61 68 8d

PRK_2e is specified in Section 4.1.1.1 of [RFC9528] ๐ŸŒŸ.

first, the Elliptic Curve Diffie-Hellman (ECDH) shared secret G_XY is computed from G_X and Y, or from G_Y and X ๐Ÿ’ฅ:

G_XY (Raw Value) (ECDH shared secret) (32 bytes)
e5 cd f3 a9 86 cd ac 5b 7b f0 46 91 e2 b0 7c 08 e7 1f 53 99 8d 8f 84
2b 7c 3f b4 d8 39 cf 7b 28

then PRK_2e is calculated using EDHOC_Extract(), which is determined by the EDHOC hash algorithm ๐Ÿง:

PRK_2e = EDHOC_Extract( salt, G_XY )
       = HMAC-SHA-256( salt, G_XY )

where salt is TH_2 ๐Ÿซถ:

salt (Raw Value) (32 bytes)
c6 40 5c 15 4c 56 74 66 ab 1d f2 03 69 50 0e 54 0e 9f 14 bd 3a 79 6a
06 52 ca e6 6c 90 61 68 8d
PRK_2e (Raw Value) (32 bytes)
d5 84 ac 2e 5d ad 5a 77 d1 4b 53 eb e7 2e f1 d5 da a8 86 0d 39 93 73
bf 2c 24 0a fa 7b a8 04 da

since METHOD = 0, the Responder authenticates using signatures ๐Ÿ’œ. since the selected cipher suite is 0, the EDHOC signature algorithm is EdDSA ๐Ÿฆ‹.

the Responder's signature key pair uses EdDSA ๐Ÿ”:

Responder's private authentication key
SK_R (Raw Value) (32 bytes)
ef 14 0f f9 00 b0 ab 03 f0 c0 8d 87 9c bb d4 b3 1e a7 1e 6e 7e e7 ff
cb 7e 79 55 77 7a 33 27 99

okay so the Responder's public key is literally right here, bestie ๐Ÿ”‘โœจ

Responder's public authentication key
PK_R (Raw Value) (32 bytes)
a1 db 47 b9 51 84 85 4a d1 2a 0c 1a 35 4e 41 8a ac e3 3a a0 f2 c6 62
c0 0b 3a c5 5d e9 2f 93 59

PRK_3e2m is specified in Section 4.1.1.2 of [RFC9528] ๐Ÿ“š so if you want the full backstory, that's where it lives ๐Ÿ’…

Since the Responder authenticates with signatures, PRK_3e2m = PRK_2e ๐Ÿซถ this is the part where the two values end up being the same thing, which is lowkey so satisfying ๐Ÿ˜ญ

PRK_3e2m (Raw Value) (32 bytes)
d5 84 ac 2e 5d ad 5a 77 d1 4b 53 eb e7 2e f1 d5 da a8 86 0d 39 93 73
bf 2c 24 0a fa 7b a8 04 da

Next up, the Responder builds the rest of what it needs to calculate MAC_2 ๐Ÿ”ฅ

MAC_2 = EDHOC_KDF( PRK_3e2m, 2, context_2, mac_length_2 )

context_2 = << C_R, ID_CRED_R, TH_2, CRED_R, ? EAD_2 >>

CRED_R gets identified by a 64-bit hash, which is such a clean move ๐Ÿฆ‹

ID_CRED_R =
{
  34 : [-15, h'79f2a41b510c1f9b']
}

So the COSE header value 34 (that's 'x5t') means "this is a hash of an X.509 certificate," and the COSE algorithm -15 means the hash is SHA-256 truncated to 64 bits. Iconic energy, no notes ๐ŸŒธ

ID_CRED_R (CBOR Data Item) (14 bytes)
a1 18 22 82 2e 48 79 f2 a4 1b 51 0c 1f 9b

CRED_R is a CBOR byte string holding the DER encoding of the X.509 certificate in Section 2.8.1 ๐ŸŽ€ the full certificate is right here, no cap:

CRED_R (Raw Value) (241 bytes)
30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31 9e c4 30 05 06 03 2b 65
70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45 44 48 4f 43 20 52 6f 6f
74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32 30 33 31 36 30 38 32 34
33 36 5a 17 0d 32 39 31 32 33 31 32 33 30 30 30 30 5a 30 22 31 20 30
1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 52 65 73 70 6f 6e 64 65 72
20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b 65 70 03 21 00 a1 db 47
b9 51 84 85 4a d1 2a 0c 1a 35 4e 41 8a ac e3 3a a0 f2 c6 62 c0 0b 3a
c5 5d e9 2f 93 59 30 05 06 03 2b 65 70 03 41 00 b7 23 bc 01 ea b0 92
8e 8b 2b 6c 98 de 19 cc 38 23 d4 6e 7d 69 87 b0 32 47 8f ec fa f1 45
37 a1 af 14 cc 8b e8 29 c6 b7 30 44 10 18 37 eb 4a bc 94 95 65 d8 6d
ce 51 cf ae 52 ab 82 c1 52 cb 02

And here's the same thing wrapped as a CBOR data item ๐Ÿงš

CRED_R (CBOR Data Item) (243 bytes)
58 f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31 9e c4 30 05 06 03
2b 65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45 44 48 4f 43 20 52
6f 6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32 30 33 31 36 30 38
32 34 33 36 5a 17 0d 32 39 31 32 33 31 32 33 30 30 30 30 5a 30 22 31
20 30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 52 65 73 70 6f 6e 64
65 72 20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b 65 70 03 21 00 a1
db 47 b9 51 84 85 4a d1 2a 0c 1a 35 4e 41 8a ac e3 3a a0 f2 c6 62 c0
0b 3a c5 5d e9 2f 93 59 30 05 06 03 2b 65 70 03 41 00 b7 23 bc 01 ea
b0 92 8e 8b 2b 6c 98 de 19 cc 38 23 d4 6e 7d 69 87 b0 32 47 8f ec fa
f1 45 37 a1 af 14 cc 8b e8 29 c6 b7 30 44 10 18 37 eb 4a bc 94 95 65
d8 6d ce 51 cf ae 52 ab 82 c1 52 cb 02

No external authorization data this time, so EAD_2 is empty ๐Ÿ™ˆ

EAD_2 (CBOR Sequence) (0 bytes)

Now let's put context_2 together ๐Ÿ’–

context_2 = << C_R, ID_CRED_R, TH_2, CRED_R, ? EAD_2 >>

context_2 (CBOR Sequence) (293 bytes)
41 18 a1 18 22 82 2e 48 79 f2 a4 1b 51 0c 1f 9b 58 20 c6 40 5c 15 4c
56 74 66 ab 1d f2 03 69 50 0e 54 0e 9f 14 bd 3a 79 6a 06 52 ca e6 6c
90 61 68 8d 58 f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31 9e c4
30 05 06 03 2b 65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45 44 48
4f 43 20 52 6f 6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32 30 33
31 36 30 38 32 34 33 36 5a 17 0d 32 39 31 32 33 31 32 33 30 30 30 30
5a 30 22 31 20 30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 52 65 73
70 6f 6e 64 65 72 20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b 65 70
03 21 00 a1 db 47 b9 51 84 85 4a d1 2a 0c 1a 35 4e 41 8a ac e3 3a a0
f2 c6 62 c0 0b 3a c5 5d e9 2f 93 59 30 05 06 03 2b 65 70 03 41 00 b7
23 bc 01 ea b0 92 8e 8b 2b 6c 98 de 19 cc 38 23 d4 6e 7d 69 87 b0 32
47 8f ec fa f1 45 37 a1 af 14 cc 8b e8 29 c6 b7 30 44 10 18 37 eb 4a
bc 94 95 65 d8 6d ce 51 cf ae 52 ab 82 c1 52 cb 02

context_2 (CBOR byte string) (296 bytes)
59 01 25 41 18 a1 18 22 82 2e 48 79 f2 a4 1b 51 0c 1f 9b 58 20 c6 40
5c 15 4c 56 74 66 ab 1d f2 03 69 50 0e 54 0e 9f 14 bd 3a 79 6a 06 52
ca e6 6c 90 61 68 8d 58 f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62
31 9e c4 30 05 06 03 2b 65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12
45 44 48 4f 43 20 52 6f 6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d 32
32 30 33 31 36 30 38 32 34 33 36 5a 17 0d 32 39 31 32 33 31 32 33 30
30 30 30 5a 30 22 31 20 30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20
52 65 73 70 6f 6e 64 65 72 20 45 64 32 35 35 31 39 30 2a 30 05 06 03
2b 65 70 03 21 00 a1 db 47 b9 51 84 85 4a d1 2a 0c 1a 35 4e 41 8a ac
e3 3a a0 f2 c6 62 c0 0b 3a c5 5d e9 2f 93 59 30 05 06 03 2b 65 70 03
41 00 b7 23 bc 01 ea b0 92 8e 8b 2b 6c 98 de 19 cc 38 23 d4 6e 7d 69
87 b0 32 47 8f ec fa f1 45 37 a1 af 14 cc 8b e8 29 c6 b7 30 44 10 18
37 eb 4a bc 94 95 65 d8 6d ce 51 cf ae 52 ab 82 c1 52 cb 02

MAC_2 gets computed through EDHOC_Expand() using the EDHOC hash algorithm (see Section 4.1.2 of [RFC9528]) ๐Ÿงช the formula is pretty slay:

MAC_2 = HKDF-Expand( PRK_3e2m, info, mac_length_2 )

where

info = ( 2, context_2, mac_length_2 )

Since METHOD = 0, mac_length_2 is given by the EDHOC hash algorithm ๐Ÿ’œ

Here's the info for MAC_2, written out in full:

info =
(
  2,
  h'4118a11822822e4879f2a41b510c1f9b5820c6405c154c56
    7466ab1df20369500e540e9f14bd3a796a0652cae66c9061
    688d58f13081ee3081a1a003020102020462319ec4300506
    032b6570301d311b301906035504030c124544484f432052
    6f6f742045643235353139301e170d323230333136303832
    3433365a170d3239313233313233303030305a3022312030
    1e06035504030c174544484f4320526573706f6e64657220
    45643235353139302a300506032b6570032100a1db47b951
    84854ad12a0c1a354e418aace33aa0f2c662c00b3ac55de9
    2f9359300506032b6570034100b723bc01eab0928e8b2b6c
    98de19cc3823d46e7d6987b032478fecfaf14537a1af14cc
    8be829c6b73044101837eb4abc949565d86dce51cfae52ab
    82c152cb02',
  32
)

The last value there is the output size of the EDHOC hash algorithm, in bytes โœจ

info for MAC_2 (CBOR Sequence) (299 bytes)
02 59 01 25 41 18 a1 18 22 82 2e 48 79 f2 a4 1b 51 0c 1f 9b 58 20 c6
40 5c 15 4c 56 74 66 ab 1d f2 03 69 50 0e 54 0e 9f 14 bd 3a 79 6a 06
52 ca e6 6c 90 61 68 8d 58 f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04
62 31 9e c4 30 05 06 03 2b 65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c
12 45 44 48 4f 43 20 52 6f 6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d
32 32 30 33 31 36 30 38 32 34 33 36 5a 17 0d 32 39 31 32 33 31 32 33
30 30 30 30 5a 30 22 31 20 30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43
20 52 65 73 70 6f 6e 64 65 72 20 45 64 32 35 35 31 39 30 2a 30 05 06
03 2b 65 70 03 21 00 a1 db 47 b9 51 84 85 4a d1 2a 0c 1a 35 4e 41 8a
ac e3 3a a0 f2 c6 62 c0 0b 3a c5 5d e9 2f 93 59 30 05 06 03 2b 65 70
03 41 00 b7 23 bc 01 ea b0 92 8e 8b 2b 6c 98 de 19 cc 38 23 d4 6e 7d
69 87 b0 32 47 8f ec fa f1 45 37 a1 af 14 cc 8b e8 29 c6 b7 30 44 10
18 37 eb 4a bc 94 95 65 d8 6d ce 51 cf ae 52 ab 82 c1 52 cb 02 18 20

And the MAC_2 value itself ๐Ÿ”

MAC_2 (Raw Value) (32 bytes)
86 2a 7e 5e f1 47 f9 a5 f4 c5 12 e1 b6 62 3c d6 6c d1 7a 72 72 07 2b
fe 5b 60 2f fe 30 7e e0 e9

MAC_2 (CBOR Data Item) (34 bytes)
58 20 86 2a 7e 5e f1 47 f9 a5 f4 c5 12 e1 b6 62 3c d6 6c d1 7a 72 72
07 2b fe 5b 60 2f fe 30 7e e0 e9

Since METHOD = 0, Signature_or_MAC_2 is the 'signature' of the COSE_Sign1 object ๐ŸŒˆ

The Responder now builds the message to be signed ๐Ÿซถ

[
  "Signature1",
  << ID_CRED_R >>,
  << TH_2, CRED_R, ? EAD_2 >>,
  MAC_2
] =

[
  "Signature1",
  h'a11822822e4879f2a41b510c1f9b',
  h'5820c6405c154c567466ab1df20369500e540e9f14bd3a79
    6a0652cae66c9061688d58f13081ee3081a1a00302010202
    0462319ec4300506032b6570301d311b301906035504030c
    124544484f4320526f6f742045643235353139301e170d32
    32303331363038323433365a170d32393132333132333030
    30305a30223120301e06035504030c174544484f43205265
    73706f6e6465722045643235353139302a300506032b6570
    032100a1db47b95184854ad12a0c1a354e418aace33aa0f2
    c662c00b3ac55de92f9359300506032b6570034100b723bc
    01eab0928e8b2b6c98de19cc3823d46e7d6987b032478fec
    faf14537a1af14cc8be829c6b73044101837eb4abc949565
    d86dce51cfae52ab82c152cb02',
  h'862a7e5ef147f9a5f4c512e1b6623cd66cd17a7272072bfe
    5b602ffe307ee0e9'
]

Message to be signed in message_2 (CBOR Data Item) (341 bytes)
84 6a 53 69 67 6e 61 74 75 72 65 31 4e a1 18 22 82 2e 48 79 f2 a4 1b
51 0c 1f 9b 59 01 15 58 20 c6 40 5c 15 4c 56 74 66 ab 1d f2 03 69 50
0e 54 0e 9f 14 bd 3a 79 6a 06 52 ca e6 6c 90 61 68 8d 58 f1 30 81 ee
30 81 a1 a0 03 02 01 02 02 04 62 31 9e c4 30 05 06 03 2b 65 70 30 1d
31 1b 30 19 06 03 55 04 03 0c 12 45 44 48 4f 43 20 52 6f 6f 74 20 45
64 32 35 35 31 39 30 1e 17 0d 32 32 30 33 31 36 30 38 32 34 33 36 5a
17 0d 32 39 31 32 33 31 32 33 30 30 30 30 5a 30 22 31 20 30 1e 06 03
55 04 03 0c 17 45 44 48 4f 43 20 52 65 73 70 6f 6e 64 65 72 20 45 64
32 35 35 31 39 30 2a 30 05 06 03 2b 65 70 03 21 00 a1 db 47 b9 51 84
85 4a d1 2a 0c 1a 35 4e 41 8a ac e3 3a a0 f2 c6 62 c0 0b 3a c5 5d e9
2f 93 59 30 05 06 03 2b 65 70 03 41 00 b7 23 bc 01 ea b0 92 8e 8b 2b
6c 98 de 19 cc 38 23 d4 6e 7d 69 87 b0 32 47 8f ec fa f1 45 37 a1 af
14 cc 8b e8 29 c6 b7 30 44 10 18 37 eb 4a bc 94 95 65 d8 6d ce 51 cf
ae 52 ab 82 c1 52 cb 02 58 20 86 2a 7e 5e f1 47 f9 a5 f4 c5 12 e1 b6
62 3c d6 6c d1 7a 72 72 07 2b fe 5b 60 2f fe 30 7e e0 e9

The Responder signs using the private authentication key SK_R โœ๏ธ and the result is:

Signature_or_MAC_2 (Raw Value) (64 bytes)
c3 b5 bd 44 d1 e4 4a 08 5c 03 d3 ae de 4e 1e 6c 11 c5 72 a1 96 8c c3
62 9b 50 5f 98 c6 81 60 8d 3d 1d e7 93 d1 c4 0e b5 dd 5d 89 ac f1 96
6a ea 07 02 2b 48 cd c9 98 70 eb c4 03 74 e8 fa 6e 09

Signature_or_MAC_2 (CBOR Data Item) (66 bytes)
58 40 c3 b5 bd 44 d1 e4 4a 08 5c 03 d3 ae de 4e 1e 6c 11 c5 72 a1 96
8c c3 62 9b 50 5f 98 c6 81 60 8d 3d 1d e7 93 d1 c4 0e b5 dd 5d 89 ac
f1 96 6a ea 07 02 2b 48 cd c9 98 70 eb c4 03 74 e8 fa 6e 09

Next, the Responder constructs PLAINTEXT_2 ๐ŸŽ€

PLAINTEXT_2 =
(
  C_R,
  ID_CRED_R / bstr / -24..23,
  Signature_or_MAC_2,
  ? EAD_2
)

PLAINTEXT_2 (CBOR Sequence) (82 bytes)
41 18 a1 18 22 82 2e 48 79 f2 a4 1b 51 0c 1f 9b 58 40 c3 b5 bd 44 d1
e4 4a 08 5c 03 d3 ae de 4e 1e 6c 11 c5 72 a1 96 8c c3 62 9b 50 5f 98
c6 81 60 8d 3d 1d e7 93 d1 c4 0e b5 dd 5d 89 ac f1 96 6a ea 07 02 2b
48 cd c9 98 70 eb c4 03 74 e8 fa 6e 09

The input for KEYSTREAM_2 is defined in Section 4.1.2 of [RFC9528], using EDHOC_Expand() with the EDHOC hash algorithm ๐Ÿง

KEYSTREAM_2 = EDHOC_KDF( PRK_2e, 0, TH_2, plaintext_length )
            = HKDF-Expand( PRK_2e, info, plaintext_length )

Here, plaintext_length is the length in bytes of PLAINTEXT_2 (in bytes), and the info for KEYSTREAM_2 looks like this ๐ŸŒธ

info =
(
  0,
  h'c6405c154c567466ab1df20369500e540e9f14bd3a796a06
    52cae66c9061688d',
  82
)

The last value there is the length in bytes of PLAINTEXT_2 โœจ

info for KEYSTREAM_2 (CBOR Sequence) (37 bytes)
00 58 20 c6 40 5c 15 4c 56 74 66 ab 1d f2 03 69 50 0e 54 0e 9f 14 bd
3a 79 6a 06 52 ca e6 6c 90 61 68 8d 18 52

KEYSTREAM_2 (Raw Value) (82 bytes)
fd 3e 7c 3f 2d 6b ee 64 3d 3c 9d 2f 28 47 03 5d 73 e2 ec b0 f8 db 5c
d1 c6 85 4e 24 89 6a f2 11 88 b2 c4 34 4e 68 9e c2 98 42 83 d9 fb c6
9c e1 c5 db 10 dc ff f2 4d f9 a4 9a 04 a9 40 58 27 7b c7 fa 9a d6 c6
b1 94 ab 32 8b 44 5e b0 80 49 0c d7 86

The Responder calculates CIPHERTEXT_2 as an XOR between PLAINTEXT_2 and KEYSTREAM_2 ๐Ÿ”ฅ

CIPHERTEXT_2 (Raw Value) (82 bytes)
bc 26 dd 27 0f e9 c0 2c 44 ce 39 34 79 4b 1c c6 2b a2 2f 05 45 9f 8d
35 8c 8d 12 27 5a c4 2c 5f 96 de d5 f1 3c c9 08 4e 5b 20 18 89 a4 5e
5a 60 a5 56 2d c1 18 61 9c 3d aa 2f d9 f4 c9 f4 d6 ed ad 10 9d d4 ed
f9 59 62 aa fb af 9a b3 f4 a1 f6 b9 8f

Finally, the Responder constructs message_2 ๐Ÿ‘‘

message_2 =
(
  G_Y_CIPHERTEXT_2
)

where G_Y_CIPHERTEXT_2 is the bstr encoding of the concatenation of the raw values of G_Y and CIPHERTEXT_2. and that's a wrap on message_2, bestie ๐ŸŒˆ๐Ÿ’–

2.3. message_3 ๐ŸŽ€

okay so message_3 is the Initiator's big moment ๐Ÿ’… because METHOD = 0 means the Initiator authenticates using signatures, and the selected cipher suite is 0, so the EDHOC signature algorithm is EdDSA. bestie, EdDSA is giving main character energy โœจ

The Initiator's signature key pair uses EdDSA ๐Ÿฆ‹

Initiator's private authentication key SK_I (Raw Value) (32 bytes)

4c 5b 25 87 8f 50 7c 6b 9d ae 68 fb d4 fd 3f f9 97 53 3d b0 af 00 b2
5d 32 4e a2 8e 6c 21 3b c8

Initiator's public authentication key PK_I (Raw Value) (32 bytes)

ed 06 a8 ae 61 a8 29 ba 5f a5 45 25 c9 d0 7f 48 dd 44 a3 02 f4 3e 0f
23 d8 cc 20 b7 30 85 14 1e

PRK_4e3m is specified in Section 4.1.1.3 of [RFC9528] ๐ŸŒธ. Since the Initiator authenticates with signatures, PRK_4e3m = PRK_3e2m. so the value is the same, no cap ๐Ÿ™ˆ

PRK_4e3m (Raw Value) (32 bytes)

d5 84 ac 2e 5d ad 5a 77 d1 4b 53 eb e7 2e f1 d5 da a8 86 0d 39 93 73
bf 2c 24 0a fa 7b a8 04 da

The transcript hash TH_3 is calculated using the EDHOC hash algorithm ๐Ÿงš

TH_3 = H( TH_2, PLAINTEXT_2, CRED_R )

Input to calculate TH_3 (CBOR Sequence) (359 bytes)

58 20 c6 40 5c 15 4c 56 74 66 ab 1d f2 03 69 50 0e 54 0e 9f 14 bd 3a
79 6a 06 52 ca e6 6c 90 61 68 8d 41 18 a1 18 22 82 2e 48 79 f2 a4 1b
51 0c 1f 9b 58 40 c3 b5 bd 44 d1 e4 4a 08 5c 03 d3 ae de 4e 1e 6c 11
c5 72 a1 96 8c c3 62 9b 50 5f 98 c6 81 60 8d 3d 1d e7 93 d1 c4 0e b5
dd 5d 89 ac f1 96 6a ea 07 02 2b 48 cd c9 98 70 eb c4 03 74 e8 fa 6e
09 58 f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31 9e c4 30 05 06
03 2b 65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45 44 48 4f 43 20
52 6f 6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32 30 33 31 36 30
38 32 34 33 36 5a 17 0d 32 39 31 32 33 31 32 33 30 30 30 30 5a 30 22
31 20 30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 52 65 73 70 6f 6e
64 65 72 20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b 65 70 03 21 00
a1 db 47 b9 51 84 85 4a d1 2a 0c 1a 35 4e 41 8a ac e3 3a a0 f2 c6 62
c0 0b 3a c5 5d e9 2f 93 59 30 05 06 03 2b 65 70 03 41 00 b7 23 bc 01
ea b0 92 8e 8b 2b 6c 98 de 19 cc 38 23 d4 6e 7d 69 87 b0 32 47 8f ec
fa f1 45 37 a1 af 14 cc 8b e8 29 c6 b7 30 44 10 18 37 eb 4a bc 94 95
65 d8 6d ce 51 cf ae 52 ab 82 c1 52 cb 02

TH_3 (Raw Value) (32 bytes)

5b 7d f9 b4 f5 8f 24 0c e0 41 8e 48 19 1b 5f ff 3a 22 b5 ca 57 f6 69
b1 67 77 99 65 92 e9 28 bc

TH_3 (CBOR Data Item) (34 bytes)

58 20 5b 7d f9 b4 f5 8f 24 0c e0 41 8e 48 19 1b 5f ff 3a 22 b5 ca 57
f6 69 b1 67 77 99 65 92 e9 28 bc

The Initiator constructs the remaining input needed to calculate MAC_3 ๐Ÿ“

MAC_3 = EDHOC_KDF( PRK_4e3m, 6, context_3, mac_length_3 )

where

context_3 = << ID_CRED_I, TH_3, CRED_I, ? EAD_3 >>

CRED_I is identified by a 64-bit hash ๐Ÿ”ฅ

ID_CRED_I =
{
  34 : [-15, h'c24ab2fd7643c79f']
}

where the COSE header value 34 ('x5t') indicates a hash of an X.509 certificate, and the COSE algorithm -15 indicates the hash algorithm SHA-256 truncated to 64 bits.

ID_CRED_I (CBOR Data Item) (14 bytes)

a1 18 22 82 2e 48 c2 4a b2 fd 76 43 c7 9f

CRED_I is a CBOR byte string of the DER encoding of the X.509 certificate in Section 2.8.2 ๐ŸŽ€:

CRED_I (Raw Value) (241 bytes)

30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31 9e a0 30 05 06 03 2b 65
70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45 44 48 4f 43 20 52 6f 6f
74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32 30 33 31 36 30 38 32 34
30 30 5a 17 0d 32 39 31 32 33 31 32 33 30 30 30 30 5a 30 22 31 20 30
1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 49 6e 69 74 69 61 74 6f 72
20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b 65 70 03 21 00 ed 06 a8
ae 61 a8 29 ba 5f a5 45 25 c9 d0 7f 48 dd 44 a3 02 f4 3e 0f 23 d8 cc
20 b7 30 85 14 1e 30 05 06 03 2b 65 70 03 41 00 52 12 41 d8 b3 a7 70
99 6b cf c9 b9 ea d4 e7 e0 a1 c0 db 35 3a 3b df 29 10 b3 92 75 ae 48
b7 56 01 59 81 85 0d 27 db 67 34 e3 7f 67 21 22 67 dd 05 ee ff 27 b9
e7 a8 13 fa 57 4b 72 a0 0b 43 0b

CRED_I (CBOR Data Item) (243 bytes)

58 f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31 9e a0 30 05 06 03
2b 65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45 44 48 4f 43 20 52
6f 6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32 30 33 31 36 30 38
32 34 30 30 5a 17 0d 32 39 31 32 33 31 32 33 30 30 30 30 5a 30 22 31
20 30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 49 6e 69 74 69 61 74
6f 72 20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b 65 70 03 21 00 ed
06 a8 ae 61 a8 29 ba 5f a5 45 25 c9 d0 7f 48 dd 44 a3 02 f4 3e 0f 23
d8 cc 20 b7 30 85 14 1e 30 05 06 03 2b 65 70 03 41 00 52 12 41 d8 b3
a7 70 99 6b cf c9 b9 ea d4 e7 e0 a1 c0 db 35 3a 3b df 29 10 b3 92 75
ae 48 b7 56 01 59 81 85 0d 27 db 67 34 e3 7f 67 21 22 67 dd 05 ee ff
27 b9 e7 a8 13 fa 57 4b 72 a0 0b 43 0b

No external authorization data ๐Ÿ™ˆ:

EAD_3 (CBOR Sequence) (0 bytes)

context_3 = << ID_CRED_I, TH_3, CRED_I, ? EAD_3 >>

context_3 (CBOR Sequence) (291 bytes)

a1 18 22 82 2e 48 c2 4a b2 fd 76 43 c7 9f 58 20 5b 7d f9 b4 f5 8f 24
0c e0 41 8e 48 19 1b 5f ff 3a 22 b5 ca 57 f6 69 b1 67 77 99 65 92 e9
28 bc 58 f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31 9e a0 30 05
06 03 2b 65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45 44 48 4f 43
20 52 6f 6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32 30 33 31 36
30 38 32 34 30 30 5a 17 0d 32 39 31 32 33 31 32 33 30 30 30 30 5a 30
22 31 20 30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 49 6e 69 74 69
61 74 6f 72 20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b 65 70 03 21
00 ed 06 a8 ae 61 a8 29 ba 5f a5 45 25 c9 d0 7f 48 dd 44 a3 02 f4 3e
0f 23 d8 cc 20 b7 30 85 14 1e 30 05 06 03 2b 65 70 03 41 00 52 12 41
d8 b3 a7 70 99 6b cf c9 b9 ea d4 e7 e0 a1 c0 db 35 3a 3b df 29 10 b3
92 75 ae 48 b7 56 01 59 81 85 0d 27 db 67 34 e3 7f 67 21 22 67 dd 05
ee ff 27 b9 e7 a8 13 fa 57 4b 72 a0 0b 43 0b

context_3 (CBOR byte string) (294 bytes)

59 01 23 a1 18 22 82 2e 48 c2 4a b2 fd 76 43 c7 9f 58 20 5b 7d f9 b4
f5 8f 24 0c e0 41 8e 48 19 1b 5f ff 3a 22 b5 ca 57 f6 69 b1 67 77 99
65 92 e9 28 bc 58 f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31 9e
a0 30 05 06 03 2b 65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45 44
48 4f 43 20 52 6f 6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32 30
33 31 36 30 38 32 34 30 30 5a 17 0d 32 39 31 32 33 31 32 33 30 30 30
30 5a 30 22 31 20 30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 49 6e
69 74 69 61 74 6f 72 20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b 65
70 03 21 00 ed 06 a8 ae 61 a8 29 ba 5f a5 45 25 c9 d0 7f 48 dd 44 a3
02 f4 3e 0f 23 d8 cc 20 b7 30 85 14 1e 30 05 06 03 2b 65 70 03 41 00
52 12 41 d8 b3 a7 70 99 6b cf c9 b9 ea d4 e7 e0 a1 c0 db 35 3a 3b df
29 10 b3 92 75 ae 48 b7 56 01 59 81 85 0d 27 db 67 34 e3 7f 67 21 22
67 dd 05 ee ff 27 b9 e7 a8 13 fa 57 4b 72 a0 0b 43 0b

MAC_3 is computed through EDHOC_Expand() using the EDHOC hash algorithm (see Section 4.1.2 of [RFC9528]) ๐Ÿฆ‹:

MAC_3 = HKDF-Expand( PRK_4e3m, info, mac_length_3 )

where

info = ( 6, context_3, mac_length_3 )

where

context_3 = << ID_CRED_I, TH_3, CRED_I, ? EAD_3 >>

Since METHOD = 0, mac_length_3 is given by the EDHOC hash algorithm โœจ.

info for MAC_3 is:

info =
(
  6,
  h'a11822822e48c24ab2fd7643c79f58205b7df9b4f58f240c
    e0418e48191b5fff3a22b5ca57f669b16777996592e928bc
    58f13081ee3081a1a003020102020462319ea0300506032b
    6570301d311b301906035504030c124544484f4320526f6f
    742045643235353139301e170d3232303331363038323430
    305a170d3239313233313233303030305a30223120301e06
    035504030c174544484f4320496e69746961746f72204564
    3235353139302a300506032b6570032100ed06a8ae61a829
    ba5fa54525c9d07f48dd44a302f43e0f23d8cc20b7308514
    1e300506032b6570034100521241d8b3a770996bcfc9b9ea
    d4e7e0a1c0db353a3bdf2910b39275ae48b756015981850d
    27db6734e37f67212267dd05eeff27b9e7a813fa574b72a0
    0b430b',
  32
)

where the last value is the output size of the EDHOC hash algorithm in bytes.

info for MAC_3 (CBOR Sequence) (297 bytes)

06 59 01 23 a1 18 22 82 2e 48 c2 4a b2 fd 76 43 c7 9f 58 20 5b 7d f9
b4 f5 8f 24 0c e0 41 8e 48 19 1b 5f ff 3a 22 b5 ca 57 f6 69 b1 67 77
99 65 92 e9 28 bc 58 f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31
9e a0 30 05 06 03 2b 65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45
44 48 4f 43 20 52 6f 6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32
30 33 31 36 30 38 32 34 30 30 5a 17 0d 32 39 31 32 33 31 32 33 30 30
30 30 5a 30 22 31 20 30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 49
6e 69 74 69 61 74 6f 72 20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b
65 70 03 21 00 ed 06 a8 ae 61 a8 29 ba 5f a5 45 25 c9 d0 7f 48 dd 44
a3 02 f4 3e 0f 23 d8 cc 20 b7 30 85 14 1e 30 05 06 03 2b 65 70 03 41
00 52 12 41 d8 b3 a7 70 99 6b cf c9 b9 ea d4 e7 e0 a1 c0 db 35 3a 3b
df 29 10 b3 92 75 ae 48 b7 56 01 59 81 85 0d 27 db 67 34 e3 7f 67 21
22 67 dd 05 ee ff 27 b9 e7 a8 13 fa 57 4b 72 a0 0b 43 0b 18 20

MAC_3 (Raw Value) (32 bytes)

39 b1 27 c1 30 12 9a fa 30 61 8c 75 13 29 e6 37 cc 37 34 27 0d 4b 01
25 84 45 a8 ee 02 da a3 bd

MAC_3 (CBOR Data Item) (34 bytes)

58 20 39 b1 27 c1 30 12 9a fa 30 61 8c 75 13 29 e6 37 cc 37 34 27 0d
4b 01 25 84 45 a8 ee 02 da a3 bd

Since METHOD = 0, Signature_or_MAC_3 is the 'signature' of the COSE_Sign1 object ๐Ÿ”ฅ.

The Initiator constructs the message to be signed ๐Ÿซถ:

[
  "Signature1",
  << ID_CRED_I >>,
  << TH_3, CRED_I, ? EAD_3 >>,
  MAC_3
] =

[
  "Signature1",
  h'a11822822e48c24ab2fd7643c79f',
  h'58205b7df9b4f58f240ce0418e48191b5fff3a22b5ca57f6
    69b16777996592e928bc58f13081ee3081a1a00302010202
    0462319ea0300506032b6570301d311b301906035504030c
    124544484f4320526f6f742045643235353139301e170d32
    32303331363038323430305a170d32393132333132333030
    30305a30223120301e06035504030c174544484f4320496e
    69746961746f722045643235353139302a300506032b6570
    032100ed06a8ae61a829ba5fa54525c9d07f48dd44a302f4
    3e0f23d8cc20b73085141e300506032b6570034100521241
    d8b3a770996bcfc9b9ead4e7e0a1c0db353a3bdf2910b392
    75ae48b756015981850d27db6734e37f67212267dd05eeff
    27b9e7a813fa574b72a00b430b',
  h'39b127c130129afa30618c751329e637cc3734270d4b0125
    8445a8ee02daa3bd'
]

Message to be signed in message_3 (CBOR Data Item) (341 bytes)

84 6a 53 69 67 6e 61 74 75 72 65 31 4e a1 18 22 82 2e 48 c2 4a b2 fd
76 43 c7 9f 59 01 15 58 20 5b 7d f9 b4 f5 8f 24 0c e0 41 8e 48 19 1b
5f ff 3a 22 b5 ca 57 f6 69 b1 67 77 99 65 92 e9 28 bc 58 f1 30 81 ee
30 81 a1 a0 03 02 01 02 02 04 62 31 9e a0 30 05 06 03 2b 65 70 30 1d
31 1b 30 19 06 03 55 04 03 0c 12 45 44 48 4f 43 20 52 6f 6f 74 20 45
64 32 35 35 31 39 30 1e 17 0d 32 32 30 33 31 36 30 38 32 34 30 30 5a
17 0d 32 39 31 32 33 31 32 33 30 30 30 30 5a 30 22 31 20 30 1e 06 03
55 04 03 0c 17 45 44 48 4f 43 20 49 6e 69 74 69 61 74 6f 72 20 45 64
32 35 35 31 39 30 2a 30 05 06 03 2b 65 70 03 21 00 ed 06 a8 ae 61 a8
29 ba 5f a5 45 25 c9 d0 7f 48 dd 44 a3 02 f4 3e 0f 23 d8 cc 20 b7 30
85 14 1e 30 05 06 03 2b 65 70 03 41 00 52 12 41 d8 b3 a7 70 99 6b cf
c9 b9 ea d4 e7 e0 a1 c0 db 35 3a 3b df 29 10 b3 92 75 ae 48 b7 56 01
59 81 85 0d 27 db 67 34 e3 7f 67 21 22 67 dd 05 ee ff 27 b9 e7 a8 13
fa 57 4b 72 a0 0b 43 0b 58 20 39 b1 27 c1 30 12 9a fa 30 61 8c 75 13
29 e6 37 cc 37 34 27 0d 4b 01 25 84 45 a8 ee 02 da a3 bd

The Initiator signs using the private authentication key SK_I:

Okay bestie, we're still deep in message_3 and it's giving intense ๐Ÿงš๐Ÿ’– The Initiator is about to hand over its signature, wrapped up in CBOR, and then everything gets sealed up for the Responder. Let's gooo โœจ

Here's the raw 64-byte signature (or MAC) as bytes:

Signature_or_MAC_3 (Raw Value) (64 bytes)
96 e1 cd 5f ce ad fa c1 b5 af 81 94 43 f7 09 24 f5 71 99 55 95 7f d0
26 55 be b4 77 5e 1a 73 18 6a 0d 1d 3e a6 83 f0 8f 8d 03 dc ec b9 cf
15 4e 1c 6f 55 5a 1e 12 ca 11 8c e4 2b db a6 87 89 07

And wrapped as a CBOR data item, which adds the 58 40 header (66 bytes total) ๐ŸŽ€:

Signature_or_MAC_3 (CBOR Data Item) (66 bytes)
58 40 96 e1 cd 5f ce ad fa c1 b5 af 81 94 43 f7 09 24 f5 71 99 55 95
7f d0 26 55 be b4 77 5e 1a 73 18 6a 0d 1d 3e a6 83 f0 8f 8d 03 dc ec
b9 cf 15 4e 1c 6f 55 5a 1e 12 ca 11 8c e4 2b db a6 87 89 07

The Initiator builds PLAINTEXT_3 now. It's a CBOR Sequence with three bits in it: the ID_CRED_I (or a bstr, or a small integer from -24 to 23), the Signature_or_MAC_3, and an optional EAD_3 ๐Ÿฆ‹

PLAINTEXT_3 =
(
  ID_CRED_I / bstr / -24..23,
  Signature_or_MAC_3,
  ? EAD_3
)
PLAINTEXT_3 (CBOR Sequence) (80 bytes)
a1 18 22 82 2e 48 c2 4a b2 fd 76 43 c7 9f 58 40 96 e1 cd 5f ce ad fa
c1 b5 af 81 94 43 f7 09 24 f5 71 99 55 95 7f d0 26 55 be b4 77 5e 1a
73 18 6a 0d 1d 3e a6 83 f0 8f 8d 03 dc ec b9 cf 15 4e 1c 6f 55 5a 1e
12 ca 11 8c e4 2b db a6 87 89 07

Next up is the associated data A_3 for message_3. It's an array with "Encrypt0", an empty bstr, and the hash value, which is the TH_3 we already worked out ๐ŸŒธ

A_3 =
[
  "Encrypt0",
  h'',
  h'5b7df9b4f58f240ce0418e48191b5fff3a22b5ca57f669b1
    6777996592e928bc'
]
A_3 (CBOR Data Item) (45 bytes)
83 68 45 6e 63 72 79 70 74 30 40 58 20 5b 7d f9 b4 f5 8f 24 0c e0 41
8e 48 19 1b 5f ff 3a 22 b5 ca 57 f6 69 b1 67 77 99 65 92 e9 28 bc

Now the key K_3 ๐Ÿ’… The Initiator uses the EDHOC hash algorithm to derive it (see Section 4.1.2 of [RFC9528]). The derivation runs through EDHOC_KDF with PRK_3e2m, the label 3, TH_3, and key_length. That expands to HKDF-Expand over an info structure. key_length is the key length in bytes for the EDHOC AEAD algorithm.

K_3 = EDHOC_KDF( PRK_3e2m, 3, TH_3, key_length )
    = HKDF-Expand( PRK_3e2m, info, key_length )

Here's the info for K_3, where the last value is the key length in bytes for the EDHOC AEAD algorithm:

info =
(
  3,
  h'5b7df9b4f58f240ce0418e48191b5fff3a22b5ca57f669b1
    6777996592e928bc',
  16
)
info for K_3 (CBOR Sequence) (36 bytes)
03 58 20 5b 7d f9 b4 f5 8f 24 0c e0 41 8e 48 19 1b 5f ff 3a 22 b5 ca
57 f6 69 b1 67 77 99 65 92 e9 28 bc 10
K_3 (Raw Value) (16 bytes)
da 19 5e 5f 64 8a c6 3b 0e 8f b0 c4 55 20 51 39

The nonce IV_3 is next, and it's derived the same way (see Section 4.1.2 of [RFC9528]) with the EDHOC hash algorithm. This time the label is 4 and the length is iv_length, which is the nonce length in bytes for the EDHOC AEAD algorithm ๐Ÿงš

IV_3 = EDHOC_KDF( PRK_3e2m, 4, TH_3, iv_length )
     = HKDF-Expand( PRK_3e2m, info, iv_length )

Here's the info for IV_3. The last value is the nonce length in bytes for the EDHOC AEAD algorithm:

info =
(
  4,
  h'5b7df9b4f58f240ce0418e48191b5fff3a22b5ca57f669b1
    6777996592e928bc',
  13
)
info for IV_3 (CBOR Sequence) (36 bytes)
04 58 20 5b 7d f9 b4 f5 8f 24 0c e0 41 8e 48 19 1b 5f ff 3a 22 b5 ca
57 f6 69 b1 67 77 99 65 92 e9 28 bc 0d
IV_3 (Raw Value) (13 bytes)
38 d8 c6 4c 56 25 5a ff a4 49 f4 be d7

Time for the encryption! ๐Ÿ”ฅ The Initiator produces CIPHERTEXT_3 as the 'ciphertext' of a COSE_Encrypt0. It uses the EDHOC AEAD algorithm with PLAINTEXT_3 as the plaintext, A_3 as the additional data, K_3 as the key, and IV_3 as the nonce.

CIPHERTEXT_3 (Raw Value) (88 bytes)
25 c3 45 88 4a aa eb 22 c5 27 f9 b1 d2 b6 78 72 07 e0 16 3c 69 b6 2a
0d 43 92 81 50 42 72 03 c3 16 74 e4 51 4e a6 e3 83 b5 66 eb 29 76 3e
fe b0 af a5 18 77 6a e1 c6 5f 85 6d 84 bf 32 af 3a 78 36 97 04 66 dc
b7 1f 76 74 5d 39 d3 02 5e 77 03 e0 c0 32 eb ad 51 94 7c

message_3 is just the CBOR bstr encoding of CIPHERTEXT_3, so it's a 58 58 header followed by those 88 bytes, 90 bytes total ๐ŸŽ€

message_3 (CBOR Sequence) (90 bytes)
58 58 25 c3 45 88 4a aa eb 22 c5 27 f9 b1 d2 b6 78 72 07 e0 16 3c 69
b6 2a 0d 43 92 81 50 42 72 03 c3 16 74 e4 51 4e a6 e3 83 b5 66 eb 29
76 3e fe b0 af a5 18 77 6a e1 c6 5f 85 6d 84 bf 32 af 3a 78 36 97 04
66 dc b7 1f 76 74 5d 39 d3 02 5e 77 03 e0 c0 32 eb ad 51 94 7c

Now the transcript hash TH_4 gets calculated with the EDHOC hash algorithm. It's built from TH_3, PLAINTEXT_3, and CRED_I ๐ŸŒˆ

TH_4 = H( TH_3, PLAINTEXT_3, CRED_I )

Here's the input that goes into TH_4, all 357 bytes of it:

Input to calculate TH_4 (CBOR Sequence) (357 bytes)
58 20 5b 7d f9 b4 f5 8f 24 0c e0 41 8e 48 19 1b 5f ff 3a 22 b5 ca 57
f6 69 b1 67 77 99 65 92 e9 28 bc a1 18 22 82 2e 48 c2 4a b2 fd 76 43
c7 9f 58 40 96 e1 cd 5f ce ad fa c1 b5 af 81 94 43 f7 09 24 f5 71 99
55 95 7f d0 26 55 be b4 77 5e 1a 73 18 6a 0d 1d 3e a6 83 f0 8f 8d 03
dc ec b9 cf 15 4e 1c 6f 55 5a 1e 12 ca 11 8c e4 2b db a6 87 89 07 58
f1 30 81 ee 30 81 a1 a0 03 02 01 02 02 04 62 31 9e a0 30 05 06 03 2b
65 70 30 1d 31 1b 30 19 06 03 55 04 03 0c 12 45 44 48 4f 43 20 52 6f
6f 74 20 45 64 32 35 35 31 39 30 1e 17 0d 32 32 30 33 31 36 30 38 32
34 30 30 5a 17 0d 32 39 31 32 33 31 32 33 30 30 30 30 5a 30 22 31 20
30 1e 06 03 55 04 03 0c 17 45 44 48 4f 43 20 49 6e 69 74 69 61 74 6f
72 20 45 64 32 35 35 31 39 30 2a 30 05 06 03 2b 65 70 03 21 00 ed 06
a8 ae 61 a8 29 ba 5f a5 45 25 c9 d0 7f 48 dd 44 a3 02 f4 3e 0f 23 d8
cc 20 b7 30 85 14 1e 30 05 06 03 2b 65 70 03 41 00 52 12 41 d8 b3 a7
70 99 6b cf c9 b9 ea d4 e7 e0 a1 c0 db 35 3a 3b df 29 10 b3 92 75 ae
48 b7 56 01 59 81 85 0d 27 db 67 34 e3 7f 67 21 22 67 dd 05 ee ff 27
b9 e7 a8 13 fa 57 4b 72 a0 0b 43 0b

And here's the TH_4 result, 32 bytes raw and 34 bytes as a CBOR data item ๐Ÿ’œ

TH_4 (Raw Value) (32 bytes)
0e b8 68 f2 63 cf 35 55 dc cd 39 6d d8 de c2 9d 37 50 d5 99 be 42 d5
a4 1a 5a 37 c8 96 f2 94 ac
TH_4 (CBOR Data Item) (34 bytes)
58 20 0e b8 68 f2 63 cf 35 55 dc cd 39 6d d8 de c2 9d 37 50 d5 99 be
42 d5 a4 1a 5a 37 c8 96 f2 94 ac

2.4. message_4

Okay, message_4 is where the Responder gets to wrap things up ๐Ÿ™ˆ It's giving minimal vibes, because there's no external authorization data at all.

No external authorization data:

EAD_4 (CBOR Sequence) (0 bytes)

The Responder builds PLAINTEXT_4 out of that optional EAD_4, so it ends up empty too ๐Ÿ“

PLAINTEXT_4 =
(
  ? EAD_4
)
PLAINTEXT_4 (CBOR Sequence) (0 bytes)

The Responder then builds the associated data for message_4, which is an array with "Encrypt0", an empty bstr, and TH_4 ๐Ÿ’–

A_4 =
[
  "Encrypt0",
  h'',
  h'0eb868f263cf3555dccd396dd8dec29d3750d599be42d5a4
    1a5a37c896f294ac'
]
A_4 (CBOR Data Item) (45 bytes)
83 68 45 6e 63 72 79 70 74 30 40 58 20 0e b8 68 f2 63 cf 35 55 dc cd
39 6d d8 de c2 9d 37 50 d5 99 be 42 d5 a4 1a 5a 37 c8 96 f2 94 ac

Next is the EDHOC message_4 key K_4. The Responder derives it with the EDHOC hash algorithm (see Section 4.1.2 of [RFC9528]), using EDHOC_KDF with PRK_4e3m, the label 8, TH_4, and key_length. key_length is the key length in bytes for the EDHOC AEAD algorithm โœจ

K_4 = EDHOC_KDF( PRK_4e3m, 8, TH_4, key_length )
    = HKDF-Expand( PRK_4e3m, info, key_length )

Here's the info for K_4. The last value is the key length in bytes for the EDHOC AEAD algorithm:

info =
(
  8,
  h'0eb868f263cf3555dccd396dd8dec29d3750d599be42d5a4
    1a5a37c896f294ac',
  16
)
info for K_4 (CBOR Sequence) (36 bytes)
08 58 20 0e b8 68 f2 63 cf 35 55 dc cd 39 6d d8 de c2 9d 37 50 d5 99
be 42 d5 a4 1a 5a 37 c8 96 f2 94 ac 10
K_4 (Raw Value) (16 bytes)
df 8c b5 86 1e 1f df ed d3 b2 30 15 a3 9d 1e 2e

The Responder also derives the EDHOC message_4 nonce (see Section 4.1.2 of [RFC9528]) with the EDHOC hash algorithm. The label is 9 and the length is iv_length, where length is the nonce length in bytes for the EDHOC AEAD algorithm ๐Ÿฆ‹

IV_4 = EDHOC_KDF( PRK_4e3m, 9, TH_4, iv_length )
     = HKDF-Expand( PRK_4e3m, info, iv_length )

Here's the info for IV_4. The last value is the nonce length in bytes for the EDHOC AEAD algorithm:

info =
(
  9,
  h'0eb868f263cf3555dccd396dd8dec29d3750d599be42d5a4
    1a5a37c896f294ac',
  13
)
info for IV_4 (CBOR Sequence) (36 bytes)
09 58 20 0e b8 68 f2 63 cf 35 55 dc cd 39 6d d8 de c2 9d 37 50 d5 99
be 42 d5 a4 1a 5a 37 c8 96 f2 94 ac 0d
IV_4 (Raw Value) (13 bytes)
12 8e c6 58 d9 70 d7 38 0f 74 fc 6c 27

The Responder then makes CIPHERTEXT_4 as the 'ciphertext' of a COSE_Encrypt0. It uses the EDHOC AEAD algorithm with PLAINTEXT_4 as the plaintext, A_4 as the additional data, K_4 as the key, and IV_4 as the nonce ๐ŸŒธ

CIPHERTEXT_4 (8 bytes)
4f 0e de e3 66 e5 c8 83

message_4 is the CBOR bstr encoding of CIPHERTEXT_4, so it's a 48 header followed by those 8 bytes, 9 bytes total ๐ŸŽ€

message_4 (CBOR Sequence) (9 bytes)
48 4f 0e de e3 66 e5 c8 83

2.5. PRK_out and PRK_exporter

Last stop for this part: the keys that come out at the end of the handshake ๐ŸŒˆ PRK_out is specified in Section 4.1.3 of [RFC9528]. It's derived with EDHOC_KDF using PRK_4e3m, the label 7, TH_4, and hash_length, which is the length in bytes of the output of the EDHOC hash algorithm.

PRK_out = EDHOC_KDF( PRK_4e3m, 7, TH_4, hash_length )
        = HKDF-Expand( PRK_4e3m, info, hash_length )

Here's the info for PRK_out. The last value is the length in bytes of the output of the EDHOC hash algorithm:

info =
(
  7,
  h'0eb868f263cf3555dccd396dd8dec29d3750d599be42d5a4
    1a5a37c896f294ac',
  32
)
info for PRK_out (CBOR Sequence) (37 bytes)
07 58 20 0e b8 68 f2 63 cf 35 55 dc cd 39 6d d8 de c2 9d 37 50 d5 99
be 42 d5 a4 1a 5a 37 c8 96 f2 94 ac 18 20
PRK_out (Raw Value) (32 bytes)
b7 44 cb 7d 8a 87 cc 04 47 c3 35 0e 16 5b 25 0d ab 12 ec 45 33 25 ab
b9 22 b3 03 07 e5 c3 68 f0

The OSCORE Master Secret and OSCORE Master Salt are derived with the EDHOC_Exporter, as specified in Section 4.2.1 of [RFC9528] ๐Ÿ’…

EDHOC_Exporter( exporter_label, context, length )
= EDHOC_KDF( PRK_exporter, exporter_label, context, length )

PRK_exporter comes from PRK_out, so it's derived with the label 10, an empty context, and hash_length, which is the length in bytes of the output of the EDHOC hash algorithm:

PRK_exporter = EDHOC_KDF( PRK_out, 10, h'', hash_length )
             = HKDF-Expand( PRK_out, info, hash_length )

Here's the info for PRK_exporter. The last value is the length in bytes of the output of the EDHOC hash algorithm:

info =
(
  10,
  h'',
  32
)
info for PRK_exporter (CBOR Sequence) (4 bytes)
0a 40 18 20
PRK_exporter (Raw Value) (32 bytes)
2a ae c8 fc 4a b3 bc 32 95 de f6 b5 51 05 1a 2f a5 61 42 4d b3 01 fa
84 f6 42 f5 57 8a 6d f5 1a

2.6. OSCORE Parameters โœจ

okay so the OSCORE parameters are derived in Appendix A.1 of [RFC9528] ๐Ÿ’… the AEAD and hash algorithms come from whatever cipher suite got selected:

Application AEAD Algorithm (int)
10

Application Hash Algorithm (int)
-16

the mapping from EDHOC connection identifiers to OSCORE Sender/Recipient IDs lives in Section 3.3.3 of [RFC9528] ๐Ÿฆ‹

C_R maps to the server's Recipient ID, which is the same thing as the client's Sender ID. so the byte string 0x18 (encoded as the CBOR byte string 0x4118 when it's C_R) gets converted into the server Recipient ID 0x18 ๐ŸŒธ

Client's OSCORE Sender ID (Raw Value) (1 byte)
18

C_I maps to the client's Recipient ID, which is the server's Sender ID. the byte string 0x2d (encoded as the CBOR integer 0x2d when it's C_I) becomes the client Recipient ID 0x2d ๐Ÿงš

Server's OSCORE Sender ID (Raw Value) (1 byte)
2d

the OSCORE Master Secret comes out of EDHOC_Expand() using the application hash algorithm (see Appendix A.1 of [RFC9528]) ๐Ÿ”

OSCORE Master Secret = EDHOC_Exporter( 0, h'', oscore_key_length )
= EDHOC_KDF( PRK_exporter, 0, h'', oscore_key_length )
= HKDF-Expand( PRK_exporter, info, oscore_key_length )

where oscore_key_length is the key length in bytes for the application AEAD algorithm by default, and the info for the OSCORE Master Secret is:

info =
(
  0,
  h'',
  16
)

that last value is the key length in bytes for the application AEAD algorithm, btw ๐ŸŽ€

info for OSCORE Master Secret (CBOR Sequence) (3 bytes)
00 40 10
OSCORE Master Secret (Raw Value) (16 bytes)
1e 1c 6b ea c3 a8 a1 ca c4 35 de 7e 2f 9a e7 ff

the OSCORE Master Salt is also computed through EDHOC_Expand() with the application hash algorithm (see Section 4.2 of [RFC9528]) ๐ŸŒˆ

OSCORE Master Salt = EDHOC_Exporter( 1, h'', oscore_salt_length )
= EDHOC_KDF( PRK_exporter, 1, h'', oscore_salt_length )
= HKDF-Expand( PRK_exporter, info, oscore_salt_length )

where oscore_salt_length is the length in bytes of the OSCORE Master Salt, and the info for the OSCORE Master Salt is:

info =
(
  1,
  h'',
  8
)

and that last value is the length in bytes of the OSCORE Master Salt ๐Ÿ’œ

info for OSCORE Master Salt (CBOR Sequence) (3 bytes)
01 40 08
OSCORE Master Salt (Raw Value) (8 bytes)
ce 7a b8 44 c0 10 6d 73

2.7. Key Update ๐Ÿ”„

key update is defined in Appendix H of [RFC9528] โœจ

EDHOC_KeyUpdate( context ):
PRK_out = EDHOC_KDF( PRK_out, 11, context, hash_length )
        = HKDF-Expand( PRK_out, info, hash_length )

where hash_length is the length in bytes of the output of the EDHOC hash function, and the context for KeyUpdate is:

context for KeyUpdate (Raw Value) (16 bytes)
d6 be 16 96 02 b8 bc ea a0 11 58 fd b8 20 89 0c
context for KeyUpdate (CBOR Data Item) (17 bytes)
50 d6 be 16 96 02 b8 bc ea a0 11 58 fd b8 20 89 0c

where the info for KeyUpdate is:

info =
(
  11,
  h'd6be169602b8bceaa01158fdb820890c',
  32
)
info for KeyUpdate (CBOR Sequence) (20 bytes)
0b 50 d6 be 16 96 02 b8 bc ea a0 11 58 fd b8 20 89 0c 18 20
PRK_out after KeyUpdate (Raw Value) (32 bytes)
da 6e ac d9 a9 85 f4 fb a9 ae c2 a9 29 90 22 97 6b 25 b1 4e 89 fa 15
97 94 f2 8d 82 fa f2 da ad

after the key update, the PRK_exporter has to be derived anew ๐ŸŒŸ

PRK_exporter = EDHOC_KDF( PRK_out, 10, h'', hash_length )
             = HKDF-Expand( PRK_out, info, hash_length )

where info and hash_length are unchanged from Section 2.5 ๐Ÿซถ

PRK_exporter after KeyUpdate (Raw Value) (32 bytes)
00 14 d2 52 5e e0 d8 e2 13 ea 59 08 02 8e 9a 1c e9 a0 1c 30 54 6f 09
30 c0 44 d3 8d b5 36 2c 05

the OSCORE Master Secret gets derived with the updated PRK_exporter:

OSCORE Master Secret
= HKDF-Expand( PRK_exporter, info, oscore_key_length )

where info and oscore_key_length are unchanged from Section 2.6 ๐Ÿ“

OSCORE Master Secret after KeyUpdate (Raw Value) (16 bytes)
ee 0f f5 42 c4 7e b0 e0 9c 69 30 76 49 bd bb e5

and the OSCORE Master Salt gets derived with the updated PRK_exporter too:

OSCORE Master Salt
= HKDF-Expand( PRK_exporter, info, oscore_salt_length )

where info and oscore_salt_length are unchanged from Section 2.6 ๐Ÿฆ‹

OSCORE Master Salt after KeyUpdate (Raw Value) (8 bytes)
80 ce de 2a 1e 5a ab 48

2.8. Certificates ๐Ÿ“œ

2.8.1. Responder Certificate ๐ŸŽ€

Version: 3 (0x2)
Serial Number: 1647419076 (0x62319ec4)
Signature Algorithm: ED25519
Issuer: CN = EDHOC Root Ed25519
Validity
    Not Before: Mar 16 08:24:36 2022 GMT
    Not After : Dec 31 23:00:00 2029 GMT
Subject: CN = EDHOC Responder Ed25519
Subject Public Key Info:
    Public Key Algorithm: ED25519
        ED25519 Public-Key:
        pub:
            a1 db 47 b9 51 84 85 4a d1 2a 0c 1a 35 4e 41
            8a ac e3 3a a0 f2 c6 62 c0 0b 3a c5 5d e9 2f
            93 59
Signature Algorithm: ED25519
Signature Value:
    b7 23 bc 01 ea b0 92 8e 8b 2b 6c 98 de 19 cc 38 23 d4
    6e 7d 69 87 b0 32 47 8f ec fa f1 45 37 a1 af 14 cc 8b
    e8 29 c6 b7 30 44 10 18 37 eb 4a bc 94 95 65 d8 6d ce
    51 cf ae 52 ab 82 c1 52 cb 02

2.8.2. Initiator Certificate ๐ŸŒธ

Version: 3 (0x2)
Serial Number: 1647419040 (0x62319ea0)
Signature Algorithm: ED25519
Issuer: CN = EDHOC Root Ed25519
Validity
    Not Before: Mar 16 08:24:00 2022 GMT
    Not After : Dec 31 23:00:00 2029 GMT
Subject: CN = EDHOC Initiator Ed25519
Subject Public Key Info:
    Public Key Algorithm: ED25519
        ED25519 Public-Key:
        pub:
            ed 06 a8 ae 61 a8 29 ba 5f a5 45 25 c9 d0 7f
            48 dd 44 a3 02 f4 3e 0f 23 d8 cc 20 b7 30 85
            14 1e
Signature Algorithm: ED25519
Signature Value:
    52 12 41 d8 b3 a7 70 99 6b cf c9 b9 ea d4 e7 e0 a1 c0
    db 35 3a 3b df 29 10 b3 92 75 ae 48 b7 56 01 59 81 85
    0d 27 db 67 34 e3 7f 67 21 22 67 dd 05 ee ff 27 b9 e7
    a8 13 fa 57 4b 72 a0 0b 43 0b

2.8.3. Common Root Certificate ๐Ÿ‘‘

Version: 3 (0x2)
Serial Number: 1647418996 (0x62319e74)
Signature Algorithm: ED25519
Issuer: CN = EDHOC Root Ed25519
Validity
    Not Before: Mar 16 08:23:16 2022 GMT
    Not After : Dec 31 23:00:00 2029 GMT
Subject: CN = EDHOC Root Ed25519
Subject Public Key Info:
    Public Key Algorithm: ED25519
        ED25519 Public-Key:
        pub:
            2b 7b 3e 80 57 c8 64 29 44 d0 6a fe 7a 71 d1
            c9 bf 96 1b 62 92 ba c4 b0 4f 91 66 9b bb 71
            3b e4
X509v3 extensions:
    X509v3 Key Usage: critical
        Certificate Sign
    X509v3 Basic Constraints: critical
        CA:TRUE
Signature Algorithm: ED25519
Signature Value:
    4b b5 2b bf 15 39 b7 1a 4a af 42 97 78 f2 9e da 7e 81
    46 80 69 8f 16 c4 8f 2a 6f a4 db e8 25 41 c5 82 07 ba
    1b c9 cd b0 c2 fa 94 7f fb f0 f0 ec 0e e9 1a 7f f3 7a
    94 d9 25 1f a5 cd f1 e6 7a 0f

3. Authentication with Static DH, CCS Identified by 'kid' ๐Ÿ”‘

in this example, the Initiator and the Responder get authenticated with ephemeral-static Diffie-Hellman (METHOD = 3) ๐Ÿ’– the Initiator supports cipher suites 6 and 2 (in order of preference), and the Responder only supports cipher suite 2. after an initial negotiation message exchange, cipher suite 2 gets used, and that decides the algorithms:

  • EDHOC AEAD algorithm = AES-CCM-16-64-128
  • EDHOC hash algorithm = SHA-256
  • EDHOC MAC length in bytes (Static DH) = 8
  • EDHOC key exchange algorithm (ECDH curve) = P-256
  • EDHOC signature algorithm = ES256
  • application AEAD algorithm = AES-CCM-16-64-128
  • application hash algorithm = SHA-256

the public keys are represented as raw public keys (RPKs), encoded in a CWT Claims Set (CCS), and identified by the COSE header parameter 'kid' ๐Ÿงš

3.1. message_1 (First Time) ๐Ÿฆ‹

both endpoints get authenticated with static DH, so METHOD = 3 โœจ

METHOD (CBOR Data Item) (1 byte)
03

the Initiator picks its preferred cipher suite 6. a single cipher suite gets encoded as an int ๐Ÿ’…

SUITES_I (CBOR Data Item) (1 byte)
06

the Initiator creates an ephemeral key pair for the EDHOC key exchange algorithm ๐ŸŒˆ

Initiator's ephemeral private key
X (Raw Value) (32 bytes)
5c 41 72 ac a8 b8 2b 5a 62 e6 6f 72 22 16 f5 a1 0f 72 aa 69 f4 2c 1d
1c d3 cc d7 bf d2 9c a4 e9
Initiator's ephemeral public key, 'x'-coordinate
G_X (Raw Value) (32 bytes)
74 1a 13 d7 ba 04 8f bb 61 5e 94 38 6a a3 b6 1b ea 5b 3d 8f 65 f3 26
20 b7 49 be e8 d2 78 ef a9
Initiator's ephemeral public key, 'x'-coordinate
G_X (CBOR Data Item) (34 bytes)
58 20 74 1a 13 d7 ba 04 8f bb 61 5e 94 38 6a a3 b6 1b ea 5b 3d 8f 65
f3 26 20 b7 49 be e8 d2 78 ef a9

the Initiator selects its connection identifier C_I to be the byte string 0x0e. it gets encoded as 0x0e, since it's represented by the 1-byte CBOR int 14 ๐Ÿ“

Connection identifier chosen by the Initiator
C_I (Raw Value) (1 byte)
0e
Connection identifier chosen by the Initiator
C_I (CBOR Data Item) (1 byte)
0e

no external authorization data here ๐Ÿ™ˆ

EAD_1 (CBOR Sequence) (0 bytes)

the Initiator constructs message_1 ๐Ÿ‘‘

message_1 =
(
  3,
  6,
  h'741a13d7ba048fbb615e94386aa3b61bea5b3d8f65f32620
    b749bee8d278efa9',
  14
)
message_1 (CBOR Sequence) (37 bytes)
03 06 58 20 74 1a 13 d7 ba 04 8f bb 61 5e 94 38 6a a3 b6 1b ea 5b 3d
8f 65 f3 26 20 b7 49 be e8 d2 78 ef a9 0e

3.2. error ๐Ÿšซ

the Responder doesn't support cipher suite 6, so it sends an error with ERR_CODE 2 containing SUITES_R as ERR_INFO. the Responder proposes cipher suite 2, a single cipher suite thus encoded as an int. ๐Ÿซถ

SUITES_R
02
error (CBOR Sequence) (2 bytes)
02 02

3.3. message_1 (Second Time) ๐Ÿ”โœจ

Okay so this is basically the same routine as message_1 the first time ([Section 3.1](#)) ๐Ÿ’…, just with SUITES_I glowed up for round two ๐ŸŒธ. Both endpoints get authenticated with static DH, which means METHOD = 3 ๐Ÿ’œ:

METHOD (CBOR Data Item) (1 byte)
03

The Initiator picks cipher suite 2 ๐ŸŽ€ and flags the more preferred cipher suite(s), which here is 6. Everything gets packed into the array [6, 2] ๐Ÿงš:

SUITES_I (CBOR Data Item) (3 bytes)
82 06 02

Next, the Initiator makes an ephemeral key pair for the EDHOC key exchange algorithm ๐Ÿฆ‹. Here's the private key first:

Initiator's ephemeral private key
X (Raw Value) (32 bytes)
36 8e c1 f6 9a eb 65 9b a3 7d 5a 8d 45 b2 1b dc 02 99 dc ea a8 ef 23
5f 3c a4 2c e3 53 0f 95 25

Then the public key's 'x'-coordinate, both raw and as a CBOR data item ๐ŸŒˆ:

Initiator's ephemeral public key, 'x'-coordinate
G_X (Raw Value) (32 bytes)
8a f6 f4 30 eb e1 8d 34 18 40 17 a9 a1 1b f5 11 c8 df f8 f8 34 73 0b
96 c1 b7 c8 db ca 2f c3 b6
Initiator's ephemeral public key, one 'y'-coordinate
(Raw Value) (32 bytes)
51 e8 af 6c 6e db 78 16 01 ad 1d 9c 5f a8 bf 7a a1 57 16 c7 c0 6a 5d
03 85 03 c6 14 ff 80 c9 b3
Initiator's ephemeral public key, 'x'-coordinate
G_X (CBOR Data Item) (34 bytes)
58 20 8a f6 f4 30 eb e1 8d 34 18 40 17 a9 a1 1b f5 11 c8 df f8 f8 34
73 0b 96 c1 b7 c8 db ca 2f c3 b6

Now the connection identifier. The Initiator chooses C_I to be the byte string 0x37 โญ. That gets encoded as 0x37 itself, since it's represented by the 1-byte CBOR int -24 ๐Ÿ™ˆ. Both forms look like this:

Connection identifier chosen by the Initiator
C_I (Raw Value) (1 byte)
37
Connection identifier chosen by the Initiator
C_I (CBOR Data Item) (1 byte)
37

And there's no external authorization data this time, so EAD_1 is empty ๐Ÿซถ:

EAD_1 (CBOR Sequence) (0 bytes)

Finally, the Initiator constructs message_1 ๐Ÿ‘‘:

message_1 =
(
  3,
  [6, 2],
  h'8af6f430ebe18d34184017a9a11bf511c8dff8f834730b96
    c1b7c8dbca2fc3b6',
  -24
)

And the encoded result, 39 bytes of pure main character energy ๐Ÿ’–:

message_1 (CBOR Sequence) (39 bytes)
03 82 06 02 58 20 8a f6 f4 30 eb e1 8d 34 18 40 17 a9 a1 1b f5 11 c8
df f8 f8 34 73 0b 96 c1 b7 c8 db ca 2f c3 b6 37

3.4. message_2 โœจ

okay so the Responder is like "cipher suite 2 is giving, and I'm not gonna be picky about it" ๐Ÿ’… It supports the selected cipher suite 2 but not the Initiator's more preferred cipher suite(s) 6, so SUITES_I is acceptable ๐ŸŽ€

The Responder makes an ephemeral key pair for the EDHOC key exchange algorithm ๐Ÿฆ‹

Responder's ephemeral private key
Y (Raw Value) (32 bytes)
e2 f4 12 67 77 20 5e 85 3b 43 7d 6e ac a1 e1 f7 53 cd cc 3e 2c 69 fa
88 4b 0a 1a 64 09 77 e4 18
Responder's ephemeral public key, 'x'-coordinate
G_Y (Raw Value) (32 bytes)
41 97 01 d7 f0 0a 26 c2 dc 58 7a 36 dd 75 25 49 f3 37 63 c8 93 42 2c
8e a0 f9 55 a1 3a 4f f5 d5
Responder's ephemeral public key, one 'y'-coordinate
(Raw Value) (32 bytes)
5e 4f 0d d8 a3 da 0b aa 16 b9 d3 ad 56 a0 c1 86 0a 94 0a f8 59 14 91
5e 25 01 9b 40 24 17 e9 9d
Responder's ephemeral public key, 'x'-coordinate
G_Y (CBOR Data Item) (34 bytes)
58 20 41 97 01 d7 f0 0a 26 c2 dc 58 7a 36 dd 75 25 49 f3 37 63 c8 93
42 2c 8e a0 f9 55 a1 3a 4f f5 d5

The Responder picks its connection identifier C_R to be the byte string 0x27, which gets encoded as 0x27 because it's represented by the 1-byte CBOR int -8 ๐ŸŒธ

Connection identifier chosen by the Responder
C_R (raw value) (1 byte)
27
Connection identifier chosen by the Responder
C_R (CBOR Data Item) (1 byte)
27

Next up, the transcript hash TH_2 gets calculated with the EDHOC hash algorithm ๐Ÿงš

TH_2 = H( G_Y, H(message_1) )
H(message_1) (Raw Value) (32 bytes)
ca 02 ca bd a5 a8 90 27 49 b4 2f 71 10 50 bb 4d bd 52 15 3e 87 52 75
94 b3 9f 50 cd f0 19 88 8c
H(message_1) (CBOR Data Item) (34 bytes)
58 20 ca 02 ca bd a5 a8 90 27 49 b4 2f 71 10 50 bb 4d bd 52 15 3e 87
52 75 94 b3 9f 50 cd f0 19 88 8c

The input for calculating TH_2 is the CBOR sequence:

G_Y, H(message_1)
Input to calculate TH_2 (CBOR Sequence) (68 bytes)
58 20 41 97 01 d7 f0 0a 26 c2 dc 58 7a 36 dd 75 25 49 f3 37 63 c8 93
42 2c 8e a0 f9 55 a1 3a 4f f5 d5 58 20 ca 02 ca bd a5 a8 90 27 49 b4
2f 71 10 50 bb 4d bd 52 15 3e 87 52 75 94 b3 9f 50 cd f0 19 88 8c
TH_2 (Raw Value) (32 bytes)
35 6e fd 53 77 14 25 e0 08 f3 fe 3a 86 c8 3f f4 c6 b1 6e 57 02 8f f3
9d 52 36 c1 82 b2 02 08 4b
TH_2 (CBOR Data Item) (34 bytes)
58 20 35 6e fd 53 77 14 25 e0 08 f3 fe 3a 86 c8 3f f4 c6 b1 6e 57 02
8f f3 9d 52 36 c1 82 b2 02 08 4b

PRK_2e is specified in Section 4.1.1.1 of [RFC9528] ๐ŸŒˆ

First up, the ECDH shared secret G_XY gets computed from G_X and Y, or from G_Y and X:

G_XY (Raw Value) (ECDH shared secret) (32 bytes)
2f 0c b7 e8 60 ba 53 8f bf 5c 8b de d0 09 f6 25 9b 4b 62 8f e1 eb 7d
be 93 78 e5 ec f7 a8 24 ba

Then PRK_2e is calculated with EDHOC_Extract(), which is set by the EDHOC hash algorithm โœจ

PRK_2e = EDHOC_Extract( salt, G_XY )
       = HMAC-SHA-256( salt, G_XY )

where salt is TH_2:

salt (Raw Value) (32 bytes)
35 6e fd 53 77 14 25 e0 08 f3 fe 3a 86 c8 3f f4 c6 b1 6e 57 02 8f f3
9d 52 36 c1 82 b2 02 08 4b
PRK_2e (Raw Value) (32 bytes)
5a a0 d6 9f 3e 3d 1e 0c 47 9f 0b 8a 48 66 90 c9 80 26 30 c3 46 6b 1d
c9 23 71 c9 82 56 31 70 b5

Since METHOD = 3, the Responder authenticates using static DH ๐Ÿ”ฅ The EDHOC key exchange algorithm uses the same curve as the ephemeral keys, which is P-256, since the selected cipher suite is 2 ๐Ÿ‘‘

The Responder's static Diffie-Hellman P-256 key pair has a private key and a public key ๐Ÿซถ

Responder's private authentication key
SK_R (Raw Value) (32 bytes)
72 cc 47 61 db d4 c7 8f 75 89 31 aa 58 9d 34 8d 1e f8 74 a7 e3 03 ed
e2 f1 40 dc f3 e6 aa 4a ac
Responder's public authentication key, 'x'-coordinate
(Raw Value) (32 bytes)
bb c3 49 60 52 6e a4 d3 2e 94 0c ad 2a 23 41 48 dd c2 17 91 a1 2a fb
cb ac 93 62 20 46 dd 44 f0
Responder's public authentication key, 'y'-coordinate
(Raw Value) (32 bytes)
45 19 e2 57 23 6b 2a 0c e2 02 3f 09 31 f1 f3 86 ca 7a fd a6 4f cd e0
10 8c 22 4c 51 ea bf 60 72

Since the Responder authenticates with static DH (METHOD = 3), PRK_3e2m is derived from SALT_3e2m and G_RX ๐Ÿฆ‹

The input needed to calculate SALT_3e2m is defined in Section 4.1.2 of [RFC9528], using EDHOC_Expand() with the EDHOC hash algorithm:

SALT_3e2m = EDHOC_KDF( PRK_2e, 1, TH_2, hash_length )
          = HKDF-Expand( PRK_2e, info, hash_length )

where hash_length is the length in bytes of the output of the EDHOC hash algorithm, and the info for SALT_3e2m is:

info =
(
  1,
  h'356efd53771425e008f3fe3a86c83ff4c6b16e57028ff39d
    5236c182b202084b',
  32
)
info for SALT_3e2m (CBOR Sequence) (37 bytes)
01 58 20 35 6e fd 53 77 14 25 e0 08 f3 fe 3a 86 c8 3f f4 c6 b1 6e 57
02 8f f3 9d 52 36 c1 82 b2 02 08 4b 18 20
SALT_3e2m (Raw Value) (32 bytes)
af 4e 10 3a 47 cb 3c f3 25 70 d5 c2 5a d2 77 32 bd 8d 81 78 e9 a6 9d
06 1c 31 a2 7f 8e 3c a9 26

PRK_3e2m is specified in Section 4.1.1.2 of [RFC9528] ๐Ÿ“

PRK_3e2m is derived from G_RX using EDHOC_Extract() with the EDHOC hash algorithm:

PRK_3e2m = EDHOC_Extract( SALT_3e2m, G_RX )
         = HMAC-SHA-256( SALT_3e2m, G_RX )

where G_RX is the ECDH shared secret calculated from G_X and R, or from G_R and X.

G_RX (Raw Value) (ECDH shared secret) (32 bytes)
f2 b6 ee a0 22 20 b9 5e ee 5a 0b c7 01 f0 74 e0 0a 84 3e a0 24 22 f6
08 25 fb 26 9b 3e 16 14 23
PRK_3e2m (Raw Value) (32 bytes)
0c a3 d3 39 82 96 b3 c0 39 00 98 76 20 c1 1f 6f ce 70 78 1c 1d 12 19
72 0f 9e c0 8c 12 2d 84 34

The Responder builds the rest of the input needed to calculate MAC_2 ๐Ÿ“

MAC_2 = EDHOC_KDF( PRK_3e2m, 2, context_2, mac_length_2 )
context_2 = << C_R, ID_CRED_R, TH_2, CRED_R, ? EAD_2 >>

CRED_R is identified by a 'kid' with the byte string value 0x32 ๐ŸŒธ

ID_CRED_R =
{
  4 : h'32'
}
ID_CRED_R (CBOR Data Item) (4 bytes)
a1 04 41 32

CRED_R is an RPK encoded as a CCS ๐Ÿ’–

{                                              /CCS/
  2 : "example.edu",                           /sub/
  8 : {                                        /cnf/
    1 : {                                      /COSE_Key/
      1 : 2,                                   /kty/
      2 : h'32',                               /kid/
     -1 : 1,                                   /crv/
     -2 : h'bbc34960526ea4d32e940cad2a234148
            ddc21791a12afbcbac93622046dd44f0', /x/
     -3 : h'4519e257236b2a0ce2023f0931f1f386
            ca7afda64fcde0108c224c51eabf6072'  /y/
    }
  }
}
CRED_R (CBOR Data Item) (95 bytes)
a2 02 6b 65 78 61 6d 70 6c 65 2e 65 64 75 08 a1 01 a5 01 02 02 41 32
20 01 21 58 20 bb c3 49 60 52 6e a4 d3 2e 94 0c ad 2a 23 41 48 dd c2
17 91 a1 2a fb cb ac 93 62 20 46 dd 44 f0 22 58 20 45 19 e2 57 23 6b
2a 0c e2 02 3f 09 31 f1 f3 86 ca 7a fd a6 4f cd e0 10 8c 22 4c 51 ea
bf 60 72

No external authorization data here ๐Ÿ™ˆ

EAD_2 (CBOR Sequence) (0 bytes)
context_2 = << C_R, ID_CRED_R, TH_2, CRED_R, ? EAD_2 >>
context_2 (CBOR Sequence) (134 bytes)
27 a1 04 41 32 58 20 35 6e fd 53 77 14 25 e0 08 f3 fe 3a 86 c8 3f f4
c6 b1 6e 57 02 8f f3 9d 52 36 c1 82 b2 02 08 4b a2 02 6b 65 78 61 6d
70 6c 65 2e 65 64 75 08 a1 01 a5 01 02 02 41 32 20 01 21 58 20 bb c3
49 60 52 6e a4 d3 2e 94 0c ad 2a 23 41 48 dd c2 17 91 a1 2a fb cb ac
93 62 20 46 dd 44 f0 22 58 20 45 19 e2 57 23 6b 2a 0c e2 02 3f 09 31
f1 f3 86 ca 7a fd a6 4f cd e0 10 8c 22 4c 51 ea bf 60 72
context_2 (CBOR byte string) (136 bytes)
58 86 27 a1 04 41 32 58 20 35 6e fd 53 77 14 25 e0 08 f3 fe 3a 86 c8
3f f4 c6 b1 6e 57 02 8f f3 9d 52 36 c1 82 b2 02 08 4b a2 02 6b 65 78
61 6d 70 6c 65 2e 65 64 75 08 a1 01 a5 01 02 02 41 32 20 01 21 58 20
bb c3 49 60 52 6e a4 d3 2e 94 0c ad 2a 23 41 48 dd c2 17 91 a1 2a fb
cb ac 93 62 20 46 dd 44 f0 22 58 20 45 19 e2 57 23 6b 2a 0c e2 02 3f
09 31 f1 f3 86 ca 7a fd a6 4f cd e0 10 8c 22 4c 51 ea bf 60 72

MAC_2 gets computed through EDHOC_Expand() with the EDHOC hash algorithm (see Section 4.1.2 of [RFC9528]) ๐Ÿงš

MAC_2 = HKDF-Expand( PRK_3e2m, info, mac_length_2 )

where

info = ( 2, context_2, mac_length_2 )

Since METHOD = 3, mac_length_2 is given by the EDHOC MAC length โœจ

info for MAC_2 is:

info =
(
  2,
  h'27a10441325820356efd53771425e008f3fe3a86c83ff4c6
    b16e57028ff39d5236c182b202084ba2026b6578616d706c
    652e65647508a101a501020241322001215820bbc3496052
    6ea4d32e940cad2a234148ddc21791a12afbcbac93622046
    dd44f02258204519e257236b2a0ce2023f0931f1f386ca7a
    fda64fcde0108c224c51eabf6072',
  8
)

where the last value is the EDHOC MAC length in bytes.

info for MAC_2 (CBOR Sequence) (138 bytes)
02 58 86 27 a1 04 41 32 58 20 35 6e fd 53 77 14 25 e0 08 f3 fe 3a 86
c8 3f f4 c6 b1 6e 57 02 8f f3 9d 52 36 c1 82 b2 02 08 4b a2 02 6b 65
78 61 6d 70 6c 65 2e 65 64 75 08 a1 01 a5 01 02 02 41 32 20 01 21 58
20 bb c3 49 60 52 6e a4 d3 2e 94 0c ad 2a 23 41 48 dd c2 17 91 a1 2a
fb cb ac 93 62 20 46 dd 44 f0 22 58 20 45 19 e2 57 23 6b 2a 0c e2 02
3f 09 31 f1 f3 86 ca 7a fd a6 4f cd e0 10 8c 22 4c 51 ea bf 60 72 08
MAC_2 (Raw Value) (8 bytes)
09 43 30 5c 89 9f 5c 54
MAC_2 (CBOR Data Item) (9 bytes)
48 09 43 30 5c 89 9f 5c 54

Since METHOD = 3, Signature_or_MAC_2 is MAC_2 ๐Ÿฆ‹

Signature_or_MAC_2 (Raw Value) (8 bytes)
09 43 30 5c 89 9f 5c 54
Signature_or_MAC_2 (CBOR Data Item) (9 bytes)
48 09 43 30 5c 89 9f 5c 54

The Responder builds PLAINTEXT_2 ๐Ÿ’…

PLAINTEXT_2 =
(
  C_R,
  ID_CRED_R / bstr / -24..23,
  Signature_or_MAC_2,
  ? EAD_2
)

Since ID_CRED_R has a single 'kid' parameter, only the byte string value goes into the plaintext, represented as described in Section 3.3.2 of [RFC9528]. The CBOR map { 4 : h'32' } is replaced, not by the CBOR byte string 0x4132, but by the CBOR int 0x32, since that's a one-byte encoding of a CBOR integer (-19) ๐ŸŽ€

PLAINTEXT_2 (CBOR Sequence) (11 bytes)
27 32 48 09 43 30 5c 89 9f 5c 54

The input needed to calculate KEYSTREAM_2 is defined in Section 4.1.2 of [RFC9528], using EDHOC_Expand() with the EDHOC hash algorithm ๐ŸŒˆ

KEYSTREAM_2 = EDHOC_KDF( PRK_2e, 0, TH_2, plaintext_length )
            = HKDF-Expand( PRK_2e, info, plaintext_length )

where plaintext_length is the length in bytes of PLAINTEXT_2, and the info for KEYSTREAM_2 is:

info =
(
  0,
  h'356efd53771425e008f3fe3a86c83ff4c6b16e57028ff39d
    5236c182b202084b',
  11
)

where the last value is the length in bytes of PLAINTEXT_2.

info for KEYSTREAM_2 (CBOR Sequence) (36 bytes)
00 58 20 35 6e fd 53 77 14 25 e0 08 f3 fe 3a 86 c8 3f f4 c6 b1 6e 57
02 8f f3 9d 52 36 c1 82 b2 02 08 4b 0b
KEYSTREAM_2 (Raw Value) (11 bytes)
bf 50 e9 e7 ba d0 bb 68 17 33 99

The Responder works out CIPHERTEXT_2 as an XOR between PLAINTEXT_2 and KEYSTREAM_2 ๐Ÿ”ฅ

CIPHERTEXT_2 (Raw Value) (11 bytes)
98 62 a1 ee f9 e0 e7 e1 88 6f cd

Last step, the Responder builds message_2 ๐Ÿซถ

message_2 =
(
  G_Y_CIPHERTEXT_2
)

where G_Y_CIPHERTEXT_2 is the bstr encoding of the concatenation of the raw values of G_Y and CIPHERTEXT_2 ๐Ÿ’œ

message_2 (CBOR Sequence) (45 bytes)
58 2b 41 97 01 d7 f0 0a 26 c2 dc 58 7a 36 dd 75 25 49 f3 37 63 c8 93
42 2c 8e a0 f9 55 a1 3a 4f f5 d5 98 62 a1 ee f9 e0 e7 e1 88 6f cd

3.5. message_3 โœจ

okay bestie, this is where the Initiator finally sends message_3 and it is GIVING main character energy ๐Ÿ’…. First up, TH_3 gets cooked with the EDHOC hash algorithm ๐Ÿณ:

TH_3 = H( TH_2, PLAINTEXT_2, CRED_R )

Input to calculate TH_3 (CBOR Sequence) (140 bytes) ๐Ÿง

58 20 35 6e fd 53 77 14 25 e0 08 f3 fe 3a 86 c8 3f f4 c6 b1 6e 57 02
8f f3 9d 52 36 c1 82 b2 02 08 4b 27 32 48 09 43 30 5c 89 9f 5c 54 a2
02 6b 65 78 61 6d 70 6c 65 2e 65 64 75 08 a1 01 a5 01 02 02 41 32 20
01 21 58 20 bb c3 49 60 52 6e a4 d3 2e 94 0c ad 2a 23 41 48 dd c2 17
91 a1 2a fb cb ac 93 62 20 46 dd 44 f0 22 58 20 45 19 e2 57 23 6b 2a
0c e2 02 3f 09 31 f1 f3 86 ca 7a fd a6 4f cd e0 10 8c 22 4c 51 ea bf
60 72

TH_3 (Raw Value) (32 bytes)

ad af 67 a7 8a 4b cc 91 e0 18 f8 88 27 62 a7 22 00 0b 25 07 03 9d f0
bc 1b bf 0c 16 1b b3 15 5c

TH_3 (CBOR Data Item) (34 bytes)

58 20 ad af 67 a7 8a 4b cc 91 e0 18 f8 88 27 62 a7 22 00 0b 25 07 03
9d f0 bc 1b bf 0c 16 1b b3 15 5c

Since METHOD = 3, the Initiator authenticates using static DH. The EDHOC key exchange algorithm is based on the same curve as for the ephemeral keys, which is P-256, since the selected cipher suite is 2. ๐Ÿฆ‹

The Initiator's static Diffie-Hellman P-256 key pair consists of a private key and a public key ๐Ÿ”‘:

Initiator's private authentication key SK_I (Raw Value) (32 bytes)

fb 13 ad eb 65 18 ce e5 f8 84 17 66 08 41 14 2e 83 0a 81 fe 33 43 80
a9 53 40 6a 13 05 e8 70 6b

Initiator's public authentication key, 'x'-coordinate (Raw Value) (32 bytes)

ac 75 e9 ec e3 e5 0b fc 8e d6 03 99 88 95 22 40 5c 47 bf 16 df 96 66
0a 41 29 8c b4 30 7f 7e b6

Initiator's public authentication key, 'y'-coordinate (Raw Value) (32 bytes)

6e 5d e6 11 38 8a 4b 8a 82 11 33 4a c7 d3 7e cb 52 a3 87 d2 57 e6 db
3c 2a 93 df 21 ff 3a ff c8

Since I authenticates with static DH (METHOD = 3), PRK_4e3m is derived from SALT_4e3m and G_IY. ๐Ÿ’œ

The input needed to calculate SALT_4e3m is defined in Section 4.1.2 of [RFC9528], using EDHOC_Expand() with the EDHOC hash algorithm:

SALT_4e3m = EDHOC_KDF( PRK_3e2m, 5, TH_3, hash_length )
          = HKDF-Expand( PRK_3e2m, info, hash_length )

where hash_length is the length in bytes of the output of the EDHOC hash algorithm, and info for SALT_4e3m is:

info =
(
  5,
  h'adaf67a78a4bcc91e018f8882762a722000b2507039df0bc
    1bbf0c161bb3155c',
  32
)

info for SALT_4e3m (CBOR Sequence) (37 bytes)

05 58 20 ad af 67 a7 8a 4b cc 91 e0 18 f8 88 27 62 a7 22 00 0b 25 07
03 9d f0 bc 1b bf 0c 16 1b b3 15 5c 18 20

SALT_4e3m (Raw Value) (32 bytes)

cf dd f9 51 5a 7e 46 e7 b4 db ff 31 cb d5 6c d0 4b a3 32 25 0d e9 ea
5d e1 ca f9 f6 d1 39 14 a7

PRK_4e3m is specified in Section 4.1.1.3 of [RFC9528]. ๐ŸŽ€

Since I authenticates with static DH (METHOD = 3), PRK_4e3m is derived from G_IY using EDHOC_Extract() with the EDHOC hash algorithm:

PRK_4e3m = EDHOC_Extract(SALT_4e3m, G_IY)
         = HMAC-SHA-256(SALT_4e3m, G_IY)

where G_IY is the ECDH shared secret calculated from G_I and Y, or G_Y and I.

G_IY (Raw Value) (ECDH shared secret) (32 bytes)

08 0f 42 50 85 bc 62 49 08 9e ac 8f 10 8e a6 23 26 85 7e 12 ab 07 d7
20 28 ca 1b 5f 36 e0 04 b3

PRK_4e3m (Raw Value) (32 bytes)

81 cc 8a 29 8e 35 70 44 e3 c4 66 bb 5c 0a 1e 50 7e 01 d4 92 38 ae ba
13 8d f9 46 35 40 7c 0f f7

The Initiator constructs the remaining input needed to calculate MAC_3 ๐Ÿงฉ:

MAC_3 = EDHOC_KDF( PRK_4e3m, 6, context_3, mac_length_3 )

context_3 = << ID_CRED_I, TH_3, CRED_I, ? EAD_3 >>

CRED_I is identified by a 'kid' with byte string value 0x2b:

ID_CRED_I =
{
  4 : h'2b'
}

ID_CRED_I (CBOR Data Item) (4 bytes)

a1 04 41 2b

CRED_I is an RPK encoded as a CCS:

{                                              /CCS/
  2 : "42-50-31-FF-EF-37-32-39",               /sub/
  8 : {                                        /cnf/
    1 : {                                      /COSE_Key/
      1 : 2,                                   /kty/
      2 : h'2b',                               /kid/
     -1 : 1,                                   /crv/
     -2 : h'ac75e9ece3e50bfc8ed6039988952240
            5c47bf16df96660a41298cb4307f7eb6'  /x/
     -3 : h'6e5de611388a4b8a8211334ac7d37ecb
            52a387d257e6db3c2a93df21ff3affc8'  /y/
    }
  }
}

CRED_I (CBOR Data Item) (107 bytes)

a2 02 77 34 32 2d 35 30 2d 33 31 2d 46 46 2d 45 46 2d 33 37 2d 33 32
2d 33 39 08 a1 01 a5 01 02 02 41 2b 20 01 21 58 20 ac 75 e9 ec e3 e5
0b fc 8e d6 03 99 88 95 22 40 5c 47 bf 16 df 96 66 0a 41 29 8c b4 30
7f 7e b6 22 58 20 6e 5d e6 11 38 8a 4b 8a 82 11 33 4a c7 d3 7e cb 52
a3 87 d2 57 e6 db 3c 2a 93 df 21 ff 3a ff c8

No external authorization data, so:

EAD_3 (CBOR Sequence) (0 bytes)
context_3 = << ID_CRED_I, TH_3, CRED_I, ? EAD_3 >>

context_3 (CBOR Sequence) (145 bytes)

a1 04 41 2b 58 20 ad af 67 a7 8a 4b cc 91 e0 18 f8 88 27 62 a7 22 00
0b 25 07 03 9d f0 bc 1b bf 0c 16 1b b3 15 5c a2 02 77 34 32 2d 35 30
2d 33 31 2d 46 46 2d 45 46 2d 33 37 2d 33 32 2d 33 39 08 a1 01 a5 01
02 02 41 2b 20 01 21 58 20 ac 75 e9 ec e3 e5 0b fc 8e d6 03 99 88 95
22 40 5c 47 bf 16 df 96 66 0a 41 29 8c b4 30 7f 7e b6 22 58 20 6e 5d
e6 11 38 8a 4b 8a 82 11 33 4a c7 d3 7e cb 52 a3 87 d2 57 e6 db 3c 2a
93 df 21 ff 3a ff c8

context_3 (CBOR byte string) (147 bytes)

58 91 a1 04 41 2b 58 20 ad af 67 a7 8a 4b cc 91 e0 18 f8 88 27 62 a7
22 00 0b 25 07 03 9d f0 bc 1b bf 0c 16 1b b3 15 5c a2 02 77 34 32 2d
35 30 2d 33 31 2d 46 46 2d 45 46 2d 33 37 2d 33 32 2d 33 39 08 a1 01
a5 01 02 02 41 2b 20 01 21 58 20 ac 75 e9 ec e3 e5 0b fc 8e d6 03 99
88 95 22 40 5c 47 bf 16 df 96 66 0a 41 29 8c b4 30 7f 7e b6 22 58 20
6e 5d e6 11 38 8a 4b 8a 82 11 33 4a c7 d3 7e cb 52 a3 87 d2 57 e6 db
3c 2a 93 df 21 ff 3a ff c8

MAC_3 is computed through EDHOC_Expand() using the EDHOC hash algorithm (see Section 4.1.2 of [RFC9528]) ๐Ÿ”:

MAC_3 = HKDF-Expand( PRK_4e3m, info, mac_length_3 )

where

info = ( 6, context_3, mac_length_3 )

Since METHOD = 3, mac_length_3 is given by the EDHOC MAC length.

info for MAC_3 is:

info =
(
  6,
  h'a104412b5820adaf67a78a4bcc91e018f8882762a722000b
    2507039df0bc1bbf0c161bb3155ca2027734322d35302d33
    312d46462d45462d33372d33322d333908a101a501020241
    2b2001215820ac75e9ece3e50bfc8ed60399889522405c47
    bf16df96660a41298cb4307f7eb62258206e5de611388a4b
    8a8211334ac7d37ecb52a387d257e6db3c2a93df21ff3aff
    c8',
  8
)

where the last value is the EDHOC MAC length in bytes.

info for MAC_3 (CBOR Sequence) (149 bytes)

06 58 91 a1 04 41 2b 58 20 ad af 67 a7 8a 4b cc 91 e0 18 f8 88 27 62
a7 22 00 0b 25 07 03 9d f0 bc 1b bf 0c 16 1b b3 15 5c a2 02 77 34 32
2d 35 30 2d 33 31 2d 46 46 2d 45 46 2d 33 37 2d 33 32 2d 33 39 08 a1
01 a5 01 02 02 41 2b 20 01 21 58 20 ac 75 e9 ec e3 e5 0b fc 8e d6 03
99 88 95 22 40 5c 47 bf 16 df 96 66 0a 41 29 8c b4 30 7f 7e b6 22 58
20 6e 5d e6 11 38 8a 4b 8a 82 11 33 4a c7 d3 7e cb 52 a3 87 d2 57 e6
db 3c 2a 93 df 21 ff 3a ff c8 08

MAC_3 (Raw Value) (8 bytes)

62 3c 91 df 41 e3 4c 2f

MAC_3 (CBOR Data Item) (9 bytes)

48 62 3c 91 df 41 e3 4c 2f

Since METHOD = 3, Signature_or_MAC_3 is MAC_3 ๐Ÿฅฐ:

Signature_or_MAC_3 (Raw Value) (8 bytes)

62 3c 91 df 41 e3 4c 2f

Signature_or_MAC_3 (CBOR Data Item) (9 bytes)

48 62 3c 91 df 41 e3 4c 2f

The Initiator constructs PLAINTEXT_3 ๐Ÿ“:

PLAINTEXT_3 =
(
  ID_CRED_I / bstr / -24..23,
  Signature_or_MAC_3,
  ? EAD_3
)

Since ID_CRED_I contains a single 'kid' parameter, only the byte string value is included in the plaintext, represented as described in Section 3.3.2 of [RFC9528]. The CBOR map { 4 : h'2b' } is thus replaced, not by the CBOR byte string 0x412b, but by the CBOR int 0x2b, since that is a one-byte encoding of a CBOR integer (-12). ๐Ÿ™ˆ

PLAINTEXT_3 (CBOR Sequence) (10 bytes)

2b 48 62 3c 91 df 41 e3 4c 2f

The Initiator constructs the associated data for message_3 ๐ŸŽ:

A_3 =
[
  "Encrypt0",
  h'',
  h'adaf67a78a4bcc91e018f8882762a722000b2507039df0bc
    1bbf0c161bb3155c'
]

A_3 (CBOR Data Item) (45 bytes)

83 68 45 6e 63 72 79 70 74 30 40 58 20 ad af 67 a7 8a 4b cc 91 e0 18
f8 88 27 62 a7 22 00 0b 25 07 03 9d f0 bc 1b bf 0c 16 1b b3 15 5c

The Initiator constructs the input needed to derive the key K_3 (see Section 4.1.2 of [RFC9528]) using the EDHOC hash algorithm ๐Ÿ—๏ธ:

K_3 = EDHOC_KDF( PRK_3e2m, 3, TH_3, key_length )
    = HKDF-Expand( PRK_3e2m, info, key_length )

where key_length is the key length in bytes for the EDHOC AEAD algorithm, and info for K_3 is:

info =
(
  3,
  h'adaf67a78a4bcc91e018f8882762a722000b2507039df0bc
    1bbf0c161bb3155c',
  16
)

where the last value is the key length in bytes for the EDHOC AEAD algorithm.

info for K_3 (CBOR Sequence) (36 bytes)

03 58 20 ad af 67 a7 8a 4b cc 91 e0 18 f8 88 27 62 a7 22 00 0b 25 07
03 9d f0 bc 1b bf 0c 16 1b b3 15 5c 10

K_3 (Raw Value) (16 bytes)

8e 7a 30 04 20 00 f7 90 0e 81 74 13 1f 75 f3 ed

The Initiator constructs the input needed to derive the nonce IV_3 (see Section 4.1.2 of [RFC9528]) using the EDHOC hash algorithm ๐ŸŒธ:

IV_3 = EDHOC_KDF( PRK_3e2m, 4, TH_3, iv_length )
     = HKDF-Expand( PRK_3e2m, info, iv_length )

where iv_length is the nonce length in bytes for the EDHOC AEAD algorithm, and info for IV_3 is:

info =
(
  4,
  h'adaf67a78a4bcc91e018f8882762a722000b2507039df0bc
    1bbf0c161bb3155c',
  13
)

where the last value is the nonce length in bytes for the EDHOC AEAD algorithm.

info for IV_3 (CBOR Sequence) (36 bytes)

04 58 20 ad af 67 a7 8a 4b cc 91 e0 18 f8 88 27 62 a7 22 00 0b 25 07
03 9d f0 bc 1b bf 0c 16 1b b3 15 5c 0d

IV_3 (Raw Value) (13 bytes)

6d 83 00 c1 e2 3b 56 15 3a e7 0e e4 57

The Initiator calculates CIPHERTEXT_3 as 'ciphertext' of COSE_Encrypt0 applied using the EDHOC AEAD algorithm with plaintext PLAINTEXT_3, additional data A_3, key K_3, and nonce IV_3. ๐Ÿ”ฅ

CIPHERTEXT_3 (Raw Value) (18 bytes)

e5 62 09 7b c4 17 dd 59 19 48 5a c7 89 1f fd 90 a9 fc

message_3 is the CBOR bstr encoding of CIPHERTEXT_3 โœจ:

message_3 (CBOR Sequence) (19 bytes)

52 e5 62 09 7b c4 17 dd 59 19 48 5a c7 89 1f fd 90 a9 fc

The transcript hash TH_4 is calculated using the EDHOC hash algorithm ๐Ÿง:

TH_4 = H( TH_3, PLAINTEXT_3, CRED_I )

Input to calculate TH_4 (CBOR Sequence) (151 bytes)

58 20 ad af 67 a7 8a 4b cc 91 e0 18 f8 88 27 62 a7 22 00 0b 25 07 03
9d f0 bc 1b bf 0c 16 1b b3 15 5c 2b 48 62 3c 91 df 41 e3 4c 2f a2 02
77 34 32 2d 35 30 2d 33 31 2d 46 46 2d 45 46 2d 33 37 2d 33 32 2d 33
39 08 a1 01 a5 01 02 02 41 2b 20 01 21 58 20 ac 75 e9 ec e3 e5 0b fc
8e d6 03 99 88 95 22 40 5c 47 bf 16 df 96 66 0a 41 29 8c b4 30 7f 7e
b6 22 58 20 6e 5d e6 11 38 8a 4b 8a 82 11 33 4a c7 d3 7e cb 52 a3 87
d2 57 e6 db 3c 2a 93 df 21 ff 3a ff c8

TH_4 (Raw Value) (32 bytes)

c9 02 b1 e3 a4 32 6c 93 c5 55 1f 5f 3a a6 c5 ec c0 24 68 06 76 56 12
e5 2b 5d 99 e6 05 9d 6b 6e

TH_4 (CBOR Data Item) (34 bytes)

58 20 c9 02 b1 e3 a4 32 6c 93 c5 55 1f 5f 3a a6 c5 ec c0 24 68 06 76
56 12 e5 2b 5d 99 e6 05 9d 6b 6e

3.6. message_4 ๐ŸŽ€

Okay so there's no external authorization data here, bestie ๐Ÿ’… That means EAD_4 is a CBOR Sequence that's 0 bytes long. Simple and iconic โœจ

EAD_4 (CBOR Sequence) (0 bytes)

The Responder builds PLAINTEXT_4 like this:

PLAINTEXT_4 =
(
  ? EAD_4
)
PLAINTEXT_4 (CBOR Sequence) (0 bytes)

Next up, the Responder makes the associated data for message_4 ๐Ÿงš

A_4 =
[
  "Encrypt0",
  h'',
  h'c902b1e3a4326c93c5551f5f3aa6c5ecc0246806765612e5
    2b5d99e6059d6b6e'
]
A_4 (CBOR Data Item) (45 bytes)
83 68 45 6e 63 72 79 70 74 30 40 58 20 c9 02 b1 e3 a4 32 6c 93 c5 55
1f 5f 3a a6 c5 ec c0 24 68 06 76 56 12 e5 2b 5d 99 e6 05 9d 6b 6e

Now the Responder gets the inputs it needs to derive the EDHOC message_4 key (see Section 4.1.2 of [RFC9528]) using the EDHOC hash algorithm. The math is giving main character energy ๐Ÿฆ‹

K_4 = EDHOC_KDF( PRK_4e3m, 8, TH_4, key_length )
    = HKDF-Expand( PRK_4e3m, info, key_length )

Here, key_length is the key length in bytes for the EDHOC AEAD algorithm, and info for K_4 is:

info =
(
  8,
  h'c902b1e3a4326c93c5551f5f3aa6c5ecc0246806765612e5
    2b5d99e6059d6b6e',
  16
)

The last value is the key length in bytes for the EDHOC AEAD algorithm.

info for K_4 (CBOR Sequence) (36 bytes)
08 58 20 c9 02 b1 e3 a4 32 6c 93 c5 55 1f 5f 3a a6 c5 ec c0 24 68 06
76 56 12 e5 2b 5d 99 e6 05 9d 6b 6e 10
K_4 (Raw Value) (16 bytes)
d3 c7 78 72 b6 ee b5 08 91 1b db d3 08 b2 e6 a0

The Responder also gets the input needed to derive the EDHOC message_4 nonce (see Section 4.1.2 of [RFC9528]) using the EDHOC hash algorithm. Nonce era ๐ŸŒธ

IV_4 = EDHOC_KDF( PRK_4e3m, 9, TH_4, iv_length )
     = HKDF-Expand( PRK_4e3m, info, iv_length )

Here, iv_length is the nonce length in bytes for the EDHOC AEAD algorithm, and info for IV_4 is:

info =
(
  9,
  h'c902b1e3a4326c93c5551f5f3aa6c5ecc0246806765612e5
    2b5d99e6059d6b6e',
  13
)

The last value is the nonce length in bytes for the EDHOC AEAD algorithm.

info for IV_4 (CBOR Sequence) (36 bytes)
09 58 20 c9 02 b1 e3 a4 32 6c 93 c5 55 1f 5f 3a a6 c5 ec c0 24 68 06
76 56 12 e5 2b 5d 99 e6 05 9d 6b 6e 0d
IV_4 (Raw Value) (13 bytes)
04 ff 0f 44 45 6e 96 e2 17 85 3c 36 01

The Responder works out CIPHERTEXT_4 as the 'ciphertext' of a COSE_Encrypt0 that uses the EDHOC AEAD algorithm, with plaintext PLAINTEXT_4, additional data A_4, key K_4, and nonce IV_4. Encryption, bestie ๐Ÿ”

CIPHERTEXT_4 (8 bytes)
28 c9 66 b7 ca 30 4f 83

message_4 is the CBOR bstr encoding of CIPHERTEXT_4 ๐Ÿ“

message_4 (CBOR Sequence) (9 bytes)
48 28 c9 66 b7 ca 30 4f 83

3.7. PRK_out and PRK_exporter ๐Ÿ’œ

PRK_out is specified in Section 4.1.3 of [RFC9528]. It's giving key derivation realness โœจ

PRK_out = EDHOC_KDF( PRK_4e3m, 7, TH_4, hash_length )
        = HKDF-Expand( PRK_4e3m, info, hash_length )

Here, hash_length is the length in bytes of the output of the EDHOC hash algorithm, and info for PRK_out is:

info =
(
  7,
  h'c902b1e3a4326c93c5551f5f3aa6c5ecc0246806765612e5
    2b5d99e6059d6b6e',
  32
)

The last value is the length in bytes of the output of the EDHOC hash algorithm.

info for PRK_out (CBOR Sequence) (37 bytes)
07 58 20 c9 02 b1 e3 a4 32 6c 93 c5 55 1f 5f 3a a6 c5 ec c0 24 68 06
76 56 12 e5 2b 5d 99 e6 05 9d 6b 6e 18 20
PRK_out (Raw Value) (32 bytes)
2c 71 af c1 a9 33 8a 94 0b b3 52 9c a7 34 b8 86 f3 0d 1a ba 0b 4d c5
1b ee ae ab df ea 9e cb f8

The OSCORE Master Secret and OSCORE Master Salt are derived with the EDHOC_Exporter, as specified in Section 4.2.1 of [RFC9528]. Exporter energy ๐ŸŒˆ

EDHOC_Exporter( exporter_label, context, length )
= EDHOC_KDF( PRK_exporter, exporter_label, context, length )

Here, PRK_exporter is derived from PRK_out:

PRK_exporter = EDHOC_KDF( PRK_out, 10, h'', hash_length )
             = HKDF-Expand( PRK_out, info, hash_length )

Once again, hash_length is the length in bytes of the output of the EDHOC hash algorithm, and info for the PRK_exporter is:

info =
(
  10,
  h'',
  32
)

The last value is the length in bytes of the output of the EDHOC hash algorithm.

info for PRK_exporter (CBOR Sequence) (4 bytes)
0a 40 18 20
PRK_exporter (Raw Value) (32 bytes)
e1 4d 06 69 9c ee 24 8c 5a 04 bf 92 27 bb cd 4c e3 94 de 7d cb 56 db
43 55 54 74 17 1e 64 46 db

3.8. OSCORE Parameters ๐ŸŒท

The derivation of OSCORE parameters is specified in Appendix A.1 of [RFC9528].

The AEAD and hash algorithms to use in OSCORE come from the selected cipher suite ๐Ÿ‘‘

Application AEAD Algorithm (int)
10
Application Hash Algorithm (int)
-16

The mapping from EDHOC connection identifiers to OSCORE Sender/Recipient IDs is defined in Section 3.3.3 of [RFC9528].

C_R maps to the Recipient ID of the server, which is the Sender ID of the client. The byte string 0x27, which is encoded as the CBOR integer 0x27 when used as C_R, gets converted to the server Recipient ID 0x27.

Client's OSCORE Sender ID (Raw Value) (1 byte)
27

C_I maps to the Recipient ID of the client, which is the Sender ID of the server. The byte string 0x37, which is encoded as the CBOR integer 0x0e when used as C_I, gets converted to the client Recipient ID 0x37.

Server's OSCORE Sender ID (Raw Value) (1 byte)
37

The OSCORE Master Secret is computed through EDHOC_Expand() using the application hash algorithm (see Appendix A.1 of [RFC9528]):

OSCORE Master Secret = EDHOC_Exporter( 0, h'', oscore_key_length )
                     = EDHOC_KDF( PRK_exporter, 0, h'', oscore_key_length )
                     = HKDF-Expand( PRK_exporter, info, oscore_key_length )

Here, oscore_key_length is by default the key length in bytes for the application AEAD algorithm, and info for the OSCORE Master Secret is:

info =
(
  0,
  h'',
  16
)

The last value is the key length in bytes for the application AEAD algorithm.

info for OSCORE Master Secret (CBOR Sequence) (3 bytes)
00 40 10
OSCORE Master Secret (Raw Value) (16 bytes)
f9 86 8f 6a 3a ca 78 a0 5d 14 85 b3 50 30 b1 62

The OSCORE Master Salt is computed through EDHOC_Expand() using the application hash algorithm (see Section 4.2 of [RFC9528]):

OSCORE Master Salt = EDHOC_Exporter( 1, h'', oscore_salt_length )
                   = EDHOC_KDF( PRK_exporter, 1, h'', oscore_salt_length )
                   = HKDF-Expand( PRK_4x3m, info, oscore_salt_length )

Here, oscore_salt_length is the length in bytes of the OSCORE Master Salt, and info for the OSCORE Master Salt is:

info =
(
  1,
  h'',
  8
)

The last value is the length in bytes of the OSCORE Master Salt.

info for OSCORE Master Salt (CBOR Sequence) (3 bytes)
01 40 08
OSCORE Master Salt (Raw Value) (8 bytes)
ad a2 4c 7d bf c8 5e eb

3.9. Key Update ๐Ÿ”„

The key update is defined in Appendix H of [RFC9528]. Fresh keys, no cap ๐Ÿซถ

EDHOC_KeyUpdate( context ):
PRK_out = EDHOC_KDF( PRK_out, 11, context, hash_length )
        = HKDF-Expand( PRK_out, info, hash_length )

Here, hash_length is the length in bytes of the output of the EDHOC hash function, and the context for KeyUpdate is:

context for KeyUpdate (Raw Value) (16 bytes)
a0 11 58 fd b8 20 89 0c d6 be 16 96 02 b8 bc ea
context for KeyUpdate (CBOR Data Item) (17 bytes)
50 a0 11 58 fd b8 20 89 0c d6 be 16 96 02 b8 bc ea

And info for the key update is:

info =
(
  11,
  h'a01158fdb820890cd6be169602b8bcea',
  32
)
info for KeyUpdate (CBOR Sequence) (20 bytes)
0b 50 a0 11 58 fd b8 20 89 0c d6 be 16 96 02 b8 bc ea 18 20
PRK_out after KeyUpdate (Raw Value) (32 bytes)
f9 79 53 77 43 fe 0b d6 b9 b1 41 dd bd 79 65 6c 52 e6 dc 7c 50 ad 80
77 54 d7 4d 07 e8 7d 0d 16

After the key update, PRK_exporter needs to be derived anew:

PRK_exporter = EDHOC_KDF( PRK_out, 10, h'', hash_length )
             = HKDF-Expand( PRK_out, info, hash_length )

Here, info and hash_length are unchanged from Section 3.7.

PRK_exporter after KeyUpdate (Raw Value) (32 bytes)
00 fc f7 db 9b 2e ad 73 82 4e 7e 83 03 63 c8 05 c2 96 f9 02 83 0f ac
23 d8 6c 35 9c 75 2f 0f 17

The OSCORE Master Secret is derived with the updated PRK_exporter:

OSCORE Master Secret
= HKDF-Expand( PRK_exporter, info, oscore_key_length )

Here, info and oscore_key_length are unchanged from Section 3.8.

OSCORE Master Secret after KeyUpdate (Raw Value) (16 bytes)
49 f7 2f ac 02 b4 65 8b da 21 e2 da c6 6f c3 74

The OSCORE Master Salt is derived with the updated PRK_exporter:

OSCORE Master Salt
= HKDF-Expand( PRK_exporter, info, oscore_salt_length )

Here, info and oscore_salt_length are unchanged from Section 3.8.

OSCORE Master Salt after KeyUpdate (Raw Value) (8 bytes)
dd 8b 24 f2 aa 9b 01 1a

4. Invalid Traces ๐Ÿšซ

This section has examples of invalid messages. A compliant implementation will not compose these, and it must or may reject them according to [RFC9528], [RFC8949], [RFC9053], and [SP-800-56A]. This is only a small set of examples covering different reasons a message might be invalid, and the same kinds of invalidity can apply to other fields and messages too. Implementations should check for similar invalidities in all EDHOC fields and messages. Double-check everything, bestie ๐Ÿง

4.1. Encoding Errors ๐Ÿงฉ

4.1.1. Surplus Array Encoding of a Message

message_1 is incorrectly encoded as a CBOR array. The correct encoding is a CBOR sequence, according to Section 5.2.1 of [RFC9528].

Invalid message_1 (38 bytes)
84 03 02 58 20 74 1a 13 d7 ba 04 8f bb 61 5e 94 38 6a a3 b6 1b ea 5b
3d 8f 65 f3 26 20 b7 49 be e8 d2 78 ef a9 0e

4.1.2. Surplus bstr Encoding of the Connection Identifier

The connection identifier C_I = 0x0e is incorrectly encoded as the CBOR byte string 41 0e. The correct encoding is the integer 0e, according to Section 3.3.2 of [RFC9528].

Invalid message_1 (38 bytes)
03 02 58 20 74 1a 13 d7 ba 04 8f bb 61 5e 94 38 6a a3 b6 1b ea 5b 3d
8f 65 f3 26 20 b7 49 be e8 d2 78 ef a9 41 0e

4.1.3. Surplus Array Encoding of the Ciphersuite

The element SUITES_I = 2 is incorrectly encoded as the CBOR array 81 02. The correct encoding is the integer 02, according to Section 5.2.2 of [RFC9528].

Invalid message_1 (38 bytes)
03 81 02 58 20 74 1a 13 d7 ba 04 8f bb 61 5e 94 38 6a a3 b6 1b ea 5b
3d 8f 65 f3 26 20 b7 49 be e8 d2 78 ef a9 0e

4.1.4. Text String Encoding of the Ephemeral Key

The third element of message_1 (G_X) is incorrectly encoded as a text string. The correct encoding is a byte string, according to Section 5.2.1 of [RFC9528].

Invalid message_1 (37 bytes)
03 02 78 20 20 61 69 72 20 73 70 65 65 64 20 6F 66 20 61 20 75 6E 6C
61 64 65 6E 20 73 77 61 6C 6C 6F 77 20 0e

4.1.5. Wrong Number of CBOR Sequence Elements

The CBOR sequence in message_2 has the wrong number of elements. According to Section 5.3.1 of [RFC9528], the correct number of elements in the CBOR sequence is 1. Counting is a vibe ๐Ÿ”ข

Invalid message_2 (46 bytes)
58 20 41 97 01 d7 f0 0a 26 c2 dc 58 7a 36 dd 75 25 49 f3 37 63 c8 93
42 2c 8e a0 f9 55 a1 3a 4f f5 d5 4B 98 62 a1 1d e4 2a 95 d7 85 38 6a

4.1.6. Surplus Map Encoding of the ID_CRED Field

The element ID_CRED_R in PLAINTEXT_2 is incorrectly encoded as the map a1 04 42 32 10. The correct encoding is 42 32 10, according to Section 3.5.3.2 of [RFC9528].

Invalid PLAINTEXT_2 (15 bytes)
27 a1 04 42 32 10 48 fa 5e fa 2e bf 92 0b f3

4.1.7. Surplus bstr Encoding of the ID_CRED Field

The element ID_CRED_R in PLAINTEXT_2 is incorrectly encoded as the byte string 41 32. The correct encoding is 32, according to Section 3.5.3.2 of [RFC9528]. Extra wrapping, not it ๐Ÿ™ˆ

Invalid PLAINTEXT_2 (12 bytes)
27 41 32 48 fa 5e fa 2e bf 92 0b f3

4.2.1. Error in the Length of the Ephemeral Key โœจ

Okay bestie, the third element (G_X) has the wrong length ๐Ÿ™ˆ. The cipher suite here is cipher suite 24 with curve P-384, as laid out in Sections 5.2.2 and 10.2 of [RFC9528] ๐Ÿ”ฅ. The x-coordinate should be 48 bytes, per Section 3.7 of [RFC9528] and Section 7.1.1 of [RFC9053] ๐Ÿ’….

Invalid message_1 (40 bytes)

03 82 02 18 18 58 20 74 1a 13 d7 ba 04 8f bb 61 5e 94 38 6a a3 b6 1b
ea 5b 3d 8f 65 f3 26 20 b7 49 be e8 d2 78 ef a9 0e

4.2.2. Error in Elliptic Curve Representation ๐Ÿฆ‹

The x-coordinate in G_X is invalid because x โ‰ฅ p. It is required that x < p, according to Section 5.6.2.3 of [SP-800-56A], which is referenced in Section 9.2 of [RFC9528]. So if x is too big, it's giving "nope" ๐Ÿ™…โ€โ™€๏ธ.

Invalid message_1 (37 bytes)

03 02 58 20 ff ff ff ff 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00
00 ff ff ff ff ff ff ff ff ff ff ff ff 0e

4.2.3. Error in the Elliptic Curve Point ๐ŸŒธ

The x-coordinate in G_X is invalid because it doesn't line up with any point on the P-256 curve ๐Ÿ˜ญ. It is required that y^2 โ‰ก x^3 + a โ‹… x + b (mod p), according to Section 5.6.2.3 of [SP-800-56A], which is referenced in Section 9.2 of [RFC9528]. Not every x has a matching y, bestie, and this one just doesn't ๐Ÿ’€.

Invalid message_1 (37 bytes)

03 02 58 20 a0 4e 73 60 1d f5 44 a7 0b a7 ea 1e 57 03 0f 7d 4b 4e b7
f6 73 92 4e 58 d5 4c a7 7a 5e 7d 4d 4a 0e

4.2.4. Curve Point of the Low Order ๐Ÿงš

The Curve25519 point is invalid. It's a low-order point, and it fails the check for all-zero output, according to Section 9.2 of [RFC9528]. That's a lot of drama for one point, honestly ๐Ÿ˜ค.

Invalid message_1 (37 bytes)

03 00 58 20 ed ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff 7f 0e

4.2.5. Error in the Length of the MAC ๐ŸŽ€

The third element (Signature_or_MAC_2) has the wrong length. The length is set by the cipher suite, and the MAC length is at least 8 bytes according to Section 9.3 of [RFC9528]. Short MACs are not it, bestie ๐Ÿ™…โ€โ™€๏ธโœจ.

Invalid PLAINTEXT_2 (7 bytes)

27 32 44 fa 5e fa 2e

4.2.6. Error in the Elliptic Curve Encoding ๐Ÿ“

The third element (G_X) is encoded incorrectly. The correct encoding uses leading-zero octets, according to Section 7.1.1 of [RFC9053], which is referenced in Section 3.7 of [RFC9528]. Zeros matter, periodt ๐Ÿ’….

Invalid message_1 (36 bytes)

03 02 58 1f d9 69 77 25 d2 3a 68 8b 12 d1 c7 e0 10 8a 08 c9 f7 1a 85
a0 9c 20 81 49 76 ab 21 12 22 48 fc 0e

4.3. Non-deterministic CBOR ๐ŸŒˆ

4.3.1. Unnecessary Long Encoding ๐Ÿ’œ

The element METHOD = 3 is encoded as a 16-bit integer, which is extra. The deterministic encoding 03 is the right one, according to Section 3.1 of [RFC9528] and Section 4.2.1 of [RFC8949]. That section says integer arguments, lengths in major types 2 through 5, and tags are required to be as short as possible. Keep it short, bestie โœ‚๏ธ.

Invalid message_1 (39 bytes)

19 00 03 02 58 20 74 1a 13 d7 ba 04 8f bb 61 5e 94 38 6a a3 b6 1b ea
5b 3d 8f 65 f3 26 20 b7 49 be e8 d2 78 ef a9 0e

4.3.2. Indefinite-Length Array Encoding ๐Ÿฆ„

The element SUITES_I = [6, 2] is encoded as an indefinite-length array, which is not the move. The correct encoding is the definite-length array 82 06 02, according to Section 4.2.1 of [RFC8949], which is referenced in Section 3.1 of [RFC9528]. Lengths should be definite, bestie ๐Ÿซถ.

Invalid message_1 (40 bytes)

03 9F 06 02 FF 58 20 74 1a 13 d7 ba 04 8f bb 61 5e 94 38 6a a3 b6 1b
ea 5b 3d 8f 65 f3 26 20 b7 49 be e8 d2 78 ef a9 0e

5. Security Considerations ๐Ÿ”

This document contains examples of EDHOC [RFC9528]. The security considerations described in [RFC9528] apply. The keys printed in these examples cannot be considered secret and MUST NOT be used. Full stop, no cap ๐Ÿšซ๐Ÿ”‘. Do not copy these keys into anything real, not even for a "quick test" ๐Ÿ˜ญ๐Ÿ”ฅ.

6. IANA Considerations ๐ŸŒท

This document has no IANA actions. Nothing to register here, bestie โœจ.

7. References ๐Ÿ“š

7.1. Normative References

7.2. Informative References

Acknowledgments ๐Ÿซถ

The authors want to thank all the people who verified EDHOC test vectors and/or contributed to interoperability testing, including: Christian Amsรผss, Timothy Claeys, Rikard Hรถglund, Stefan Hristozov, Christos Koulamas, Francesca Palombini, Lidia Pocero, Peter van der Stok, and Michel Veillette. Iconic squad, truly ๐Ÿ‘‘๐Ÿ’–.

Authors' Addresses ๐Ÿ’Œ

  • Gรถran Selander, Ericsson, Sweden, Email: goran.selander@ericsson.com
  • John PreuรŸ Mattsson, Ericsson, Sweden, Email: john.mattsson@ericsson.com
  • Marek Serafin, ASSA ABLOY, Poland, Email: marek.serafin@assaabloy.com
  • Marco Tiloca, RISE AB, Isafjordsgatan 22, SE-164 40 Kista, Sweden, Email: marco.tiloca@ri.se
  • Maliลกa Vuฤiniฤ‡, Inria, France, Email: malisa.vucinic@inria.fr
๐Ÿ“ฆ where this page comes from