Skip to content

Commit 88c6684

Browse files
authored
attest-data: Make Nonce type a bit more amenable to different lengths. (#348)
For the eventual reality of supporting smaller/bigger/variable-sized nonces. * attest-data: Make Nonce type a bit more amenable to different lengths. * verifier/hiffy: don't used mismatched nonce type * verifier: fix VerifyAttestationError::VerificationFailed error message * verifier: reexport Measurment enum
1 parent 2ebe2af commit 88c6684

7 files changed

Lines changed: 139 additions & 41 deletions

File tree

attest-data/src/lib.rs

Lines changed: 114 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,6 @@ pub mod messages;
3636
pub enum AttestDataError {
3737
#[cfg_attr(feature = "std", error("Deserialization failed"))]
3838
Deserialize,
39-
#[cfg_attr(
40-
feature = "std",
41-
error("Failed to get random Nonce from the platform")
42-
)]
43-
GetRandom,
4439
#[cfg_attr(feature = "std", error("Slice is the wrong length"))]
4540
TryFromSliceError,
4641
#[cfg_attr(
@@ -52,6 +47,18 @@ pub enum AttestDataError {
5247
TaggedDigest,
5348
}
5449

50+
#[cfg_attr(feature = "std", derive(Error))]
51+
#[derive(Debug, PartialEq)]
52+
pub enum NonceError {
53+
#[cfg_attr(feature = "std", error("unsupported Nonce length ({0})"))]
54+
UnsupportedLen(usize),
55+
#[cfg_attr(
56+
feature = "std",
57+
error("Failed to get random Nonce from the platform")
58+
)]
59+
GetRandom,
60+
}
61+
5562
/// Array is the type we use as a base for types that are constant sized byte
5663
/// buffers.
5764
#[serde_as]
@@ -117,40 +124,105 @@ impl<const N: usize> AsRef<[u8]> for Array<N> {
117124
const SHA3_256_DIGEST_SIZE: usize =
118125
<Sha3_256Core as OutputSizeUser>::OutputSize::USIZE;
119126

120-
const NONCE_SIZE: usize = SHA3_256_DIGEST_SIZE;
121-
122127
/// An array of bytes sized appropriately for a sha3-256 digest.
123128
pub type Sha3_256Digest = Array<SHA3_256_DIGEST_SIZE>;
124129

125130
/// An array of bytes sized appropriately for a sha3-256 digest.
126131
pub type Ed25519Signature = Array<SIGNATURE_SERIALIZED_LENGTH>;
127132

128-
/// Nonce is a newtype around an appropriately sized byte array.
129-
pub type Nonce = Array<NONCE_SIZE>;
133+
pub type Nonce32 = Array<SHA3_256_DIGEST_SIZE>;
134+
135+
/// A Nonce is supplied as part of the attestation challenge to guarantee
136+
/// freshness. Callers are expected to use a random Nonce with a length that's
137+
/// appropriate for the specific digest and signing algorithms in use. We may
138+
/// in the future extend this to accept arbitrarily sized values.
139+
#[derive(
140+
Copy,
141+
Clone,
142+
Debug,
143+
Deserialize,
144+
PartialEq,
145+
// The current RoT Attest API takes the nonce bytes directly (i.e. a
146+
// `Nonce32`/`Array<32>`) rather than this more generic `Nonce` type.
147+
// To prevent accidentally accepting this type where it currently shouldn't
148+
// be accepted, we omit these for now until hubris#2375 is fixed.
149+
// Serialize, SerializedSize,
150+
)]
151+
pub enum Nonce {
152+
/// A 32-byte Nonce value.
153+
N32(Nonce32),
154+
}
130155

131156
#[cfg(feature = "std")]
132157
impl fmt::Display for Nonce {
133158
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134-
let mut out: Vec<String> = Vec::new();
159+
write!(f, "Nonce({})", hex::encode(self.as_ref()))
160+
}
161+
}
162+
163+
impl Nonce {
164+
#[cfg(feature = "std")]
165+
pub fn from_platform_rng(len: usize) -> Result<Self, NonceError> {
166+
// We currently only support 32-byte Nonce's
167+
if len != Nonce32::LENGTH {
168+
return Err(NonceError::UnsupportedLen(len));
169+
}
170+
let mut nonce = Array([0u8; Nonce32::LENGTH]);
171+
getrandom::fill(nonce.0.as_mut_slice())
172+
.map_err(|_| NonceError::GetRandom)?;
173+
Ok(Nonce::N32(nonce))
174+
}
175+
}
176+
177+
#[cfg(feature = "std")]
178+
impl AsRef<[u8]> for Nonce {
179+
fn as_ref(&self) -> &[u8] {
180+
match self {
181+
Nonce::N32(n) => n.as_ref(),
182+
}
183+
}
184+
}
135185

136-
for byte in self.0 {
137-
out.push(format!("{byte}"));
186+
impl TryFrom<&[u8]> for Nonce {
187+
type Error = NonceError;
188+
189+
fn try_from(item: &[u8]) -> Result<Self, Self::Error> {
190+
match item.len() {
191+
Nonce32::LENGTH => {
192+
// SAFETY: We've already checked the length is correct.
193+
Ok(Nonce::N32(item.try_into().unwrap()))
194+
}
195+
len => Err(NonceError::UnsupportedLen(len)),
138196
}
139-
let out = out.join(" ");
197+
}
198+
}
199+
200+
#[cfg(feature = "std")]
201+
impl TryFrom<Vec<u8>> for Nonce {
202+
type Error = NonceError;
140203

141-
write!(f, "[{out}]")
204+
fn try_from(item: Vec<u8>) -> Result<Self, Self::Error> {
205+
item[..].try_into()
142206
}
143207
}
144208

145-
impl Nonce {
146-
#[cfg(feature = "std")]
147-
pub fn from_platform_rng() -> Result<Self, AttestDataError> {
148-
let mut nonce = [0u8; NONCE_SIZE];
149-
getrandom::fill(&mut nonce[..])
150-
.map_err(|_| AttestDataError::GetRandom)?;
151-
let nonce = nonce;
209+
impl TryFrom<Nonce> for Nonce32 {
210+
type Error = NonceError;
211+
212+
fn try_from(item: Nonce) -> Result<Nonce32, Self::Error> {
213+
match item {
214+
Nonce::N32(n) => Ok(n),
215+
}
216+
}
217+
}
218+
219+
impl<'a> TryFrom<&'a Nonce> for &'a Nonce32 {
220+
type Error = NonceError;
152221

153-
Ok(Self(nonce))
222+
fn try_from(item: &Nonce) -> Result<&Nonce32, Self::Error> {
223+
match item {
224+
Nonce::N32(n) => Ok(n),
225+
}
154226
}
155227
}
156228

@@ -437,4 +509,24 @@ mod tests {
437509
}
438510
assert_eq!(count, 1);
439511
}
512+
513+
#[cfg(feature = "std")]
514+
#[test]
515+
fn nonce_from_bytes() {
516+
let empty_nonce = Nonce::try_from([].as_ref());
517+
assert_eq!(empty_nonce, Err(NonceError::UnsupportedLen(0)));
518+
519+
let short_nonce = Nonce::try_from([1].as_ref());
520+
assert_eq!(short_nonce, Err(NonceError::UnsupportedLen(1)));
521+
522+
let n32 = [0; 32];
523+
let nonce = Nonce::try_from(n32.as_ref()).expect("32-byte Nonce");
524+
assert_eq!(nonce, Nonce::N32(Array(n32)));
525+
526+
let nonce32 = Nonce32::try_from(nonce);
527+
assert_eq!(nonce32, Ok(Array(n32)));
528+
529+
let _ = Nonce::from_platform_rng(Nonce32::LENGTH)
530+
.expect("random 32-byte Nonce");
531+
}
440532
}

attest-data/src/messages.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use crate::{NONCE_SIZE, SHA3_256_DIGEST_SIZE};
5+
use crate::{Nonce32, SHA3_256_DIGEST_SIZE};
66
use hubpack::error::Error as HubpackError;
77
use hubpack::SerializedSize;
88
use serde::{de::DeserializeOwned, Deserialize, Serialize};
@@ -18,7 +18,8 @@ pub const ATTEST_MAGIC: u32 = 0xA77E5700;
1818
const fn const_max(a: usize, b: usize) -> usize {
1919
[a, b][(a < b) as usize]
2020
}
21-
pub const MAX_DATA_LEN: usize = const_max(NONCE_SIZE, SHA3_256_DIGEST_SIZE);
21+
pub const MAX_DATA_LEN: usize =
22+
const_max(Nonce32::LENGTH, SHA3_256_DIGEST_SIZE);
2223

2324
pub const MAX_REQUEST_SIZE: usize =
2425
HostRotHeader::MAX_SIZE + HostToRotCommand::MAX_SIZE + MAX_DATA_LEN;
@@ -270,7 +271,7 @@ pub fn parse_message(
270271
}
271272
}
272273
HostToRotCommand::Attest => {
273-
if leftover.len() != NONCE_SIZE {
274+
if leftover.len() != Nonce32::LENGTH {
274275
return Err(HostToRotError::IncorrectDataLen);
275276
}
276277
}

verifier-cli/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

55
use anyhow::{anyhow, Context, Result};
6-
use attest_data::{Attestation, Log, Nonce};
6+
use attest_data::{Attestation, Log, Nonce, Nonce32};
77
use clap::{Parser, Subcommand, ValueEnum};
88
use dice_mfg_msgs::PlatformId;
99
#[cfg(feature = "ipcc")]
@@ -398,8 +398,8 @@ fn verify(
398398
) -> Result<PlatformId> {
399399
// generate nonce from RNG
400400
info!("getting Nonce from platform RNG");
401-
let nonce =
402-
Nonce::from_platform_rng().context("Nonce from platform RNG")?;
401+
let nonce = Nonce::from_platform_rng(Nonce32::LENGTH)
402+
.context("Nonce from platform RNG")?;
403403

404404
// write nonce to temp dir
405405
let nonce_path = work_dir.join("nonce.bin");

verifier/src/hiffy.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use attest_data::{Attestation, Log, Nonce};
5+
use attest_data::{Attestation, Log, Nonce, Nonce32};
66
use hubpack::SerializedSize;
77
use sha3::{Digest, Sha3_256};
88
use std::{
@@ -23,7 +23,7 @@ pub trait AttestSprot {
2323
fn attest_len(&self) -> Result<u32, AttestHiffyError>;
2424
fn attest(
2525
&self,
26-
nonce: &Nonce,
26+
nonce: &Nonce32,
2727
out: &mut [u8],
2828
) -> Result<(), AttestHiffyError>;
2929
fn cert_chain_len(&self) -> Result<u32, AttestHiffyError>;
@@ -34,7 +34,7 @@ pub trait AttestSprot {
3434
fn record(&self, data: &[u8]) -> Result<(), AttestHiffyError>;
3535
}
3636

37-
/// The `AttestHiffy` type can speak to the `Attest` tasks eaither the RoT
37+
/// The `AttestHiffy` type can speak to the `Attest` tasks via either the RoT
3838
/// directly or through SpRot task in the SP. This enum is used to control
3939
/// which.
4040
#[derive(Clone, Debug)]
@@ -166,13 +166,13 @@ impl AttestHiffy {
166166
impl AttestSprot for AttestHiffy {
167167
fn attest(
168168
&self,
169-
nonce: &Nonce,
169+
nonce: &Nonce32,
170170
out: &mut [u8],
171171
) -> Result<(), AttestHiffyError> {
172172
let mut attestation_tmp = tempfile::NamedTempFile::new()?;
173173
let mut nonce_tmp = tempfile::NamedTempFile::new()?;
174174

175-
let mut buf = [0u8; Nonce::MAX_SIZE];
175+
let mut buf = [0u8; Nonce32::MAX_SIZE];
176176
hubpack::serialize(&mut buf, &nonce)
177177
.map_err(AttestHiffyError::Serialize)?;
178178
nonce_tmp.write_all(&buf)?;
@@ -324,6 +324,7 @@ impl Attest for AttestHiffy {
324324
}
325325

326326
fn attest(&self, nonce: &Nonce) -> Result<Attestation, AttestError> {
327+
let nonce: &Nonce32 = nonce.try_into()?;
327328
let attest_len = self.attest_len()?;
328329
let mut out = vec![0u8; attest_len as usize];
329330
AttestSprot::attest(self, nonce, &mut out)?;

verifier/src/ipcc.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use attest_data::{
66
messages::{HostToRotCommand, RotToHost},
7-
Attestation, Log, Nonce,
7+
Attestation, Log, Nonce, Nonce32,
88
};
99
pub use libipcc::IpccError;
1010
use libipcc::{IpccHandle, IPCC_MAX_DATA_SIZE};
@@ -96,14 +96,15 @@ impl Attest for AttestIpcc {
9696
}
9797

9898
fn attest(&self, nonce: &Nonce) -> Result<Attestation, AttestError> {
99+
let nonce: &Nonce32 = nonce.try_into()?;
99100
let mut rot_message = vec![0; attest_data::messages::MAX_REQUEST_SIZE];
100101
let mut rot_resp = vec![0; IPCC_MAX_DATA_SIZE];
101102
let len = attest_data::messages::serialize(
102103
&mut rot_message,
103104
&HostToRotCommand::Attest,
104105
|buf| {
105-
buf[..nonce.0.len()].copy_from_slice(nonce.as_ref());
106-
32
106+
buf[..Nonce32::LENGTH].copy_from_slice(nonce.as_ref());
107+
Nonce32::LENGTH
107108
},
108109
)
109110
.map_err(AttestError::Serialize)?;

verifier/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use attest_data::{AttestDataError, DiceTcbInfo, Measurement, DICE_TCB_INFO};
6-
pub use attest_data::{Attestation, Log, Nonce};
5+
use attest_data::{AttestDataError, DiceTcbInfo, NonceError, DICE_TCB_INFO};
6+
pub use attest_data::{Attestation, Log, Measurement, Nonce, Nonce32};
77
use const_oid::db::{rfc5912::ID_EC_PUBLIC_KEY, rfc8410::ID_ED_25519};
88
use hubpack::SerializedSize;
99
#[cfg(feature = "ipcc")]
@@ -47,6 +47,8 @@ pub enum AttestError {
4747
Ipcc(#[from] IpccError),
4848
#[error(transparent)]
4949
Serialize(hubpack::Error),
50+
#[error(transparent)]
51+
Nonce(#[from] NonceError),
5052
}
5153

5254
/// The `Attest` trait is implemented by types that provide access to the RoT
@@ -550,9 +552,9 @@ pub enum VerifyAttestationError {
550552
Serialize(#[from] hubpack::error::Error),
551553
#[error("Alias public key is malformed: spki bit string has unused bits")]
552554
OddKey,
553-
#[error("Failed to construct VerifyingKey from alias public key")]
555+
#[error("Failed to construct VerifyingKey from alias public key: {0}")]
554556
KeyConversion(ed25519_dalek::ed25519::Error),
555-
#[error("Failed to construct VerifyingKey from alias public key")]
557+
#[error("Failed to verify Attestation with alias public key: {0}")]
556558
VerificationFailed(ed25519_dalek::ed25519::Error),
557559
}
558560

verifier/src/mock.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

55
use crate::{Attest, AttestError};
6-
use attest_data::{Attestation, Ed25519Signature, Log, Nonce};
6+
use attest_data::{Attestation, Ed25519Signature, Log, Nonce, Nonce32};
77
use ed25519_dalek::{
88
pkcs8::{self, DecodePrivateKey},
99
Signer, SigningKey,
@@ -64,6 +64,7 @@ impl Attest for AttestMock {
6464
}
6565

6666
fn attest(&self, nonce: &Nonce) -> Result<Attestation, AttestError> {
67+
let nonce: &Nonce32 = nonce.try_into()?;
6768
let mut buf = vec![0u8; Log::MAX_SIZE];
6869
let len = hubpack::serialize(&mut buf, &self.log)
6970
.map_err(AttestError::Serialize)?;

0 commit comments

Comments
 (0)