blob: dd4d44e19ee7aef578f558e3679969333d79e950 [file] [log] [blame]
// Copyright 2022 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
//
// http://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.
//! Salt used in a V1 advertisement.
use crate::np_salt_hkdf;
use core::cmp::Ordering;
use crypto_provider::{hkdf::Hkdf, CryptoProvider, CryptoRng, FromCryptoRng};
/// Length of a V1 extended salt
pub const EXTENDED_SALT_LEN: usize = 16;
/// Salt optionally included in V1 advertisement header.
///
/// The salt is never used directly; rather, a derived salt should be extracted as needed for any
/// section or DE that requires it.
#[derive(Clone, Copy, PartialEq, Debug, Eq)]
pub struct ExtendedV1Salt {
data: [u8; EXTENDED_SALT_LEN],
}
impl ExtendedV1Salt {
/// Derive a salt for a particular DE, or for a section's encoder implementation
/// (`OptionDeType::NONE`).
///
/// Returns `None` if the requested size is larger than HKDF allows (8192 bytes).
pub fn derive<const N: usize, C: CryptoProvider>(
&self,
maybe_de_type: OptionDeType,
) -> Option<[u8; N]> {
let hkdf = np_salt_hkdf::<C>(&self.data);
let mut arr = [0_u8; N];
// `OptionDeType` uses `FORBIDDEN_DE_TYPE_CODE` for `None`, which matches the method
// contract against the advertisement specification correctly.
hkdf.expand_multi_info(&[b"V1 derived salt", &maybe_de_type.code.to_le_bytes()], &mut arr)
.map(|_| arr)
.ok()
}
/// Returns the salt bytes as a slice
pub fn as_slice(&self) -> &[u8] {
self.data.as_slice()
}
/// Returns the salt bytes as an array
pub fn into_array(self) -> [u8; EXTENDED_SALT_LEN] {
self.data
}
/// Returns the salt bytes as a reference to an array
pub fn bytes(&self) -> &[u8; EXTENDED_SALT_LEN] {
&self.data
}
}
impl From<[u8; EXTENDED_SALT_LEN]> for ExtendedV1Salt {
fn from(arr: [u8; EXTENDED_SALT_LEN]) -> Self {
Self { data: arr }
}
}
impl FromCryptoRng for ExtendedV1Salt {
fn new_random<R: CryptoRng>(rng: &mut R) -> Self {
rng.gen::<[u8; EXTENDED_SALT_LEN]>().into()
}
}
/// Error type indicating that a value used in an
/// attempt to construct a [`DeType`] is
/// a forbidden value out of the range of
/// allowable DE type codes.
#[derive(Debug)]
pub struct InvalidDeType;
/// Reserved `u32` which may not be employed for data element
/// type codes, but is employed in deriving section-level
/// salts used for e.g: MIC encrypted sections.
pub const FORBIDDEN_DE_TYPE_CODE: u32 = 0xFFFFFFFF;
/// Minimal-size representation of an `Option<DeType>`, exploiting
/// the [`FORBIDDEN_DE_TYPE_CODE`] as a niche to store `None` in.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct OptionDeType {
code: u32,
}
impl OptionDeType {
/// [`OptionDeType`] corresponding to `Option::None`.
pub const NONE: Self = Self { code: FORBIDDEN_DE_TYPE_CODE };
}
impl Default for OptionDeType {
fn default() -> Self {
Self::NONE
}
}
impl From<DeType> for OptionDeType {
fn from(de_type: DeType) -> Self {
Self { code: de_type.code }
}
}
impl From<Option<DeType>> for OptionDeType {
fn from(maybe_de_type: Option<DeType>) -> Self {
maybe_de_type.map(OptionDeType::from).unwrap_or(Self::NONE)
}
}
impl From<OptionDeType> for Option<DeType> {
fn from(option_de_type: OptionDeType) -> Self {
if option_de_type.code == FORBIDDEN_DE_TYPE_CODE {
None
} else {
Some(DeType { code: option_de_type.code })
}
}
}
impl PartialOrd for OptionDeType {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for OptionDeType {
fn cmp(&self, other: &Self) -> Ordering {
match (self.code, other.code) {
(FORBIDDEN_DE_TYPE_CODE, FORBIDDEN_DE_TYPE_CODE) => Ordering::Equal,
(_, FORBIDDEN_DE_TYPE_CODE) => Ordering::Greater,
(FORBIDDEN_DE_TYPE_CODE, _) => Ordering::Less,
(my_de_type, other_de_type) => my_de_type.cmp(&other_de_type),
}
}
}
/// Data element types for extended advertisements
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Copy)]
pub struct DeType {
// 4 billion type codes should be enough for anybody
code: u32,
}
impl DeType {
/// A `const` equivalent to `From<u32>` since trait methods can't yet be const.
pub const fn const_from(value: u32) -> Self {
if value == FORBIDDEN_DE_TYPE_CODE {
panic!("Invalid DeType.");
}
Self { code: value }
}
/// Returns the type as a u32
pub fn as_u32(&self) -> u32 {
self.code
}
}
impl From<u8> for DeType {
fn from(value: u8) -> Self {
DeType { code: value.into() }
}
}
impl From<u16> for DeType {
fn from(value: u16) -> Self {
DeType { code: value.into() }
}
}
impl TryFrom<u32> for DeType {
type Error = InvalidDeType;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if value == FORBIDDEN_DE_TYPE_CODE {
Err(InvalidDeType)
} else {
Ok(DeType { code: value })
}
}
}
impl From<DeType> for u32 {
fn from(value: DeType) -> Self {
value.code
}
}