Example #1
0
 internal IncrementalHashHasher(HashAlgorithmName hashAlgorithmName)
 {
     _incrementalHash = IncrementalHash.CreateHash(hashAlgorithmName);
 }
Example #2
0
 internal MD5SHA1IncrementalHasher()
 {
     _md5  = IncrementalHash.CreateHash(HashAlgorithmName.MD5);
     _sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
 }
        /// <summary>
        /// The method that performs the actual validation.
        /// More information about checksum algorithm:
        /// https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#portable-pdb-checksum
        /// </summary>
        /// <param name="targetDirectory">The directory used during the current validation.</param>
        /// <param name="packageId">Package Id.</param>
        /// <param name="packageNormalizedVersion">PackageNormalized version.</param>
        /// <returns></returns>
        public virtual IValidationResult ValidateSymbolMatching(string targetDirectory, string packageId, string packageNormalizedVersion)
        {
            foreach (string extension in PEExtensionsPatterns)
            {
                foreach (string peFile in Directory.GetFiles(targetDirectory, extension, SearchOption.AllDirectories))
                {
                    using (var peStream = File.OpenRead(peFile))
                        using (var peReader = new PEReader(peStream))
                        {
                            // This checks if portable PDB is associated with the PE file and opens it for reading.
                            // It also validates that it matches the PE file.
                            // It does not validate that the checksum matches, so we need to do that in the following block.
                            if (peReader.TryOpenAssociatedPortablePdb(peFile, File.OpenRead, out var pdbReaderProvider, out var pdbPath) &&
                                // No need to validate embedded PDB (pdbPath == null for embedded)
                                pdbPath != null)
                            {
                                // Get all checksum entries. There can be more than one. At least one must match the PDB.
                                var checksumRecords = peReader.ReadDebugDirectory().Where(entry => entry.Type == DebugDirectoryEntryType.PdbChecksum)
                                                      .Select(e => peReader.ReadPdbChecksumDebugDirectoryData(e))
                                                      .ToArray();

                                if (checksumRecords.Length == 0)
                                {
                                    _telemetryService.TrackSymbolsValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Failed, nameof(ValidationIssue.SymbolErrorCode_ChecksumDoesNotMatch));
                                    return(ValidationResult.FailedWithIssues(ValidationIssue.SymbolErrorCode_ChecksumDoesNotMatch));
                                }

                                var pdbBytes = File.ReadAllBytes(pdbPath);
                                var hashes   = new Dictionary <string, byte[]>();

                                using (pdbReaderProvider)
                                {
                                    var pdbReader = pdbReaderProvider.GetMetadataReader();
                                    int idOffset  = pdbReader.DebugMetadataHeader.IdStartOffset;

                                    foreach (var checksumRecord in checksumRecords)
                                    {
                                        if (!hashes.TryGetValue(checksumRecord.AlgorithmName, out var hash))
                                        {
                                            HashAlgorithmName han = new HashAlgorithmName(checksumRecord.AlgorithmName);
                                            using (var hashAlg = IncrementalHash.CreateHash(han))
                                            {
                                                hashAlg.AppendData(pdbBytes, 0, idOffset);
                                                hashAlg.AppendData(new byte[20]);
                                                int offset = idOffset + 20;
                                                int count  = pdbBytes.Length - offset;
                                                hashAlg.AppendData(pdbBytes, offset, count);
                                                hash = hashAlg.GetHashAndReset();
                                            }
                                            hashes.Add(checksumRecord.AlgorithmName, hash);
                                        }
                                        if (checksumRecord.Checksum.ToArray().SequenceEqual(hash))
                                        {
                                            // found the right checksum
                                            _telemetryService.TrackSymbolsValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Succeeded, "");
                                            return(ValidationResult.Succeeded);
                                        }
                                    }

                                    // Not found any checksum record that matches the PDB.
                                    _telemetryService.TrackSymbolsValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Failed, nameof(ValidationIssue.SymbolErrorCode_ChecksumDoesNotMatch));
                                    return(ValidationResult.FailedWithIssues(ValidationIssue.SymbolErrorCode_ChecksumDoesNotMatch));
                                }
                            }
                        }
                    _telemetryService.TrackSymbolsValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Failed, nameof(ValidationIssue.SymbolErrorCode_MatchingPortablePDBNotFound));
                    return(ValidationResult.FailedWithIssues(ValidationIssue.SymbolErrorCode_MatchingPortablePDBNotFound));
                }
            }
            // If did not return there were not any PE files to validate. In this case return error to not proceeed with an ingestion.
            _logger.LogError("{ValidatorName}: There were not any dll or exe files found locally." +
                             "This could indicate an issue in the execution or the package was not correct created. PackageId {PackageId} PackageNormalizedVersion {PackageNormalizedVersion}. " +
                             "SymbolCount: {SymbolCount}",
                             ValidatorName.SymbolsValidator,
                             packageId,
                             packageNormalizedVersion,
                             Directory.GetFiles(targetDirectory, SymbolExtensionPattern, SearchOption.AllDirectories));
            _telemetryService.TrackSymbolsValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Failed, nameof(ValidationIssue.SymbolErrorCode_MatchingPortablePDBNotFound));
            return(ValidationResult.FailedWithIssues(ValidationIssue.SymbolErrorCode_MatchingPortablePDBNotFound));
        }
Example #4
0
        internal SignerInfoAsn Sign(
            ReadOnlyMemory <byte> data,
            string contentTypeOid,
            bool silent,
            out X509Certificate2Collection chainCerts)
        {
            HashAlgorithmName hashAlgorithmName = Helpers.GetDigestAlgorithm(DigestAlgorithm);
            IncrementalHash   hasher            = IncrementalHash.CreateHash(hashAlgorithmName);

            hasher.AppendData(data.Span);
            byte[] dataHash = hasher.GetHashAndReset();

            SignerInfoAsn newSignerInfo = new SignerInfoAsn();

            newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm;

            if ((SignedAttributes != null && SignedAttributes.Count > 0) || contentTypeOid == null)
            {
                List <AttributeAsn> signedAttrs = BuildAttributes(SignedAttributes);

                using (var writer = new AsnWriter(AsnEncodingRules.DER))
                {
                    writer.PushSetOf();
                    writer.WriteOctetString(dataHash);
                    writer.PopSetOf();

                    signedAttrs.Add(
                        new AttributeAsn
                    {
                        AttrType   = new Oid(Oids.MessageDigest, Oids.MessageDigest),
                        AttrValues = writer.Encode(),
                    });
                }

                if (contentTypeOid != null)
                {
                    using (var writer = new AsnWriter(AsnEncodingRules.DER))
                    {
                        writer.PushSetOf();
                        writer.WriteObjectIdentifier(contentTypeOid);
                        writer.PopSetOf();

                        signedAttrs.Add(
                            new AttributeAsn
                        {
                            AttrType   = new Oid(Oids.ContentType, Oids.ContentType),
                            AttrValues = writer.Encode(),
                        });
                    }
                }

                // Use the serializer/deserializer to DER-normalize the attribute order.
                newSignerInfo.SignedAttributes = Helpers.NormalizeSet(
                    signedAttrs.ToArray(),
                    normalized =>
                {
                    AsnReader reader = new AsnReader(normalized, AsnEncodingRules.DER);
                    hasher.AppendData(reader.PeekContentBytes().Span);
                });

                dataHash = hasher.GetHashAndReset();
            }

            switch (SignerIdentifierType)
            {
            case SubjectIdentifierType.IssuerAndSerialNumber:
                byte[] serial = Certificate.GetSerialNumber();
                Array.Reverse(serial);

                newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
                {
                    Issuer       = Certificate.IssuerName.RawData,
                    SerialNumber = serial,
                };

                newSignerInfo.Version = 1;
                break;

            case SubjectIdentifierType.SubjectKeyIdentifier:
                newSignerInfo.Sid.SubjectKeyIdentifier = Certificate.GetSubjectKeyIdentifier();
                newSignerInfo.Version = 3;
                break;

            case SubjectIdentifierType.NoSignature:
                newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
                {
                    Issuer       = SubjectIdentifier.DummySignerEncodedValue,
                    SerialNumber = new byte[1],
                };
                newSignerInfo.Version = 1;
                break;

            default:
                Debug.Fail($"Unresolved SignerIdentifierType value: {SignerIdentifierType}");
                throw new CryptographicException();
            }

            if (UnsignedAttributes != null && UnsignedAttributes.Count > 0)
            {
                List <AttributeAsn> attrs = BuildAttributes(UnsignedAttributes);

                newSignerInfo.UnsignedAttributes = Helpers.NormalizeSet(attrs.ToArray());
            }

            bool signed = CmsSignature.Sign(
                dataHash,
                hashAlgorithmName,
                Certificate,
                PrivateKey,
                silent,
                out Oid signatureAlgorithm,
                out ReadOnlyMemory <byte> signatureValue);

            if (!signed)
            {
                throw new CryptographicException(SR.Cryptography_Cms_CannotDetermineSignatureAlgorithm);
            }

            newSignerInfo.SignatureValue = signatureValue;
            newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm;

            X509Certificate2Collection certs = new X509Certificate2Collection();

            certs.AddRange(Certificates);

            if (SignerIdentifierType != SubjectIdentifierType.NoSignature)
            {
                if (IncludeOption == X509IncludeOption.EndCertOnly)
                {
                    certs.Add(Certificate);
                }
                else if (IncludeOption != X509IncludeOption.None)
                {
                    X509Chain chain = new X509Chain();
                    chain.ChainPolicy.RevocationMode    = X509RevocationMode.NoCheck;
                    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;

                    if (!chain.Build(Certificate))
                    {
                        foreach (X509ChainStatus status in chain.ChainStatus)
                        {
                            if (status.Status == X509ChainStatusFlags.PartialChain)
                            {
                                throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain);
                            }
                        }
                    }

                    X509ChainElementCollection elements = chain.ChainElements;
                    int count = elements.Count;
                    int last  = count - 1;

                    if (last == 0)
                    {
                        // If there's always one cert treat it as EE, not root.
                        last = -1;
                    }

                    for (int i = 0; i < count; i++)
                    {
                        X509Certificate2 cert = elements[i].Certificate;

                        if (i == last &&
                            IncludeOption == X509IncludeOption.ExcludeRoot &&
                            cert.SubjectName.RawData.AsSpan().SequenceEqual(cert.IssuerName.RawData))
                        {
                            break;
                        }

                        certs.Add(cert);
                    }
                }
            }

            chainCerts = certs;
            return(newSignerInfo);
        }
Example #5
0
        private byte[] InitKey(int mNumCyclesPower, byte[] salt, byte[] pass)
        {
            if (mNumCyclesPower == 0x3F)
            {
                var key = new byte[32];

                int pos;
                for (pos = 0; pos < salt.Length; pos++)
                {
                    key[pos] = salt[pos];
                }

                for (int i = 0; i < pass.Length && pos < 32; i++)
                {
                    key[pos++] = pass[i];
                }

                return(key);
            }
            else
            {
#if NETSTANDARD1_3 || NETSTANDARD2_0
                using (IncrementalHash sha = IncrementalHash.CreateHash(HashAlgorithmName.SHA256))
                {
                    byte[] counter   = new byte[8];
                    long   numRounds = 1L << mNumCyclesPower;
                    for (long round = 0; round < numRounds; round++)
                    {
                        sha.AppendData(salt, 0, salt.Length);
                        sha.AppendData(pass, 0, pass.Length);
                        sha.AppendData(counter, 0, 8);

                        // This mirrors the counter so we don't have to convert long to byte[] each round.
                        // (It also ensures the counter is little endian, which BitConverter does not.)
                        for (int i = 0; i < 8; i++)
                        {
                            if (++counter[i] != 0)
                            {
                                break;
                            }
                        }
                    }
                    return(sha.GetHashAndReset());
                }
#else
                using (var sha = SHA256.Create())
                {
                    byte[] counter   = new byte[8];
                    long   numRounds = 1L << mNumCyclesPower;
                    for (long round = 0; round < numRounds; round++)
                    {
                        sha.TransformBlock(salt, 0, salt.Length, null, 0);
                        sha.TransformBlock(pass, 0, pass.Length, null, 0);
                        sha.TransformBlock(counter, 0, 8, null, 0);

                        // This mirrors the counter so we don't have to convert long to byte[] each round.
                        // (It also ensures the counter is little endian, which BitConverter does not.)
                        for (int i = 0; i < 8; i++)
                        {
                            if (++counter[i] != 0)
                            {
                                break;
                            }
                        }
                    }

                    sha.TransformFinalBlock(counter, 0, 0);
                    return(sha.Hash);
                }
#endif
            }
        }
Example #6
0
 public static void UnknownDigestAlgorithm()
 {
     Assert.ThrowsAny <CryptographicException>(
         () => IncrementalHash.CreateHash(new HashAlgorithmName("SHA0")));
 }
Example #7
0
        /// <summary>
        /// Extracts the <see cref="ArchiveEntry"/> objects from a CPIO file.
        /// </summary>
        /// <param name="file">
        /// The CPIO file from which to extract the entries.
        /// </param>
        /// <returns>
        /// A list of <see cref="ArchiveEntry"/> objects representing the data in the CPIO file.
        /// </returns>
        public List <ArchiveEntry> FromCpio(CpioFile file)
        {
            List <ArchiveEntry> value = new List <ArchiveEntry>();

            byte[] buffer     = new byte[1024];
            byte[] fileHeader = null;

            while (file.Read())
            {
                fileHeader = null;

                ArchiveEntry entry = new ArchiveEntry()
                {
                    FileSize       = file.EntryHeader.FileSize,
                    Group          = "root",
                    Owner          = "root",
                    Inode          = file.EntryHeader.Ino,
                    Mode           = file.EntryHeader.FileMode,
                    Modified       = file.EntryHeader.LastModified,
                    TargetPath     = file.FileName,
                    Type           = ArchiveEntryType.None,
                    LinkTo         = string.Empty,
                    Sha256         = Array.Empty <byte>(),
                    SourceFilename = null,
                    IsAscii        = true
                };

                if (entry.Mode.HasFlag(LinuxFileMode.S_IFREG) && !entry.Mode.HasFlag(LinuxFileMode.S_IFLNK))
                {
                    using (var fileStream = file.Open())
                        using (var hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256))
                        {
                            int read;

                            while (true)
                            {
                                read = fileStream.Read(buffer, 0, buffer.Length);

                                if (fileHeader == null)
                                {
                                    fileHeader = new byte[read];
                                    Buffer.BlockCopy(buffer, 0, fileHeader, 0, read);
                                }

                                hasher.AppendData(buffer, 0, read);
                                entry.IsAscii = entry.IsAscii && fileHeader.All(c => c < 128);

                                if (read < buffer.Length)
                                {
                                    break;
                                }
                            }

                            entry.Sha256 = hasher.GetHashAndReset();
                        }

                    entry.Type = this.GetArchiveEntryType(fileHeader);
                }
                else if (entry.Mode.HasFlag(LinuxFileMode.S_IFLNK))
                {
                    using (var fileStrema = file.Open())
                        using (var reader = new StreamReader(fileStrema, Encoding.UTF8))
                        {
                            entry.LinkTo = reader.ReadToEnd();
                        }
                }
                else
                {
                    file.Skip();
                }

                if (entry.Mode.HasFlag(LinuxFileMode.S_IFDIR))
                {
                    entry.FileSize = 0x1000;
                }

                if (entry.TargetPath.StartsWith("."))
                {
                    entry.TargetPath = entry.TargetPath.Substring(1);
                }

                value.Add(entry);
            }

            return(value);
        }
 public IncrementalHasher()
 {
     _hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
     _buffer = ArrayPool <byte> .Shared.Rent(s_bufferByteSize);
 }
Example #9
0
        protected void AddFile(string entry, string relativePath, string prefix, List <ArchiveEntry> value, ITaskItem[] metadata)
        {
            var fileName = Path.GetFileName(entry);

            byte[] fileHeader = null;
            byte[] hash       = null;
            byte[] md5hash    = null;
            byte[] buffer     = new byte[1024];
            bool   isAscii    = true;

            var fileMetadata = metadata.SingleOrDefault(m => m.IsPublished() && string.Equals(relativePath, m.GetPublishedPath()));

            using (Stream fileStream = File.OpenRead(entry))
            {
                if (fileName.StartsWith(".") || fileStream.Length == 0)
                {
                    // Skip hidden and empty files - this would case rmplint errors.
                    return;
                }

                using (var hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256))
                    using (var md5hasher = IncrementalHash.CreateHash(HashAlgorithmName.MD5))
                    {
                        int read;

                        while (true)
                        {
                            read = fileStream.Read(buffer, 0, buffer.Length);

                            if (fileHeader == null)
                            {
                                fileHeader = new byte[read];
                                Buffer.BlockCopy(buffer, 0, fileHeader, 0, read);
                            }

                            hasher.AppendData(buffer, 0, read);
                            md5hasher.AppendData(buffer, 0, read);
                            isAscii = isAscii && buffer.All(c => c < 128);

                            if (read < buffer.Length)
                            {
                                break;
                            }
                        }

                        hash    = hasher.GetHashAndReset();
                        md5hash = md5hasher.GetHashAndReset();
                    }

                // Only support ELF32 and ELF64 colors; otherwise default to BLACK.
                ArchiveEntryType entryType = this.GetArchiveEntryType(fileHeader);

                var mode = LinuxFileMode.S_IROTH | LinuxFileMode.S_IRGRP | LinuxFileMode.S_IRUSR | LinuxFileMode.S_IFREG;

                if (entryType == ArchiveEntryType.Executable32 || entryType == ArchiveEntryType.Executable64)
                {
                    mode |= LinuxFileMode.S_IXOTH | LinuxFileMode.S_IXGRP | LinuxFileMode.S_IWUSR | LinuxFileMode.S_IXUSR;
                }

                // If a Linux path has been specified, use that one, else, use the default one based on the prefix
                // + current file name.
                string name = fileMetadata?.GetLinuxPath();

                if (name == null)
                {
                    name = prefix + "/" + fileName;
                }

                string linkTo = string.Empty;

                if (mode.HasFlag(LinuxFileMode.S_IFLNK))
                {
                    // Find the link text
                    int stringEnd = 0;

                    while (stringEnd < fileHeader.Length - 1 && fileHeader[stringEnd] != 0)
                    {
                        stringEnd++;
                    }

                    linkTo = Encoding.UTF8.GetString(fileHeader, 0, stringEnd + 1);
                    hash   = new byte[] { };
                }

                // If the user has chosen to override the file node, respect that
                var overridenFileMode = fileMetadata?.GetLinuxFileMode();

                if (overridenFileMode != null)
                {
                    // We expect the user to specify the file mode in its octal representation.
                    try
                    {
                        mode = (LinuxFileMode)Convert.ToUInt32(overridenFileMode, 8);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception($"Could not parse the file mode '{overridenFileMode}' for file '{name}'. Make sure to set the LinuxFileMode attriubute to an octal representation of a Unix file mode.");
                    }
                }

                ArchiveEntry archiveEntry = new ArchiveEntry()
                {
                    FileSize       = (uint)fileStream.Length,
                    Group          = fileMetadata.GetGroup(),
                    Owner          = fileMetadata.GetOwner(),
                    Modified       = File.GetLastAccessTimeUtc(entry),
                    SourceFilename = entry,
                    TargetPath     = name,
                    Sha256         = hash,
                    Md5Hash        = md5hash,
                    Type           = entryType,
                    LinkTo         = linkTo,
                    Inode          = this.inode++,
                    IsAscii        = isAscii,
                    Mode           = mode
                };

                value.Add(archiveEntry);
            }
        }
Example #10
0
        public static async Task <string> UploadStreamAsync(this S3Helper s3,
                                                            string bucketName,
                                                            string key,
                                                            Stream inputStream,
                                                            string keyId                        = null,
                                                            string contentType                  = "application/octet-stream",
                                                            bool throwIfAlreadyExists           = false,
                                                            int msTimeout                       = int.MaxValue,
                                                            CancellationToken cancellationToken = default(CancellationToken))
        {
            CancellationToken ct;

            void UpdateCancellationToken()
            {
                if (cancellationToken != null)
                {
                    ct = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken).Token;
                }
                else
                {
                    ct = new CancellationTokenSource().Token;
                }
            }

            UpdateCancellationToken();

            if (throwIfAlreadyExists &&
                await s3.ObjectExistsAsync(bucketName: bucketName, key: key, cancellationToken: ct)
                .TryCancelAfter(ct, msTimeout: msTimeout))
            {
                throw new Exception($"Object {key} in bucket {bucketName} already exists.");
            }

            if (keyId == "")
            {
                keyId = null;
            }

            if (keyId != null && !keyId.IsGuid())
            {
                UpdateCancellationToken();
                var alias = await(new KMSHelper(s3._credentials)).GetKeyAliasByNameAsync(name: keyId, cancellationToken: ct)
                            .TryCancelAfter(ct, msTimeout: msTimeout);
                keyId = alias.TargetKeyId;
            }

            var    bufferSize = 128 * 1024;
            var    blob = inputStream.ToMemoryBlob(maxLength: s3.MaxSinglePartSize, bufferSize: bufferSize);
            var    ih = IncrementalHash.CreateHash(HashAlgorithmName.MD5);
            string md5, etag;

            if (blob.Length < s3.MaxSinglePartSize)
            {
                UpdateCancellationToken();
                using (var ms = blob.CopyToMemoryStream(bufferSize: (int)blob.Length))
                {
                    var spResult = s3.PutObjectAsync(bucketName: bucketName, key: key, inputStream: ms, keyId: keyId, cancellationToken: ct, contentType: contentType)
                                   .TryCancelAfter(ct, msTimeout: msTimeout);

                    blob.Seek(0, SeekOrigin.Begin);
                    ih.AppendData(blob.ToArray());

                    md5  = ih.GetHashAndReset().ToHexString();
                    etag = (await spResult).ETag.Trim('"');
                    return(md5);
                }
            }

            UpdateCancellationToken();
            var init = await s3.InitiateMultipartUploadAsync(bucketName, key, contentType : contentType, keyId : keyId, cancellationToken : ct)
                       .TryCancelAfter(ct, msTimeout: msTimeout);

            var partNumber = 0;
            var tags       = new List <PartETag>();

            while (blob.Length > 0)
            {
                partNumber = ++partNumber;
                UpdateCancellationToken();

                //copy so new part can be read at the same time
                using (var ms = blob.CopyToMemoryStream(bufferSize: (int)blob.Length))
                {
                    var tUpload = s3.UploadPartAsync(
                        bucketName: bucketName,
                        key: key,
                        uploadId: init.UploadId,
                        partNumber: partNumber,
                        partSize: (int)ms.Length,
                        inputStream: ms,
                        progress: null,
                        cancellationToken: ct).TryCancelAfter(ct, msTimeout: msTimeout);

                    if (ct.IsCancellationRequested)
                    {
                        throw new OperationCanceledException("Operation was cancelled or timed out.");
                    }

                    if (blob.Length <= s3.DefaultPartSize) //read next part from input before stream gets uploaded
                    {
                        blob = inputStream.ToMemoryBlob(maxLength: s3.DefaultPartSize, bufferSize: bufferSize);
                    }

                    tags.Add(new PartETag(partNumber, (await tUpload).ETag));

                    ms.Seek(0, SeekOrigin.Begin);
                    ih.AppendData(ms.ToArray());
                }
            }

            UpdateCancellationToken();
            var mpResult = await s3.CompleteMultipartUploadAsync(
                bucketName : bucketName,
                key : key,
                uploadId : init.UploadId,
                partETags : tags,
                cancellationToken : ct).TryCancelAfter(ct, msTimeout: msTimeout);

            md5  = ih.GetHashAndReset().ToHexString();
            etag = mpResult.ETag.Trim('"');
            return(md5);
        }
 protected override object GetInitialState(BinarySerializationContext context)
 {
     return(IncrementalHash.CreateHash(HashAlgorithmName.SHA256));
 }
Example #12
0
        internal SignerInfoAsn Sign(
            ReadOnlyMemory <byte> data,
            string contentTypeOid,
            bool silent,
            out X509Certificate2Collection chainCerts)
        {
            HashAlgorithmName hashAlgorithmName = PkcsHelpers.GetDigestAlgorithm(DigestAlgorithm);
            IncrementalHash   hasher            = IncrementalHash.CreateHash(hashAlgorithmName);

            hasher.AppendData(data.Span);
            byte[] dataHash = hasher.GetHashAndReset();

            SignerInfoAsn newSignerInfo = default;

            newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm;

            // If the user specified attributes (not null, count > 0) we need attributes.
            // If the content type is null we're counter-signing, and need the message digest attr.
            // If the content type is otherwise not-data we need to record it as the content-type attr.
            if (SignedAttributes?.Count > 0 || contentTypeOid != Oids.Pkcs7Data)
            {
                List <AttributeAsn> signedAttrs = BuildAttributes(SignedAttributes);

                using (var writer = new AsnWriter(AsnEncodingRules.DER))
                {
                    writer.WriteOctetString(dataHash);
                    signedAttrs.Add(
                        new AttributeAsn
                    {
                        AttrType   = new Oid(Oids.MessageDigest, Oids.MessageDigest),
                        AttrValues = new[] { new ReadOnlyMemory <byte>(writer.Encode()) },
                    });
                }

                if (contentTypeOid != null)
                {
                    using (var writer = new AsnWriter(AsnEncodingRules.DER))
                    {
                        writer.WriteObjectIdentifier(contentTypeOid);
                        signedAttrs.Add(
                            new AttributeAsn
                        {
                            AttrType   = new Oid(Oids.ContentType, Oids.ContentType),
                            AttrValues = new[] { new ReadOnlyMemory <byte>(writer.Encode()) },
                        });
                    }
                }

                // Use the serializer/deserializer to DER-normalize the attribute order.
                SignedAttributesSet signedAttrsSet = default;
                signedAttrsSet.SignedAttributes = PkcsHelpers.NormalizeAttributeSet(
                    signedAttrs.ToArray(),
                    normalized => hasher.AppendData(normalized));

                // Since this contains user data in a context where BER is permitted, use BER.
                // There shouldn't be any observable difference here between BER and DER, though,
                // since the top level fields were written by NormalizeSet.
                using (AsnWriter attrsWriter = new AsnWriter(AsnEncodingRules.BER))
                {
                    signedAttrsSet.Encode(attrsWriter);
                    newSignerInfo.SignedAttributes = attrsWriter.Encode();
                }

                dataHash = hasher.GetHashAndReset();
            }

            switch (SignerIdentifierType)
            {
            case SubjectIdentifierType.IssuerAndSerialNumber:
                byte[] serial = Certificate.GetSerialNumber();
                Array.Reverse(serial);

                newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
                {
                    Issuer       = Certificate.IssuerName.RawData,
                    SerialNumber = serial,
                };

                newSignerInfo.Version = 1;
                break;

            case SubjectIdentifierType.SubjectKeyIdentifier:
                newSignerInfo.Sid.SubjectKeyIdentifier = PkcsPal.Instance.GetSubjectKeyIdentifier(Certificate);
                newSignerInfo.Version = 3;
                break;

            case SubjectIdentifierType.NoSignature:
                newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
                {
                    Issuer       = SubjectIdentifier.DummySignerEncodedValue,
                    SerialNumber = new byte[1],
                };
                newSignerInfo.Version = 1;
                break;

            default:
                Debug.Fail($"Unresolved SignerIdentifierType value: {SignerIdentifierType}");
                throw new CryptographicException();
            }

            if (UnsignedAttributes != null && UnsignedAttributes.Count > 0)
            {
                List <AttributeAsn> attrs = BuildAttributes(UnsignedAttributes);

                newSignerInfo.UnsignedAttributes = PkcsHelpers.NormalizeAttributeSet(attrs.ToArray());
            }

            bool signed;
            Oid  signatureAlgorithm;
            ReadOnlyMemory <byte> signatureValue;

            if (SignerIdentifierType == SubjectIdentifierType.NoSignature)
            {
                signatureAlgorithm = new Oid(Oids.NoSignature, null);
                signatureValue     = dataHash;
                signed             = true;
            }
            else
            {
                signed = CmsSignature.Sign(
                    dataHash,
                    hashAlgorithmName,
                    Certificate,
                    PrivateKey,
                    silent,
                    out signatureAlgorithm,
                    out signatureValue);
            }

            if (!signed)
            {
                throw new CryptographicException(SR.Cryptography_Cms_CannotDetermineSignatureAlgorithm);
            }

            newSignerInfo.SignatureValue = signatureValue;
            newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm;

            X509Certificate2Collection certs = new X509Certificate2Collection();

            certs.AddRange(Certificates);

            if (SignerIdentifierType != SubjectIdentifierType.NoSignature)
            {
                if (IncludeOption == X509IncludeOption.EndCertOnly)
                {
                    certs.Add(Certificate);
                }
                else if (IncludeOption != X509IncludeOption.None)
                {
                    X509Chain chain = new X509Chain();
                    chain.ChainPolicy.RevocationMode    = X509RevocationMode.NoCheck;
                    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;

                    if (!chain.Build(Certificate))
                    {
                        foreach (X509ChainStatus status in chain.ChainStatus)
                        {
                            if (status.Status == X509ChainStatusFlags.PartialChain)
                            {
                                throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain);
                            }
                        }
                    }

                    X509ChainElementCollection elements = chain.ChainElements;
                    int count = elements.Count;
                    int last  = count - 1;

                    if (last == 0)
                    {
                        // If there's always one cert treat it as EE, not root.
                        last = -1;
                    }

                    for (int i = 0; i < count; i++)
                    {
                        X509Certificate2 cert = elements[i].Certificate;

                        if (i == last &&
                            IncludeOption == X509IncludeOption.ExcludeRoot &&
                            cert.SubjectName.RawData.AsSpan().SequenceEqual(cert.IssuerName.RawData))
                        {
                            break;
                        }

                        certs.Add(cert);
                    }
                }
            }

            chainCerts = certs;
            return(newSignerInfo);
        }
        public void SealWithMac(
            ReadOnlySpan <char> password,
            HashAlgorithmName hashAlgorithm,
            int iterationCount)
        {
            if (iterationCount < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(iterationCount));
            }
            if (IsSealed)
            {
                throw new InvalidOperationException(SR.Cryptography_Pkcs12_PfxIsSealed);
            }

            byte[]? rentedAuthSafe = null;
            Span <byte> authSafeSpan = default;

            byte[]? rentedMac = null;
            Span <byte> macSpan = default;
            Span <byte> salt    = stackalloc byte[0];

            try
            {
                AsnWriter contentsWriter = new AsnWriter(AsnEncodingRules.BER);

                using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithm))
                {
                    contentsWriter.PushSequence();
                    if (_contents != null)
                    {
                        foreach (ContentInfoAsn contentInfo in _contents)
                        {
                            contentInfo.Encode(contentsWriter);
                        }
                    }
                    contentsWriter.PopSequence();

                    rentedAuthSafe = CryptoPool.Rent(contentsWriter.GetEncodedLength());

                    if (!contentsWriter.TryEncode(rentedAuthSafe, out int written))
                    {
                        Debug.Fail("TryEncode failed with a pre-allocated buffer");
                        throw new InvalidOperationException();
                    }

                    authSafeSpan = rentedAuthSafe.AsSpan(0, written);

                    // Get an array of the proper size for the hash.
                    byte[] macKey = hasher.GetHashAndReset();
                    rentedMac = CryptoPool.Rent(macKey.Length);
                    macSpan   = rentedMac.AsSpan(0, macKey.Length);

                    // Since the biggest supported hash is SHA-2-512 (64 bytes), the
                    // 128-byte cap here shouldn't ever come into play.
                    Debug.Assert(macKey.Length <= 128);
                    salt = stackalloc byte[Math.Min(macKey.Length, 128)];
                    RandomNumberGenerator.Fill(salt);

                    Pkcs12Kdf.DeriveMacKey(
                        password,
                        hashAlgorithm,
                        iterationCount,
                        salt,
                        macKey);

                    using (IncrementalHash mac = IncrementalHash.CreateHMAC(hashAlgorithm, macKey))
                    {
                        mac.AppendData(authSafeSpan);

                        if (!mac.TryGetHashAndReset(macSpan, out int bytesWritten) || bytesWritten != macSpan.Length)
                        {
                            Debug.Fail($"TryGetHashAndReset wrote {bytesWritten} of {macSpan.Length} bytes");
                            throw new CryptographicException();
                        }
                    }
                }

                // https://tools.ietf.org/html/rfc7292#section-4
                //
                // PFX ::= SEQUENCE {
                //   version    INTEGER {v3(3)}(v3,...),
                //   authSafe   ContentInfo,
                //   macData    MacData OPTIONAL
                // }
                AsnWriter writer = new AsnWriter(AsnEncodingRules.BER);
                {
                    writer.PushSequence();

                    writer.WriteInteger(3);

                    writer.PushSequence();
                    {
                        writer.WriteObjectIdentifierForCrypto(Oids.Pkcs7Data);

                        Asn1Tag contextSpecific0 = new Asn1Tag(TagClass.ContextSpecific, 0);

                        writer.PushSequence(contextSpecific0);
                        {
                            writer.WriteOctetString(authSafeSpan);
                            writer.PopSequence(contextSpecific0);
                        }

                        writer.PopSequence();
                    }

                    // https://tools.ietf.org/html/rfc7292#section-4
                    //
                    // MacData ::= SEQUENCE {
                    //   mac        DigestInfo,
                    //   macSalt    OCTET STRING,
                    //   iterations INTEGER DEFAULT 1
                    //   -- Note: The default is for historical reasons and its use is
                    //   -- deprecated.
                    // }
                    writer.PushSequence();
                    {
                        writer.PushSequence();
                        {
                            writer.PushSequence();
                            {
                                writer.WriteObjectIdentifierForCrypto(PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm));
                                writer.PopSequence();
                            }

                            writer.WriteOctetString(macSpan);
                            writer.PopSequence();
                        }

                        writer.WriteOctetString(salt);

                        if (iterationCount > 1)
                        {
                            writer.WriteInteger(iterationCount);
                        }

                        writer.PopSequence();
                    }

                    writer.PopSequence();
                    _sealedData = writer.Encode();
                }
            }
            finally
            {
                CryptographicOperations.ZeroMemory(macSpan);
                CryptographicOperations.ZeroMemory(authSafeSpan);

                if (rentedMac != null)
                {
                    // Already cleared
                    CryptoPool.Return(rentedMac, clearSize: 0);
                }

                if (rentedAuthSafe != null)
                {
                    // Already cleared
                    CryptoPool.Return(rentedAuthSafe, clearSize: 0);
                }
            }
        }