Skip to content

Commit 3ff8821

Browse files
openssl/ts: add functionality for a basic Time Stamp Authority
This change adds the missing wrappers to be able to generate a minimally functional Time Stamp Authority, signing time stamp requests and generating time stamp responses.
1 parent 12912fc commit 3ff8821

File tree

4 files changed

+222
-3
lines changed

4 files changed

+222
-3
lines changed

openssl-sys/src/ts.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use *;
66
pub enum TS_MSG_IMPRINT {}
77
pub enum TS_REQ {}
88
pub enum TS_RESP {}
9+
pub enum TS_RESP_CTX {}
910

1011
cfg_if! {
1112
if #[cfg(ossl110)] {
@@ -51,6 +52,13 @@ pub const TS_VFY_ALL_DATA: c_uint = TS_VFY_SIGNATURE
5152
| TS_VFY_SIGNER
5253
| TS_VFY_TSA_NAME;
5354

55+
pub const TS_STATUS_GRANTED: c_uint = 0;
56+
pub const TS_STATUS_GRANTED_WITH_MODS: c_uint = 1;
57+
pub const TS_STATUS_REJECTION: c_uint = 2;
58+
pub const TS_STATUS_WAITING: c_uint = 3;
59+
pub const TS_STATUS_REVOCATION_WARNING: c_uint = 4;
60+
pub const TS_STATUS_REVOCATION_NOTIFICATION: c_uint = 5;
61+
5462
extern "C" {
5563
pub fn TS_MSG_IMPRINT_new() -> *mut TS_MSG_IMPRINT;
5664
pub fn TS_MSG_IMPRINT_free(a: *mut TS_MSG_IMPRINT);
@@ -87,6 +95,14 @@ extern "C" {
8795

8896
pub fn TS_REQ_to_TS_VERIFY_CTX(req: *mut TS_REQ, ctx: *mut TS_VERIFY_CTX)
8997
-> *mut TS_VERIFY_CTX;
98+
99+
pub fn TS_RESP_CTX_new() -> *mut TS_RESP_CTX;
100+
pub fn TS_RESP_CTX_free(ctx: *mut TS_RESP_CTX);
101+
pub fn TS_RESP_CTX_set_signer_cert(ctx: *mut TS_RESP_CTX, signer: *mut X509) -> c_int;
102+
pub fn TS_RESP_CTX_set_signer_key(ctx: *mut TS_RESP_CTX, key: *mut EVP_PKEY) -> c_int;
103+
pub fn TS_RESP_CTX_add_md(ctx: *mut TS_RESP_CTX, md: *const EVP_MD) -> c_int;
104+
105+
pub fn TS_RESP_create_response(ctx: *mut TS_RESP_CTX, req_bio: *mut BIO) -> *mut TS_RESP;
90106
}
91107

92108
cfg_if! {
@@ -96,13 +112,32 @@ cfg_if! {
96112
a: *mut TS_REQ,
97113
policy: *const ASN1_OBJECT
98114
) -> c_int;
115+
pub fn TS_RESP_CTX_set_def_policy(
116+
ctx: *mut TS_RESP_CTX,
117+
def_policy: *const ASN1_OBJECT
118+
) -> c_int;
99119
}
100120
} else {
101121
extern "C" {
102122
pub fn TS_REQ_set_policy_id(
103123
a: *mut TS_REQ,
104124
policy: *mut ASN1_OBJECT
105125
) -> c_int;
126+
pub fn TS_RESP_CTX_set_def_policy(
127+
ctx: *mut TS_RESP_CTX,
128+
def_policy: *mut ASN1_OBJECT
129+
) -> c_int;
130+
}
131+
}
132+
}
133+
134+
cfg_if! {
135+
if #[cfg(ossl110)] {
136+
extern "C" {
137+
pub fn TS_RESP_CTX_set_signer_digest(
138+
ctx: *mut TS_RESP_CTX,
139+
signer_digest: *const EVP_MD,
140+
) -> c_int;
106141
}
107142
}
108143
}

openssl/src/ts.rs

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ use asn1::{Asn1IntegerRef, Asn1ObjectRef};
1212
use error::ErrorStack;
1313
use ffi;
1414
use hash::MessageDigest;
15-
use x509::X509Algorithm;
15+
use pkey::{HasPrivate, PKeyRef};
16+
use x509::{X509Algorithm, X509Ref};
1617
use {cvt, cvt_p};
1718

1819
foreign_type_and_impl_send_sync! {
1920
type CType = ffi::TS_MSG_IMPRINT;
2021
fn drop = ffi::TS_MSG_IMPRINT_free;
2122

22-
/// A message imprint contains the has of the data to be timestamped.
23+
/// A message imprint contains the hash of the data to be timestamped.
2324
pub struct TsMsgImprint;
2425

2526
/// Reference to `TsMsgImprint`.
@@ -271,13 +272,125 @@ impl TsVerifyContext {
271272
}
272273
}
273274

275+
foreign_type_and_impl_send_sync! {
276+
type CType = ffi::TS_RESP_CTX;
277+
fn drop = ffi::TS_RESP_CTX_free;
278+
279+
/// A context object used to sign timestamp requests.
280+
pub struct TsRespContext;
281+
282+
/// Reference to `TsRespContext`.
283+
pub struct TsRespContextRef;
284+
}
285+
286+
impl TsRespContextRef {
287+
/// Creates a signed timestamp response for the request.
288+
///
289+
/// This corresponds to `TS_RESP_create_response`.
290+
pub fn create_response(&mut self, request: &TsReqRef) -> Result<TsResp, ErrorStack> {
291+
unsafe {
292+
let der = request.to_der()?;
293+
let bio = ::bio::MemBioSlice::new(&der)?;
294+
let response = cvt_p(ffi::TS_RESP_create_response(self.as_ptr(), bio.as_ptr()))?;
295+
Ok(TsResp::from_ptr(response))
296+
}
297+
}
298+
}
299+
300+
pub struct TsRespContextBuilder(TsRespContext);
301+
302+
impl TsRespContextBuilder {
303+
/// Creates a new builder.
304+
pub fn new() -> Result<TsRespContextBuilder, ErrorStack> {
305+
unsafe {
306+
ffi::init();
307+
let resp_context: *mut ffi::TS_RESP_CTX = cvt_p(ffi::TS_RESP_CTX_new())?;
308+
Ok(TsRespContextBuilder(TsRespContext::from_ptr(resp_context)))
309+
}
310+
}
311+
312+
/// Sets the OID of the default policy used by the TSA.
313+
///
314+
/// This corresponds to `TS_RESP_CTX_set_def_policy`.
315+
pub fn set_default_policy(&mut self, policy: &Asn1ObjectRef) -> Result<(), ErrorStack> {
316+
unsafe {
317+
cvt(ffi::TS_RESP_CTX_set_def_policy(
318+
self.0.as_ptr(),
319+
policy.as_ptr(),
320+
))
321+
.map(|_| ())
322+
}
323+
}
324+
325+
/// Sets the certificate the TSA uses to sign the request.
326+
///
327+
/// This corresponds to `TS_RESP_CTX_set_signer_cert`.
328+
pub fn set_signer_cert(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> {
329+
unsafe {
330+
cvt(ffi::TS_RESP_CTX_set_signer_cert(
331+
self.0.as_ptr(),
332+
cert.as_ptr(),
333+
))
334+
.map(|_| ())
335+
}
336+
}
337+
338+
/// Sets the private key the TSA uses to sign the request.
339+
///
340+
/// The private key match the X.509 certificate set by `set_signer_cert`.
341+
///
342+
/// This corresponds to `TS_RESP_CTX_set_signer_key`.
343+
pub fn set_signer_key<T>(&mut self, pkey: &PKeyRef<T>) -> Result<(), ErrorStack>
344+
where
345+
T: HasPrivate,
346+
{
347+
unsafe {
348+
cvt(ffi::TS_RESP_CTX_set_signer_key(
349+
self.0.as_ptr(),
350+
pkey.as_ptr(),
351+
))
352+
.map(|_| ())
353+
}
354+
}
355+
356+
/// Sets the message digest algorithm to use for the signature.
357+
///
358+
/// This corresponds to `TS_RESP_CTX_set_signer_digest`.
359+
pub fn set_signer_digest(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
360+
unsafe {
361+
cvt(ffi::TS_RESP_CTX_set_signer_digest(
362+
self.0.as_ptr(),
363+
md.as_ptr(),
364+
))
365+
.map(|_| ())
366+
}
367+
}
368+
369+
/// Add an accepted message digest algorithm.
370+
///
371+
/// At least one accepted digest algorithm should be added to the context.
372+
///
373+
/// This corresponds to `TS_RESP_CTX_add_md`.
374+
pub fn add_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
375+
unsafe { cvt(ffi::TS_RESP_CTX_add_md(self.0.as_ptr(), md.as_ptr())).map(|_| ()) }
376+
}
377+
378+
/// Consume the builder and return the response context.
379+
pub fn build(self) -> TsRespContext {
380+
self.0
381+
}
382+
}
383+
274384
#[cfg(test)]
275385
mod tests {
276386
use super::*;
277387

278-
use asn1::Asn1Integer;
388+
use asn1::{Asn1Integer, Asn1Object};
279389
use bn::BigNum;
390+
use hash::MessageDigest;
391+
use pkey::PKey;
280392
use sha::sha512;
393+
use x509::X509;
281394

282395
#[test]
283396
fn test_request() {
@@ -317,4 +430,28 @@ mod tests {
317430
let context = TsVerifyContext::from_req(&request).unwrap();
318431
response.verify(&context).unwrap();
319432
}
433+
434+
#[test]
435+
fn test_response_context() {
436+
let mut response_context = TsRespContextBuilder::new().unwrap();
437+
response_context
438+
.set_default_policy(&Asn1Object::from_str("1.2.3.4").unwrap())
439+
.unwrap();
440+
let cert = X509::from_pem(include_bytes!("../test/ts-cert.pem")).unwrap();
441+
response_context.set_signer_cert(&cert).unwrap();
442+
let key = PKey::private_key_from_pem(include_bytes!("../test/ts-key.pem")).unwrap();
443+
response_context.set_signer_key(&key).unwrap();
444+
445+
response_context
446+
.set_signer_digest(MessageDigest::sha256())
447+
.unwrap();
448+
response_context.add_md(MessageDigest::sha512()).unwrap();
449+
let mut response_context = response_context.build();
450+
451+
let request = TsReq::from_der(include_bytes!("../test/ts-request.der")).unwrap();
452+
let response = response_context.create_response(&request).unwrap();
453+
454+
let context = TsVerifyContext::from_req(&request).unwrap();
455+
response.verify(&context).unwrap();
456+
}
320457
}

openssl/test/ts-cert.pem

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDOjCCAiKgAwIBAgIFN8DNd/kwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMC
3+
QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
4+
dHMgUHR5IEx0ZDAeFw0yMDEwMjcwODQwMTBaFw0zMTEwMTAwODQwMTBaMFwxCzAJ
5+
BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l
6+
dCBXaWRnaXRzIFB0eSBMdGQxFTATBgNVBAMTDFRpbWVzdGFtcGluZzCCASIwDQYJ
7+
KoZIhvcNAQEBBQADggEPADCCAQoCggEBANUTsyhUuzou06s/XXelCLSF7sd8xtFO
8+
4OJFEWAulg5K4m7w/GG/VIaelvqgNxdSHdzheT1l1UrMP6na2tAcAS5tBv+X0Q0C
9+
T3+FqlqcgV2HSUNKJy3CJ1CoNQCuN6eaqO7y3O9yfdze8jCpHcrYrx4BFNISw8/T
10+
KaAKQRHjrVbAOlIA+nCc7MGYXJS9ZVwfXNASrhRBoswLoesSf4mX1PPXwjnhGJMq
11+
nVhVI+1G1gD9t4l7CrdOhx5vONffOoSjmqDVfRmcyYNT33V8zlynUoKwLF985TKo
12+
hJAfA8qQDczLkPv6shssNWk8BJ+mYRz3sU9QHM4dkX1oql3oWlOx+o0CAwEAAaMa
13+
MBgwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggEBAC1D
14+
6A5FrPsYV4Q2Zar/MdYsuA2XM2j8SA/H2m4uGz8Sg52/V1VWBouB4JVz1EHytAlG
15+
BD2A+71Bk/Y6JpxdU51O/ZgGlrOaCs+1L4+WLe1cgXGcgC65fP7eMCF3ajuOYZid
16+
q5cJxpbBEspecus7ArqEQ9+ahAVZXcSuKfHOcW+3DxqP3/GPvbt4vLdxjPbe1Dbx
17+
Le+UkPPIQoKNk7yOILMuZrR+O4E4O0cn7E2qodUoYIxSOWIg9euvfntFyR66NzXL
18+
+pDtbVzWPKNiqgvhx5n5GjdyQGHA0X2gEpepT3p3S9dHMcdjJfDixVKp8F5f9mbx
19+
6wgs4XG7rb8sRDsAmAc=
20+
-----END CERTIFICATE-----

openssl/test/ts-key.pem

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEpAIBAAKCAQEA1ROzKFS7Oi7Tqz9dd6UItIXux3zG0U7g4kURYC6WDkribvD8
3+
Yb9Uhp6W+qA3F1Id3OF5PWXVSsw/qdra0BwBLm0G/5fRDQJPf4WqWpyBXYdJQ0on
4+
LcInUKg1AK43p5qo7vLc73J93N7yMKkdytivHgEU0hLDz9MpoApBEeOtVsA6UgD6
5+
cJzswZhclL1lXB9c0BKuFEGizAuh6xJ/iZfU89fCOeEYkyqdWFUj7UbWAP23iXsK
6+
t06HHm841986hKOaoNV9GZzJg1PfdXzOXKdSgrAsX3zlMqiEkB8DypANzMuQ+/qy
7+
Gyw1aTwEn6ZhHPexT1Aczh2RfWiqXehaU7H6jQIDAQABAoIBAQDCoR76DQOELvfL
8+
qbKfshDUjK5Ca9hTokBKjppDh+orHf3dJqTySElWOhBg1+3akHiUpSQQkC8XBqB0
9+
b2OFyr7NgGtvFmavAlhJfHfSErkcDJJAM8C7zGgLBcp8V6agouYCdbaXxbXwBXmm
10+
NyPugKTcvFIfXWKdOB4CgLtVMunHnVz97+2O6HwN3nPfnsUTBNlImyO9UFO+2utB
11+
kk8vx9qvxhVo6bRmL85+CmuPAN0XiYaXqKRyF55b37c69A1mrMsGimRCO9fxuhhW
12+
JW7UwbXM/RHEDKRfD2P8dWMWyw+jQEBzQKrGZ/2dbjuXOVsTT6h/UVnLdFR7ntrk
13+
HljlNsXhAoGBAPwOyAIPBe9MZPmItO4Ssw2hr62MglmXYQITT+i6uuun2LMr6X/E
14+
zTlMNZGTOXPt8mDuPmELXX4rpKu1lBTuhM9/csbYG32kQK2I6j0QuY1fQ+NHnzcA
15+
4e9SkctZSx0auSo4u3+8SaoRpFfbxs6+tv7xJCcmvxkJZrKnTS7Z0SOJAoGBANho
16+
17fBqN63ogcf8y74D15KyxJi/spr7ZAZY1mH212TGf2xOC2gWpLyl+mENjj9L0+j
17+
qRh0J7y0wcnX9ZuuZHmU647/9bM3q21ZLpoP/gbUdZEBU9CnurCk+tTlGZ49iiN0
18+
KqX86JuynNlou/gcNFJS1v62TCnTr+aWGH6vOmnlAoGBALbTQd+8ZeGc1+Dnd9T3
19+
W0iX7oVDVYkGdCa9O0jjqJElvdi4ETXL2c+lp3VgBFxCS3xjUnuxcq8BmP+zRSWp
20+
nEuldesk9Uu8x+0XUk/Ywb35S5SfbqzGxxqAGaAVtJX3vDcTz2xndkcVZM8Vaq6r
21+
RrDE2CRNxm6ykvsivqks9LWBAoGAfuqF0KPHyM5DPRB0y0/5m2Ab1m2uZcKEMWVi
22+
SaiOc0OJE6pyeve3BsU1aGL8ddGuhHNEAS5l+5q6qAh6Z1IQZOl8eIIOc4urgtax
23+
qPLGFPVW+bKgmBc2OtCWtnKh4pbOw9omBPDc7isDJ9HvoyPPX5RruDfrVQBsAbx3
24+
IxzbEi0CgYAXZDdGCX1GiW1qBOGzgN/omrEKuXVQ9QEQYfAy+EWYw6EfWYSzgi3d
25+
QRV2x5q/T4IdPlrFM5fNilQGH2F9fGLX/CSJLWqnvPEKaB6e89y2ncFFZFIaP0YT
26+
UxDNUSmAmdP+GBYZCl3cYAUtVBKrjjyHRkCy8to62ldbSV7jsngX0A==
27+
-----END RSA PRIVATE KEY-----

0 commit comments

Comments
 (0)