Skip to content

Commit

Permalink
Add CMS_NO_SIGNING_TIME flag to CMS_sign(), CMS_add1_signer()
Browse files Browse the repository at this point in the history
Previously there was no way to create a CMS SignedData signature without a
signing time attribute, because CMS_SignerInfo_sign added it unconditionally.
However, there is a use case (PAdES signatures) where this attribute is not
allowed, so this commit introduces a new flag to the CMS API that causes this
attribute to be omitted at signing time.

Also add -no_signing_time option to cms command.

Fixes openssl#15777

Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from openssl#15783)
  • Loading branch information
juhaszp-uhu authored and t8m committed Dec 26, 2024
1 parent b85e6f5 commit 34ea176
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 6 deletions.
12 changes: 12 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ OpenSSL 3.5

*Ramkumar*

* Add CMS_NO_SIGNING_TIME flag to CMS_sign(), CMS_add1_signer()

Previously there was no way to create a CMS SignedData signature without a
signing time attribute, because CMS_SignerInfo_sign added it unconditionally.
However, there is a use case (PAdES signatures [ETSI EN 319 142-1](https://www.etsi.org/deliver/etsi_en/319100_319199/31914201/01.01.01_60/en_31914201v010101p.pdf) )
where this attribute is not allowed, so a new flag was added to the CMS API
that causes this attribute to be omitted at signing time.

The new `-no_signing_time` option of the `cms` command enables this flag.

*Juhász Péter*

OpenSSL 3.4
-----------

Expand Down
8 changes: 7 additions & 1 deletion apps/cms.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ typedef enum OPTION_choice {
OPT_DIGEST, OPT_DIGEST_CREATE, OPT_COMPRESS, OPT_UNCOMPRESS,
OPT_ED_DECRYPT, OPT_ED_ENCRYPT, OPT_DEBUG_DECRYPT, OPT_TEXT,
OPT_ASCIICRLF, OPT_NOINTERN, OPT_NOVERIFY, OPT_NOCERTS,
OPT_NOATTR, OPT_NODETACH, OPT_NOSMIMECAP, OPT_BINARY, OPT_KEYID,
OPT_NOATTR, OPT_NODETACH, OPT_NOSMIMECAP, OPT_NO_SIGNING_TIME,
OPT_BINARY, OPT_KEYID,
OPT_NOSIGS, OPT_NO_CONTENT_VERIFY, OPT_NO_ATTR_VERIFY, OPT_INDEF,
OPT_NOINDEF, OPT_CRLFEOL, OPT_NOOUT, OPT_RR_PRINT,
OPT_RR_ALL, OPT_RR_FIRST, OPT_RCTFORM, OPT_CERTFILE, OPT_CAFILE,
Expand Down Expand Up @@ -186,6 +187,8 @@ const OPTIONS cms_options[] = {
"Don't include signer's certificate when signing"},
{"noattr", OPT_NOATTR, '-', "Don't include any signed attributes"},
{"nosmimecap", OPT_NOSMIMECAP, '-', "Omit the SMIMECapabilities attribute"},
{"no_signing_time", OPT_NO_SIGNING_TIME, '-',
"Omit the signing time attribute"},
{"receipt_request_all", OPT_RR_ALL, '-',
"When signing, create a receipt request for all recipients"},
{"receipt_request_first", OPT_RR_FIRST, '-',
Expand Down Expand Up @@ -429,6 +432,9 @@ int cms_main(int argc, char **argv)
case OPT_NOSMIMECAP:
flags |= CMS_NOSMIMECAP;
break;
case OPT_NO_SIGNING_TIME:
flags |= CMS_NO_SIGNING_TIME;
break;
case OPT_BINARY:
flags |= CMS_BINARY;
break;
Expand Down
2 changes: 2 additions & 0 deletions crypto/cms/cms_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ struct CMS_SignerInfo_st {
EVP_MD_CTX *mctx;
EVP_PKEY_CTX *pctx;
const CMS_CTX *cms_ctx;
/* Set to 1 if signing time attribute is to be omitted */
int omit_signing_time;
};

struct CMS_SignerIdentifier_st {
Expand Down
12 changes: 11 additions & 1 deletion crypto/cms/cms_sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms,
si->signer = signer;
si->mctx = EVP_MD_CTX_new();
si->pctx = NULL;
si->omit_signing_time = 0;

if (si->mctx == NULL) {
ERR_raise(ERR_LIB_CMS, ERR_R_EVP_LIB);
Expand Down Expand Up @@ -456,6 +457,14 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms,
goto err;
}
}
if ((flags & CMS_NO_SIGNING_TIME) != 0) {
/*
* The signing-time signed attribute (NID_pkcs9_signingTime)
* would normally be added later, in CMS_SignerInfo_sign(),
* unless we set this flag here
*/
si->omit_signing_time = 1;
}
if (flags & CMS_CADES) {
ESS_SIGNING_CERT *sc = NULL;
ESS_SIGNING_CERT_V2 *sc2 = NULL;
Expand Down Expand Up @@ -839,7 +848,8 @@ int CMS_SignerInfo_sign(CMS_SignerInfo *si)
si->digestAlgorithm->algorithm, 0) <= 0)
return 0;

if (CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1) < 0) {
if (!si->omit_signing_time
&& CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1) < 0) {
if (!cms_add1_signingTime(si, NULL))
goto err;
}
Expand Down
8 changes: 7 additions & 1 deletion doc/man1/openssl-cms.pod.in
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Signing options:
[B<-nocerts>]
[B<-noattr>]
[B<-nosmimecap>]
[B<-no_signing_time>]
[B<-receipt_request_all>]
[B<-receipt_request_first>]
[B<-receipt_request_from> I<emailaddress>]
Expand Down Expand Up @@ -492,7 +493,12 @@ option they are not included.
=item B<-nosmimecap>

Exclude the list of supported algorithms from signed attributes, other options
such as signing time and content type are still included.
such as content type and (optionally) signing time are still included.

=item B<-no_signing_time>

Exclude the signing time from signed attributes, other options
such as content type are still included.

=item B<-receipt_request_all>, B<-receipt_request_first>

Expand Down
3 changes: 2 additions & 1 deletion doc/man3/CMS_add1_signer.pod
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ previously signed message.
The SignedData structure includes several CMS signedAttributes including the
signing time, the CMS content type and the supported list of ciphers in an
SMIMECapabilities attribute. If B<CMS_NOATTR> is set then no signedAttributes
will be used. If B<CMS_NOSMIMECAP> is set then just the SMIMECapabilities are
will be used at all. If B<CMS_NOSMIMECAP> is set then the SMIMECapabilities
will be omitted. If B<CMS_NO_SIGNING_TIME> is set then the signing time will be
omitted.

OpenSSL will by default identify signing certificates using issuer name
Expand Down
3 changes: 2 additions & 1 deletion doc/man3/CMS_sign.pod
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ otherwise the translation will corrupt it.
The SignedData structure includes several CMS signedAttributes including the
signing time, the CMS content type and the supported list of ciphers in an
SMIMECapabilities attribute. If B<CMS_NOATTR> is set then no signedAttributes
will be used. If B<CMS_NOSMIMECAP> is set then just the SMIMECapabilities are
will be used at all. If B<CMS_NOSMIMECAP> is set then the SMIMECapabilities
will be omitted. If B<CMS_NO_SIGNING_TIME> is set then the signing time will be
omitted.

If present the SMIMECapabilities attribute indicates support for the following
Expand Down
1 change: 1 addition & 0 deletions include/openssl/cms.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ CMS_ContentInfo *CMS_ContentInfo_new_ex(OSSL_LIB_CTX *libctx, const char *propq)
# define CMS_ASCIICRLF 0x80000
# define CMS_CADES 0x100000
# define CMS_USE_ORIGINATOR_KEYID 0x200000
# define CMS_NO_SIGNING_TIME 0x400000

const ASN1_OBJECT *CMS_get0_type(const CMS_ContentInfo *cms);

Expand Down
42 changes: 41 additions & 1 deletion test/recipes/80-test_cms.t
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)

$no_rc2 = 1 if disabled("legacy");

plan tests => 25;
plan tests => 26;

ok(run(test(["pkcs7_test"])), "test pkcs7");

Expand Down Expand Up @@ -1065,6 +1065,46 @@ subtest "CMS signed digest, DER format" => sub {
"Verify CMS signed digest, DER format");
};

subtest "CMS signed digest, DER format, no signing time" => sub {
# This test also enables CAdES mode and disables S/MIME capabilities
# to approximate the kind of signature required for a PAdES-compliant
# PDF signature.
plan tests => 4;

# Pre-computed SHA256 digest of $smcont in hexadecimal form
my $digest = "ff236ef61b396355f75a4cc6e1c306d4c309084ae271a9e2ad6888f10a101b32";

my $sig_file = "signature.der";
ok(run(app(["openssl", "cms", @prov, "-sign", "-digest", $digest,
"-outform", "DER",
"-no_signing_time",
"-nosmimecap",
"-cades",
"-certfile", catfile($smdir, "smroot.pem"),
"-signer", catfile($smdir, "smrsa1.pem"),
"-out", $sig_file])),
"CMS sign pre-computed digest, DER format, no signing time");

my $exit = 0;
my $dump = join "\n",
run(app(["openssl", "cms", @prov, "-cmsout", "-noout", "-print",
"-in", $sig_file,
"-inform", "DER"]),
capture => 1,
statusvar => $exit);

is($exit, 0, "Parse CMS signed digest, DER format, no signing time");
is(index($dump, 'signingTime'), -1,
"Check that CMS signed digest does not contain signing time");

ok(run(app(["openssl", "cms", @prov, "-verify", "-in", $sig_file,
"-inform", "DER",
"-CAfile", catfile($smdir, "smroot.pem"),
"-content", $smcont])),
"Verify CMS signed digest, DER format, no signing time");
};


subtest "CMS signed digest, S/MIME format" => sub {
plan tests => 2;

Expand Down

0 comments on commit 34ea176

Please sign in to comment.