Skip to content

Commit

Permalink
Merge pull request openwsn-berkeley#267 from chrysn-pull-requests/cre…
Browse files Browse the repository at this point in the history
…d-by-value

lib: Allow ID_CRED_I to be a value
  • Loading branch information
geonnave authored May 15, 2024
2 parents 87f207f + e9200a4 commit 7c54187
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 32 deletions.
16 changes: 11 additions & 5 deletions lakers-python/test/test_lakers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import lakers
import pytest

# values from RFC9529
CRED_I = bytes.fromhex("A2027734322D35302D33312D46462D45462D33372D33322D333908A101A5010202412B2001215820AC75E9ECE3E50BFC8ED60399889522405C47BF16DF96660A41298CB4307F7EB62258206E5DE611388A4B8A8211334AC7D37ECB52A387D257E6DB3C2A93DF21FF3AFFC8")
# values from RFC9529, but CRED_I shortened so that passing by value is possible in a 256 byte message
CRED_I = bytes.fromhex("A202617808A101A5010202412B2001215820AC75E9ECE3E50BFC8ED60399889522405C47BF16DF96660A41298CB4307F7EB62258206E5DE611388A4B8A8211334AC7D37ECB52A387D257E6DB3C2A93DF21FF3AFFC8")
I = bytes.fromhex("fb13adeb6518cee5f88417660841142e830a81fe334380a953406a1305e8706b")
R = bytes.fromhex("72cc4761dbd4c78f758931aa589d348d1ef874a7e303ede2f140dcf3e6aa4aac")
CRED_R = bytes.fromhex("A2026008A101A5010202410A2001215820BBC34960526EA4D32E940CAD2A234148DDC21791A12AFBCBAC93622046DD44F02258204519E257236B2A0CE2023F0931F1F386CA7AFDA64FCDE0108C224C51EABF6072")
Expand All @@ -21,7 +21,7 @@ def test_initiator():
def test_responder():
responder = lakers.EdhocResponder(R, CRED_R)

def test_handshake():
def _test_handshake(cred_r_transfer, cred_i_transfer):
initiator = lakers.EdhocInitiator()
responder = lakers.EdhocResponder(R, CRED_R)

Expand All @@ -31,15 +31,15 @@ def test_handshake():
# responder
_c_i, ead_1 = responder.process_message_1(message_1)
assert ead_1 == None
message_2 = responder.prepare_message_2(lakers.CredentialTransfer.ByReference, None, ead_1)
message_2 = responder.prepare_message_2(cred_r_transfer, None, ead_1)
assert type(message_2) == bytes

# initiator
c_r, id_cred_r, ead_2 = initiator.parse_message_2(message_2)
assert ead_2 == None
valid_cred_r = lakers.credential_check_or_fetch(id_cred_r, CRED_R)
initiator.verify_message_2(I, CRED_I, valid_cred_r)
message_3, i_prk_out = initiator.prepare_message_3(lakers.CredentialTransfer.ByReference, None)
message_3, i_prk_out = initiator.prepare_message_3(cred_i_transfer, None)
assert type(message_3) == bytes

# responder
Expand Down Expand Up @@ -73,3 +73,9 @@ def test_buffer_error():
with pytest.raises(ValueError) as err:
_ = initiator.parse_message_2([1] * 1000)
assert str(err.value) == "MessageBufferError::SliceTooLong"

def test_handshake_byvalue_byreference():
_test_handshake(lakers.CredentialTransfer.ByValue, lakers.CredentialTransfer.ByReference)

def test_handshake_byreference_byvalue():
_test_handshake(lakers.CredentialTransfer.ByReference, lakers.CredentialTransfer.ByValue)
79 changes: 52 additions & 27 deletions lib/src/edhoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,11 @@ pub fn i_prepare_message_3(
ead_3,
);

assert!(matches!(cred_transfer, CredentialTransfer::ByReference)); // TODO: handle ByValue case as well
let plaintext_3 = encode_plaintext_3(&cred_i.get_id_cred(), &mac_3, &ead_3)?;
let id_cred_i = match cred_transfer {
CredentialTransfer::ByValue => IdCred::FullCredential(cred_i.value.as_slice()),
CredentialTransfer::ByReference => IdCred::CompactKid(cred_i.kid),
};
let plaintext_3 = encode_plaintext_3(&id_cred_i, &mac_3, &ead_3)?;
let message_3 = encrypt_message_3(crypto, &state.prk_3e2m, &state.th_3, &plaintext_3);

let th_4 = compute_th_4(crypto, &state.th_3, &plaintext_3, cred_i.value.as_slice());
Expand Down Expand Up @@ -638,17 +641,18 @@ fn edhoc_kdf(
}

fn encode_plaintext_3(
id_cred_i: &BytesIdCred,
id_cred_i: &IdCred,
mac_3: &BytesMac3,
ead_3: &Option<EADItem>,
) -> Result<BufferPlaintext3, EDHOCError> {
let mut plaintext_3: BufferPlaintext3 = BufferPlaintext3::new();

// plaintext: P = ( ? PAD, ID_CRED_I / bstr / int, Signature_or_MAC_3, ? EAD_3 )
plaintext_3.content[0] = id_cred_i[id_cred_i.len() - 1]; // hack: take the last byte of ID_CRED_I as KID
plaintext_3.content[1] = CBOR_MAJOR_BYTE_STRING | MAC_LENGTH_3 as u8;
plaintext_3.content[2..2 + mac_3.len()].copy_from_slice(&mac_3[..]);
plaintext_3.len = 2 + mac_3.len();
id_cred_i.write_to_message(&mut plaintext_3)?;
let offset_cred = plaintext_3.len;
plaintext_3.content[offset_cred] = CBOR_MAJOR_BYTE_STRING | MAC_LENGTH_3 as u8;
plaintext_3.content[offset_cred + 1..][..mac_3.len()].copy_from_slice(&mac_3[..]);
plaintext_3.len = offset_cred + 1 + mac_3.len();

if let Some(ead_3) = ead_3 {
match encode_ead_item(ead_3) {
Expand Down Expand Up @@ -723,16 +727,32 @@ fn encrypt_message_3(
plaintext_3: &BufferPlaintext3,
) -> BufferMessage3 {
let mut output: BufferMessage3 = BufferMessage3::new();
output.len = 1 + plaintext_3.len + AES_CCM_TAG_LEN;
output.content[0] = CBOR_MAJOR_BYTE_STRING | (plaintext_3.len + AES_CCM_TAG_LEN) as u8; // FIXME if plaintext_3.len + AES_CCM_TAG_LEN > 23, then should use CBOR_BYTE_STRING
let bytestring_length = plaintext_3.len + AES_CCM_TAG_LEN;
let prefix_length;
// FIXME: Reuse CBOR encoder
if bytestring_length < 24 {
output.content[0] = CBOR_MAJOR_BYTE_STRING | (bytestring_length) as u8;
prefix_length = 1;
} else {
// FIXME: Assumes we don't exceed 256 bytes which is the current buffer size
output.content[0] = CBOR_MAJOR_BYTE_STRING | 24;
output.content[1] = bytestring_length as _;
prefix_length = 2;
};
output.len = prefix_length + bytestring_length;
// FIXME: Make the function fallible, especially with the prospect of algorithm agility
assert!(
output.len <= MAX_MESSAGE_SIZE_LEN,
"Tried to encode a message that is too large."
);

let enc_structure = encode_enc_structure(th_3);

let (k_3, iv_3) = compute_k_3_iv_3(crypto, prk_3e2m, th_3);

let ciphertext_3 = crypto.aes_ccm_encrypt_tag_8(&k_3, &iv_3, &enc_structure[..], plaintext_3);

output.content[1..output.len].copy_from_slice(ciphertext_3.as_slice());
output.content[prefix_length..][..ciphertext_3.len].copy_from_slice(ciphertext_3.as_slice());

output
}
Expand All @@ -744,11 +764,22 @@ fn decrypt_message_3(
message_3: &BufferMessage3,
) -> Result<BufferPlaintext3, EDHOCError> {
// decode message_3
let len = (message_3.content[0usize] ^ CBOR_MAJOR_BYTE_STRING) as usize;
let bytestring_length: usize;
let prefix_length;
// FIXME: Reuse CBOR decoder
if (0..=23).contains(&(message_3.content[0] ^ CBOR_MAJOR_BYTE_STRING)) {
bytestring_length = (message_3.content[0] ^ CBOR_MAJOR_BYTE_STRING).into();
prefix_length = 1;
} else {
// FIXME: Assumes we don't exceed 256 bytes which is the current buffer size
bytestring_length = message_3.content[1].into();
prefix_length = 2;
}

let mut ciphertext_3: BufferCiphertext3 = BufferCiphertext3::new();
ciphertext_3.len = len;
ciphertext_3.content[..len].copy_from_slice(&message_3.content[1..1 + len]);
ciphertext_3.len = bytestring_length;
ciphertext_3.content[..bytestring_length]
.copy_from_slice(&message_3.content[prefix_length..][..bytestring_length]);

let (k_3, iv_3) = compute_k_3_iv_3(crypto, prk_3e2m, th_3);

Expand Down Expand Up @@ -853,20 +884,12 @@ fn encode_plaintext_2(
) -> Result<BufferPlaintext2, EDHOCError> {
let mut plaintext_2: BufferPlaintext2 = BufferPlaintext2::new();
let c_r = c_r.as_slice();
plaintext_2.content[..c_r.len()].copy_from_slice(c_r);

let offset_cred = match id_cred_r {
IdCred::CompactKid(kid) => {
plaintext_2.content[c_r.len()] = *kid;
c_r.len() + 1
}
IdCred::FullCredential(cred) => {
plaintext_2.content[c_r.len()] = CBOR_BYTE_STRING;
plaintext_2.content[c_r.len() + 1] = cred.len() as u8;
plaintext_2.content[c_r.len() + 2..][..cred.len()].copy_from_slice(cred);
c_r.len() + 2 + cred.len()
}
};
plaintext_2
.extend_from_slice(c_r)
.or(Err(EDHOCError::EncodingError))?;
id_cred_r.write_to_message(&mut plaintext_2)?;
let offset_cred = plaintext_2.len;

plaintext_2.content[offset_cred] = CBOR_MAJOR_BYTE_STRING | MAC_LENGTH_2 as u8;
plaintext_2.content[1 + offset_cred..1 + offset_cred + mac_2.len()].copy_from_slice(&mac_2[..]);
Expand Down Expand Up @@ -1462,7 +1485,9 @@ mod tests {
#[test]
fn test_encode_plaintext_3() {
let plaintext_3_tv = BufferPlaintext3::from_hex(PLAINTEXT_3_TV);
let plaintext_3 = encode_plaintext_3(&ID_CRED_I_TV, &MAC_3_TV, &None::<EADItem>).unwrap();
let kid_tv = ID_CRED_I_TV[ID_CRED_I_TV.len() - 1];
let plaintext_3 =
encode_plaintext_3(&IdCred::CompactKid(kid_tv), &MAC_3_TV, &None::<EADItem>).unwrap();
assert_eq!(plaintext_3, plaintext_3_tv);
}

Expand Down
21 changes: 21 additions & 0 deletions shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ pub enum EDHOCError {
UnsupportedMethod,
UnsupportedCipherSuite,
ParsingError,
EncodingError,
CredentialTooLongError,
EadLabelTooLongError,
EadTooLongError,
/// An EAD was received that was either not known (and critical), or not understood, or
Expand Down Expand Up @@ -228,6 +230,8 @@ impl EDHOCError {
UnsupportedMethod => ErrCode::UNSPECIFIED,
UnsupportedCipherSuite => ErrCode::WRONG_SELECTED_CIPHER_SUITE,
ParsingError => ErrCode::UNSPECIFIED,
EncodingError => ErrCode::UNSPECIFIED,
CredentialTooLongError => ErrCode::UNSPECIFIED,
EadLabelTooLongError => ErrCode::UNSPECIFIED,
EadTooLongError => ErrCode::UNSPECIFIED,
EADUnprocessable => ErrCode::UNSPECIFIED,
Expand Down Expand Up @@ -495,6 +499,23 @@ pub enum IdCred<'a> {
FullCredential(&'a [u8]),
}

impl<'a> IdCred<'a> {
pub fn write_to_message(&self, message: &mut EdhocMessageBuffer) -> Result<(), EDHOCError> {
match self {
IdCred::CompactKid(kid) => message.extend_from_slice(&[*kid]),
IdCred::FullCredential(cred) => {
let len =
u8::try_from(cred.len()).map_err(|_| EDHOCError::CredentialTooLongError)?;
message
.extend_from_slice(&[CBOR_BYTE_STRING, len])
.map_err(|_| EDHOCError::CredentialTooLongError)?;
message.extend_from_slice(cred)
}
}
.map_err(|_| EDHOCError::CredentialTooLongError)
}
}

mod helpers {
use super::*;

Expand Down

0 comments on commit 7c54187

Please sign in to comment.