@@ -7,6 +7,8 @@ use der::{
7
7
DecodeValue , EncodeValue , ErrorKind , FixedTag , Header , Length , Reader , Result , Tag , ValueOrd ,
8
8
Writer ,
9
9
} ;
10
+ #[ cfg( feature = "builder" ) ]
11
+ use { alloc:: vec, signature:: rand_core:: CryptoRngCore } ;
10
12
11
13
use crate :: certificate:: { Profile , Rfc5280 } ;
12
14
@@ -67,6 +69,56 @@ impl<P: Profile> SerialNumber<P> {
67
69
}
68
70
}
69
71
72
+ #[ cfg( feature = "builder" ) ]
73
+ impl < P : Profile > SerialNumber < P > {
74
+ /// Generates a random serial number from RNG.
75
+ ///
76
+ /// This follows the recommendation the CAB forum [ballot 164] and uses a minimum of 64 bits
77
+ /// of output from the CSPRNG. This currently defaults to a 17-bytes long serial number.
78
+ ///
79
+ /// [ballot 164]: https://cabforum.org/2016/03/31/ballot-164/
80
+ pub fn generate ( rng : & mut impl CryptoRngCore ) -> Result < Self > {
81
+ Self :: generate_with_prefix ( & [ ] , 17 , rng)
82
+ }
83
+
84
+ /// Generates a random serial number from RNG. Include a prefix value.
85
+ ///
86
+ /// This follows the recommendation the CAB forum [ballot 164] and uses a minimum of 64 bits
87
+ /// of output from the CSPRNG.
88
+ ///
89
+ /// The specified length does not include the length of the prefix, the maximum length must be
90
+ /// equal or below 19 (to account for leading sign disembiguation, and the maximum length of 20).
91
+ ///
92
+ /// [ballot 164]: https://cabforum.org/2016/03/31/ballot-164/
93
+ pub fn generate_with_prefix (
94
+ prefix : & [ u8 ] ,
95
+ rand_len : usize ,
96
+ rng : & mut impl CryptoRngCore ,
97
+ ) -> Result < Self > {
98
+ // CABF requires a minimum of 64 bits of random
99
+ if rand_len < 8 {
100
+ return Err ( ErrorKind :: Failed . into ( ) ) ;
101
+ }
102
+
103
+ if rand_len + prefix. len ( ) > 19 {
104
+ return Err ( ErrorKind :: Failed . into ( ) ) ;
105
+ }
106
+
107
+ let mut buf = vec ! [ 0 ; prefix. len( ) + rand_len] ;
108
+ buf[ ..prefix. len ( ) ] . copy_from_slice ( prefix) ;
109
+
110
+ let rand_buf = & mut buf[ prefix. len ( ) ..] ;
111
+
112
+ // Make sure the first byte isn't 0, [`Int`] will otherwise optimize out the leading zeros,
113
+ // shorten the value of the serial and trigger false positives in linters.
114
+ while rand_buf[ 0 ] == 0 {
115
+ rng. fill_bytes ( rand_buf) ;
116
+ }
117
+
118
+ Self :: new ( & buf)
119
+ }
120
+ }
121
+
70
122
impl < P : Profile > EncodeValue for SerialNumber < P > {
71
123
fn value_len ( & self ) -> Result < Length > {
72
124
self . inner . value_len ( )
@@ -193,4 +245,37 @@ mod tests {
193
245
assert_eq ! ( sn. to_string( ) , "01" )
194
246
}
195
247
}
248
+
249
+ #[ cfg( feature = "builder" ) ]
250
+ #[ test]
251
+ fn serial_number_generate ( ) {
252
+ let sn = SerialNumber :: < Rfc5280 > :: generate ( & mut rand:: thread_rng ( ) ) . unwrap ( ) ;
253
+
254
+ // Underlying storage uses signed int for compatibility reasons,
255
+ // we may need to prefix the value with 0x00 to make it an unsigned.
256
+ // in which case the length is going to be 18.
257
+ assert ! ( matches!( sn. as_bytes( ) . len( ) , 17 ..=18 ) ) ;
258
+
259
+ let sn =
260
+ SerialNumber :: < Rfc5280 > :: generate_with_prefix ( & [ ] , 8 , & mut rand:: thread_rng ( ) ) . unwrap ( ) ;
261
+ assert ! ( matches!( sn. as_bytes( ) . len( ) , 8 ..=9 ) ) ;
262
+
263
+ let sn =
264
+ SerialNumber :: < Rfc5280 > :: generate_with_prefix ( & [ 1 , 2 , 3 ] , 8 , & mut rand:: thread_rng ( ) )
265
+ . unwrap ( ) ;
266
+ assert ! ( matches!( sn. as_bytes( ) . len( ) , 11 ..=12 ) ) ;
267
+ assert_eq ! ( & sn. as_bytes( ) [ ..3 ] , & [ 1 , 2 , 3 ] ) ;
268
+
269
+ let sn = SerialNumber :: < Rfc5280 > :: generate_with_prefix ( & [ ] , 7 , & mut rand:: thread_rng ( ) ) ;
270
+ assert ! ( sn. is_err( ) ) ;
271
+
272
+ let sn = SerialNumber :: < Rfc5280 > :: generate_with_prefix ( & [ ] , 20 , & mut rand:: thread_rng ( ) ) ;
273
+ assert ! ( sn. is_err( ) ) ;
274
+
275
+ let sn = SerialNumber :: < Rfc5280 > :: generate_with_prefix ( & [ ] , 19 , & mut rand:: thread_rng ( ) ) ;
276
+ assert ! ( sn. is_ok( ) ) ;
277
+
278
+ let sn = SerialNumber :: < Rfc5280 > :: generate_with_prefix ( & [ 1 ] , 19 , & mut rand:: thread_rng ( ) ) ;
279
+ assert ! ( sn. is_err( ) ) ;
280
+ }
196
281
}
0 commit comments