| File: MachO\BinaryFormat\Blobs\EmbeddedSignatureBlob.cs | Web Access |
| Project: src\runtime\src\installer\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj (Microsoft.NET.HostModel) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable enable using System; using System.Collections.Immutable; using System.IO; namespace Microsoft.NET.HostModel.MachO; /// <summary> /// Format based off of https://github.com/apple-oss-distributions/Security/blob/3dab46a11f45f2ffdbd70e2127cc5a8ce4a1f222/OSX/libsecurity_codesigning/lib/cscdefs.h#L23 /// Code Signature data is always big endian / network order. /// The EmbeddedSignatureBlob is a SuperBlob that usually contains the CodeDirectoryBlob, RequirementsBlob, and CmsWrapperBlob. /// The RequirementsBlob and CmsWrapperBlob may be null if the blob is not present in the read file (usually linker signed MachO files), /// but will be present in newly created signatures. /// </summary> internal sealed class EmbeddedSignatureBlob : IBlob { private SuperBlob _inner; public EmbeddedSignatureBlob(SuperBlob superBlob) { _inner = superBlob; if (superBlob.Magic != BlobMagic.EmbeddedSignature) { throw new InvalidDataException($"Invalid magic for EmbeddedSignatureBlob: {superBlob.Magic}"); } } /// <summary> /// Creates a new EmbeddedSignatureBlob with the specified blobs. /// </summary> public EmbeddedSignatureBlob( CodeDirectoryBlob codeDirectoryBlob, RequirementsBlob requirementsBlob, CmsWrapperBlob cmsWrapperBlob) { int blobCount = 3; var blobs = ImmutableArray.CreateBuilder<IBlob>(blobCount); var blobIndices = ImmutableArray.CreateBuilder<BlobIndex>(blobCount); uint expectedOffset = (uint)(sizeof(uint) * 3 + (BlobIndex.Size * blobCount)); blobs.Add(codeDirectoryBlob); blobIndices.Add(new BlobIndex(CodeDirectorySpecialSlot.CodeDirectory, expectedOffset)); expectedOffset += codeDirectoryBlob.Size; blobs.Add(requirementsBlob); blobIndices.Add(new BlobIndex(CodeDirectorySpecialSlot.Requirements, expectedOffset)); expectedOffset += requirementsBlob.Size; blobs.Add(cmsWrapperBlob); blobIndices.Add(new BlobIndex(CodeDirectorySpecialSlot.CmsWrapper, expectedOffset)); _inner = new SuperBlob(BlobMagic.EmbeddedSignature, blobIndices.MoveToImmutable(), blobs.MoveToImmutable()); } public BlobMagic Magic => _inner.Magic; public uint Size => _inner.Size; public uint SubBlobCount => _inner.SubBlobCount; /// <summary> /// The CodeDirectoryBlob. This is always present in the signature. /// </summary> public CodeDirectoryBlob CodeDirectoryBlob => (CodeDirectoryBlob)GetBlob(BlobMagic.CodeDirectory, throwIfNotFound: true)!; /// <summary> /// The RequirementsBlob. This may be null if the blob is not present in the read file, but will be present in newly created signatures /// </summary> public RequirementsBlob? RequirementsBlob => GetBlob(BlobMagic.Requirements) as RequirementsBlob; /// <summary> /// The CmsWrapperBlob. This may be null if the blob is not present in the read file, but will be present in newly created signatures /// </summary> public CmsWrapperBlob? CmsWrapperBlob => GetBlob(BlobMagic.CmsWrapper) as CmsWrapperBlob; public uint GetSpecialSlotHashCount() { uint maxSlot = 0; foreach (var b in _inner.BlobIndices) { // Blobs that have special slots hashes have their slot value in the lower 8 bits. // CMSWrapperBlob has a special slot value of 0x1000 and does not have a hash. uint slot = 0xFF & (uint)b.Slot; if (slot > maxSlot) { maxSlot = slot; } } return maxSlot; } public int Write(IMachOFileWriter writer, long offset) { return _inner.Write(writer, offset); } /// <summary> /// Gets the largest size estimate for a code signature. /// </summary> public static unsafe long GetLargestSizeEstimate(uint fileSize, string identifier, byte? hashSize = null) { byte usedHashSize = hashSize ?? CodeDirectoryBlob.DefaultHashType.GetHashSize(); long size = 0; // SuperBlob header size += sizeof(BlobMagic); size += sizeof(uint); // Blob size size += sizeof(uint); // Blob count size += sizeof(BlobIndex) * 3; // 3 sub-blobs: CodeDirectory, Requirements, CmsWrapper // CodeDirectoryBlob size += sizeof(BlobMagic); size += sizeof(uint); // Blob size size += sizeof(CodeDirectoryBlob.CodeDirectoryHeader); // CodeDirectory header size += CodeDirectoryBlob.GetIdentifierLength(identifier); // Identifier size += (long)CodeDirectoryBlob.GetCodeSlotCount(fileSize) * usedHashSize; // Code hashes size += (long)(uint)CodeDirectorySpecialSlot.Requirements * usedHashSize; // Special code hashes size += RequirementsBlob.Empty.Size; // Requirements is always written as an empty blob size += CmsWrapperBlob.Empty.Size; // CMS blob is always written as an empty blob return size; } /// <summary> /// Returns the size of a signature used to replace an existing one. /// If the existing signature is null, it will assume sizing using the default signature, which includes the Requirements and CMS blobs. /// </summary> internal static unsafe long GetSignatureSize(uint fileSize, string identifier, byte? hashSize = null) { byte usedHashSize = hashSize ?? CodeDirectoryBlob.DefaultHashType.GetHashSize(); uint specialCodeSlotCount = (uint)CodeDirectorySpecialSlot.Requirements; uint embeddedSignatureSubBlobCount = 3; // CodeDirectory, Requirements, CMS Wrapper are always present // Calculate the size of the new signature long size = 0; // EmbeddedSignature size += sizeof(BlobMagic); // Signature blob Magic number size += sizeof(uint); // Size field size += sizeof(uint); // Blob count size += sizeof(BlobIndex) * embeddedSignatureSubBlobCount; // EmbeddedSignature sub-blobs // CodeDirectory size += sizeof(BlobMagic); // CD Magic number size += sizeof(uint); // CD Size field size += sizeof(CodeDirectoryBlob.CodeDirectoryHeader); // CodeDirectory header size += CodeDirectoryBlob.GetIdentifierLength(identifier); // Identifier size += specialCodeSlotCount * usedHashSize; // Special code hashes size += CodeDirectoryBlob.GetCodeSlotCount(fileSize) * usedHashSize; // Code hashes // RequirementsBlob size += RequirementsBlob.Empty.Size; // CmsWrapperBlob size += CmsWrapperBlob.Empty.Size; return size; } private IBlob? GetBlob(BlobMagic magic, bool throwIfNotFound = false) { foreach (var b in _inner.Blobs) { if (b.Magic == magic) { return b; } } if (throwIfNotFound) { throw new InvalidOperationException($"{magic} blob not found."); } return null; } public static void AssertEquivalent(EmbeddedSignatureBlob? a, EmbeddedSignatureBlob? b) { if (a == null && b == null) return; if (a == null || b == null) throw new ArgumentNullException("Both EmbeddedSignatureBlobs must be non-null for comparison."); if (a.GetSpecialSlotHashCount() != b.GetSpecialSlotHashCount()) throw new ArgumentException("Special slot hash counts are not equivalent."); if (!a.CodeDirectoryBlob.Equals(b.CodeDirectoryBlob)) throw new ArgumentException("CodeDirectory blobs are not equivalent"); if (a.RequirementsBlob?.Size != b.RequirementsBlob?.Size) throw new ArgumentException("Requirements blobs are not equivalent"); if (a.CmsWrapperBlob?.Size != b.CmsWrapperBlob?.Size) throw new ArgumentException("CMS Wrapper blobs are not equivalent"); } }