Description
Hi. Recently I ran into this PKCS7/CMS structure which contains what seems like non-canonical signed/authenticated attributes. It was failing signature verification in Rust code and after closer look, it seems like reencode of the signed/authenticated attributes returns a different DER encoding than OpenSSL for example. This results in different digest being calculated and even though everything is correctly verified, the hashes won't match.
Here's the PKCS7:
-----BEGIN PKCS7-----
MIIOtgYJKoZIhvcNAQcCoIIOpzCCDqMCAQMxDzANBglghkgBZQMEAgEFADB3Bgsq
hkiG9w0BCRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAE
II0q38EcQ5R9Xqa36BQprPBCmTC+YP1wxBwm6OfFsXruAhAAyFUUJYnDeJ5/1abK
npRXGA8yMDE2MDMyODE4MjEwNVqgggvBMIIGiDCCBXCgAwIBAgIQAs5ClFkCpPPA
QLD/d5PRTzANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM
RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQD
EyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTE1
MTIyNDAwMDAwMFoXDTI1MDEwNzAwMDAwMFowUjELMAkGA1UEBhMCVVMxFzAVBgNV
BAoTDkRpZ2lDZXJ0LCBJbmMuMSowKAYDVQQDEyFEaWdpQ2VydCBTSEEyIFRpbWVz
dGFtcCBSZXNwb25kZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDK
3JuzEc2uIHplciw1L945+SgkkdGU5aDUHl5oDDgot3i9np/x8qXeEMygOAEBqXoX
sICBLbDBhAIW/RB27EcsTW6WKNBlBBFdeMgXH4xOh1cdIevsHOfMfzT44a8rrGau
Cik37dYFj7wN3GuZLhyErnDDarcuWEsRx6ZQOvsWG/AGOfCaizghD2JeKh2F6PeH
yv3HF9iuWCdHFkZ08Dt5+cGocyi2+QzfxYrxzsCc+MtFH34bKvz73h6l6WPdKn+8
l0IiE/BUC9BKXEsIHuz/5163R9ui31O+GZ5HzYLj1ng2JAYsQj4G5AZvvFzHT9si
1eXIqp6tF8jP9dPW5HaBAgMBAAGjggM4MIIDNDAOBgNVHQ8BAf8EBAMCB4AwDAYD
VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDCCAb8GA1UdIASCAbYw
ggGyMIIBoQYJYIZIAYb9bAcBMIIBkjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cu
ZGlnaWNlcnQuY29tL0NQUzCCAWQGCCsGAQUFBwICMIIBVh6CAVIAQQBuAHkAIAB1
AHMAZQAgAG8AZgAgAHQAaABpAHMAIABDAGUAcgB0AGkAZgBpAGMAYQB0AGUAIABj
AG8AbgBzAHQAaQB0AHUAdABlAHMAIABhAGMAYwBlAHAAdABhAG4AYwBlACAAbwBm
ACAAdABoAGUAIABEAGkAZwBpAEMAZQByAHQAIABDAFAALwBDAFAAUwAgAGEAbgBk
ACAAdABoAGUAIABSAGUAbAB5AGkAbgBnACAAUABhAHIAdAB5ACAAQQBnAHIAZQBl
AG0AZQBuAHQAIAB3AGgAaQBjAGgAIABsAGkAbQBpAHQAIABsAGkAYQBiAGkAbABp
AHQAeQAgAGEAbgBkACAAYQByAGUAIABpAG4AYwBvAHIAcABvAHIAYQB0AGUAZAAg
AGgAZQByAGUAaQBuACAAYgB5ACAAcgBlAGYAZQByAGUAbgBjAGUALjALBglghkgB
hv1sAxUwHwYDVR0jBBgwFoAU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHQYDVR0OBBYE
FCR52VgCnqj486lPziFWQ4nmo1SiMHEGA1UdHwRqMGgwMqAwoC6GLGh0dHA6Ly9j
cmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtdHMuY3JsMDKgMKAuhixodHRw
Oi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLXRzLmNybDCBhQYIKwYB
BQUHAQEEeTB3MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20w
TwYIKwYBBQUHMAKGQ2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
dFNIQTJBc3N1cmVkSURUaW1lc3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQAD
ggEBALT8t75EH5RG003hoiSZ2Xocn2w8amGrNc18t0FphyX8J5XaL1scebkfMCai
GEnQYpsGyZqsB20Gz6SAhnEVry6xQOrRg6w1tKb6YYEyu+pDnPdV6WNVMmum1OHF
JzZ8VTLQW3wQr+9iYEDYyz/itixcqrwbiLsVi8k3yBgcnDuAJqgPV1/+z+5lkJUB
bshewVvAlftVToXb9oSedQg8Zn0Fm4karF6zDEu1ZkEXCjUXHCJfdjzxLPIH8QOx
SXKgRTsmm7/h2ozINzNnNNKRFE+8RHNx8zQtp7doyJqxtiNlChogD3qRw5MaeHz4
N8vvN4b9a36ybPPjcs0i6xI6HqIwggUxMIIEGaADAgECAhAKoSXW1jIbfkHkBdo2
l8IVMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0Rp
Z2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAwMDBaFw0zMTAx
MDcxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNI
QTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdFM1EQfdD5fU1o
fue2oPSNs4jkl79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ/l9lP+Cb6+NG
RwYaVX4LJ37AovWg4N4iPw7/fpX786O6Ij4YrBHk8JkDbTuFfAnT7l3ImgtU46gJ
cWvgzyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8YGqsLwfM/fDqR9mIUF79
Zm5WYScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F0IQZchfxFwbv
Pc3WTe8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHOMIIByjAdBgNV
HQ4EFgQU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAUReuir/SSy4Ix
LVGLp6chnfNtyA8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYw
EwYDVR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzAB
hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9j
YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQw
gYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdp
Q2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2lj
ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwUAYDVR0gBEkwRzA4
BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0
LmNvbS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IBAQBxlRLpUYdW
ac3v3dp8qmN6s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwhWiq3BTQdaq6Z
+CeiZr8JqmDfdqQ6kw/4stHYfBli6F6CJR7Euhx7LCHi1lssFDVDBGiy23UC4HLH
mNY8ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSlgNcSR3CzddWThZN+tpJn
+1Nhiaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UBJrZspe6HUSHk
WGCbugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0e/VWMyIvIjay
S6JKldj1po5SMYICTTCCAkkCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoT
DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UE
AxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBDQQIQAs5C
lFkCpPPAQLD/d5PRTzANBglghkgBZQMEAgEFAKCBmDAaBgkqhkiG9w0BCQMxDQYL
KoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTE2MDMyODE4MjEwNVowLwYJKoZI
hvcNAQkEMSIEIALRfHgBo/1U0NtasppsMkFq1GGUR6zdDguTsWvQR25jMCsGCyqG
SIb3DQEJEAIMMRwwGjAYMBYEFMY29N2ofO49gmO/miUUtFM0aNdeMA0GCSqGSIb3
DQEBAQUABIIBAJT4B7VgkVo2iUBC0loKNOy/s5F8TA4KAt4nAYwRbFwPHx2HylG9
Zah2PT0JrF4wG7TfvF7rVenG3wCnJY9J+YZVOqNTSZUKrZGwtrpLDUoB5O4Lok7P
WHakBQkVvrRWUhdRmK/0PIXuUzfDjYq7xJq06WVd/OUlwK43+TSa6pWjtbm+cNiB
CG8exiWVh1Osba6sGHeYsLJnfuvRzc2YmmQTKX0L2h1irZv2f8YjAR6UMuN5FO2p
glg+ttCzSwBLwVE+u74k7ovMMMIYJAFCpANcEe1FsmvVfbFXN72JCksdkdJM870s
bBZGJZsE6wvM7sLLApbgzRsesql3KX5j4GM=
-----END PKCS7-----
Code used to extract the signed attributes from this on the Rust side:
let mut reader = SliceReader::new(raw_der).unwrap();
let cms_content = ContentInfo::decode(&mut reader).unwrap();
let signed_data = cms_content.content.decode_as::<SignedData>().unwrap();
let signer_info = signed_data.signer_infos.0.get(0).unwrap();
let attrs = &signer_info.signed_attrs;
let mut reencode = Vec::new();
let _ = attrs.encode_to_vec(&mut reencode).unwrap();
Here's what I get from your crate:
$ openssl asn1parse -inform der -in rust.der -i 23.269ms 2024-04-19 14:17:37
0:d=0 hl=3 l= 152 cons: SET
3:d=1 hl=2 l= 26 cons: SEQUENCE
5:d=2 hl=2 l= 9 prim: OBJECT :contentType
16:d=2 hl=2 l= 13 cons: SET
18:d=3 hl=2 l= 11 prim: OBJECT :id-smime-ct-TSTInfo
31:d=1 hl=2 l= 28 cons: SEQUENCE
33:d=2 hl=2 l= 9 prim: OBJECT :signingTime
44:d=2 hl=2 l= 15 cons: SET
46:d=3 hl=2 l= 13 prim: UTCTIME :160328182105Z
61:d=1 hl=2 l= 43 cons: SEQUENCE
63:d=2 hl=2 l= 11 prim: OBJECT :id-smime-aa-signingCertificate
76:d=2 hl=2 l= 28 cons: SET
78:d=3 hl=2 l= 26 cons: SEQUENCE
80:d=4 hl=2 l= 24 cons: SEQUENCE
82:d=5 hl=2 l= 22 cons: SEQUENCE
84:d=6 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:C636F4DDA87CEE3D8263BF9A2514B4533468D75E
106:d=1 hl=2 l= 47 cons: SEQUENCE
108:d=2 hl=2 l= 9 prim: OBJECT :messageDigest
119:d=2 hl=2 l= 34 cons: SET
121:d=3 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:02D17C7801A3FD54D0DB5AB29A6C32416AD4619447ACDD0E0B93B16BD0476E63
Here's what I get from OpenSSL:
0:d=0 hl=3 l= 152 cons: SET
3:d=1 hl=2 l= 26 cons: SEQUENCE
5:d=2 hl=2 l= 9 prim: OBJECT :contentType
16:d=2 hl=2 l= 13 cons: SET
18:d=3 hl=2 l= 11 prim: OBJECT :id-smime-ct-TSTInfo
31:d=1 hl=2 l= 28 cons: SEQUENCE
33:d=2 hl=2 l= 9 prim: OBJECT :signingTime
44:d=2 hl=2 l= 15 cons: SET
46:d=3 hl=2 l= 13 prim: UTCTIME :160328182105Z
61:d=1 hl=2 l= 47 cons: SEQUENCE
63:d=2 hl=2 l= 9 prim: OBJECT :messageDigest
74:d=2 hl=2 l= 34 cons: SET
76:d=3 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:02D17C7801A3FD54D0DB5AB29A6C32416AD4619447ACDD0E0B93B16BD0476E63
110:d=1 hl=2 l= 43 cons: SEQUENCE
112:d=2 hl=2 l= 11 prim: OBJECT :id-smime-aa-signingCertificate
125:d=2 hl=2 l= 28 cons: SET
127:d=3 hl=2 l= 26 cons: SEQUENCE
129:d=4 hl=2 l= 24 cons: SEQUENCE
131:d=5 hl=2 l= 22 cons: SEQUENCE
133:d=6 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:C636F4DDA87CEE3D8263BF9A2514B4533468D75E
Here's the base64 encoded DER of what OpenSSL gives me (considering I use PKCS7_ATTR_VERIFY
, see below for more info):
MYGYMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMTYwMzI4MTgy
MTA1WjAvBgkqhkiG9w0BCQQxIgQgAtF8eAGj/VTQ21qymmwyQWrUYZRHrN0OC5Oxa9BHbmMwKwYL
KoZIhvcNAQkQAgwxHDAaMBgwFgQUxjb03ah87j2CY7+aJRS0UzRo114=
Seems to be related to ordering of sets in the canonical form which is mentioned in your codebase and also OpenSSL with their PKCS7_ATTR_VERIFY
which was specially created for this purpose.
I am just considering what are my options here because if the sets are ordered during decode, then I won't really get back the original order. So the very first question would be whether it's even somehow possible to use this crate for my use-case? If not, would you be interested in maybe supporting also non-canonical form? I'm not sure what it entails all together other than ordering, but I wouldn't have problem contributing to this if it helped me to obtain exactly what I want. Thanks!