]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/signer.cpp
Security-59306.61.1.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / signer.cpp
index daa2dac7b30357987d9221680acbdd972ef1357c..8927923867b57a600c09081f2e6c09af6272c2ae 100644 (file)
@@ -24,6 +24,8 @@
 //
 // signer - Signing operation supervisor and controller
 //
 //
 // signer - Signing operation supervisor and controller
 //
+#include "bundlediskrep.h"
+#include "der_plist.h"
 #include "signer.h"
 #include "resources.h"
 #include "signerutils.h"
 #include "signer.h"
 #include "resources.h"
 #include "signerutils.h"
@@ -58,10 +60,22 @@ void SecCodeSigner::Signer::sign(SecCSFlags flags)
 
        PreSigningContext context(*this);
 
 
        PreSigningContext context(*this);
 
+       considerTeamID(context);
+
+       if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
+               signMachO(fat, context);
+       } else {
+               signArchitectureAgnostic(context);
+       }
+}
+       
+       
+void SecCodeSigner::Signer::considerTeamID(const PreSigningContext& context)
+{
        /* If an explicit teamID was passed in it must be
         the same as what came from the cert */
        std::string teamIDFromCert = state.getTeamIDFromSigner(context.certs);
        /* If an explicit teamID was passed in it must be
         the same as what came from the cert */
        std::string teamIDFromCert = state.getTeamIDFromSigner(context.certs);
-
+       
        if (state.mPreserveMetadata & kSecCodeSignerPreserveTeamIdentifier) {
                /* If preserving the team identifier, teamID is set previously when the
                 code object is still available */
        if (state.mPreserveMetadata & kSecCodeSignerPreserveTeamIdentifier) {
                /* If preserving the team identifier, teamID is set previously when the
                 code object is still available */
@@ -81,14 +95,8 @@ void SecCodeSigner::Signer::sign(SecCSFlags flags)
                        MacOSError::throwMe(errSecCSInvalidFlags);
                }
        }
                        MacOSError::throwMe(errSecCSInvalidFlags);
                }
        }
-
-       if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
-               signMachO(fat, context);
-       } else {
-               signArchitectureAgnostic(context);
-       }
 }
 }
-
+       
 
 //
 // Remove any existing code signature from code
 
 //
 // Remove any existing code signature from code
@@ -100,9 +108,13 @@ void SecCodeSigner::Signer::remove(SecCSFlags flags)
                MacOSError::throwMe(errSecCSNotSupported);
 
        rep = code->diskRep();
                MacOSError::throwMe(errSecCSNotSupported);
 
        rep = code->diskRep();
+
+       if (state.mPreserveAFSC)
+               rep->writer()->setPreserveAFSC(state.mPreserveAFSC);
+
        if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
                // architecture-sensitive removal
        if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
                // architecture-sensitive removal
-               MachOEditor editor(rep->writer(), *fat, kSecCodeSignatureNoHash, rep->mainExecutablePath());
+               MachOEditor editor(rep->writer(), *fat, digestAlgorithms(), rep->mainExecutablePath());
                editor.allocate();              // create copy
                editor.commit();                // commit change
        } else {
                editor.allocate();              // create copy
                editor.commit();                // commit change
        } else {
@@ -121,7 +133,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
 {
        // make sure the rep passes strict validation
        if (strict)
 {
        // make sure the rep passes strict validation
        if (strict)
-               rep->strictValidate(NULL, MacOSErrorSet());
+               rep->strictValidate(NULL, MacOSErrorSet(), flags | (kSecCSQuickCheck|kSecCSRestrictSidebandData));
        
        // initialize progress/cancellation state
        code->prepareProgress(0);                       // totally fake workload - we don't know how many files we'll encounter
        
        // initialize progress/cancellation state
        code->prepareProgress(0);                       // totally fake workload - we don't know how many files we'll encounter
@@ -138,14 +150,14 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
        if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier))
                identifier = code->identifier();
        if (identifier.empty()) {
        if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier))
                identifier = code->identifier();
        if (identifier.empty()) {
-               identifier = rep->recommendedIdentifier(state);
+               identifier = rep->recommendedIdentifier(*this);
                if (identifier.find('.') == string::npos)
                        identifier = state.mIdentifierPrefix + identifier;
                if (identifier.find('.') == string::npos)
                        identifier = state.mIdentifierPrefix + identifier;
-               if (identifier.find('.') == string::npos && state.isAdhoc())
+               if (identifier.find('.') == string::npos && isAdhoc())
                        identifier = identifier + "-" + uniqueName();
                        identifier = identifier + "-" + uniqueName();
-               secdebug("signer", "using default identifier=%s", identifier.c_str());
+               secinfo("signer", "using default identifier=%s", identifier.c_str());
        } else
        } else
-               secdebug("signer", "using explicit identifier=%s", identifier.c_str());
+               secinfo("signer", "using explicit identifier=%s", identifier.c_str());
 
        teamID = state.mTeamID;
        if (teamID.empty() && (inherit & kSecCodeSignerPreserveTeamIdentifier)) {
 
        teamID = state.mTeamID;
        if (teamID.empty() && (inherit & kSecCodeSignerPreserveTeamIdentifier)) {
@@ -154,15 +166,22 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
                        teamID = c_id;
        }
        
                        teamID = c_id;
        }
        
+       // Digest algorithms: explicit or preserved. Subject to diskRep defaults or final default later.
+       hashAlgorithms = state.mDigestAlgorithms;
+       if (hashAlgorithms.empty() && (inherit & kSecCodeSignerPreserveDigestAlgorithm))
+               hashAlgorithms = code->hashAlgorithms();
+       
        entitlements = state.mEntitlementData;
        if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements))
                entitlements = code->component(cdEntitlementSlot);
        entitlements = state.mEntitlementData;
        if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements))
                entitlements = code->component(cdEntitlementSlot);
+
+       generateEntitlementDER = signingFlags() & kSecCSSignGenerateEntitlementDER;
        
        // work out the CodeDirectory flags word
        bool haveCdFlags = false;
        if (!haveCdFlags && state.mCdFlagsGiven) {
                cdFlags = state.mCdFlags;
        
        // work out the CodeDirectory flags word
        bool haveCdFlags = false;
        if (!haveCdFlags && state.mCdFlagsGiven) {
                cdFlags = state.mCdFlags;
-               secdebug("signer", "using explicit cdFlags=0x%x", cdFlags);
+               secinfo("signer", "using explicit cdFlags=0x%x", cdFlags);
                haveCdFlags = true;
        }
        if (!haveCdFlags) {
                haveCdFlags = true;
        }
        if (!haveCdFlags) {
@@ -171,10 +190,10 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
                        if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) {
                                if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
                                        cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags));
                        if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) {
                                if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
                                        cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags));
-                                       secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
+                                       secinfo("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
                                } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
                                        cdFlags = cdTextFlags(cfString(CFStringRef(csflags)));
                                } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
                                        cdFlags = cdTextFlags(cfString(CFStringRef(csflags)));
-                                       secdebug("signer", "using text cdFlags=0x%x from Info.plist", cdFlags);
+                                       secinfo("signer", "using text cdFlags=0x%x from Info.plist", cdFlags);
                                } else
                                        MacOSError::throwMe(errSecCSBadDictionaryFormat);
                                haveCdFlags = true;
                                } else
                                        MacOSError::throwMe(errSecCSBadDictionaryFormat);
                                haveCdFlags = true;
@@ -182,12 +201,13 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
        }
        if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) {
                cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc;
        }
        if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) {
                cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc;
-               secdebug("signer", "using inherited cdFlags=0x%x", cdFlags);
+               secinfo("signer", "using inherited cdFlags=0x%x", cdFlags);
                haveCdFlags = true;
        }
        if (!haveCdFlags)
                cdFlags = 0;
                haveCdFlags = true;
        }
        if (!haveCdFlags)
                cdFlags = 0;
-       if (state.mSigner == SecIdentityRef(kCFNull))   // ad-hoc signing requested...
+       if ((state.mSigner == SecIdentityRef(kCFNull)) &&
+               !state.mOmitAdhocFlag)  // ad-hoc signing requested...
                cdFlags |= kSecCodeSignatureAdhoc;      // ... so note that
 
        // prepare the internal requirements input
                cdFlags |= kSecCodeSignatureAdhoc;      // ... so note that
 
        // prepare the internal requirements input
@@ -211,9 +231,11 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
        
        // prepare the resource directory, if any
        string rpath = rep->resourcesRootPath();
        
        // prepare the resource directory, if any
        string rpath = rep->resourcesRootPath();
+       string rrpath;
+       CFCopyRef<CFDictionaryRef> resourceRules;
        if (!rpath.empty()) {
                // explicitly given resource rules always win
        if (!rpath.empty()) {
                // explicitly given resource rules always win
-               CFCopyRef<CFDictionaryRef> resourceRules = state.mResourceRules;
+               resourceRules = state.mResourceRules;
                
                // inherited rules come next (overriding embedded ones!)
                if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules))
                
                // inherited rules come next (overriding embedded ones!)
                if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules))
@@ -240,37 +262,126 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
 
                // finally, ask the DiskRep for its default
                if (!resourceRules)
 
                // finally, ask the DiskRep for its default
                if (!resourceRules)
-                       resourceRules.take(rep->defaultResourceRules(state));
+                       resourceRules.take(rep->defaultResourceRules(*this));
 
                // resource root can optionally be the canonical bundle path,
                // but sealed resource paths are always relative to rpath
 
                // resource root can optionally be the canonical bundle path,
                // but sealed resource paths are always relative to rpath
-               string root = rpath;
-               if (state.signingFlags() & kSecCSSignBundleRoot)
-                       root = cfStringRelease(rep->copyCanonicalPath());
-
-               // build the resource directory
-               buildResources(root, rpath, resourceRules);
+               rrpath = rpath;
+               if (signingFlags() & kSecCSSignBundleRoot)
+                       rrpath = cfStringRelease(rep->copyCanonicalPath());
        }
        
        // screen and set the signing time
        }
        
        // screen and set the signing time
-       CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
        if (state.mSigningTime == CFDateRef(kCFNull)) {
        if (state.mSigningTime == CFDateRef(kCFNull)) {
-               signingTime = 0;                // no time at all
+               emitSigningTime = false;                // no time at all
        } else if (!state.mSigningTime) {
        } else if (!state.mSigningTime) {
-               signingTime = now;              // default
+               emitSigningTime = true;
+               signingTime = 0;                        // wall clock, established later
        } else {
                CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
        } else {
                CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
-               if (time > now) // not allowed to post-date a signature
+               if (time > CFAbsoluteTimeGetCurrent())  // not allowed to post-date a signature
                        MacOSError::throwMe(errSecCSBadDictionaryFormat);
                        MacOSError::throwMe(errSecCSBadDictionaryFormat);
+               emitSigningTime = true;
                signingTime = time;
        }
        
                signingTime = time;
        }
        
-       pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(state);
-    
-    // Timestamping setup
-    CFRef<SecIdentityRef> mTSAuth;     // identity for client-side authentication to the Timestamp server
+       pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(*this);
+       
+       // Allow the DiskRep to modify the signing parameters. This sees explicit and inherited values but not defaults.
+       rep->prepareForSigning(*this);
+       
+       // apply some defaults after diskRep intervention
+       if (hashAlgorithms.empty()) {   // default to SHA256 + SHA-1
+               hashAlgorithms.insert(kSecCodeSignatureHashSHA1);
+               hashAlgorithms.insert(kSecCodeSignatureHashSHA256);
+       }
+       
+       // build the resource directory (once and for all, using the digests determined above)
+       if (!rpath.empty()) {
+               buildResources(rrpath, rpath, resourceRules);
+       }
+
+       
+       
+       if (inherit & kSecCodeSignerPreservePEH) {
+               /* We need at least one architecture in all cases because we index our
+                * PreEncryptionMaps by architecture. However, only machOs have any
+                * architecture at all, for generic targets there will just be one
+                * PreEncryptionHashMap.
+                * So if the main executable is not a machO, we just choose the local
+                * (signer's) main architecture as dummy value for the first element in our pair. */
+               preEncryptMainArch = (code->diskRep()->mainExecutableIsMachO() ?
+                                                         code->diskRep()->mainExecutableImage()->bestNativeArch() :
+                                                          Architecture::local());
+
+               addPreEncryptHashes(preEncryptHashMaps[preEncryptMainArch], code);
+               
+               code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) {
+                       Universal *fat = subcode->diskRep()->mainExecutableImage();
+                       assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
+                       Architecture arch = fat->bestNativeArch();      // actually, only architecture for this slice.
+                       addPreEncryptHashes(preEncryptHashMaps[arch], subcode);
+               });
+       }
+
+       if (inherit & kSecCodeSignerPreserveRuntime) {
+               /* We need at least one architecture in all cases because we index our
+                * RuntimeVersionMaps by architecture. However, only machOs have any
+                * architecture at all, for generic targets there will just be one
+                * RuntimeVersionMap.
+                * So if the main executable is not a machO, we just choose the local
+                * (signer's) main architecture as dummy value for the first element in our pair. */
+               runtimeVersionMainArch = (code->diskRep()->mainExecutableIsMachO() ?
+                                                         code->diskRep()->mainExecutableImage()->bestNativeArch() :
+                                                         Architecture::local());
+
+               addRuntimeVersions(runtimeVersionMap[runtimeVersionMainArch], code);
+
+               code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) {
+                       Universal *fat = subcode->diskRep()->mainExecutableImage();
+                       assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
+                       Architecture arch = fat->bestNativeArch();      // actually, only architecture for this slice.
+                       addRuntimeVersions(runtimeVersionMap[arch], subcode);
+               });
+       }
 }
 
 }
 
+void SecCodeSigner::Signer::addPreEncryptHashes(PreEncryptHashMap &map, SecStaticCode const *code) {
+       SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories();
+       
+       if (cds != NULL) {
+               for(auto const& pair : *cds) {
+                       CodeDirectory::HashAlgorithm const alg = pair.first;
+                       CFDataRef const cddata = pair.second;
+                       
+                       CodeDirectory const * cd =
+                       reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata));
+                       if (cd->preEncryptHashes() != NULL) {
+                               CFRef<CFDataRef> preEncrypt = makeCFData(cd->preEncryptHashes(),
+                                                                                                                cd->nCodeSlots * cd->hashSize);
+                               map[alg] = preEncrypt;
+                       }
+               }
+       }
+}
+
+void SecCodeSigner::Signer::addRuntimeVersions(RuntimeVersionMap &map, const SecStaticCode *code)
+{
+       SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories();
+
+       if (cds != NULL) {
+               for(auto const& pair : *cds) {
+                       CodeDirectory::HashAlgorithm const alg = pair.first;
+                       CFDataRef const cddata = pair.second;
+
+                       CodeDirectory const * cd =
+                       reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata));
+                       if (cd->runtimeVersion()) {
+                               map[alg] = cd->runtimeVersion();
+                       }
+               }
+       }
+}
 
 //
 // Collect the resource seal for a program.
 
 //
 // Collect the resource seal for a program.
@@ -280,7 +391,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase
 {
        typedef ResourceBuilder::Rule Rule;
        
 {
        typedef ResourceBuilder::Rule Rule;
        
-       secdebug("codesign", "start building resource directory");
+       secinfo("codesign", "start building resource directory");
        __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
        
        CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules");
        __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
        
        CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules");
@@ -295,10 +406,11 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase
        }
 
        CFDictionaryRef files2 = NULL;
        }
 
        CFDictionaryRef files2 = NULL;
-       if (!(state.signingFlags() & kSecCSSignV1)) {
+       if (!(signingFlags() & kSecCSSignV1)) {
                CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2");
                if (!rules2) {
                CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2");
                if (!rules2) {
-                       // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules).
+            // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules,
+            // because the default weight, according to ResourceBuilder::addRule(), is 1).
                        // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
                        rules2 = cfmake<CFDictionaryRef>("{+%O"
                                "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
                        // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
                        rules2 = cfmake<CFDictionaryRef>("{+%O"
                                "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
@@ -311,7 +423,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase
                // build the modern (V2) resource seal
                __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
                CFMutableDictionaryRef filesRef = files.get();  // (into block)
                // build the modern (V2) resource seal
                __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
                CFMutableDictionaryRef filesRef = files.get();  // (into block)
-               ResourceBuilder resourceBuilder(root, relBase, rules2, digestAlgorithm(), strict, MacOSErrorSet());
+               ResourceBuilder resourceBuilder(root, relBase, rules2, strict, MacOSErrorSet());
                ResourceBuilder &resources = resourceBuilder;   // (into block)
                rep->adjustResources(resources);
 
                ResourceBuilder &resources = resourceBuilder;   // (into block)
                rep->adjustResources(resources);
 
@@ -331,8 +443,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase
                                        target[len] = '\0';
                                        seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target));
                                } else {
                                        target[len] = '\0';
                                        seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target));
                                } else {
-                                       seal.take(cfmake<CFMutableDictionaryRef>("{hash=%O}",
-                                               CFRef<CFDataRef>(resources.hashFile(accpath.c_str())).get()));
+                                       seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight));
                                }
                                if (ruleFlags & ResourceBuilder::optional)
                                        CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
                                }
                                if (ruleFlags & ResourceBuilder::optional)
                                        CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
@@ -352,10 +463,10 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase
        }
        
        CFDictionaryAddValue(result, CFSTR("rules"), rules);    // preserve V1 rules in any case
        }
        
        CFDictionaryAddValue(result, CFSTR("rules"), rules);    // preserve V1 rules in any case
-       if (!(state.signingFlags() & kSecCSSignNoV1)) {
+       if (!(signingFlags() & kSecCSSignNoV1)) {
                // build the legacy (V1) resource seal
                __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
                // build the legacy (V1) resource seal
                __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
-               ResourceBuilder resourceBuilder(root, relBase, rules, digestAlgorithm(), strict, MacOSErrorSet());
+               ResourceBuilder resourceBuilder(root, relBase, rules, strict, MacOSErrorSet());
                ResourceBuilder &resources = resourceBuilder;
                rep->adjustResources(resources);        // DiskRep-specific adjustments
                resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) {
                ResourceBuilder &resources = resourceBuilder;
                rep->adjustResources(resources);        // DiskRep-specific adjustments
                resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) {
@@ -369,14 +480,14 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase
                                                        hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash")));
                                        }
                                if (!hash)
                                                        hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash")));
                                        }
                                if (!hash)
-                                       hash.take(resources.hashFile(ent->fts_accpath));
+                                       hash.take(resources.hashFile(ent->fts_accpath, kSecCodeSignatureHashSHA1));
                                if (ruleFlags == 0) {   // default case - plain hash
                                        cfadd(files, "{%s=%O}", relpath.c_str(), hash.get());
                                if (ruleFlags == 0) {   // default case - plain hash
                                        cfadd(files, "{%s=%O}", relpath.c_str(), hash.get());
-                                       secdebug("csresource", "%s added simple (rule %p)", relpath.c_str(), rule);
+                                       secinfo("csresource", "%s added simple (rule %p)", relpath.c_str(), rule);
                                } else {        // more complicated - use a sub-dictionary
                                        cfadd(files, "{%s={hash=%O,optional=%B}}",
                                                relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional);
                                } else {        // more complicated - use a sub-dictionary
                                        cfadd(files, "{%s={hash=%O,optional=%B}}",
                                                relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional);
-                                       secdebug("csresource", "%s added complex (rule %p)", relpath.c_str(), rule);
+                                       secinfo("csresource", "%s added complex (rule %p)", relpath.c_str(), rule);
                                }
                        }
                });
                                }
                        }
                });
@@ -396,12 +507,14 @@ CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path
        // sign nested code and collect nesting information
        try {
                SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
        // sign nested code and collect nesting information
        try {
                SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
-               if (state.signingFlags() & kSecCSSignNestedCode)
-                       this->state.sign(code, state.signingFlags());
+               if (signingFlags() & kSecCSSignNestedCode)
+                       this->state.sign(code, signingFlags());
                std::string dr = Dumper::dump(code->designatedRequirement());
                std::string dr = Dumper::dump(code->designatedRequirement());
-               return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}",
-                       Dumper::dump(code->designatedRequirement()).c_str(),
-                       code->cdHash());
+               if (CFDataRef hash = code->cdHash())
+                       return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}",
+                               Dumper::dump(code->designatedRequirement()).c_str(),
+                               hash);
+               MacOSError::throwMe(errSecCSUnsigned);
        } catch (const CommonError &err) {
                CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase()));
        }
        } catch (const CommonError &err) {
                CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase()));
        }
@@ -417,9 +530,14 @@ CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path
 void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context)
 {
        // Mach-O executable at the core - perform multi-architecture signing
 void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context)
 {
        // Mach-O executable at the core - perform multi-architecture signing
+       RefPointer<DiskRep::Writer> writer = rep->writer();
+
+       if (state.mPreserveAFSC)
+               writer->setPreserveAFSC(state.mPreserveAFSC);
+
        auto_ptr<ArchEditor> editor(state.mDetached
                ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
        auto_ptr<ArchEditor> editor(state.mDetached
                ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
-               : new MachOEditor(rep->writer(), *fat, this->digestAlgorithm(), rep->mainExecutablePath()));
+               : new MachOEditor(writer, *fat, this->digestAlgorithms(), rep->mainExecutablePath()));
        assert(editor->count() > 0);
        if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
                populate(*editor);
        assert(editor->count() > 0);
        if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
                populate(*editor);
@@ -435,12 +553,32 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context
                                MacOSError::throwMe(errSecCSBadLVArch);
                        }
                }
                                MacOSError::throwMe(errSecCSBadLVArch);
                        }
                }
-               
-               arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, state), context);
+
+               bool mainBinary = arch.source.get()->type() == MH_EXECUTE;
+
+               uint32_t runtimeVersion = 0;
+               if (cdFlags & kSecCodeSignatureRuntime) {
+                       runtimeVersion = state.mRuntimeVersionOverride ? state.mRuntimeVersionOverride : arch.source.get()->sdkVersion();
+               }
+
+               arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, *this), context);
                if (editor->attribute(writerNoGlobal))  // can't store globally, add per-arch
                        populate(arch);
                if (editor->attribute(writerNoGlobal))  // can't store globally, add per-arch
                        populate(arch);
-               populate(arch.cdbuilder, arch, arch.ireqs,
-                       arch.source->offset(), arch.source->signingExtent());
+               for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
+                       uint32_t runtimeVersionToUse = runtimeVersion;
+                       if ((cdFlags & kSecCodeSignatureRuntime) && runtimeVersionMap.count(arch.architecture)) {
+                               if (runtimeVersionMap[arch.architecture].count(*type)) {
+                                       runtimeVersionToUse = runtimeVersionMap[arch.architecture][*type];
+                               }
+                       }
+                       arch.eachDigest(^(CodeDirectory::Builder& builder) {
+                               populate(builder, arch, arch.ireqs,
+                                                arch.source->offset(), arch.source->signingExtent(),
+                                                mainBinary, rep->execSegBase(&(arch.architecture)), rep->execSegLimit(&(arch.architecture)),
+                                                unsigned(digestAlgorithms().size()-1),
+                                                preEncryptHashMaps[arch.architecture], runtimeVersionToUse);
+                       });
+               }
        
                // add identification blob (made from this architecture) only if we're making a detached signature
                if (state.mDetached) {
        
                // add identification blob (made from this architecture) only if we're making a detached signature
                if (state.mDetached) {
@@ -450,8 +588,11 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context
                }
                
                // prepare SuperBlob size estimate
                }
                
                // prepare SuperBlob size estimate
-               size_t cdSize = arch.cdbuilder.size(CodeDirectory::currentVersion);
-               arch.blobSize = arch.size(cdSize, state.mCMSSize, 0);
+               __block std::vector<size_t> sizes;
+               arch.eachDigest(^(CodeDirectory::Builder& builder){
+                       sizes.push_back(builder.size(CodeDirectory::currentVersion));
+               });
+               arch.blobSize = arch.size(sizes, state.mCMSSize, 0);
        }
        
        editor->allocate();
        }
        
        editor->allocate();
@@ -461,12 +602,19 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context
                MachOEditor::Arch &arch = *it->second;
                editor->reset(arch);
 
                MachOEditor::Arch &arch = *it->second;
                editor->reset(arch);
 
-               // finish CodeDirectory (off new binary) and sign it
-               CodeDirectory *cd = arch.cdbuilder.build();
-               CFRef<CFDataRef> signature = signCodeDirectory(cd);
+               // finish CodeDirectories (off new binary) and sign it
+               __block CodeDirectorySet cdSet;
+               arch.eachDigest(^(CodeDirectory::Builder &builder) {
+                       CodeDirectory *cd = builder.build();
+                       cdSet.add(cd);
+               });
+
+               CFRef<CFDictionaryRef> hashDict = cdSet.hashDict();
+               CFRef<CFArrayRef> hashList = cdSet.hashList();
+               CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList);
                
                // complete the SuperBlob
                
                // complete the SuperBlob
-               arch.add(cdCodeDirectorySlot, cd);      // takes ownership
+               cdSet.populate(&arch);
                arch.add(cdSignatureSlot, BlobWrapper::alloc(
                        CFDataGetBytePtr(signature), CFDataGetLength(signature)));
                if (!state.mDryRun) {
                arch.add(cdSignatureSlot, BlobWrapper::alloc(
                        CFDataGetBytePtr(signature), CFDataGetLength(signature)));
                if (!state.mDryRun) {
@@ -476,8 +624,9 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context
        }
        
        // done: write edit copy back over the original
        }
        
        // done: write edit copy back over the original
-       if (!state.mDryRun)
+       if (!state.mDryRun) {
                editor->commit();
                editor->commit();
+       }
 }
 
 
 }
 
 
@@ -490,26 +639,46 @@ void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context
        // non-Mach-O executable - single-instance signing
        RefPointer<DiskRep::Writer> writer = state.mDetached ?
                (new DetachedBlobWriter(*this)) : rep->writer();
        // non-Mach-O executable - single-instance signing
        RefPointer<DiskRep::Writer> writer = state.mDetached ?
                (new DetachedBlobWriter(*this)) : rep->writer();
-       CodeDirectory::Builder builder(state.mDigestAlgorithm);
-       InternalRequirements ireqs;
-       ireqs(requirements, rep->defaultRequirements(NULL, state), context);
-       populate(*writer);
-       populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit());
+
+       if(state.mPreserveAFSC)
+               writer->setPreserveAFSC(state.mPreserveAFSC);
+
+       CodeDirectorySet cdSet;
+
+       for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
+               CodeDirectory::Builder builder(*type);
+               InternalRequirements ireqs;
+               ireqs(requirements, rep->defaultRequirements(NULL, *this), context);
+               populate(*writer);
+               populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit(),
+                                false,         // only machOs can currently be main binaries
+                                rep->execSegBase(NULL), rep->execSegLimit(NULL),
+                                unsigned(digestAlgorithms().size()-1),
+                                preEncryptHashMaps[preEncryptMainArch], // Only one map, the default.
+                                (cdFlags & kSecCodeSignatureRuntime) ? state.mRuntimeVersionOverride : 0);
+               
+               CodeDirectory *cd = builder.build();
+               if (!state.mDryRun)
+                       cdSet.add(cd);
+       }
        
        // add identification blob (made from this architecture) only if we're making a detached signature
        if (state.mDetached) {
                CFRef<CFDataRef> identification = rep->identification();
                writer->component(cdIdentificationSlot, identification);
        
        // add identification blob (made from this architecture) only if we're making a detached signature
        if (state.mDetached) {
                CFRef<CFDataRef> identification = rep->identification();
                writer->component(cdIdentificationSlot, identification);
-       }       
-       
-       CodeDirectory *cd = builder.build();
-       CFRef<CFDataRef> signature = signCodeDirectory(cd);
-       if (!state.mDryRun) {
-               writer->codeDirectory(cd);
-               writer->signature(signature);
-               writer->flush();
        }
        }
-       ::free(cd);
+
+       // write out all CodeDirectories
+       if (!state.mDryRun)
+               cdSet.populate(writer);
+
+       CFRef<CFDictionaryRef> hashDict = cdSet.hashDict();
+       CFRef<CFArrayRef> hashList = cdSet.hashList();
+       CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList);
+       writer->signature(signature);
+       
+       // commit to storage
+       writer->flush();
 }
 
 
 }
 
 
@@ -530,7 +699,11 @@ void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
 // for the purposes of this call.
 //
 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
 // for the purposes of this call.
 //
 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
-       InternalRequirements &ireqs, size_t offset /* = 0 */, size_t length /* = 0 */)
+                                                                        InternalRequirements &ireqs, size_t offset, size_t length,
+                                                                        bool mainBinary, size_t execSegBase, size_t execSegLimit,
+                                                                        unsigned alternateDigestCount,
+                                                                        PreEncryptHashMap const &preEncryptHashMap,
+                                                                        uint32_t runtimeVersion)
 {
        // fill the CodeDirectory
        builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
 {
        // fill the CodeDirectory
        builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
@@ -538,6 +711,10 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W
        builder.identifier(identifier);
        builder.teamID(teamID);
        builder.platform(state.mPlatform);
        builder.identifier(identifier);
        builder.teamID(teamID);
        builder.platform(state.mPlatform);
+       builder.execSeg(execSegBase, execSegLimit, mainBinary ? kSecCodeExecSegMainBinary : 0);
+       builder.generatePreEncryptHashes(signingFlags() & kSecCSSignGeneratePEH);
+       builder.preservePreEncryptHashMap(preEncryptHashMap);
+       builder.runTimeVersion(runtimeVersion);
 
        if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
                builder.specialSlot(cdInfoSlot, data);
 
        if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
                builder.specialSlot(cdInfoSlot, data);
@@ -548,24 +725,57 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W
        }
        if (resourceDirectory)
                builder.specialSlot(cdResourceDirSlot, resourceDictData);
        }
        if (resourceDirectory)
                builder.specialSlot(cdResourceDirSlot, resourceDictData);
-#if NOT_YET
-       if (state.mApplicationData)
-               builder.specialSlot(cdApplicationSlot, state.mApplicationData);
-#endif
        if (entitlements) {
                writer.component(cdEntitlementSlot, entitlements);
                builder.specialSlot(cdEntitlementSlot, entitlements);
        if (entitlements) {
                writer.component(cdEntitlementSlot, entitlements);
                builder.specialSlot(cdEntitlementSlot, entitlements);
+
+               if (mainBinary) {
+                       CFRef<CFDataRef> entitlementDER;
+                       uint64_t execSegFlags = 0;
+                       cookEntitlements(entitlements, generateEntitlementDER,
+                                                        &execSegFlags, &entitlementDER.aref());
+
+                       if (generateEntitlementDER) {
+                               writer.component(cdEntitlementDERSlot, entitlementDER);
+                               builder.specialSlot(cdEntitlementDERSlot, entitlementDER);
+                       }
+
+                       builder.addExecSegFlags(execSegFlags);
+               }
        }
        }
+       if (CFRef<CFDataRef> repSpecific = rep->component(cdRepSpecificSlot))
+               builder.specialSlot(cdRepSpecificSlot, repSpecific);
        
        writer.addDiscretionary(builder);
        
        writer.addDiscretionary(builder);
+       
+#if 0 // rdar://problem/25720754
+       if ((signingFlags() & (kSecCSSignOpaque|kSecCSSignV1)) == 0 && builder.hashType() != kSecCodeSignatureHashSHA1) {
+               // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any)
+               // (but not for opaque or V1 construction, which must remain bit-for-bit compatible)
+               std::vector<Endian<uint32_t> > slotVector;
+               slotVector.push_back(cdCodeDirectorySlot);      // mandatory
+               std::set<CodeDirectory::Slot> filledSlots = builder.filledSpecialSlots();
+               filledSlots.insert(cdTopDirectorySlot);         // will be added below
+               copy(filledSlots.begin(), filledSlots.end(), back_inserter(slotVector));
+               for (unsigned n = 0; n < alternateDigestCount; n++)
+                       slotVector.push_back(cdAlternateCodeDirectorySlots + n);
+               slotVector.push_back(cdSignatureSlot);
+               CFTempData cfSlotVector(&slotVector[0], slotVector.size() * sizeof(slotVector[0]));
+               writer.component(cdTopDirectorySlot, cfSlotVector);
+               builder.specialSlot(cdTopDirectorySlot, cfSlotVector);
+       }
+#endif
 }
 
 }
 
+       
 #include <security_smime/tsaSupport.h>
 
 //
 // Generate the CMS signature for a (finished) CodeDirectory.
 //
 #include <security_smime/tsaSupport.h>
 
 //
 // Generate the CMS signature for a (finished) CodeDirectory.
 //
-CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
+CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd,
+                                                                                                  CFDictionaryRef hashDict,
+                                                                                                  CFArrayRef hashList)
 {
        assert(state.mSigner);
        CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
 {
        assert(state.mSigner);
        CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
@@ -577,20 +787,37 @@ CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
        // generate CMS signature
        CFRef<CMSEncoderRef> cms;
        MacOSError::check(CMSEncoderCreate(&cms.aref()));
        // generate CMS signature
        CFRef<CMSEncoderRef> cms;
        MacOSError::check(CMSEncoderCreate(&cms.aref()));
-       MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
+       MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRootOrFail));
        CMSEncoderAddSigners(cms, state.mSigner);
        CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256);
        MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
        
        CMSEncoderAddSigners(cms, state.mSigner);
        CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256);
        MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
        
-       if (signingTime) {
+       if (emitSigningTime) {
                MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
                MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
-               MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime));
+               CFAbsoluteTime time = signingTime ? signingTime : CFAbsoluteTimeGetCurrent();
+               MacOSError::check(CMSEncoderSetSigningTime(cms, time));
+       }
+       
+       if (hashDict != NULL) {
+               assert(hashList != NULL);
+
+               // V2 Hash Agility
+
+               MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgilityV2));
+               MacOSError::check(CMSEncoderSetAppleCodesigningHashAgilityV2(cms, hashDict));
+
+               // V1 Hash Agility
+
+               CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashList);
+               CFRef<CFDataRef> hashAgilityV1Attribute = makeCFData(hashDict.get());
+
+               MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgility));
+               MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashAgilityV1Attribute));
        }
        
        MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
     
     // Set up to call Timestamp server if requested
        }
        
        MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
     
     // Set up to call Timestamp server if requested
-    
     if (state.mWantTimeStamp)
     {
         CFRef<CFErrorRef> error = NULL;
     if (state.mWantTimeStamp)
     {
         CFRef<CFErrorRef> error = NULL;
@@ -603,16 +830,40 @@ CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
                 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService);
             if (state.mNoTimeStampCerts)
                 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue);
                 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService);
             if (state.mNoTimeStampCerts)
                 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue);
-       }
+               }
             
             
-        CmsMessageSetTSAContext(cms, defaultTSContext);
+               CmsMessageSetTSAContext(cms, defaultTSContext);
     }
     }
-    
+       
        CFDataRef signature;
        MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
 
        return signature;
 }
        CFDataRef signature;
        MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
 
        return signature;
 }
+       
+       
+//
+// Our DiskRep::signingContext methods communicate with the signing subsystem
+// in terms those callers can easily understand.
+//
+string SecCodeSigner::Signer::sdkPath(const std::string &path) const
+{
+       assert(path[0] == '/'); // need absolute path here
+       if (state.mSDKRoot)
+               return cfString(state.mSDKRoot) + path;
+       else
+               return path;
+}
+
+bool SecCodeSigner::Signer::isAdhoc() const
+{
+       return state.mSigner == SecIdentityRef(kCFNull);
+}
+
+SecCSFlags SecCodeSigner::Signer::signingFlags() const
+{
+       return state.mOpFlags;
+}
 
 
 //
 
 
 //
@@ -665,6 +916,289 @@ std::string SecCodeSigner::Signer::uniqueName() const
        return result;
 }
 
        return result;
 }
 
+bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict, CFStringRef key) {
+       CFBooleanRef entValue = (CFBooleanRef)CFDictionaryGetValue(entDict, key);
+
+       if (entValue == NULL || CFGetTypeID(entValue) != CFBooleanGetTypeID()) {
+               return false;
+       }
+
+       return CFBooleanGetValue(entValue);
+}
+
+void SecCodeSigner::Signer::cookEntitlements(CFDataRef entitlements, bool generateDER,
+                                                                                        uint64_t *execSegFlags, CFDataRef *entitlementDER)
+{
+       if (!entitlements) {
+               return; // nothing to do.
+       }
+
+       EntitlementDERBlob *derBlob = NULL;
+
+       try {
+               const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlements));
+
+               if (blob == NULL || !blob->validateBlob(CFDataGetLength(entitlements))) {
+                       MacOSError::throwMe(errSecCSInvalidEntitlements);
+               }
+
+               CFRef<CFDictionaryRef> entDict = blob->entitlements();
+
+               if (generateDER) {
+                       CFRef<CFErrorRef> error = NULL;
+                       size_t const der_size = der_sizeof_plist(entDict, &error.aref());
+
+                       if (der_size == 0) {
+                               secerror("Getting DER size for entitlement plist failed: %@", error.get());
+                               MacOSError::throwMe(errSecCSInvalidEntitlements);
+                       }
+
+                       derBlob = EntitlementDERBlob::alloc(der_size);
+
+                       if (derBlob == NULL) {
+                               secerror("Cannot allocate buffer for DER entitlements of size %zu", der_size);
+                               MacOSError::throwMe(errSecCSInvalidEntitlements);
+                       }
+                       uint8_t * const der_end = derBlob->der() + der_size;
+                       uint8_t * const der_start = der_encode_plist(entDict, &error.aref(), derBlob->der(), der_end);
+
+                       if (der_start != derBlob->der()) {
+                               secerror("Entitlement DER start mismatch (%zu)", (size_t)(der_start - derBlob->der()));
+                               free(derBlob);
+                               MacOSError::throwMe(errSecCSInvalidEntitlements);
+                       }
+
+                       *entitlementDER = makeCFData(derBlob, derBlob->length());
+                       free(derBlob);
+                       derBlob = NULL;
+               }
+
+               if (execSegFlags != NULL) {
+                       uint64_t flags = 0;
+
+                       flags |= booleanEntitlement(entDict, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash : 0;
+
+                       *execSegFlags = flags;
+               }
+
+       } catch (const CommonError &err) {
+               free(derBlob);
+               // Not fatal if we're not asked to generate DER entitlements.
+
+               secwarning("failed to parse entitlements: %s", err.what());
+               if (generateDER) {
+                       throw;
+               }
+       }
+}
+
+//// Signature Editing
+       
+void SecCodeSigner::Signer::edit(SecCSFlags flags)
+{
+       rep = code->diskRep()->base();
+       
+       Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage();
+       
+       prepareForEdit(flags);
+       
+       if (fat != NULL) {
+               editMachO(fat);
+       } else {
+               editArchitectureAgnostic();
+       }
+}
+
+EditableDiskRep *SecCodeSigner::Signer::editMainExecutableRep(DiskRep *rep)
+{
+       EditableDiskRep *mainExecRep = NULL;
+       BundleDiskRep *bundleDiskRep = dynamic_cast<BundleDiskRep*>(rep);
+       
+       if (bundleDiskRep) {
+               mainExecRep = dynamic_cast<EditableDiskRep*>(bundleDiskRep->mainExecRep());
+       }
+       
+       return mainExecRep;
+}
+       
+void SecCodeSigner::Signer::prepareForEdit(SecCSFlags flags) {
+       setDigestAlgorithms(code->hashAlgorithms());
+       
+       Universal *machO = (code->diskRep()->mainExecutableIsMachO() ?
+                                               code->diskRep()->mainExecutableImage() : NULL);
+       
+       /* We need at least one architecture in all cases because we index our
+        * RawComponentMaps by architecture. However, only machOs have any
+        * architecture at all, for generic targets there will just be one
+        * RawComponentMap.
+        * So if the main executable is not a machO, we just choose the local
+        * (signer's) main architecture as dummy value for the first element in our pair. */
+       editMainArch = (machO != NULL ? machO->bestNativeArch() : Architecture::local());
+
+       if (machO != NULL) {
+               if (machO->narrowed()) {
+                       /* --arch gives us a narrowed SecStaticCode, but because
+                        * codesign_allocate always creates or replaces signatures
+                        * for all slices, we must operate on the universal
+                        * SecStaticCode. Instead, we provide --edit-arch to specify
+                        * which slices to edit, the others have their code signature
+                        * copied without modifications.
+                        */
+                       MacOSError::throwMe(errSecCSNotSupported,
+                                                               "Signature editing must be performed on universal binary instead of narrow slice (using --edit-arch instead of --arch).");
+               }
+
+               if (state.mEditArch && !machO->isUniversal()) {
+                       MacOSError::throwMe(errSecCSInvalidFlags,
+                                                               "--edit-arch is only valid for universal binaries.");
+               }
+               
+               if (state.mEditCMS && machO->isUniversal() && !state.mEditArch) {
+                       /* Each slice has its own distinct code signature,
+                        * so a CMS blob is only valid for its one slice.
+                        * Therefore, replacing all CMS blobs in all slices
+                        * with the same blob is rather nonsensical, and we refuse.
+                        *
+                        * (Universal binaries with only one slice can exist,
+                        * and in that case the slice to operate on would be
+                        * umambiguous, but we are not treating those binaries
+                        * specially and still want --edit-arch for consistency.)
+                        */
+                       MacOSError::throwMe(errSecCSNotSupported,
+                                                               "CMS editing must be performed on specific slice (specified with --edit-arch).");
+               }
+       }
+       
+       void (^editArch)(SecStaticCode *code, Architecture arch) =
+       ^(SecStaticCode *code, Architecture arch) {
+               EditableDiskRep *editRep = dynamic_cast<EditableDiskRep *>(code->diskRep());
+               
+               if (editRep == NULL) {
+                       MacOSError::throwMe(errSecCSNotSupported,
+                                                               "Signature editing not supported for code of this type.");
+               }
+               
+               EditableDiskRep *mainExecRep = editMainExecutableRep(code->diskRep());
+               
+               if (mainExecRep != NULL) {
+                       // Delegate editing to the main executable if it is an EditableDiskRep.
+                       //(Which is the case for machOs.)
+                       editRep = mainExecRep;
+               }
+
+               editComponents[arch] = std::make_unique<RawComponentMap>(editRep->createRawComponents());
+               
+               if (!state.mEditArch || arch == state.mEditArch) {
+                       if (state.mEditCMS) {
+                               CFDataRef cms = state.mEditCMS.get();
+                               (*editComponents[arch])[cdSignatureSlot] = cms;
+                       }
+               }
+       };
+       
+       editArch(code, editMainArch);
+       
+       code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) {
+               Universal *fat = subcode->diskRep()->mainExecutableImage();
+               assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
+               Architecture arch = fat->bestNativeArch();      // actually, only architecture for this slice.
+               editArch(subcode, arch);
+       });
+       
+       /* The resource dictionary is special, because it is
+        * considered "global" instead of per architecture.
+        * For editing, that means it's usually not embedded
+        * in the main executable's signature if it exists,
+        * but in the containing disk rep (e.g. the
+        * CodeResources file if the rep is a Bundle).
+        */
+       resourceDictData = rep->component(cdResourceDirSlot);
+}
+       
+void SecCodeSigner::Signer::editMachO(Universal *fat) {
+       // Mach-O executable at the core - perform multi-architecture signature editing
+       RefPointer<DiskRep::Writer> writer = rep->writer();
+       
+       if (state.mPreserveAFSC)
+               writer->setPreserveAFSC(state.mPreserveAFSC);
+       
+       unique_ptr<ArchEditor> editor(new MachOEditor(writer, *fat,
+                                                                                                 this->digestAlgorithms(),
+                                                                                                 rep->mainExecutablePath()));
+       assert(editor->count() > 0);
+       
+       if (resourceDictData && !editor->attribute(writerNoGlobal)) {
+               // For when the resource dict is "global", e.g. for bundles.
+               editor->component(cdResourceDirSlot, resourceDictData);
+       }
+       
+       for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
+               MachOEditor::Arch &arch = *it->second;
+               arch.source.reset(fat->architecture(it->first)); // transfer ownership
+               
+               if (resourceDictData && editor->attribute(writerNoGlobal)) {
+                       // Technically possible to embed a resource dict in the embedded sig.
+                       arch.component(cdResourceDirSlot, resourceDictData);
+               }
+               
+               for (auto const &entry : *editComponents[arch.architecture]) {
+                       CodeDirectory::Slot slot = entry.first;
+                       CFDataRef data = entry.second.get();
+                       arch.component(slot, data);
+               }
+               
+               /* We must preserve the original superblob's size, as the size is
+                * also in the macho's load commands, which are itself covered
+                * by the signature. */
+               arch.blobSize = arch.source->signingLength();
+       }
+       
+       editor->allocate();
+       
+       for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
+               MachOEditor::Arch &arch = *it->second;
+               editor->reset(arch);
+               
+               if (!state.mDryRun) {
+                       EmbeddedSignatureBlob *blob = arch.make();
+                       editor->write(arch, blob);      // takes ownership of blob
+               }
+       }
+       
+       if (!state.mDryRun) {
+               editor->commit();
+       }
+
+}
+
+void SecCodeSigner::Signer::editArchitectureAgnostic()
+{
+       if (state.mDryRun) {
+               return;
+
+       }
+       // non-Mach-O executable - single-instance signature editing
+       RefPointer<DiskRep::Writer> writer = rep->writer();
+       
+       if(state.mPreserveAFSC)
+               writer->setPreserveAFSC(state.mPreserveAFSC);
+       
+       for (auto const &entry : *editComponents[editMainArch]) {
+               CodeDirectory::Slot slot = entry.first;
+               CFDataRef data = entry.second.get();
+               
+               writer->component(slot, data);
+       }
+
+       // commit to storage
+       writer->flush();
+}
 
 } // end namespace CodeSigning
 } // end namespace Security
 
 } // end namespace CodeSigning
 } // end namespace Security