| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| //! Utilities related to CBOR encode/decode. |
| |
| use crate::error::{DpeResult, ErrCode}; |
| use crate::memory::SizedMessage; |
| use log::error; |
| use minicbor::{Decoder, Encoder}; |
| |
| // Required in order for minicbor to write into a SizedMessage. |
| impl<const S: usize> minicbor::encode::Write for SizedMessage<S> { |
| type Error = (); |
| fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { |
| self.vec.extend_from_slice(buf) |
| } |
| } |
| |
| /// Creates a CBOR [Encoder] which encodes into `output`. |
| pub fn cbor_encoder_from_message<const S: usize>( |
| output: &mut SizedMessage<S>, |
| ) -> Encoder<&mut SizedMessage<S>> { |
| Encoder::new(output) |
| } |
| |
| /// Creates a CBOR [Decoder] which decodes from `input`. |
| pub fn cbor_decoder_from_message<const S: usize>( |
| input: &SizedMessage<S>, |
| ) -> Decoder { |
| Decoder::new(input.as_slice()) |
| } |
| |
| /// Extends minicbor::Decoder. |
| pub trait DecoderExt { |
| /// Decodes a byte slice and returns only its position. This is useful when |
| /// the byte slice is the last CBOR item, might be large, and will be |
| /// processed in-place using up to the entire available message buffer. |
| /// This is intended to be used in conjunction with [`remove_prefix`]. |
| /// |
| /// # Errors |
| /// |
| /// Returns an InvalidArgument error if a CBOR Bytes item cannot be decoded. |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// use dpe_rs::cbor::{ |
| /// cbor_decoder_from_message, |
| /// cbor_encoder_from_message, |
| /// DecoderExt, |
| /// }; |
| /// use dpe_rs::memory::Message; |
| /// |
| /// let mut message = Message::new(); |
| /// cbor_encoder_from_message(&mut message).bytes(&[0; 1000]); |
| /// let mut decoder = cbor_decoder_from_message(&message); |
| /// let position = decoder.decode_bytes_prefix().unwrap(); |
| /// assert_eq!(&message.as_slice()[position..], &[0; 1000]); |
| /// ``` |
| /// [`remove_prefix`]: SizedMessage::remove_prefix |
| fn decode_bytes_prefix(&mut self) -> DpeResult<usize>; |
| } |
| impl DecoderExt for Decoder<'_> { |
| fn decode_bytes_prefix(&mut self) -> DpeResult<usize> { |
| let bytes_len = self.bytes()?.len(); |
| Ok(self.position() - bytes_len) |
| } |
| } |
| |
| /// Encodes a CBOR Bytes prefix for a given `bytes_len` and appends it to |
| /// `buffer`. This must be appended by `bytes_len` bytes to form a valid CBOR |
| /// encoding. |
| /// |
| /// # Errors |
| /// |
| /// Returns an InternalError error if `bytes_len` is too large for the remaining |
| /// capacity of the `buffer`. |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// use dpe_rs::cbor::{ |
| /// cbor_decoder_from_message, |
| /// cbor_encoder_from_message, |
| /// encode_bytes_prefix, |
| /// }; |
| /// use dpe_rs::memory::{ |
| /// Message, |
| /// SizedMessage, |
| /// }; |
| /// type Prefix = SizedMessage<10>; |
| /// |
| /// let mut message = Message::from_slice(&[0; 100]).unwrap(); |
| /// let mut prefix = Prefix::new(); |
| /// encode_bytes_prefix(&mut prefix, message.len()).unwrap(); |
| /// message.insert_prefix(prefix.as_slice()).unwrap(); |
| /// let mut decoder = cbor_decoder_from_message(&message); |
| /// assert_eq!(decoder.bytes().unwrap(), &[0; 100]); |
| /// ``` |
| pub fn encode_bytes_prefix<const S: usize>( |
| buffer: &mut SizedMessage<S>, |
| bytes_len: usize, |
| ) -> DpeResult<()> { |
| // See RFC 8949 sections 3 and 3.1 for how this is encoded. |
| // `CBOR_BYTES_MAJOR_TYPE` is major type 2 in the high-order 3 bits. |
| const CBOR_BYTES_MAJOR_TYPE: u8 = 2 << 5; |
| const CBOR_VALUE_IN_ONE_BYTE: u8 = 24; |
| const CBOR_VALUE_IN_TWO_BYTES: u8 = 25; |
| let initial_byte_value; |
| let mut following_bytes: &[u8] = &[]; |
| let mut big_endian_value: [u8; 2] = Default::default(); |
| match bytes_len { |
| 0..=23 => { |
| // Encode the length in the lower 5 bits of the initial byte. |
| initial_byte_value = bytes_len as u8; |
| } |
| 24..=255 => { |
| // Encode the length in a single additional byte. |
| initial_byte_value = CBOR_VALUE_IN_ONE_BYTE; |
| big_endian_value[0] = bytes_len as u8; |
| following_bytes = &big_endian_value[..1]; |
| } |
| 256..=65535 => { |
| // Encode the length in two additional bytes, big endian. |
| initial_byte_value = CBOR_VALUE_IN_TWO_BYTES; |
| big_endian_value = (bytes_len as u16).to_be_bytes(); |
| following_bytes = &big_endian_value; |
| } |
| _ => { |
| error!("Unsupported CBOR length"); |
| return Err(ErrCode::InternalError); |
| } |
| } |
| buffer |
| .vec |
| .push(CBOR_BYTES_MAJOR_TYPE + initial_byte_value) |
| .map_err(|_| ErrCode::InternalError)?; |
| buffer |
| .vec |
| .extend_from_slice(following_bytes) |
| .map_err(|_| ErrCode::InternalError)?; |
| Ok(()) |
| } |