1use {
8 crate::{
9 remote_signing::{session_negotiation::PublicKeyPeerDecrypt, RemoteSignError},
10 AppleCodesignError,
11 },
12 apple_xar::table_of_contents::ChecksumType as XarChecksumType,
13 bytes::Bytes,
14 clap::ValueEnum,
15 der::{asn1, Decode, Document, Encode, SecretDocument},
16 digest::DynDigest,
17 elliptic_curve::{
18 sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
19 AffinePoint, Curve, CurveArithmetic, FieldBytesSize, SecretKey as ECSecretKey,
20 },
21 oid_registry::{
22 OID_EC_P256, OID_KEY_TYPE_EC_PUBLIC_KEY, OID_PKCS1_RSAENCRYPTION, OID_SIG_ED25519,
23 },
24 p256::NistP256,
25 pkcs1::RsaPrivateKey,
26 pkcs8::{EncodePrivateKey, ObjectIdentifier, PrivateKeyInfo},
27 ring::signature::{Ed25519KeyPair, KeyPair},
28 rsa::{pkcs1::DecodeRsaPrivateKey, BigUint, Oaep, RsaPrivateKey as RsaConstructedKey},
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)]
485pub enum DigestType {
486 None,
487 Sha1,
488 Sha256,
489 Sha256Truncated,
490 Sha384,
491 Sha512,
492 #[value(skip)]
493 Unknown(u8),
494}
495
496impl Default for DigestType {
497 fn default() -> Self {
498 Self::Sha256
499 }
500}
501
502impl TryFrom<DigestType> for DigestAlgorithm {
503 type Error = AppleCodesignError;
504
505 fn try_from(value: DigestType) -> Result<DigestAlgorithm, Self::Error> {
506 match value {
507 DigestType::Sha1 => Ok(DigestAlgorithm::Sha1),
508 DigestType::Sha256 => Ok(DigestAlgorithm::Sha256),
509 DigestType::Sha256Truncated => Ok(DigestAlgorithm::Sha256),
510 DigestType::Sha384 => Ok(DigestAlgorithm::Sha384),
511 DigestType::Sha512 => Ok(DigestAlgorithm::Sha512),
512 DigestType::Unknown(_) => Err(AppleCodesignError::DigestUnknownAlgorithm),
513 DigestType::None => Err(AppleCodesignError::DigestUnsupportedAlgorithm),
514 }
515 }
516}
517
518impl PartialOrd for DigestType {
519 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
520 Some(self.cmp(other))
521 }
522}
523
524impl Ord for DigestType {
525 fn cmp(&self, other: &Self) -> Ordering {
526 u8::from(*self).cmp(&u8::from(*other))
527 }
528}
529
530impl Display for DigestType {
531 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
532 match self {
533 DigestType::None => f.write_str("none"),
534 DigestType::Sha1 => f.write_str("sha1"),
535 DigestType::Sha256 => f.write_str("sha256"),
536 DigestType::Sha256Truncated => f.write_str("sha256-truncated"),
537 DigestType::Sha384 => f.write_str("sha384"),
538 DigestType::Sha512 => f.write_str("sha512"),
539 DigestType::Unknown(v) => f.write_fmt(format_args!("unknown: {v}")),
540 }
541 }
542}
543
544impl TryFrom<&str> for DigestType {
545 type Error = AppleCodesignError;
546
547 fn try_from(s: &str) -> Result<Self, Self::Error> {
548 match s {
549 "none" => Ok(Self::None),
550 "sha1" => Ok(Self::Sha1),
551 "sha256" => Ok(Self::Sha256),
552 "sha256-truncated" => Ok(Self::Sha256Truncated),
553 "sha384" => Ok(Self::Sha384),
554 "sha512" => Ok(Self::Sha512),
555 _ => Err(AppleCodesignError::DigestUnknownAlgorithm),
556 }
557 }
558}
559
560impl TryFrom<XarChecksumType> for DigestType {
561 type Error = AppleCodesignError;
562
563 fn try_from(c: XarChecksumType) -> Result<Self, Self::Error> {
564 match c {
565 XarChecksumType::None => Ok(Self::None),
566 XarChecksumType::Sha1 => Ok(Self::Sha1),
567 XarChecksumType::Sha256 => Ok(Self::Sha256),
568 XarChecksumType::Sha512 => Ok(Self::Sha512),
569 XarChecksumType::Md5 => Err(AppleCodesignError::DigestUnsupportedAlgorithm),
570 }
571 }
572}
573
574impl DigestType {
575 pub fn hash_len(&self) -> Result<usize, AppleCodesignError> {
577 Ok(self.digest_data(&[])?.len())
578 }
579
580 pub fn as_hasher(&self) -> Result<ring::digest::Context, AppleCodesignError> {
582 match self {
583 Self::None => Err(AppleCodesignError::DigestUnknownAlgorithm),
584 Self::Sha1 => Ok(ring::digest::Context::new(
585 &ring::digest::SHA1_FOR_LEGACY_USE_ONLY,
586 )),
587 Self::Sha256 | Self::Sha256Truncated => {
588 Ok(ring::digest::Context::new(&ring::digest::SHA256))
589 }
590 Self::Sha384 => Ok(ring::digest::Context::new(&ring::digest::SHA384)),
591 Self::Sha512 => Ok(ring::digest::Context::new(&ring::digest::SHA512)),
592 Self::Unknown(_) => Err(AppleCodesignError::DigestUnknownAlgorithm),
593 }
594 }
595
596 pub fn digest_data(&self, data: &[u8]) -> Result<Vec<u8>, AppleCodesignError> {
598 let mut hasher = self.as_hasher()?;
599
600 hasher.update(data);
601 let mut hash = hasher.finish().as_ref().to_vec();
602
603 if matches!(self, Self::Sha256Truncated) {
604 hash.truncate(20);
605 }
606
607 Ok(hash)
608 }
609}
610
611pub struct Digest<'a> {
612 pub data: Cow<'a, [u8]>,
613}
614
615impl<'a> Digest<'a> {
616 pub fn is_null(&self) -> bool {
618 self.data.iter().all(|b| *b == 0)
619 }
620
621 pub fn to_vec(&self) -> Vec<u8> {
622 self.data.to_vec()
623 }
624
625 pub fn to_owned(&self) -> Digest<'static> {
626 Digest {
627 data: Cow::Owned(self.data.clone().into_owned()),
628 }
629 }
630
631 pub fn as_hex(&self) -> String {
632 hex::encode(&self.data)
633 }
634}
635
636impl<'a> std::fmt::Debug for Digest<'a> {
637 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
638 f.write_str(&hex::encode(&self.data))
639 }
640}
641
642impl<'a> From<Vec<u8>> for Digest<'a> {
643 fn from(v: Vec<u8>) -> Self {
644 Self { data: v.into() }
645 }
646}
647
648pub struct MultiDigest {
650 pub sha1: Digest<'static>,
651 pub sha256: Digest<'static>,
652}
653
654impl MultiDigest {
655 pub fn from_reader(mut reader: impl std::io::Read) -> Result<Self, AppleCodesignError> {
659 let mut sha1 = DigestType::Sha1.as_hasher()?;
660 let mut sha256 = DigestType::Sha256.as_hasher()?;
661
662 let mut buffer = [0u8; 16384];
663
664 loop {
665 let read = reader.read(&mut buffer)?;
666 if read == 0 {
667 break;
668 }
669
670 sha1.update(&buffer[0..read]);
671 sha256.update(&buffer[0..read]);
672 }
673
674 let sha1 = sha1.finish().as_ref().to_vec();
675 let sha256 = sha256.finish().as_ref().to_vec();
676
677 Ok(Self {
678 sha1: sha1.into(),
679 sha256: sha256.into(),
680 })
681 }
682
683 pub fn from_path(path: impl AsRef<Path>) -> Result<Self, AppleCodesignError> {
685 let fh = std::fs::File::open(path.as_ref())?;
686 Self::from_reader(fh)
687 }
688}
689
690fn bmp_string(s: &str) -> Vec<u8> {
691 let utf16: Vec<u16> = s.encode_utf16().collect();
692
693 let mut bytes = Vec::with_capacity(utf16.len() * 2 + 2);
694 for c in utf16 {
695 bytes.push((c / 256) as u8);
696 bytes.push((c % 256) as u8);
697 }
698 bytes.push(0x00);
699 bytes.push(0x00);
700
701 bytes
702}
703
704pub fn parse_pfx_data(
713 data: &[u8],
714 password: &str,
715) -> Result<(CapturedX509Certificate, InMemoryPrivateKey), AppleCodesignError> {
716 let pfx = p12::PFX::parse(data).map_err(|e| {
717 AppleCodesignError::PfxParseError(format!("data does not appear to be PFX: {e:?}"))
718 })?;
719
720 if !pfx.verify_mac(password) {
721 return Err(AppleCodesignError::PfxBadPassword);
722 }
723
724 let data = match pfx.auth_safe {
727 p12::ContentInfo::Data(data) => data,
728 _ => {
729 return Err(AppleCodesignError::PfxParseError(
730 "unexpected PFX content info".to_string(),
731 ));
732 }
733 };
734
735 let content_infos = yasna::parse_der(&data, |reader| {
736 reader.collect_sequence_of(p12::ContentInfo::parse)
737 })
738 .map_err(|e| {
739 AppleCodesignError::PfxParseError(format!("failed parsing inner ContentInfo: {e:?}"))
740 })?;
741
742 let bmp_password = bmp_string(password);
743
744 let mut certificate = None;
745 let mut signing_key = None;
746
747 for content in content_infos {
748 let bags_data = match content {
749 p12::ContentInfo::Data(inner) => inner,
750 p12::ContentInfo::EncryptedData(encrypted) => {
751 encrypted.data(&bmp_password).ok_or_else(|| {
752 AppleCodesignError::PfxParseError(
753 "failed decrypting inner EncryptedData".to_string(),
754 )
755 })?
756 }
757 p12::ContentInfo::OtherContext(_) => {
758 return Err(AppleCodesignError::PfxParseError(
759 "unexpected OtherContent content in inner PFX data".to_string(),
760 ));
761 }
762 };
763
764 let bags = yasna::parse_ber(&bags_data, |reader| {
765 reader.collect_sequence_of(p12::SafeBag::parse)
766 })
767 .map_err(|e| {
768 AppleCodesignError::PfxParseError(format!(
769 "failed parsing SafeBag within inner Data: {e:?}"
770 ))
771 })?;
772
773 for bag in bags {
774 match bag.bag {
775 p12::SafeBagKind::CertBag(cert_bag) => match cert_bag {
776 p12::CertBag::X509(cert_data) => {
777 certificate = Some(CapturedX509Certificate::from_der(cert_data)?);
778 }
779 p12::CertBag::SDSI(_) => {
780 return Err(AppleCodesignError::PfxParseError(
781 "unexpected SDSI certificate data".to_string(),
782 ));
783 }
784 },
785 p12::SafeBagKind::Pkcs8ShroudedKeyBag(key_bag) => {
786 let decrypted = key_bag.decrypt(&bmp_password).ok_or_else(|| {
787 AppleCodesignError::PfxParseError(
788 "error decrypting PKCS8 shrouded key bag; is the password correct?"
789 .to_string(),
790 )
791 })?;
792
793 signing_key = Some(InMemoryPrivateKey::from_pkcs8_der(decrypted)?);
794 }
795 p12::SafeBagKind::OtherBagKind(_) => {
796 return Err(AppleCodesignError::PfxParseError(
797 "unexpected bag type in inner PFX content".to_string(),
798 ));
799 }
800 }
801 }
802 }
803
804 match (certificate, signing_key) {
805 (Some(certificate), Some(signing_key)) => Ok((certificate, signing_key)),
806 (None, Some(_)) => Err(AppleCodesignError::PfxParseError(
807 "failed to find x509 certificate in PFX data".to_string(),
808 )),
809 (_, None) => Err(AppleCodesignError::PfxParseError(
810 "failed to find signing key in PFX data".to_string(),
811 )),
812 }
813}
814
815#[allow(unused)]
826pub(crate) fn rsa_oaep_post_decrypt_decode(
827 modulus_length_bytes: usize,
828 mut em: Vec<u8>,
829 digest: &mut dyn digest::DynDigest,
830 mgf_digest: &mut dyn digest::DynDigest,
831 label: Option<String>,
832) -> Result<Vec<u8>, rsa::errors::Error> {
833 let k = modulus_length_bytes;
834 let digest_len = digest.output_size();
835
836 let label = label.unwrap_or_default();
840 digest.update(label.as_bytes());
841 let label_digest = digest.finalize_reset();
842
843 let (y, remaining) = em.split_at_mut(1);
845 let (masked_seed, masked_db) = remaining.split_at_mut(digest_len);
846
847 if masked_seed.len() != digest_len || masked_db.len() != k - digest_len - 1 {
848 return Err(rsa::errors::Error::Decryption);
849 }
850
851 mgf1_xor(masked_seed, mgf_digest, masked_db);
853 mgf1_xor(masked_db, mgf_digest, masked_seed);
854
855 let digests_equivalent = masked_db[0..digest_len].ct_eq(label_digest.as_ref());
862
863 let mut looking_for_index = Choice::from(1u8);
864 let mut index = 0u32;
865 let mut padding_invalid = Choice::from(0u8);
866
867 for (i, value) in masked_db.iter().skip(digest_len).enumerate() {
868 let is_zero = value.ct_eq(&0u8);
869 let is_one = value.ct_eq(&1u8);
870
871 index.conditional_assign(&(i as u32), looking_for_index & is_one);
872 looking_for_index &= !is_one;
873 padding_invalid |= looking_for_index & !is_zero;
874 }
875
876 let y_is_zero = y[0].ct_eq(&0u8);
877
878 let valid = y_is_zero & digests_equivalent & !padding_invalid & !looking_for_index;
879
880 let res = CtOption::new((em, index + 2 + (digest_len * 2) as u32), valid);
881
882 if res.is_none().into() {
883 return Err(rsa::errors::Error::Decryption);
884 }
885
886 let (out, index) = res.unwrap();
887
888 Ok(out[index as usize..].to_vec())
889}
890
891fn inc_counter(counter: &mut [u8; 4]) {
892 for i in (0..4).rev() {
893 counter[i] = counter[i].wrapping_add(1);
894 if counter[i] != 0 {
895 return;
897 }
898 }
899}
900
901fn mgf1_xor(out: &mut [u8], digest: &mut dyn DynDigest, seed: &[u8]) {
902 let mut counter = [0u8; 4];
903 let mut i = 0;
904
905 const MAX_LEN: u64 = core::u32::MAX as u64 + 1;
906 assert!(out.len() as u64 <= MAX_LEN);
907
908 while i < out.len() {
909 let mut digest_input = vec![0u8; seed.len() + 4];
910 digest_input[0..seed.len()].copy_from_slice(seed);
911 digest_input[seed.len()..].copy_from_slice(&counter);
912
913 digest.update(digest_input.as_slice());
914 let digest_output = &*digest.finalize_reset();
915 let mut j = 0;
916 loop {
917 if j >= digest_output.len() || i >= out.len() {
918 break;
919 }
920
921 out[i] ^= digest_output[j];
922 j += 1;
923 i += 1;
924 }
925 inc_counter(&mut counter);
926 }
927}
928
929#[cfg(test)]
930mod test {
931 use {
932 super::*,
933 ring::signature::{EcdsaKeyPair, KeyPair, RsaKeyPair},
934 x509_certificate::Sign,
935 };
936
937 const RSA_2048_PKCS8_DER: &[u8] = include_bytes!("testdata/rsa-2048.pk8");
938 const ED25519_PKCS8_DER: &[u8] = include_bytes!("testdata/ed25519.pk8");
939 const SECP256_PKCS8_DER: &[u8] = include_bytes!("testdata/secp256r1.pk8");
940
941 #[test]
942 fn parse_keychain_p12_export() {
943 let data = include_bytes!("apple-codesign-testuser.p12");
944
945 let err = parse_pfx_data(data, "bad-password").unwrap_err();
946 assert!(matches!(err, AppleCodesignError::PfxBadPassword));
947
948 parse_pfx_data(data, "password123").unwrap();
949 }
950
951 #[test]
952 fn rsa_key_operations() -> Result<(), AppleCodesignError> {
953 let ring_key = RsaKeyPair::from_pkcs8(RSA_2048_PKCS8_DER).unwrap();
954 let ring_public_key_data = ring_key.public_key().as_ref();
955
956 let pki = PrivateKeyInfo::from_der(RSA_2048_PKCS8_DER).unwrap();
957 let key = InMemoryPrivateKey::try_from(pki).unwrap();
958
959 assert_eq!(key.to_pkcs8_der().unwrap().as_bytes(), RSA_2048_PKCS8_DER);
960
961 let our_key = InMemorySigningKeyPair::try_from(key)?;
962 let our_public_key = our_key.public_key_data();
963
964 assert_eq!(our_public_key.as_ref(), ring_public_key_data);
965
966 InMemoryPrivateKey::from_pkcs8_der(RSA_2048_PKCS8_DER)?;
967
968 let random_key = rsa::RsaPrivateKey::new(&mut rand::thread_rng(), 2048).unwrap();
969 let random_key_pkcs8 = random_key.to_pkcs8_der().unwrap();
970 InMemorySigningKeyPair::from_pkcs8_der(random_key_pkcs8.as_bytes())?;
971
972 Ok(())
973 }
974
975 #[test]
976 fn ed25519_key_operations() -> Result<(), AppleCodesignError> {
977 let pki = PrivateKeyInfo::from_der(ED25519_PKCS8_DER).unwrap();
978 let seed = &pki.private_key[2..];
979 let key = InMemoryPrivateKey::try_from(pki).unwrap();
980
981 assert!(
982 InMemorySigningKeyPair::from_pkcs8_der(ED25519_PKCS8_DER).is_err(),
983 "stored key doesn't have public key, which ring rejects loading"
984 );
985
986 InMemorySigningKeyPair::from_pkcs8_der(key.to_pkcs8_der().unwrap().as_bytes()).unwrap();
988
989 let our_key = InMemorySigningKeyPair::try_from(key)?;
990 let our_public_key = our_key.public_key_data();
991
992 let ring_key = Ed25519KeyPair::from_seed_unchecked(seed).unwrap();
993 let ring_public_key_data = ring_key.public_key().as_ref();
994
995 assert_eq!(our_public_key.as_ref(), ring_public_key_data);
996
997 InMemoryPrivateKey::from_pkcs8_der(ED25519_PKCS8_DER)?;
998
999 Ok(())
1000 }
1001
1002 #[test]
1003 fn ecdsa_key_operations_secp256() -> Result<(), AppleCodesignError> {
1004 let ring_key = EcdsaKeyPair::from_pkcs8(
1005 &ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING,
1006 SECP256_PKCS8_DER,
1007 &ring::rand::SystemRandom::new(),
1008 )
1009 .unwrap();
1010 let ring_public_key_data = ring_key.public_key().as_ref();
1011
1012 let pki = PrivateKeyInfo::from_der(SECP256_PKCS8_DER).unwrap();
1013 let key = InMemoryPrivateKey::try_from(pki).unwrap();
1014
1015 assert_eq!(key.to_pkcs8_der().unwrap().as_bytes(), SECP256_PKCS8_DER);
1016
1017 InMemorySigningKeyPair::from_pkcs8_der(SECP256_PKCS8_DER)?;
1018 let our_key = InMemorySigningKeyPair::try_from(key)?;
1019 let our_public_key = our_key.public_key_data();
1020
1021 assert_eq!(our_public_key.as_ref(), ring_public_key_data);
1022
1023 InMemoryPrivateKey::from_pkcs8_der(SECP256_PKCS8_DER)?;
1024
1025 Ok(())
1026 }
1027}