Skip to content

Commit 84605cd

Browse files
committed
[pkcs8] Added: Attributes field to PrivateKeyInfo
[pkcs8] Added: Tests for PrivateKeyInfo Attributes
1 parent 7346ca9 commit 84605cd

File tree

5 files changed

+173
-27
lines changed

5 files changed

+173
-27
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkcs8/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ edition = "2024"
1717
rust-version = "1.85"
1818

1919
[dependencies]
20-
der = { version = "0.8.0-rc.12", features = ["oid"] }
20+
der = { version = "0.8", features = ["oid"] }
2121
spki = "0.8"
22+
x509-cert = { version = "0.3.0-rc.4", default-features = false }
2223

2324
# optional dependencies
2425
rand_core = { version = "0.10", optional = true, default-features = false }

pkcs8/src/private_key_info.rs

Lines changed: 100 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
use crate::{Error, Result, Version};
44
use core::fmt;
55
use der::{
6-
Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, Sequence, TagMode,
7-
TagNumber, Writer,
8-
asn1::{AnyRef, BitStringRef, ContextSpecific, OctetStringRef, SequenceRef},
6+
Decode, DecodeValue, DerOrd, Encode, EncodeValue, FixedTag, Header, Length, Reader, Sequence,
7+
TagMode, TagNumber, Writer,
8+
asn1::{AnyRef, BitStringRef, ContextSpecific, OctetStringRef, SetOfRef},
99
};
1010
use spki::AlgorithmIdentifier;
11+
use x509_cert::attr::Attribute;
1112

1213
#[cfg(feature = "alloc")]
1314
use der::{
1415
SecretDocument,
15-
asn1::{Any, BitString, OctetString},
16+
asn1::{Any, BitString, OctetString, SetOfVec},
1617
};
1718

1819
#[cfg(feature = "encryption")]
@@ -94,7 +95,7 @@ const PUBLIC_KEY_TAG: TagNumber = TagNumber(1);
9495
/// [RFC 5208 Section 5]: https://tools.ietf.org/html/rfc5208#section-5
9596
/// [RFC 5958 Section 2]: https://datatracker.ietf.org/doc/html/rfc5958#section-2
9697
#[derive(Clone)]
97-
pub struct PrivateKeyInfo<Params, Key, PubKey> {
98+
pub struct PrivateKeyInfo<Params, Key, PubKey, Attr> {
9899
/// X.509 `AlgorithmIdentifier` for the private key type.
99100
pub algorithm: AlgorithmIdentifier<Params>,
100101

@@ -103,9 +104,12 @@ pub struct PrivateKeyInfo<Params, Key, PubKey> {
103104

104105
/// Public key data, optionally available if version is V2.
105106
pub public_key: Option<PubKey>,
107+
108+
/// Attributes
109+
pub attributes: Option<Attr>,
106110
}
107111

108-
impl<Params, Key, PubKey> PrivateKeyInfo<Params, Key, PubKey> {
112+
impl<Params, Key, PubKey, Attr> PrivateKeyInfo<Params, Key, PubKey, Attr> {
109113
/// Create a new PKCS#8 [`PrivateKeyInfo`] message.
110114
///
111115
/// This is a helper method which initializes `attributes` and `public_key`
@@ -115,6 +119,7 @@ impl<Params, Key, PubKey> PrivateKeyInfo<Params, Key, PubKey> {
115119
algorithm,
116120
private_key,
117121
public_key: None,
122+
attributes: None,
118123
}
119124
}
120125

@@ -130,13 +135,15 @@ impl<Params, Key, PubKey> PrivateKeyInfo<Params, Key, PubKey> {
130135
}
131136
}
132137

133-
impl<'a, Params, Key, PubKey> PrivateKeyInfo<Params, Key, PubKey>
138+
impl<'a, Params, Key, PubKey, Attr> PrivateKeyInfo<Params, Key, PubKey, Attr>
134139
where
135140
Params: der::Choice<'a, Error = der::Error> + Encode,
136141
Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
137142
Key: EncodeValue,
138143
PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
139144
PubKey: BitStringLike,
145+
Attr: DecodeValue<'a, Error = der::Error> + FixedTag + SetOfLike<'a> + 'a,
146+
Attr::Item: Clone + Decode<'a> + DerOrd + Encode,
140147
{
141148
/// Encrypt this private key using a symmetric encryption key derived
142149
/// from the provided password.
@@ -170,7 +177,7 @@ where
170177
}
171178
}
172179

173-
impl<'a, Params, Key, PubKey> PrivateKeyInfo<Params, Key, PubKey>
180+
impl<'a, Params, Key, PubKey, Attr> PrivateKeyInfo<Params, Key, PubKey, Attr>
174181
where
175182
Params: der::Choice<'a> + Encode,
176183
PubKey: BitStringLike,
@@ -188,11 +195,30 @@ where
188195
}
189196
}
190197

191-
impl<'a, Params, Key, PubKey> DecodeValue<'a> for PrivateKeyInfo<Params, Key, PubKey>
198+
impl<'a, Params, Key, PubKey, Attr> PrivateKeyInfo<Params, Key, PubKey, Attr>
199+
where
200+
Params: der::Choice<'a> + Encode,
201+
Attr: SetOfLike<'a>,
202+
{
203+
/// Get a `SET OF` representation of the attributes, if present.
204+
fn attributes_string(&self) -> Option<ContextSpecific<SetOfRef<'a, Attr::Item>>> {
205+
self.attributes.as_ref().map(|attributes| {
206+
let value = attributes.as_set_of();
207+
ContextSpecific {
208+
tag_number: ATTRIBUTES_TAG,
209+
tag_mode: TagMode::Implicit,
210+
value,
211+
}
212+
})
213+
}
214+
}
215+
216+
impl<'a, Params, Key, PubKey, Attr> DecodeValue<'a> for PrivateKeyInfo<Params, Key, PubKey, Attr>
192217
where
193218
Params: der::Choice<'a, Error = der::Error> + Encode,
194219
Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
195220
PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
221+
Attr: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
196222
{
197223
type Error = der::Error;
198224

@@ -202,8 +228,7 @@ where
202228
let algorithm = reader.decode()?;
203229
let private_key = Key::decode(reader)?;
204230

205-
let _attributes =
206-
reader.context_specific::<&SequenceRef>(ATTRIBUTES_TAG, TagMode::Implicit)?;
231+
let attributes = reader.context_specific::<Attr>(ATTRIBUTES_TAG, TagMode::Implicit)?;
207232

208233
let public_key = reader.context_specific::<PubKey>(PUBLIC_KEY_TAG, TagMode::Implicit)?;
209234

@@ -226,49 +251,58 @@ where
226251
algorithm,
227252
private_key,
228253
public_key,
254+
attributes,
229255
})
230256
}
231257
}
232258

233-
impl<'a, Params, Key, PubKey> EncodeValue for PrivateKeyInfo<Params, Key, PubKey>
259+
impl<'a, Params, Key, PubKey, Attr> EncodeValue for PrivateKeyInfo<Params, Key, PubKey, Attr>
234260
where
235261
Params: der::Choice<'a, Error = der::Error> + Encode,
236262
Key: EncodeValue + FixedTag,
237263
PubKey: BitStringLike,
264+
Attr: SetOfLike<'a>,
265+
Attr::Item: Clone + Decode<'a> + DerOrd + Encode,
238266
{
239267
fn value_len(&self) -> der::Result<Length> {
240268
self.version().encoded_len()?
241269
+ self.algorithm.encoded_len()?
242270
+ self.private_key.encoded_len()?
271+
+ self.attributes_string().encoded_len()?
243272
+ self.public_key_bit_string().encoded_len()?
244273
}
245274

246275
fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
247276
self.version().encode(writer)?;
248277
self.algorithm.encode(writer)?;
249278
self.private_key.encode(writer)?;
279+
self.attributes_string().encode(writer)?;
250280
self.public_key_bit_string().encode(writer)?;
251281
Ok(())
252282
}
253283
}
254284

255-
impl<'a, Params, Key, PubKey> Sequence<'a> for PrivateKeyInfo<Params, Key, PubKey>
285+
impl<'a, Params, Key, PubKey, Attr> Sequence<'a> for PrivateKeyInfo<Params, Key, PubKey, Attr>
256286
where
257287
Params: der::Choice<'a, Error = der::Error> + Encode,
258288
Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
259289
Key: EncodeValue,
260290
PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
261291
PubKey: BitStringLike,
292+
Attr: DecodeValue<'a, Error = der::Error> + FixedTag + SetOfLike<'a> + 'a,
293+
Attr::Item: Clone + Decode<'a> + DerOrd + Encode,
262294
{
263295
}
264296

265-
impl<'a, Params, Key, PubKey> TryFrom<&'a [u8]> for PrivateKeyInfo<Params, Key, PubKey>
297+
impl<'a, Params, Key, PubKey, Attr> TryFrom<&'a [u8]> for PrivateKeyInfo<Params, Key, PubKey, Attr>
266298
where
267299
Params: der::Choice<'a, Error = der::Error> + Encode,
268300
Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
269301
Key: EncodeValue,
270302
PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
271303
PubKey: BitStringLike,
304+
Attr: DecodeValue<'a, Error = der::Error> + FixedTag + SetOfLike<'a> + 'a,
305+
Attr::Item: Clone + Decode<'a> + DerOrd + Encode,
272306
{
273307
type Error = Error;
274308

@@ -277,63 +311,72 @@ where
277311
}
278312
}
279313

280-
impl<Params, Key, PubKey> fmt::Debug for PrivateKeyInfo<Params, Key, PubKey>
314+
impl<Params, Key, PubKey, Attr> fmt::Debug for PrivateKeyInfo<Params, Key, PubKey, Attr>
281315
where
282316
Params: fmt::Debug,
283317
PubKey: fmt::Debug,
318+
Attr: fmt::Debug,
284319
{
285320
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286321
f.debug_struct("PrivateKeyInfo")
287322
.field("version", &self.version())
288323
.field("algorithm", &self.algorithm)
324+
.field("Attributes", &self.attributes)
289325
.field("public_key", &self.public_key)
290326
.finish_non_exhaustive()
291327
}
292328
}
293329

294330
#[cfg(feature = "alloc")]
295-
impl<'a, Params, Key, PubKey> TryFrom<PrivateKeyInfo<Params, Key, PubKey>> for SecretDocument
331+
impl<'a, Params, Key, PubKey, Attr> TryFrom<PrivateKeyInfo<Params, Key, PubKey, Attr>>
332+
for SecretDocument
296333
where
297334
Params: der::Choice<'a, Error = der::Error> + Encode,
298335
Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
299336
Key: EncodeValue,
300337
PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
301338
PubKey: BitStringLike,
339+
Attr: DecodeValue<'a, Error = der::Error> + FixedTag + SetOfLike<'a> + 'a,
340+
Attr::Item: Clone + Decode<'a> + DerOrd + Encode,
302341
{
303342
type Error = Error;
304343

305-
fn try_from(private_key: PrivateKeyInfo<Params, Key, PubKey>) -> Result<SecretDocument> {
344+
fn try_from(private_key: PrivateKeyInfo<Params, Key, PubKey, Attr>) -> Result<SecretDocument> {
306345
SecretDocument::try_from(&private_key)
307346
}
308347
}
309348

310349
#[cfg(feature = "alloc")]
311-
impl<'a, Params, Key, PubKey> TryFrom<&PrivateKeyInfo<Params, Key, PubKey>> for SecretDocument
350+
impl<'a, Params, Key, PubKey, Attr> TryFrom<&PrivateKeyInfo<Params, Key, PubKey, Attr>>
351+
for SecretDocument
312352
where
313353
Params: der::Choice<'a, Error = der::Error> + Encode,
314354
Key: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
315355
Key: EncodeValue,
316356
PubKey: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
317357
PubKey: BitStringLike,
358+
Attr: DecodeValue<'a, Error = der::Error> + FixedTag + SetOfLike<'a> + 'a,
359+
Attr::Item: Clone + Decode<'a> + DerOrd + Encode,
318360
{
319361
type Error = Error;
320362

321-
fn try_from(private_key: &PrivateKeyInfo<Params, Key, PubKey>) -> Result<SecretDocument> {
363+
fn try_from(private_key: &PrivateKeyInfo<Params, Key, PubKey, Attr>) -> Result<SecretDocument> {
322364
Ok(Self::encode_msg(private_key)?)
323365
}
324366
}
325367

326368
#[cfg(feature = "pem")]
327-
impl<Params, Key, PubKey> PemLabel for PrivateKeyInfo<Params, Key, PubKey> {
369+
impl<Params, Key, PubKey, Attr> PemLabel for PrivateKeyInfo<Params, Key, PubKey, Attr> {
328370
const PEM_LABEL: &'static str = "PRIVATE KEY";
329371
}
330372

331373
#[cfg(feature = "subtle")]
332-
impl<Params, Key, PubKey> ConstantTimeEq for PrivateKeyInfo<Params, Key, PubKey>
374+
impl<Params, Key, PubKey, Attr> ConstantTimeEq for PrivateKeyInfo<Params, Key, PubKey, Attr>
333375
where
334376
Params: Eq,
335377
Key: PartialEq + AsRef<[u8]>,
336378
PubKey: PartialEq,
379+
Attr: PartialEq,
337380
{
338381
fn ct_eq(&self, other: &Self) -> Choice {
339382
// NOTE: public fields are not compared in constant time
@@ -346,32 +389,35 @@ where
346389
}
347390

348391
#[cfg(feature = "subtle")]
349-
impl<Params, Key, PubKey> Eq for PrivateKeyInfo<Params, Key, PubKey>
392+
impl<Params, Key, PubKey, Attr> Eq for PrivateKeyInfo<Params, Key, PubKey, Attr>
350393
where
351394
Params: Eq,
352395
Key: AsRef<[u8]> + Eq,
353396
PubKey: Eq,
397+
Attr: Eq,
354398
{
355399
}
356400

357401
#[cfg(feature = "subtle")]
358-
impl<Params, Key, PubKey> PartialEq for PrivateKeyInfo<Params, Key, PubKey>
402+
impl<Params, Key, PubKey, Attr> PartialEq for PrivateKeyInfo<Params, Key, PubKey, Attr>
359403
where
360404
Params: Eq,
361405
Key: PartialEq + AsRef<[u8]>,
362406
PubKey: PartialEq,
407+
Attr: PartialEq,
363408
{
364409
fn eq(&self, other: &Self) -> bool {
365410
self.ct_eq(other).into()
366411
}
367412
}
368413

369414
/// [`PrivateKeyInfo`] with [`AnyRef`] algorithm parameters, and `&[u8]` key.
370-
pub type PrivateKeyInfoRef<'a> = PrivateKeyInfo<AnyRef<'a>, &'a OctetStringRef, BitStringRef<'a>>;
415+
pub type PrivateKeyInfoRef<'a> =
416+
PrivateKeyInfo<AnyRef<'a>, &'a OctetStringRef, BitStringRef<'a>, SetOfRef<'a, Attribute>>;
371417

372418
/// [`PrivateKeyInfo`] with [`Any`] algorithm parameters, and `Box<[u8]>` key.
373419
#[cfg(feature = "alloc")]
374-
pub type PrivateKeyInfoOwned = PrivateKeyInfo<Any, OctetString, BitString>;
420+
pub type PrivateKeyInfoOwned = PrivateKeyInfo<Any, OctetString, BitString, SetOfVec<Attribute>>;
375421

376422
/// [`BitStringLike`] marks object that will act like a BitString.
377423
///
@@ -386,6 +432,22 @@ impl BitStringLike for BitStringRef<'_> {
386432
}
387433
}
388434

435+
pub trait SetOfLike<'a> {
436+
type Item: Clone + DerOrd + Encode;
437+
fn as_set_of(&self) -> SetOfRef<'a, Self::Item>;
438+
}
439+
440+
impl<'a, T> SetOfLike<'a> for SetOfRef<'a, T>
441+
where
442+
T: Clone + DerOrd + Encode,
443+
{
444+
type Item = T;
445+
446+
fn as_set_of(&self) -> SetOfRef<'a, T> {
447+
SetOfRef::from(self)
448+
}
449+
}
450+
389451
#[cfg(feature = "alloc")]
390452
mod allocating {
391453
use super::*;
@@ -399,13 +461,25 @@ mod allocating {
399461
}
400462
}
401463

464+
impl<'a, T> SetOfLike<'a> for &'a SetOfVec<T>
465+
where
466+
T: Clone + DerOrd + Encode + 'a,
467+
{
468+
type Item = T;
469+
470+
fn as_set_of(&self) -> SetOfRef<'a, T> {
471+
self.owned_to_ref()
472+
}
473+
}
474+
402475
impl<'a> RefToOwned<'a> for PrivateKeyInfoRef<'a> {
403476
type Owned = PrivateKeyInfoOwned;
404477
fn ref_to_owned(&self) -> Self::Owned {
405478
PrivateKeyInfoOwned {
406479
algorithm: self.algorithm.ref_to_owned(),
407480
private_key: self.private_key.to_owned(),
408481
public_key: self.public_key.ref_to_owned(),
482+
attributes: self.attributes.ref_to_owned(),
409483
}
410484
}
411485
}
@@ -417,6 +491,7 @@ mod allocating {
417491
algorithm: self.algorithm.owned_to_ref(),
418492
private_key: self.private_key.borrow(),
419493
public_key: self.public_key.owned_to_ref(),
494+
attributes: self.attributes.owned_to_ref(),
420495
}
421496
}
422497
}
166 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)