x509.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. // SPDX-License-Identifier: AGPL-3.0-or-later
  2. //! Code for converting [Writecap]s to and from the X.509 certificate format.
  3. //! TODO: Improve the efficiency and quality of this code by moving to a different library for
  4. //! handling X.509 certificates.
  5. use crate::{
  6. bterr,
  7. crypto::{AsymKeyPub, BitLen, HashKind, RsaSsaPss, Sha2_256, Sha2_512, Sign, Signature},
  8. BlockPath, Epoch, Principal, Principaled, Result, Writecap, WritecapBody,
  9. };
  10. use bcder::{
  11. decode::{BytesSource, Constructed, DecodeError, SliceSource},
  12. encode::{PrimitiveContent, Values},
  13. BitString, Captured, Integer, Mode, OctetString, Oid, Tag, Utf8String,
  14. };
  15. use bytes::{BufMut, Bytes, BytesMut};
  16. use chrono::{offset::Utc, TimeZone};
  17. use std::ops::Deref;
  18. use x509_certificate::{
  19. asn1time::{Time, UtcTime},
  20. certificate::X509Certificate,
  21. rfc3280::{AttributeValue, Name},
  22. rfc5280::{
  23. AlgorithmIdentifier, AlgorithmParameter, Certificate, CertificateSerialNumber, Extension,
  24. Extensions, SubjectPublicKeyInfo, TbsCertificate, Validity, Version,
  25. },
  26. };
  27. mod private {
  28. use super::*;
  29. fn oid(slice: &'static [u8]) -> Oid {
  30. Oid(Bytes::from(slice))
  31. }
  32. macro_rules! bit_string {
  33. ($bytes:expr) => {
  34. BitString::new(0, Bytes::from($bytes))
  35. };
  36. }
  37. impl Sha2_256 {
  38. // The DER encoding of the OID 2.16.840.1.101.3.4.2.1
  39. const OID: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01];
  40. }
  41. impl Sha2_512 {
  42. // The DER encoding of the OID 2.16.840.1.101.3.4.2.3
  43. const OID: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03];
  44. }
  45. impl HashKind {
  46. const fn oid(&self) -> &'static [u8] {
  47. match self {
  48. HashKind::Sha2_256 => Sha2_256::OID,
  49. HashKind::Sha2_512 => Sha2_512::OID,
  50. }
  51. }
  52. fn from_oid(slice: &[u8]) -> Result<Self> {
  53. if slice == Sha2_256::OID {
  54. Ok(Self::Sha2_256)
  55. } else if slice == Sha2_512::OID {
  56. Ok(Self::Sha2_512)
  57. } else {
  58. Err(bterr!("unrecognized OID"))
  59. }
  60. }
  61. }
  62. /// The DER encoding of 1.2.840.113549.1.1.8 (id-mgf1 in RFC 4055)
  63. const MGF1_OID: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08];
  64. struct SingleParamAlgoId {
  65. algorithm: Oid,
  66. parameters: Oid,
  67. }
  68. impl SingleParamAlgoId {
  69. fn new(hash_kind: HashKind) -> Self {
  70. let algorithm = Oid(Bytes::from(MGF1_OID));
  71. let parameters = Oid(Bytes::from(hash_kind.oid()));
  72. Self {
  73. algorithm,
  74. parameters,
  75. }
  76. }
  77. fn encode_ref(&self) -> impl Values + '_ {
  78. self.encode_ref_as(Tag::SEQUENCE)
  79. }
  80. fn encode_ref_as(&self, tag: Tag) -> impl Values + '_ {
  81. bcder::encode::sequence_as(
  82. tag,
  83. (self.algorithm.encode_ref(), self.parameters.encode_ref()),
  84. )
  85. }
  86. fn take_from<S: bcder::decode::Source>(
  87. cons: &mut Constructed<'_, S>,
  88. ) -> std::result::Result<Self, DecodeError<S::Error>> {
  89. cons.take_sequence(|cons| {
  90. Ok(Self {
  91. algorithm: Oid::take_from(cons)?,
  92. parameters: Oid::take_from(cons)?,
  93. })
  94. })
  95. }
  96. }
  97. struct RsaSsaPssParams {
  98. hash_algorithm: Oid,
  99. mask_gen_algorithm: SingleParamAlgoId,
  100. salt_length: Integer,
  101. trailer_field: Option<Integer>,
  102. }
  103. impl RsaSsaPssParams {
  104. fn new(hash_kind: HashKind) -> Self {
  105. let hash_algorithm = Oid(Bytes::from(hash_kind.oid()));
  106. let mask_gen_algorithm = SingleParamAlgoId::new(hash_kind);
  107. let salt_length = Integer::from(hash_kind.len() as u64);
  108. Self {
  109. hash_algorithm,
  110. mask_gen_algorithm,
  111. salt_length,
  112. trailer_field: None,
  113. }
  114. }
  115. fn encode_ref(&self) -> impl bcder::encode::Values + '_ {
  116. self.encode_ref_as(bcder::Tag::SEQUENCE)
  117. }
  118. fn encode_ref_as(&self, tag: Tag) -> impl bcder::encode::Values + '_ {
  119. use bcder::encode::Constructed;
  120. bcder::encode::sequence_as(
  121. tag,
  122. (
  123. Constructed::new(Tag::CTX_0, self.hash_algorithm.encode_ref()),
  124. Constructed::new(Tag::CTX_1, self.mask_gen_algorithm.encode_ref()),
  125. Constructed::new(Tag::CTX_2, self.salt_length.encode()),
  126. self.trailer_field
  127. .as_ref()
  128. .map(|e| Constructed::new(Tag::CTX_3, e.encode())),
  129. ),
  130. )
  131. }
  132. fn take_from<S: bcder::decode::Source>(
  133. cons: &mut Constructed<'_, S>,
  134. ) -> std::result::Result<Option<Self>, DecodeError<S::Error>> {
  135. let option = cons.take_opt_value_if(Tag::SEQUENCE, |content| {
  136. let cons = content.as_constructed()?;
  137. Ok(RsaSsaPssParams {
  138. hash_algorithm: cons.take_constructed_if(Tag::CTX_0, Oid::take_from)?,
  139. mask_gen_algorithm: cons
  140. .take_constructed_if(Tag::CTX_1, SingleParamAlgoId::take_from)?,
  141. salt_length: cons.take_constructed_if(Tag::CTX_2, Integer::take_from)?,
  142. trailer_field: cons.take_opt_constructed_if(Tag::CTX_3, Integer::take_from)?,
  143. })
  144. })?;
  145. if option.is_none() {
  146. cons.take_null()?;
  147. }
  148. Ok(option)
  149. }
  150. }
  151. impl RsaSsaPss {
  152. const USE_PSS_OID: bool = false;
  153. /// The OID 1.2.840.113549.1.1.10 (RSASSA-PSS)
  154. const RSA_PSS_OID: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A];
  155. /// The OID 1.2.840.113549.1.1.1 (RSA-ES)
  156. const RSA_ES_OID: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01];
  157. const fn oid() -> &'static [u8] {
  158. if Self::USE_PSS_OID {
  159. Self::RSA_PSS_OID
  160. } else {
  161. Self::RSA_ES_OID
  162. }
  163. }
  164. fn params(&self) -> Result<Option<AlgorithmParameter>> {
  165. if Self::USE_PSS_OID {
  166. let params = RsaSsaPssParams::new(self.hash_kind);
  167. let values = params.encode_ref();
  168. let captured = Captured::from_values(bcder::Mode::Der, values);
  169. Ok(Some(AlgorithmParameter::from_captured(captured)))
  170. } else {
  171. Ok(None)
  172. }
  173. }
  174. fn from_params(sig_octet_len: u32, params: Option<&[u8]>) -> Result<Self> {
  175. let key_bits = BitLen::try_from(sig_octet_len)?;
  176. let hash_kind = match params {
  177. Some(params) => {
  178. let source = SliceSource::new(params);
  179. let params =
  180. Constructed::decode(source, Mode::Der, RsaSsaPssParams::take_from)?;
  181. if let Some(params) = params {
  182. HashKind::from_oid(params.hash_algorithm.as_ref())?
  183. } else {
  184. HashKind::default()
  185. }
  186. }
  187. None => HashKind::default(),
  188. };
  189. Ok(Self {
  190. key_bits,
  191. hash_kind,
  192. })
  193. }
  194. }
  195. impl Sign {
  196. const fn oid(&self) -> &'static [u8] {
  197. match self {
  198. Sign::RsaSsaPss(..) => RsaSsaPss::oid(),
  199. }
  200. }
  201. fn params(&self) -> Result<Option<AlgorithmParameter>> {
  202. match self {
  203. Sign::RsaSsaPss(inner) => inner.params(),
  204. }
  205. }
  206. fn from_der(sig_octet_len: u32, oid: &[u8], params: Option<&[u8]>) -> Result<Sign> {
  207. if oid == RsaSsaPss::oid() {
  208. Ok(Sign::RsaSsaPss(RsaSsaPss::from_params(
  209. sig_octet_len,
  210. params,
  211. )?))
  212. } else {
  213. Err(bterr!("OID does not match a Sign variant"))
  214. }
  215. }
  216. fn to_algo_id(self) -> Result<AlgorithmIdentifier> {
  217. Ok(AlgorithmIdentifier {
  218. algorithm: oid(self.oid()),
  219. parameters: self.params()?,
  220. })
  221. }
  222. fn from_algo_id(sig_octet_len: u32, algo_id: &AlgorithmIdentifier) -> Result<Sign> {
  223. let params = algo_id.parameters.as_ref().map(|e| e.as_ref());
  224. Sign::from_der(sig_octet_len, algo_id.algorithm.as_ref(), params)
  225. }
  226. }
  227. struct SubjectAltName {
  228. block_path: Utf8String,
  229. }
  230. impl SubjectAltName {
  231. /// OID 2.5.29.17 (Subject Alternative Name)
  232. const OID: &[u8] = &[0x55, 0x1D, 0x11];
  233. fn new(block_path: &BlockPath) -> Result<Self> {
  234. let block_path = Utf8String::from_string(block_path.to_string())
  235. .map_err(|err| bterr!("{:?}", err))?;
  236. Ok(Self { block_path })
  237. }
  238. fn encode(&self) -> impl Values + '_ {
  239. self.encode_as(Tag::SEQUENCE)
  240. }
  241. fn encode_as(&self, tag: Tag) -> impl Values + '_ {
  242. bcder::encode::sequence_as(tag, self.block_path.encode_ref())
  243. }
  244. fn take_from<S: bcder::decode::Source>(
  245. cons: &mut Constructed<'_, S>,
  246. ) -> std::result::Result<Self, DecodeError<S::Error>> {
  247. cons.take_sequence(|cons| {
  248. Ok(Self {
  249. block_path: Utf8String::take_from(cons)?,
  250. })
  251. })
  252. }
  253. fn encode_der(&self) -> Result<Bytes> {
  254. let mut writer = BytesMut::new().writer();
  255. self.encode().write_encoded(Mode::Der, &mut writer)?;
  256. Ok(writer.into_inner().into())
  257. }
  258. fn decode_der<B: AsRef<[u8]>>(bytes: B) -> Result<Self> {
  259. let source = SliceSource::new(bytes.as_ref());
  260. Constructed::decode(source, Mode::Der, Self::take_from).map_err(|err| err.into())
  261. }
  262. }
  263. impl AsymKeyPub<Sign> {
  264. fn subject_public_key_info(&self) -> Result<SubjectPublicKeyInfo> {
  265. Ok(SubjectPublicKeyInfo {
  266. algorithm: self.scheme.to_algo_id()?,
  267. subject_public_key: self.to_bit_string()?,
  268. })
  269. }
  270. fn to_bit_string(&self) -> Result<BitString> {
  271. let der = self.pkey.public_key_to_der()?;
  272. let source = BytesSource::new(Bytes::from(der));
  273. let spki = Constructed::decode(source, Mode::Der, SubjectPublicKeyInfo::take_from)?;
  274. Ok(spki.subject_public_key)
  275. }
  276. fn from_subject_public_key_info(
  277. sig_octet_len: u32,
  278. spki: &SubjectPublicKeyInfo,
  279. ) -> Result<Self> {
  280. let scheme = Sign::from_algo_id(sig_octet_len, &spki.algorithm)?;
  281. let mut der = Vec::new();
  282. spki.encode_ref().write_encoded(Mode::Der, &mut der)?;
  283. AsymKeyPub::new(scheme, der.as_slice())
  284. }
  285. pub fn to_der(&self) -> Result<Vec<u8>> {
  286. let spki = self.subject_public_key_info()?;
  287. let mut vec = Vec::new();
  288. spki.encode_ref().write_encoded(Mode::Der, &mut vec)?;
  289. Ok(vec)
  290. }
  291. }
  292. trait NameExt {
  293. fn try_get_common_name(&self) -> Result<&AttributeValue>;
  294. }
  295. impl NameExt for Name {
  296. fn try_get_common_name(&self) -> Result<&AttributeValue> {
  297. Ok(&self
  298. .iter_common_name()
  299. .next()
  300. .ok_or_else(|| bterr!("no CommonName component in Name"))?
  301. .value)
  302. }
  303. }
  304. trait TryAsStr {
  305. fn try_as_str(&self) -> Result<&str>;
  306. }
  307. impl<T: ?Sized + AsRef<[u8]>> TryAsStr for T {
  308. fn try_as_str(&self) -> Result<&str> {
  309. std::str::from_utf8(self.as_ref()).map_err(|err| err.into())
  310. }
  311. }
  312. trait TimeExt {
  313. fn try_to_epoch(&self) -> Result<Epoch>;
  314. }
  315. impl TimeExt for Time {
  316. fn try_to_epoch(&self) -> Result<Epoch> {
  317. match self {
  318. Self::UtcTime(time) => Ok(Epoch::from_value(time.timestamp() as u64)),
  319. Self::GeneralTime(..) => Err(bterr!("unsupported Time variant encountered")),
  320. }
  321. }
  322. }
  323. trait ExtensionsExt {
  324. fn find_subject_alt_name(&self) -> Result<SubjectAltName>;
  325. }
  326. impl ExtensionsExt for Extensions {
  327. fn find_subject_alt_name(&self) -> Result<SubjectAltName> {
  328. let extensions: &[Extension] = self.deref();
  329. for extension in extensions {
  330. if extension.id.as_ref() == SubjectAltName::OID {
  331. return SubjectAltName::decode_der(extension.value.to_bytes());
  332. }
  333. }
  334. Err(bterr!("SubjectAltName not found"))
  335. }
  336. }
  337. impl Principal {
  338. fn to_name(&self) -> Result<Name> {
  339. let mut name = Name::default();
  340. let string = self.to_string();
  341. name.append_common_name_utf8_string(&string)
  342. .map_err(|_| bterr!("failed to create Name for Principal"))?;
  343. Ok(name)
  344. }
  345. fn from_name(name: &Name) -> Result<Self> {
  346. let principal = name
  347. .try_get_common_name()?
  348. .to_string()?
  349. .as_str()
  350. .try_into()?;
  351. Ok(principal)
  352. }
  353. pub fn to_name_der(&self) -> Result<Vec<u8>> {
  354. let name = self.to_name()?;
  355. let mut vec = Vec::new();
  356. name.encode_ref().write_encoded(Mode::Der, &mut vec)?;
  357. Ok(vec)
  358. }
  359. }
  360. impl Writecap {
  361. fn to_cert(&self, subject_key: &AsymKeyPub<Sign>) -> Result<Vec<u8>> {
  362. let version = Some(Version::V3);
  363. let serial_number = CertificateSerialNumber::from(1);
  364. let signature_algorithm = self.body.signing_key.scheme.to_algo_id()?;
  365. let issuer = self.body.signing_key.principal().to_name()?;
  366. let expires = Utc
  367. .timestamp_millis_opt(1000 * self.body.expires.to_unix())
  368. .single()
  369. .ok_or_else(|| {
  370. bterr!("failed to convert writecap expiration to chrono DataTime")
  371. })?;
  372. let validity = Validity {
  373. not_before: Time::UtcTime(UtcTime::now()),
  374. not_after: Time::from(expires),
  375. };
  376. let subject = self.body.issued_to.to_name()?;
  377. let subject_public_key_info = subject_key.subject_public_key_info()?;
  378. let mut extensions = Extensions::default();
  379. let san = SubjectAltName::new(&self.body.path)?;
  380. extensions.push(Extension {
  381. id: oid(SubjectAltName::OID),
  382. critical: Some(false),
  383. value: OctetString::new(san.encode_der()?),
  384. });
  385. let tbs_certificate = TbsCertificate {
  386. version,
  387. serial_number,
  388. signature: signature_algorithm.clone(),
  389. issuer,
  390. validity,
  391. subject,
  392. subject_public_key_info,
  393. issuer_unique_id: None,
  394. subject_unique_id: None,
  395. extensions: Some(extensions),
  396. raw_data: None,
  397. };
  398. let cert = Certificate {
  399. tbs_certificate,
  400. signature_algorithm,
  401. signature: bit_string!(self.signature.data.clone()),
  402. };
  403. let cert: X509Certificate = cert.into();
  404. cert.encode_der().map_err(|err| err.into())
  405. }
  406. fn to_cert_chain_impl(&self, subject_key: &AsymKeyPub<Sign>) -> Result<Vec<Vec<u8>>> {
  407. let mut chain = match self.next.as_ref() {
  408. Some(next) => next.as_ref().to_cert_chain_impl(&self.body.signing_key)?,
  409. None => {
  410. // An extra cert is added to the end of the chain to contain the root
  411. // principal's signing key.
  412. let mut vec = Vec::with_capacity(2);
  413. let root_principal = self.body.signing_key.principal();
  414. let path = BlockPath::new(root_principal.clone(), vec![]);
  415. let writecap = Writecap {
  416. body: WritecapBody {
  417. issued_to: root_principal,
  418. expires: Epoch::now(),
  419. path,
  420. signing_key: self.body.signing_key.clone(),
  421. },
  422. signature: self.signature.clone(),
  423. next: None,
  424. };
  425. vec.push(writecap.to_cert(&self.body.signing_key)?);
  426. vec
  427. }
  428. };
  429. chain.push(self.to_cert(subject_key)?);
  430. Ok(chain)
  431. }
  432. pub fn to_cert_chain(&self, subject_key: &AsymKeyPub<Sign>) -> Result<Vec<Vec<u8>>> {
  433. let mut chain = self.to_cert_chain_impl(subject_key)?;
  434. chain.reverse();
  435. Ok(chain)
  436. }
  437. pub fn from_cert_chain<B: AsRef<[u8]>>(
  438. first: &B,
  439. rest: &[B],
  440. ) -> Result<(Writecap, AsymKeyPub<Sign>)> {
  441. let (next, signing_key) = if !rest.is_empty() {
  442. let (writecap, signing_key) = Self::from_cert_chain(&rest[0], &rest[1..])?;
  443. // Remove the extra writecap at the end.
  444. let writecap = if rest.len() == 1 {
  445. None
  446. } else {
  447. Some(writecap)
  448. };
  449. (writecap, Some(signing_key))
  450. } else {
  451. (None, None)
  452. };
  453. let x509_cert = X509Certificate::from_der(first)?;
  454. let cert: &Certificate = x509_cert.as_ref();
  455. if cert.signature.unused() > 0 {
  456. return Err(bterr!("signature length is not divisible by 8"));
  457. }
  458. let extensions = cert
  459. .tbs_certificate
  460. .extensions
  461. .as_ref()
  462. .ok_or_else(|| bterr!("no extensions present"))?;
  463. let san = extensions.find_subject_alt_name()?;
  464. let path = BlockPath::try_from(san.block_path.into_bytes().try_as_str()?)
  465. .map_err(|err| bterr!(err))?;
  466. let sig_octet_len: u32 = cert.signature.octet_len().try_into()?;
  467. let scheme = Sign::from_algo_id(
  468. sig_octet_len,
  469. &cert.tbs_certificate.subject_public_key_info.algorithm,
  470. )?;
  471. let signature = Signature::new(scheme, cert.signature.octet_bytes().into());
  472. let cert = &cert.tbs_certificate;
  473. let subject_key = AsymKeyPub::from_subject_public_key_info(
  474. sig_octet_len,
  475. &cert.subject_public_key_info,
  476. )?;
  477. let issued_to = Principal::from_name(&cert.subject)?;
  478. let expires = cert.validity.not_after.try_to_epoch()?;
  479. // If signing_key is None, then we're at the last certificate in the chain, which is
  480. // self-signed. So the subject_key is the same as the issuer's signing key.
  481. let signing_key = signing_key.unwrap_or_else(|| subject_key.clone());
  482. let writecap = Writecap {
  483. body: WritecapBody {
  484. issued_to,
  485. signing_key,
  486. path,
  487. expires,
  488. },
  489. signature,
  490. next: next.map(Box::new),
  491. };
  492. Ok((writecap, subject_key))
  493. }
  494. }
  495. }
  496. #[cfg(test)]
  497. mod tests {
  498. use super::*;
  499. use std::time::Duration;
  500. use webpki::EndEntityCert;
  501. use crate::{
  502. crypto::{ConcreteCreds, Creds, CredsPriv, CredsPub},
  503. test_helpers::node_creds,
  504. };
  505. #[allow(dead_code)]
  506. fn save_first_writecap_der_to_file() {
  507. let node_creds = node_creds();
  508. let chain = node_creds
  509. .writecap
  510. .as_ref()
  511. .unwrap()
  512. .to_cert_chain(&node_creds.sign.public)
  513. .unwrap();
  514. let first = chain.first().unwrap();
  515. std::fs::write("/tmp/cert.der", first).unwrap();
  516. }
  517. #[test]
  518. fn node_writecap_to_cert_chain() {
  519. let node_creds = node_creds();
  520. let result = node_creds
  521. .writecap
  522. .as_ref()
  523. .unwrap()
  524. .to_cert_chain(&node_creds.sign.public);
  525. assert!(result.is_ok())
  526. }
  527. #[test]
  528. fn node_writecap_to_cert_chain_end_cert_can_be_parsed() {
  529. let node_creds = node_creds();
  530. let chain = node_creds
  531. .writecap
  532. .as_ref()
  533. .unwrap()
  534. .to_cert_chain(&node_creds.sign.public)
  535. .unwrap();
  536. let der = chain.first().unwrap();
  537. let result = EndEntityCert::try_from(der.as_slice());
  538. result.unwrap();
  539. }
  540. #[test]
  541. fn round_trip_writecap() {
  542. let node_creds = node_creds();
  543. let expected_key = node_creds.public_sign();
  544. let expected_wc = node_creds.writecap().unwrap();
  545. let certs = expected_wc.to_cert_chain(expected_key).unwrap();
  546. let (actual_wc, actual_key) =
  547. Writecap::from_cert_chain(certs.first().unwrap(), &certs[1..]).unwrap();
  548. assert_eq!(expected_key, &actual_key);
  549. assert_eq!(expected_wc, &actual_wc);
  550. actual_wc.assert_valid_for(&expected_wc.body.path).unwrap();
  551. }
  552. #[test]
  553. fn round_trip_chain_of_length_two() {
  554. let node_creds = node_creds();
  555. let mut process_creds = ConcreteCreds::generate().unwrap();
  556. let writecap = node_creds
  557. .issue_writecap(
  558. process_creds.principal(),
  559. vec!["console".to_string()],
  560. Epoch::now() + Duration::from_secs(3600),
  561. )
  562. .unwrap();
  563. process_creds.set_writecap(writecap);
  564. let expected_key = process_creds.public_sign();
  565. let expected_wc = process_creds.writecap().unwrap();
  566. let certs = expected_wc.to_cert_chain(expected_key).unwrap();
  567. let (actual_wc, actual_key) =
  568. Writecap::from_cert_chain(certs.first().unwrap(), &certs[1..]).unwrap();
  569. assert_eq!(expected_key, &actual_key);
  570. assert_eq!(expected_wc, &actual_wc);
  571. actual_wc.assert_valid_for(&expected_wc.body.path).unwrap();
  572. }
  573. }