1use {
8 crate::{
9 AppleCodesignError,
10 remote_signing::{RemoteSignError, session_negotiation::PublicKeyPeerDecrypt},
11 },
12 apple_xar::table_of_contents::ChecksumType as XarChecksumType,
13 aws_lc_rs::signature::{Ed25519KeyPair, KeyPair},
14 bytes::Bytes,
15 clap::ValueEnum,
16 der::{Decode, Document, Encode, SecretDocument, asn1},
17 digest::DynDigest,
18 elliptic_curve::{
19 AffinePoint, Curve, CurveArithmetic, FieldBytesSize, SecretKey as ECSecretKey,
20 sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
21 },
22 oid_registry::{
23 OID_EC_P256, OID_KEY_TYPE_EC_PUBLIC_KEY, OID_PKCS1_RSAENCRYPTION, OID_SIG_ED25519,
24 },
25 p256::NistP256,
26 pkcs1::RsaPrivateKey,
27 pkcs8::{EncodePrivateKey, ObjectIdentifier, PrivateKeyInfo},
28 rsa::{BigUint, Oaep, RsaPrivateKey as RsaConstructedKey, pkcs1::DecodeRsaPrivateKey},
29 signature::Signer,
30 spki::AlgorithmIdentifier,
31 std::{
32 borrow::Cow,
33 cmp::Ordering,
34 fmt::{Display, Formatter},
35 path::Path,
36 },
37 subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
38 x509_certificate::{
39 CapturedX509Certificate, DigestAlgorithm, EcdsaCurve, InMemorySigningKeyPair, KeyAlgorithm,
40 KeyInfoSigner, Sign, Signature, SignatureAlgorithm, X509CertificateError,
41 },
42 zeroize::Zeroizing,
43};
44
45pub trait PrivateKey: KeyInfoSigner {
47 fn as_key_info_signer(&self) -> &dyn KeyInfoSigner;
48
49 fn to_public_key_peer_decrypt(
50 &self,
51 ) -> Result<Box<dyn PublicKeyPeerDecrypt>, AppleCodesignError>;
52
53 fn finish(&self) -> Result<(), AppleCodesignError>;
58}
59
60#[derive(Clone, Debug)]
61pub struct InMemoryRsaKey {
62 private_key: SecretDocument,
64}
65
66impl InMemoryRsaKey {
67 fn from_der(der_data: &[u8]) -> Result<Self, der::Error> {
69 RsaPrivateKey::from_der(der_data)?;
70
71 let private_key = Document::from_der(der_data)?.into_secret();
72
73 Ok(Self { private_key })
74 }
75
76 fn rsa_private_key(&self) -> RsaPrivateKey<'_> {
77 RsaPrivateKey::from_der(self.private_key.as_bytes())
78 .expect("internal content should be PKCS#1 DER private key data")
79 }
80}
81
82impl From<&InMemoryRsaKey> for RsaConstructedKey {
83 fn from(key: &InMemoryRsaKey) -> Self {
84 let key = key.rsa_private_key();
85
86 let n = BigUint::from_bytes_be(key.modulus.as_bytes());
87 let e = BigUint::from_bytes_be(key.public_exponent.as_bytes());
88 let d = BigUint::from_bytes_be(key.private_exponent.as_bytes());
89 let prime1 = BigUint::from_bytes_be(key.prime1.as_bytes());
90 let prime2 = BigUint::from_bytes_be(key.prime2.as_bytes());
91 let primes = vec![prime1, prime2];
92
93 Self::from_components(n, e, d, primes).expect("inputs valid")
94 }
95}
96
97impl TryFrom<InMemoryRsaKey> for InMemorySigningKeyPair {
98 type Error = AppleCodesignError;
99
100 fn try_from(value: InMemoryRsaKey) -> Result<Self, Self::Error> {
101 Ok(Self::from_pkcs8_der(
102 value
103 .to_pkcs8_der()
104 .map_err(|e| {
105 AppleCodesignError::CertificateGeneric(format!(
106 "error converting RSA key to DER: {}",
107 e
108 ))
109 })?
110 .as_bytes(),
111 )?)
112 }
113}
114
115impl EncodePrivateKey for InMemoryRsaKey {
116 fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
117 let raw = PrivateKeyInfo::new(pkcs1::ALGORITHM_ID, self.private_key.as_bytes()).to_der()?;
120
121 Ok(Document::from_der(&raw)?.into_secret())
122 }
123}
124
125impl PublicKeyPeerDecrypt for InMemoryRsaKey {
126 fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, RemoteSignError> {
127 let key = RsaConstructedKey::from_pkcs1_der(self.private_key.as_bytes())
128 .map_err(|e| RemoteSignError::Crypto(format!("failed to parse RSA key: {e}")))?;
129
130 let padding = Oaep::new::<sha2::Sha256>();
131
132 let plaintext = key
133 .decrypt(padding, ciphertext)
134 .map_err(|e| RemoteSignError::Crypto(format!("RSA decryption failure: {e}")))?;
135
136 Ok(plaintext)
137 }
138}
139
140#[derive(Clone, Debug)]
141pub struct InMemoryEcdsaKey<C>
142where
143 C: Curve + CurveArithmetic,
144 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
145 FieldBytesSize<C>: ModulusSize,
146{
147 curve: ObjectIdentifier,
148 secret_key: ECSecretKey<C>,
149}
150
151impl<C> InMemoryEcdsaKey<C>
152where
153 C: Curve + CurveArithmetic,
154 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
155 FieldBytesSize<C>: ModulusSize,
156{
157 pub fn curve(&self) -> Result<EcdsaCurve, AppleCodesignError> {
158 match self.curve.as_bytes() {
159 x if x == OID_EC_P256.as_bytes() => Ok(EcdsaCurve::Secp256r1),
160 _ => Err(AppleCodesignError::CertificateGeneric(format!(
161 "unknown ECDSA curve: {}",
162 self.curve
163 ))),
164 }
165 }
166}
167
168impl<C> TryFrom<InMemoryEcdsaKey<C>> for InMemorySigningKeyPair
169where
170 C: Curve + CurveArithmetic,
171 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
172 FieldBytesSize<C>: ModulusSize,
173{
174 type Error = AppleCodesignError;
175
176 fn try_from(key: InMemoryEcdsaKey<C>) -> Result<Self, Self::Error> {
177 Ok(Self::from_pkcs8_der(
178 key.to_pkcs8_der()
179 .map_err(|e| {
180 AppleCodesignError::CertificateGeneric(format!(
181 "error converting ECDSA key to DER: {}",
182 e
183 ))
184 })?
185 .as_bytes(),
186 )?)
187 }
188}
189
190impl<C> EncodePrivateKey for InMemoryEcdsaKey<C>
191where
192 C: Curve + CurveArithmetic,
193 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
194 FieldBytesSize<C>: ModulusSize,
195{
196 fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
197 let private_key = self.secret_key.to_sec1_der()?;
198
199 PrivateKeyInfo {
200 algorithm: AlgorithmIdentifier {
201 oid: ObjectIdentifier::from_bytes(OID_KEY_TYPE_EC_PUBLIC_KEY.as_bytes())
202 .expect("OID construction should work"),
203 parameters: Some(asn1::AnyRef::from(&self.curve)),
204 },
205 private_key: private_key.as_ref(),
206 public_key: None,
207 }
208 .try_into()
209 }
210}
211
212impl<C> PublicKeyPeerDecrypt for InMemoryEcdsaKey<C>
213where
214 C: Curve + CurveArithmetic,
215 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
216 FieldBytesSize<C>: ModulusSize,
217{
218 fn decrypt(&self, _ciphertext: &[u8]) -> Result<Vec<u8>, RemoteSignError> {
219 Err(RemoteSignError::Crypto(
220 "decryption using ECDSA keys is not yet implemented".into(),
221 ))
222 }
223}
224
225#[derive(Clone, Debug)]
226pub struct InMemoryEd25519Key {
227 private_key: Zeroizing<Vec<u8>>,
228}
229
230impl TryFrom<InMemoryEd25519Key> for InMemorySigningKeyPair {
231 type Error = AppleCodesignError;
232
233 fn try_from(key: InMemoryEd25519Key) -> Result<Self, Self::Error> {
234 Ok(Self::from_pkcs8_der(
235 key.to_pkcs8_der()
236 .map_err(|e| {
237 AppleCodesignError::CertificateGeneric(format!(
238 "error converting ED25519 key to DER: {}",
239 e
240 ))
241 })?
242 .as_bytes(),
243 )?)
244 }
245}
246
247impl EncodePrivateKey for InMemoryEd25519Key {
248 fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
249 let algorithm = AlgorithmIdentifier {
250 oid: ObjectIdentifier::from_bytes(OID_SIG_ED25519.as_bytes()).expect("OID is valid"),
251 parameters: None,
252 };
253
254 let key_ref: &[u8] = self.private_key.as_ref();
255 let value = Zeroizing::new(asn1::OctetString::new(key_ref)?.to_der()?);
256
257 let mut pki = PrivateKeyInfo::new(algorithm, value.as_ref());
258
259 let public_key =
260 if let Ok(key) = Ed25519KeyPair::from_seed_unchecked(self.private_key.as_ref()) {
261 Bytes::copy_from_slice(key.public_key().as_ref())
262 } else {
263 Bytes::new()
264 };
265
266 pki.public_key = Some(public_key.as_ref());
267
268 pki.try_into()
269 }
270}
271
272impl PublicKeyPeerDecrypt for InMemoryEd25519Key {
273 fn decrypt(&self, _ciphertext: &[u8]) -> Result<Vec<u8>, RemoteSignError> {
274 Err(RemoteSignError::Crypto(
275 "decryption using ED25519 keys is not yet implemented".into(),
276 ))
277 }
278}
279
280#[derive(Clone, Debug)]
282pub enum InMemoryPrivateKey {
283 EcdsaP256(InMemoryEcdsaKey<NistP256>),
285 Ed25519(InMemoryEd25519Key),
287 Rsa(InMemoryRsaKey),
289}
290
291impl<'a> TryFrom<PrivateKeyInfo<'a>> for InMemoryPrivateKey {
292 type Error = pkcs8::Error;
293
294 fn try_from(value: PrivateKeyInfo<'a>) -> Result<Self, Self::Error> {
295 match value.algorithm.oid {
296 x if x.as_bytes() == OID_PKCS1_RSAENCRYPTION.as_bytes() => {
297 Ok(Self::Rsa(InMemoryRsaKey::from_der(value.private_key)?))
298 }
299 x if x.as_bytes() == OID_KEY_TYPE_EC_PUBLIC_KEY.as_bytes() => {
300 let curve_oid = value.algorithm.parameters_oid()?;
301
302 match curve_oid.as_bytes() {
303 x if x == OID_EC_P256.as_bytes() => {
304 let secret_key = ECSecretKey::<NistP256>::try_from(value)?;
305
306 Ok(Self::EcdsaP256(InMemoryEcdsaKey {
307 curve: curve_oid,
308 secret_key,
309 }))
310 }
311 _ => Err(pkcs8::Error::ParametersMalformed),
312 }
313 }
314 x if x.as_bytes() == OID_SIG_ED25519.as_bytes() => {
315 Ok(Self::Ed25519(InMemoryEd25519Key {
317 private_key: Zeroizing::new((value.private_key[2..]).to_vec()),
318 }))
319 }
320 _ => Err(pkcs8::Error::KeyMalformed),
321 }
322 }
323}
324
325impl TryFrom<InMemoryPrivateKey> for InMemorySigningKeyPair {
326 type Error = AppleCodesignError;
327
328 fn try_from(key: InMemoryPrivateKey) -> Result<Self, Self::Error> {
329 match key {
330 InMemoryPrivateKey::Rsa(key) => key.try_into(),
331 InMemoryPrivateKey::EcdsaP256(key) => key.try_into(),
332 InMemoryPrivateKey::Ed25519(key) => key.try_into(),
333 }
334 }
335}
336
337impl EncodePrivateKey for InMemoryPrivateKey {
338 fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
339 match self {
340 Self::EcdsaP256(key) => key.to_pkcs8_der(),
341 Self::Ed25519(key) => key.to_pkcs8_der(),
342 Self::Rsa(key) => key.to_pkcs8_der(),
343 }
344 }
345}
346
347impl Signer<Signature> for InMemoryPrivateKey {
348 fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> {
349 let key_pair = InMemorySigningKeyPair::try_from(self.clone())
350 .map_err(signature::Error::from_source)?;
351
352 key_pair.try_sign(msg)
353 }
354}
355
356impl Sign for InMemoryPrivateKey {
357 fn sign(&self, message: &[u8]) -> Result<(Vec<u8>, SignatureAlgorithm), X509CertificateError> {
358 let algorithm = self.signature_algorithm()?;
359
360 Ok((self.try_sign(message)?.into(), algorithm))
361 }
362
363 fn key_algorithm(&self) -> Option<KeyAlgorithm> {
364 Some(match self {
365 Self::EcdsaP256(_) => KeyAlgorithm::Ecdsa(EcdsaCurve::Secp256r1),
366 Self::Ed25519(_) => KeyAlgorithm::Ed25519,
367 Self::Rsa(_) => KeyAlgorithm::Rsa,
368 })
369 }
370
371 fn public_key_data(&self) -> Bytes {
372 match self {
373 Self::EcdsaP256(key) => Bytes::copy_from_slice(
374 key.secret_key
375 .public_key()
376 .to_encoded_point(false)
377 .as_bytes(),
378 ),
379 Self::Ed25519(key) => {
380 if let Ok(key) = Ed25519KeyPair::from_seed_unchecked(key.private_key.as_ref()) {
381 Bytes::copy_from_slice(key.public_key().as_ref())
382 } else {
383 Bytes::new()
384 }
385 }
386 Self::Rsa(key) => {
387 let key = key.rsa_private_key();
388
389 Bytes::copy_from_slice(
390 key.public_key()
391 .to_der()
392 .expect("RSA public key DER encoding should not fail")
393 .as_ref(),
394 )
395 }
396 }
397 }
398
399 fn signature_algorithm(&self) -> Result<SignatureAlgorithm, X509CertificateError> {
400 Ok(match self {
401 Self::EcdsaP256(_) => SignatureAlgorithm::EcdsaSha256,
402 Self::Ed25519(_) => SignatureAlgorithm::Ed25519,
403 Self::Rsa(_) => SignatureAlgorithm::RsaSha256,
404 })
405 }
406
407 fn private_key_data(&self) -> Option<Zeroizing<Vec<u8>>> {
408 match self {
409 Self::EcdsaP256(key) => Some(Zeroizing::new(key.secret_key.to_bytes().to_vec())),
410 Self::Ed25519(key) => Some(Zeroizing::new((*key.private_key).clone())),
411 Self::Rsa(key) => Some(Zeroizing::new(key.private_key.as_bytes().to_vec())),
412 }
413 }
414
415 fn rsa_primes(
416 &self,
417 ) -> Result<Option<(Zeroizing<Vec<u8>>, Zeroizing<Vec<u8>>)>, X509CertificateError> {
418 if let Self::Rsa(key) = self {
419 let key = key.rsa_private_key();
420
421 Ok(Some((
422 Zeroizing::new(key.prime1.as_bytes().to_vec()),
423 Zeroizing::new(key.prime2.as_bytes().to_vec()),
424 )))
425 } else {
426 Ok(None)
427 }
428 }
429}
430
431impl KeyInfoSigner for InMemoryPrivateKey {}
432
433impl PublicKeyPeerDecrypt for InMemoryPrivateKey {
434 fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, RemoteSignError> {
435 match self {
436 Self::Rsa(key) => key.decrypt(ciphertext),
437 Self::EcdsaP256(key) => key.decrypt(ciphertext),
438 Self::Ed25519(key) => key.decrypt(ciphertext),
439 }
440 }
441}
442
443impl PrivateKey for InMemoryPrivateKey {
444 fn as_key_info_signer(&self) -> &dyn KeyInfoSigner {
445 self
446 }
447
448 fn to_public_key_peer_decrypt(
449 &self,
450 ) -> Result<Box<dyn PublicKeyPeerDecrypt>, AppleCodesignError> {
451 Ok(Box::new(self.clone()))
452 }
453
454 fn finish(&self) -> Result<(), AppleCodesignError> {
455 Ok(())
456 }
457}
458
459impl InMemoryPrivateKey {
460 pub fn from_pkcs1_der(data: impl AsRef<[u8]>) -> Result<Self, AppleCodesignError> {
462 let key = InMemoryRsaKey::from_der(data.as_ref()).map_err(|e| {
463 AppleCodesignError::CertificateGeneric(format!("when parsing PKCS#1 data: {e}"))
464 })?;
465
466 Ok(Self::Rsa(key))
467 }
468
469 pub fn from_pkcs8_der(data: impl AsRef<[u8]>) -> Result<Self, AppleCodesignError> {
471 let pki = PrivateKeyInfo::try_from(data.as_ref()).map_err(|e| {
472 AppleCodesignError::CertificateGeneric(format!("when parsing PKCS#8 data: {e}"))
473 })?;
474
475 pki.try_into().map_err(|e| {
476 AppleCodesignError::CertificateGeneric(format!(
477 "when converting parsed PKCS#8 to a private key: {e}"
478 ))
479 })
480 }
481}
482
483#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum, Default)]
485pub enum DigestType {
486 None,
487 Sha1,
488 #[default]
489 Sha256,
490 Sha256Truncated,
491 Sha384,
492 Sha512,
493 #[value(skip)]
494 Unknown(u8),
495}
496
497impl TryFrom<DigestType> for DigestAlgorithm {
498 type Error = AppleCodesignError;
499
500 fn try_from(value: DigestType) -> Result<DigestAlgorithm, Self::Error> {
501 match value {
502 DigestType::Sha1 => Ok(DigestAlgorithm::Sha1),
503 DigestType::Sha256 => Ok(DigestAlgorithm::Sha256),
504 DigestType::Sha256Truncated => Ok(DigestAlgorithm::Sha256),
505 DigestType::Sha384 => Ok(DigestAlgorithm::Sha384),
506 DigestType::Sha512 => Ok(DigestAlgorithm::Sha512),
507 DigestType::Unknown(_) => Err(AppleCodesignError::DigestUnknownAlgorithm),
508 DigestType::None => Err(AppleCodesignError::DigestUnsupportedAlgorithm),
509 }
510 }
511}
512
513impl PartialOrd for DigestType {
514 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
515 Some(self.cmp(other))
516 }
517}
518
519impl Ord for DigestType {
520 fn cmp(&self, other: &Self) -> Ordering {
521 u8::from(*self).cmp(&u8::from(*other))
522 }
523}
524
525impl Display for DigestType {
526 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
527 match self {
528 DigestType::None => f.write_str("none"),
529 DigestType::Sha1 => f.write_str("sha1"),
530 DigestType::Sha256 => f.write_str("sha256"),
531 DigestType::Sha256Truncated => f.write_str("sha256-truncated"),
532 DigestType::Sha384 => f.write_str("sha384"),
533 DigestType::Sha512 => f.write_str("sha512"),
534 DigestType::Unknown(v) => f.write_fmt(format_args!("unknown: {v}")),
535 }
536 }
537}
538
539impl TryFrom<&str> for DigestType {
540 type Error = AppleCodesignError;
541
542 fn try_from(s: &str) -> Result<Self, Self::Error> {
543 match s {
544 "none" => Ok(Self::None),
545 "sha1" => Ok(Self::Sha1),
546 "sha256" => Ok(Self::Sha256),
547 "sha256-truncated" => Ok(Self::Sha256Truncated),
548 "sha384" => Ok(Self::Sha384),
549 "sha512" => Ok(Self::Sha512),
550 _ => Err(AppleCodesignError::DigestUnknownAlgorithm),
551 }
552 }
553}
554
555impl TryFrom<XarChecksumType> for DigestType {
556 type Error = AppleCodesignError;
557
558 fn try_from(c: XarChecksumType) -> Result<Self, Self::Error> {
559 match c {
560 XarChecksumType::None => Ok(Self::None),
561 XarChecksumType::Sha1 => Ok(Self::Sha1),
562 XarChecksumType::Sha256 => Ok(Self::Sha256),
563 XarChecksumType::Sha512 => Ok(Self::Sha512),
564 XarChecksumType::Md5 => Err(AppleCodesignError::DigestUnsupportedAlgorithm),
565 }
566 }
567}
568
569impl DigestType {
570 pub fn hash_len(&self) -> Result<usize, AppleCodesignError> {
572 Ok(self.digest_data(&[])?.len())
573 }
574
575 pub fn as_hasher(&self) -> Result<aws_lc_rs::digest::Context, AppleCodesignError> {
577 match self {
578 Self::None => Err(AppleCodesignError::DigestUnknownAlgorithm),
579 Self::Sha1 => Ok(aws_lc_rs::digest::Context::new(
580 &aws_lc_rs::digest::SHA1_FOR_LEGACY_USE_ONLY,
581 )),
582 Self::Sha256 | Self::Sha256Truncated => {
583 Ok(aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA256))
584 }
585 Self::Sha384 => Ok(aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA384)),
586 Self::Sha512 => Ok(aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA512)),
587 Self::Unknown(_) => Err(AppleCodesignError::DigestUnknownAlgorithm),
588 }
589 }
590
591 pub fn digest_data(&self, data: &[u8]) -> Result<Vec<u8>, AppleCodesignError> {
593 let mut hasher = self.as_hasher()?;
594
595 hasher.update(data);
596 let mut hash = hasher.finish().as_ref().to_vec();
597
598 if matches!(self, Self::Sha256Truncated) {
599 hash.truncate(20);
600 }
601
602 Ok(hash)
603 }
604}
605
606pub struct Digest<'a> {
607 pub data: Cow<'a, [u8]>,
608}
609
610impl<'a> Digest<'a> {
611 pub fn is_null(&self) -> bool {
613 self.data.iter().all(|b| *b == 0)
614 }
615
616 pub fn to_vec(&self) -> Vec<u8> {
617 self.data.to_vec()
618 }
619
620 pub fn to_owned(&self) -> Digest<'static> {
621 Digest {
622 data: Cow::Owned(self.data.clone().into_owned()),
623 }
624 }
625
626 pub fn as_hex(&self) -> String {
627 hex::encode(&self.data)
628 }
629}
630
631impl<'a> std::fmt::Debug for Digest<'a> {
632 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
633 f.write_str(&hex::encode(&self.data))
634 }
635}
636
637impl<'a> From<Vec<u8>> for Digest<'a> {
638 fn from(v: Vec<u8>) -> Self {
639 Self { data: v.into() }
640 }
641}
642
643pub struct MultiDigest {
645 pub sha1: Digest<'static>,
646 pub sha256: Digest<'static>,
647}
648
649impl MultiDigest {
650 pub fn from_reader(mut reader: impl std::io::Read) -> Result<Self, AppleCodesignError> {
654 let mut sha1 = DigestType::Sha1.as_hasher()?;
655 let mut sha256 = DigestType::Sha256.as_hasher()?;
656
657 let mut buffer = [0u8; 16384];
658
659 loop {
660 let read = reader.read(&mut buffer)?;
661 if read == 0 {
662 break;
663 }
664
665 sha1.update(&buffer[0..read]);
666 sha256.update(&buffer[0..read]);
667 }
668
669 let sha1 = sha1.finish().as_ref().to_vec();
670 let sha256 = sha256.finish().as_ref().to_vec();
671
672 Ok(Self {
673 sha1: sha1.into(),
674 sha256: sha256.into(),
675 })
676 }
677
678 pub fn from_path(path: impl AsRef<Path>) -> Result<Self, AppleCodesignError> {
680 let fh = std::fs::File::open(path.as_ref())?;
681 Self::from_reader(fh)
682 }
683}
684
685fn bmp_string(s: &str) -> Vec<u8> {
686 let utf16: Vec<u16> = s.encode_utf16().collect();
687
688 let mut bytes = Vec::with_capacity(utf16.len() * 2 + 2);
689 for c in utf16 {
690 bytes.push((c / 256) as u8);
691 bytes.push((c % 256) as u8);
692 }
693 bytes.push(0x00);
694 bytes.push(0x00);
695
696 bytes
697}
698
699pub fn parse_pfx_data(
708 data: &[u8],
709 password: &str,
710) -> Result<(CapturedX509Certificate, InMemoryPrivateKey), AppleCodesignError> {
711 let pfx = p12::PFX::parse(data).map_err(|e| {
712 AppleCodesignError::PfxParseError(format!("data does not appear to be PFX: {e:?}"))
713 })?;
714
715 if !pfx.verify_mac(password) {
716 return Err(AppleCodesignError::PfxBadPassword);
717 }
718
719 let data = match pfx.auth_safe {
722 p12::ContentInfo::Data(data) => data,
723 _ => {
724 return Err(AppleCodesignError::PfxParseError(
725 "unexpected PFX content info".to_string(),
726 ));
727 }
728 };
729
730 let content_infos = yasna::parse_der(&data, |reader| {
731 reader.collect_sequence_of(p12::ContentInfo::parse)
732 })
733 .map_err(|e| {
734 AppleCodesignError::PfxParseError(format!("failed parsing inner ContentInfo: {e:?}"))
735 })?;
736
737 let bmp_password = bmp_string(password);
738
739 let mut certificate = None;
740 let mut signing_key = None;
741
742 for content in content_infos {
743 let bags_data = match content {
744 p12::ContentInfo::Data(inner) => inner,
745 p12::ContentInfo::EncryptedData(encrypted) => {
746 encrypted.data(&bmp_password).ok_or_else(|| {
747 AppleCodesignError::PfxParseError(
748 "failed decrypting inner EncryptedData".to_string(),
749 )
750 })?
751 }
752 p12::ContentInfo::OtherContext(_) => {
753 return Err(AppleCodesignError::PfxParseError(
754 "unexpected OtherContent content in inner PFX data".to_string(),
755 ));
756 }
757 };
758
759 let bags = yasna::parse_ber(&bags_data, |reader| {
760 reader.collect_sequence_of(p12::SafeBag::parse)
761 })
762 .map_err(|e| {
763 AppleCodesignError::PfxParseError(format!(
764 "failed parsing SafeBag within inner Data: {e:?}"
765 ))
766 })?;
767
768 for bag in bags {
769 match bag.bag {
770 p12::SafeBagKind::CertBag(cert_bag) => match cert_bag {
771 p12::CertBag::X509(cert_data) => {
772 certificate = Some(CapturedX509Certificate::from_der(cert_data)?);
773 }
774 p12::CertBag::SDSI(_) => {
775 return Err(AppleCodesignError::PfxParseError(
776 "unexpected SDSI certificate data".to_string(),
777 ));
778 }
779 },
780 p12::SafeBagKind::Pkcs8ShroudedKeyBag(key_bag) => {
781 let decrypted = key_bag.decrypt(&bmp_password).ok_or_else(|| {
782 AppleCodesignError::PfxParseError(
783 "error decrypting PKCS8 shrouded key bag; is the password correct?"
784 .to_string(),
785 )
786 })?;
787
788 signing_key = Some(InMemoryPrivateKey::from_pkcs8_der(decrypted)?);
789 }
790 p12::SafeBagKind::OtherBagKind(_) => {
791 return Err(AppleCodesignError::PfxParseError(
792 "unexpected bag type in inner PFX content".to_string(),
793 ));
794 }
795 }
796 }
797 }
798
799 match (certificate, signing_key) {
800 (Some(certificate), Some(signing_key)) => Ok((certificate, signing_key)),
801 (None, Some(_)) => Err(AppleCodesignError::PfxParseError(
802 "failed to find x509 certificate in PFX data".to_string(),
803 )),
804 (_, None) => Err(AppleCodesignError::PfxParseError(
805 "failed to find signing key in PFX data".to_string(),
806 )),
807 }
808}
809
810#[allow(unused)]
821pub(crate) fn rsa_oaep_post_decrypt_decode(
822 modulus_length_bytes: usize,
823 mut em: Vec<u8>,
824 digest: &mut dyn digest::DynDigest,
825 mgf_digest: &mut dyn digest::DynDigest,
826 label: Option<String>,
827) -> Result<Vec<u8>, rsa::errors::Error> {
828 let k = modulus_length_bytes;
829 let digest_len = digest.output_size();
830
831 let label = label.unwrap_or_default();
835 digest.update(label.as_bytes());
836 let label_digest = digest.finalize_reset();
837
838 let (y, remaining) = em.split_at_mut(1);
840 let (masked_seed, masked_db) = remaining.split_at_mut(digest_len);
841
842 if masked_seed.len() != digest_len || masked_db.len() != k - digest_len - 1 {
843 return Err(rsa::errors::Error::Decryption);
844 }
845
846 mgf1_xor(masked_seed, mgf_digest, masked_db);
848 mgf1_xor(masked_db, mgf_digest, masked_seed);
849
850 let digests_equivalent = masked_db[0..digest_len].ct_eq(label_digest.as_ref());
857
858 let mut looking_for_index = Choice::from(1u8);
859 let mut index = 0u32;
860 let mut padding_invalid = Choice::from(0u8);
861
862 for (i, value) in masked_db.iter().skip(digest_len).enumerate() {
863 let is_zero = value.ct_eq(&0u8);
864 let is_one = value.ct_eq(&1u8);
865
866 index.conditional_assign(&(i as u32), looking_for_index & is_one);
867 looking_for_index &= !is_one;
868 padding_invalid |= looking_for_index & !is_zero;
869 }
870
871 let y_is_zero = y[0].ct_eq(&0u8);
872
873 let valid = y_is_zero & digests_equivalent & !padding_invalid & !looking_for_index;
874
875 let res = CtOption::new((em, index + 2 + (digest_len * 2) as u32), valid);
876
877 if res.is_none().into() {
878 return Err(rsa::errors::Error::Decryption);
879 }
880
881 let (out, index) = res.unwrap();
882
883 Ok(out[index as usize..].to_vec())
884}
885
886fn inc_counter(counter: &mut [u8; 4]) {
887 for i in (0..4).rev() {
888 counter[i] = counter[i].wrapping_add(1);
889 if counter[i] != 0 {
890 return;
892 }
893 }
894}
895
896fn mgf1_xor(out: &mut [u8], digest: &mut dyn DynDigest, seed: &[u8]) {
897 let mut counter = [0u8; 4];
898 let mut i = 0;
899
900 const MAX_LEN: u64 = core::u32::MAX as u64 + 1;
901 assert!(out.len() as u64 <= MAX_LEN);
902
903 while i < out.len() {
904 let mut digest_input = vec![0u8; seed.len() + 4];
905 digest_input[0..seed.len()].copy_from_slice(seed);
906 digest_input[seed.len()..].copy_from_slice(&counter);
907
908 digest.update(digest_input.as_slice());
909 let digest_output = &*digest.finalize_reset();
910 let mut j = 0;
911 loop {
912 if j >= digest_output.len() || i >= out.len() {
913 break;
914 }
915
916 out[i] ^= digest_output[j];
917 j += 1;
918 i += 1;
919 }
920 inc_counter(&mut counter);
921 }
922}
923
924#[cfg(test)]
925mod test {
926 use {
927 super::*,
928 aws_lc_rs::signature::{EcdsaKeyPair, KeyPair, RsaKeyPair},
929 x509_certificate::Sign,
930 };
931
932 const RSA_2048_PKCS8_DER: &[u8] = include_bytes!("testdata/rsa-2048.pk8");
933 const ED25519_PKCS8_DER: &[u8] = include_bytes!("testdata/ed25519.pk8");
934 const SECP256_PKCS8_DER: &[u8] = include_bytes!("testdata/secp256r1.pk8");
935
936 #[test]
937 fn parse_keychain_p12_export() {
938 let data = include_bytes!("apple-codesign-testuser.p12");
939
940 let err = parse_pfx_data(data, "bad-password").unwrap_err();
941 assert!(matches!(err, AppleCodesignError::PfxBadPassword));
942
943 parse_pfx_data(data, "password123").unwrap();
944 }
945
946 #[test]
947 fn rsa_key_operations() -> Result<(), AppleCodesignError> {
948 let ring_key = RsaKeyPair::from_pkcs8(RSA_2048_PKCS8_DER).unwrap();
949 let ring_public_key_data = ring_key.public_key().as_ref();
950
951 let pki = PrivateKeyInfo::from_der(RSA_2048_PKCS8_DER).unwrap();
952 let key = InMemoryPrivateKey::try_from(pki).unwrap();
953
954 assert_eq!(key.to_pkcs8_der().unwrap().as_bytes(), RSA_2048_PKCS8_DER);
955
956 let our_key = InMemorySigningKeyPair::try_from(key)?;
957 let our_public_key = our_key.public_key_data();
958
959 assert_eq!(our_public_key.as_ref(), ring_public_key_data);
960
961 InMemoryPrivateKey::from_pkcs8_der(RSA_2048_PKCS8_DER)?;
962
963 let random_key = rsa::RsaPrivateKey::new(&mut rand::thread_rng(), 2048).unwrap();
964 let random_key_pkcs8 = random_key.to_pkcs8_der().unwrap();
965 InMemorySigningKeyPair::from_pkcs8_der(random_key_pkcs8.as_bytes())?;
966
967 Ok(())
968 }
969
970 #[test]
971 fn ed25519_key_operations() -> Result<(), AppleCodesignError> {
972 let pki = PrivateKeyInfo::from_der(ED25519_PKCS8_DER).unwrap();
973 let seed = &pki.private_key[2..];
974 let key = InMemoryPrivateKey::try_from(pki).unwrap();
975
976 assert!(
977 InMemorySigningKeyPair::from_pkcs8_der(ED25519_PKCS8_DER).is_err(),
978 "stored key doesn't have public key, which ring rejects loading"
979 );
980
981 InMemorySigningKeyPair::from_pkcs8_der(key.to_pkcs8_der().unwrap().as_bytes()).unwrap();
983
984 let our_key = InMemorySigningKeyPair::try_from(key)?;
985 let our_public_key = our_key.public_key_data();
986
987 let ring_key = Ed25519KeyPair::from_seed_unchecked(seed).unwrap();
988 let ring_public_key_data = ring_key.public_key().as_ref();
989
990 assert_eq!(our_public_key.as_ref(), ring_public_key_data);
991
992 InMemoryPrivateKey::from_pkcs8_der(ED25519_PKCS8_DER)?;
993
994 Ok(())
995 }
996
997 #[test]
998 fn ecdsa_key_operations_secp256() -> Result<(), AppleCodesignError> {
999 let ring_key = EcdsaKeyPair::from_pkcs8(
1000 &aws_lc_rs::signature::ECDSA_P256_SHA256_ASN1_SIGNING,
1001 SECP256_PKCS8_DER,
1002 )
1003 .unwrap();
1004 let ring_public_key_data = ring_key.public_key().as_ref();
1005
1006 let pki = PrivateKeyInfo::from_der(SECP256_PKCS8_DER).unwrap();
1007 let key = InMemoryPrivateKey::try_from(pki).unwrap();
1008
1009 assert_eq!(key.to_pkcs8_der().unwrap().as_bytes(), SECP256_PKCS8_DER);
1010
1011 InMemorySigningKeyPair::from_pkcs8_der(SECP256_PKCS8_DER)?;
1012 let our_key = InMemorySigningKeyPair::try_from(key)?;
1013 let our_public_key = our_key.public_key_data();
1014
1015 assert_eq!(our_public_key.as_ref(), ring_public_key_data);
1016
1017 InMemoryPrivateKey::from_pkcs8_der(SECP256_PKCS8_DER)?;
1018
1019 Ok(())
1020 }
1021}