Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit d438da4

Browse files
committed
Ensure that exporting an unsorted attributes signed payload doesn't sort them.
1 parent 2f8e2bb commit d438da4

5 files changed

Lines changed: 124 additions & 4 deletions

File tree

src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerInfoAsn.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ internal struct SignerInfoAsn
3535
public AlgorithmIdentifierAsn DigestAlgorithm;
3636

3737
[ExpectedTag(0)]
38-
[SetOf]
3938
[OptionalValue]
40-
public AttributeAsn[] SignedAttributes;
39+
[AnyValue]
40+
public ReadOnlyMemory<byte>? SignedAttributes;
4141

4242
public AlgorithmIdentifierAsn SignatureAlgorithm;
4343

@@ -49,4 +49,16 @@ internal struct SignerInfoAsn
4949
[OptionalValue]
5050
public AttributeAsn[] UnsignedAttributes;
5151
}
52+
53+
// This type is not properly from the ASN module, but it exists to allow for
54+
// deserialization on demand of the signed attributes, so the deserialization
55+
// and reserialization process does not modify the contents of the signed
56+
// attributes.
57+
[Choice]
58+
internal struct SignedAttributesSet
59+
{
60+
[ExpectedTag(0)]
61+
[SetOf]
62+
public AttributeAsn[] SignedAttributes;
63+
}
5264
}

src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,23 @@ internal SignerInfoAsn Sign(
151151
}
152152

153153
// Use the serializer/deserializer to DER-normalize the attribute order.
154-
newSignerInfo.SignedAttributes = Helpers.NormalizeSet(
154+
SignedAttributesSet signedAttrsSet = new SignedAttributesSet();
155+
signedAttrsSet.SignedAttributes = Helpers.NormalizeSet(
155156
signedAttrs.ToArray(),
156157
normalized =>
157158
{
158159
AsnReader reader = new AsnReader(normalized, AsnEncodingRules.DER);
159160
hasher.AppendData(reader.PeekContentBytes().Span);
160161
});
161162

163+
// Since this contains user data in a context where BER is permitted, use BER.
164+
// There shouldn't be any observable difference here between BER and DER, though,
165+
// since the top level fields were written by NormalizeSet.
166+
using (AsnWriter attrsWriter = AsnSerializer.Serialize(signedAttrsSet, AsnEncodingRules.BER))
167+
{
168+
newSignerInfo.SignedAttributes = attrsWriter.Encode();
169+
}
170+
162171
dataHash = hasher.GetHashAndReset();
163172
}
164173

src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public sealed class SignerInfo
2121

2222
private readonly Oid _digestAlgorithm;
2323
private readonly AttributeAsn[] _signedAttributes;
24+
private readonly ReadOnlyMemory<byte>? _signedAttributesMemory;
2425
private readonly Oid _signatureAlgorithm;
2526
private readonly ReadOnlyMemory<byte>? _signatureAlgorithmParameters;
2627
private readonly ReadOnlyMemory<byte> _signature;
@@ -37,12 +38,22 @@ internal SignerInfo(ref SignerInfoAsn parsedData, SignedCms ownerDocument)
3738
Version = parsedData.Version;
3839
SignerIdentifier = new SubjectIdentifier(parsedData.Sid);
3940
_digestAlgorithm = parsedData.DigestAlgorithm.Algorithm;
40-
_signedAttributes = parsedData.SignedAttributes;
41+
_signedAttributesMemory = parsedData.SignedAttributes;
4142
_signatureAlgorithm = parsedData.SignatureAlgorithm.Algorithm;
4243
_signatureAlgorithmParameters = parsedData.SignatureAlgorithm.Parameters;
4344
_signature = parsedData.SignatureValue;
4445
_unsignedAttributes = parsedData.UnsignedAttributes;
4546

47+
if (_signedAttributesMemory.HasValue)
48+
{
49+
SignedAttributesSet signedSet = AsnSerializer.Deserialize<SignedAttributesSet>(
50+
_signedAttributesMemory.Value,
51+
AsnEncodingRules.BER);
52+
53+
_signedAttributes = signedSet.SignedAttributes;
54+
Debug.Assert(_signedAttributes != null);
55+
}
56+
4657
_document = ownerDocument;
4758
}
4859

src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,56 @@ public static void VerifyUnsortedAttributeSignature()
10101010
cms.CheckSignature(true);
10111011
}
10121012

1013+
[Fact]
1014+
public static void VerifyUnsortedAttributeSignature_ImportExportImport()
1015+
{
1016+
SignedCms cms = new SignedCms();
1017+
cms.Decode(SignedDocuments.DigiCertTimeStampToken);
1018+
1019+
// Assert.NoThrows
1020+
cms.CheckSignature(true);
1021+
1022+
byte[] exported = cms.Encode();
1023+
cms = new SignedCms();
1024+
cms.Decode(exported);
1025+
1026+
// Assert.NoThrows
1027+
cms.CheckSignature(true);
1028+
}
1029+
1030+
[Fact]
1031+
public static void AddSignerToUnsortedAttributeSignature()
1032+
{
1033+
SignedCms cms = new SignedCms();
1034+
cms.Decode(SignedDocuments.DigiCertTimeStampToken);
1035+
1036+
// Assert.NoThrows
1037+
cms.CheckSignature(true);
1038+
1039+
using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey())
1040+
{
1041+
cms.ComputeSignature(
1042+
new CmsSigner(
1043+
SubjectIdentifierType.IssuerAndSerialNumber,
1044+
cert));
1045+
1046+
cms.ComputeSignature(
1047+
new CmsSigner(
1048+
SubjectIdentifierType.SubjectKeyIdentifier,
1049+
cert));
1050+
}
1051+
1052+
// Assert.NoThrows
1053+
cms.CheckSignature(true);
1054+
1055+
byte[] exported = cms.Encode();
1056+
cms = new SignedCms();
1057+
cms.Decode(exported);
1058+
1059+
// Assert.NoThrows
1060+
cms.CheckSignature(true);
1061+
}
1062+
10131063
[Theory]
10141064
[InlineData(null, "0102", Oids.Pkcs7Data)]
10151065
[InlineData(null, "010100", Oids.Pkcs7Data)]

src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignerInfoTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,44 @@ public static void AddCounterSigner_RSA(SubjectIdentifierType identifierType)
591591
cms.CheckSignature(true);
592592
}
593593

594+
[Fact]
595+
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Not supported by crypt32")]
596+
public static void AddCounterSignerToUnsortedAttributeSignature()
597+
{
598+
SignedCms cms = new SignedCms();
599+
cms.Decode(SignedDocuments.DigiCertTimeStampToken);
600+
601+
// Assert.NoThrows
602+
cms.CheckSignature(true);
603+
604+
SignerInfoCollection signers = cms.SignerInfos;
605+
Assert.Equal(1, signers.Count);
606+
SignerInfo signerInfo = signers[0];
607+
608+
using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey())
609+
{
610+
signerInfo.ComputeCounterSignature(
611+
new CmsSigner(
612+
SubjectIdentifierType.IssuerAndSerialNumber,
613+
cert));
614+
615+
signerInfo.ComputeCounterSignature(
616+
new CmsSigner(
617+
SubjectIdentifierType.SubjectKeyIdentifier,
618+
cert));
619+
}
620+
621+
// Assert.NoThrows
622+
cms.CheckSignature(true);
623+
624+
byte[] exported = cms.Encode();
625+
cms = new SignedCms();
626+
cms.Decode(exported);
627+
628+
// Assert.NoThrows
629+
cms.CheckSignature(true);
630+
}
631+
594632
[Fact]
595633
public static void AddCounterSigner_DSA()
596634
{

0 commit comments

Comments
 (0)