Code Signing Requirement Language

When you use the codesign command to sign a block of code, you can specify internal requirements; that is, the criteria that you recommend should be used to evaluate the code signature. It is up to the verifier to decide whether to apply the internal requirements or some other set of requirements when deciding how to treat the signed code. You use the code requirement language described in this chapter when specifying requirements to the codesign or csreq command (see the manual pages for codesign(1) and csreq(1)).

This chapter describes the requirement language source code. You can compile a set of requirements and save them in binary form using the csreq command. You can provide requirements to the codesign command either as source code or as a binary file. Both the codesign and csreq commands can convert a binary requirement set to text. Although there is some flexibility in the source code syntax (for example, quotes can always be used around string constants but are not always required), conversion from binary to text always uses the same form:

Language Syntax

Some basic features of the language syntax are:

Evaluation of Requirements

A requirement constitutes an expression without side effects. Each requirement can have any number of subexpressions, each of which is evaluated with a Boolean (succeed-fail) result. There is no defined order of evaluation. The subexpressions are combined using logical operators in the expression to yield an overall Boolean result for the expression. Depending on the operators used, an expression can succeed even if some subexpressions fail. For example, the expression

anchor apple or anchor = "/var/db/yourcorporateanchor.cert"

succeeds if either subexpression succeeds—that is, if the code was signed either by Apple or by your company—even though one of the subexpressions is sure to fail.

If an error occurs during evaluation, on the other hand, evaluation stops immediately and the codesign or csreq command returns with a result code indicating the reason for failure.

Constants

This section describes the use of string, integer, hash-value, and binary constants in the code signing requirement language.

String Constants

String constants must be enclosed by double quotes (" ") unless the string contains only letters, digits, and periods (.), in which case the quotes are optional. Absolute file paths, which start with a slash, do not require quotes unless they contain spaces. For example:

com.apple.mail                       //no quotes are required
"com.apple.mail"                     //quotes are optional
"My Company's signing identity"      //requires quotes for spaces and apostrophe
/Volumes/myCA/root.crt               //no quotes are required
"/Volumes/my CA/root.crt"            //space requires quotes
"/Volumes/my_CA/root.crt"            //underscore requires quotes

It’s never incorrect to enclose the string in quotes—if in doubt, use quotes.

Use a backslash to “escape” any character. For example:

"one \" embedded quote"              //one " embedded quote
"one \\ embedded backslash"          //one \ embedded backslash

There is nothing special about the single quote character (').

Integer Constants

Integer constants are written as decimal constants are in C. The language does not allow radix prefixes (such as 0x) or leading plus or minus (+ or -) signs.

Hash Constants

Hash values are written either as a hexadecimal number in quotes preceded by an H, or as a path to a file containing a binary certificate. If you use the first form, the number must include the exact number of digits in the hash value. A SHA-1 hash (the only kind currently supported) requires exactly 40 digits; for example:

H"0123456789ABCDEFFEDCBA98765432100A2BC5DA"

You can use either uppercase or lowercase letters (A..F or a..f) in the hexadecimal numbers.

If you specify a file path, the compiler reads the binary certificate and calculates the hash for you. The compiled version of the requirement code includes only the hash; the certificate file and the path are not retained. If you convert the requirement back to text, you get the hexadecimal hash constant. The file path must point to a file containing an X.509 DER encoded certificate. No container forms (PKCS7, PKCS12) are allowed, nor is the OpenSSL "PEM" form supported.

Variables

There are currently no variables in the requirement language.

Logical Operators

The requirement language includes the following logical operators, in order of decreasing precedence:

These operators can be used to combine subexpressions into more complex expressions. The negation operator (!) is a unary prefix operator. The others are infix operators. Parentheses can be used to override the precedence of the operators.

Because the language is free of side effects, evaluation order of subexpressions is unspecified.

Comparison Operations

The requirement language includes the following comparison operators:

The value-present (exists) operator is a unary suffix operator. The others are infix operators.

There are no operators for non-matches (not equal to, not greater than, and so on). Use the negation operator (!) together with the comparison operators to make non-match comparisons.

Equality

All equality operations compare some value to a constant. The value and constant must be of the same type: a string matches a string constant, a data value matches a hexadecimal constant. The equality operation evaluates to true if the value exists and is equal to the constant. String matching uses the same matching rules as CFString (see CFString Reference).

In match expressions (see “Info,” “Part of a Certificate,” and “Entitlement”), substrings of string constants can be matched by using the * wildcard character:

  • value = *constant* is true if the value exists and any substring of the value matches the constant; for example:

    • thunderbolt = *under*

    • thunderbolt = *thunder*

    • thunderbolt = *bolt*

  • value = constant* is true if the value exists and begins with the constant; for example:

    • thunderbolt = thunder*

    • thunderbolt = thun*

  • value = *constant is true if the value exists and ends with the constant; for example:

    • thunderbolt = *bolt

    • thunderbolt = *underbolt

If the constant is written with quotation marks, the asterisks must be outside the quotes. An asterisk inside the quotation marks is taken literally. For example:

  • "ten thunderbolts" = "ten thunder"* is true

  • "ten thunder*bolts" = "ten thunder*"* is true

  • "ten thunderbolts" = "ten thunder*" is false

Inequality

Inequality operations compare some value to a constant. The value and constant must be of the same type: a string matches a string constant, a data value matches a hexadecimal constant. String comparisons use the same matching rules as CFString with the kCFCompareNumerically option flag; for example, "17.4" is greater than "7.4".

Existence

The existence operator tests whether the value exists. It evaluates to false only if the value does not exist at all or is exactly the Boolean value false. An empty string and the number 0 are considered to exist.

Constraints

Several keywords in the requirement language are used to require that specific certificates be present or other conditions be met.

Identifier

The expression

identifier = constant

succeeds if the unique identifier string embedded in the code signature is exactly equal to constant. The equal sign is optional in identifier expressions. Signing identifiers can be tested only for exact equality; the wildcard character (*) can not be used with the identifier constraint, nor can identifiers be tested for inequality.

Info

The expression

info [key]match expression

succeeds if the value associated with the top-level key in the code’s info.plist file matches match expression, where match expression can include any of the operators listed in “Logical Operators” and “Comparison Operations.” For example:

info [CFBundleShortVersionString] < "17.4"

or

info [MySpecialMarker] exists

You must specify key as a string constant.

If the value of the specified key is a string, the match is applied to it directly. If the value is an array, it must be an array of strings and the match is made to each in turn, succeeding if any of them matches. Substrings of string constants can be matched by using any match expression (see “Comparison Operations”).

If the code has no info.plist file, or the info.plist does not contain the specified key, this expression evaluates to false without returning an error.

Certificate

Certificate constraints refer to certificates in the certificate chain used to validate the signature. Most uses of the certificate keyword accept an integer that indicates the position of the certificate in the chain: positive integers count from the leaf (0) toward the anchor. Negative integers count backward from the anchor (-1). For example, certificate 1 is the intermediate certificate that was used to sign the leaf (that is, the signing certificate), and certificate -2 indicates the certificate that was directly signed by the anchor. Note that this convention is the same as that used for array indexing in the Perl and Ruby programming languages:

Anchor

First intermediate

Second intermediate

Leaf

certificate 3

certificate 2

certificate 1

certificate 0

certificate -1

certificate -2

certificate -3

certificate -4

Other keywords include:

  • certificate root—the anchor certificate; same as certificate 0

  • anchor—same as certificate root

  • certificate leaf—the signing certificate; same as certificate -1

If there is no certificate at the specified position, the constraint evaluates to false without returning an error.

If the code was signed using an ad-hoc signature, there are no certificates at all and all certificate constraints evaluate to false. (An ad-hoc signature is created by signing with the pseudo-identity - (a dash). An ad-hoc signature does not use or record a cryptographic identity, and thus identifies exactly and only the one program being signed.)

If the code was signed by a self-signed certificate, then the leaf and root refer to the same single certificate.

Whole Certificate

To require a particular certificate to be present in the certificate chain, use the form

certificate position = hash

or one of the equivalent forms discussed above, such as anchor = hash. Hash constants are described in “Hash Constants.”

For Apple’s own code, signed by Apple, you can use the short form

anchor apple

For code signed by Apple, including code signed using a signing certificate issued by Apple to other developers, use the form

anchor apple generic

Part of a Certificate

To match a well-defined element of a certificate, use the form

certificate position[element]match expression

where match expression can include the * wildcard character and any of the operators listed in “Logical Operators” and “Comparison Operations.” The currently supported elements are as follows:

Element name

Meaning

Comments

subject.CN

Subject common name

Shown in Keychain Access utility

subject.C

Subject country name

subject.D

Subject description

subject.L

Subject locality

subject.O

Subject organization

Usually company or organization

subject.OU

Subject organizational unit

subject.STREET

Subject street address

Certificate field by OID

To check for the existence of any certificate field identified by its X.509 object identifier (OID), use the form

certificate position [field.OID] exists

The object identifier must be written in numeric form (x.y.z...) and can be the OID of a certificate extension or of a conventional element of a certificate as defined by the CSSM standard (see Chapter 31 in Common Security: CDSA and CSSM, version 2 (with corrigenda) by the Open Group (http://www.opengroup.org/security/cdsa.htm)).

Trusted

The expression

certificate position trusted

succeeds if the certificate specified by position is marked trusted for the code signing certificate policy in the system’s Trust Settings database. The position argument is an integer or keyword that indicates the position of the certificate in the chain; see the discussion under “Certificate.”

The expression

anchor trusted

succeeds if any certificate in the signature’s certificate chain is marked trusted for the code signing certificate policy in the system’s Trust Settings database, provided that no certificate closer to the leaf certificate is explicitly untrusted.

Thus, using the trusted keyword with a certificate position checks only the specified certificate, while using it with the anchor keyword checks all the certificates, giving precedence to the trust setting found closest to the leaf.

Certificates can have per-user trust settings and system-wide trust settings, and trust settings apply to specific policies. The trusted keyword in the code signing requirement language causes trust to be checked for the specified certificate or certificates for the user performing the validation. If there are no settings for that user, then the system settings are used. In all cases, only the trust settings for the code-signing policy are checked. Policies and trust are discussed in Certificate, Key, and Trust Services Programming Guide.

Entitlement

The expression

entitlement [key] match expression

succeeds if the value associated with the specified key in the signature’s embedded entitlement dictionary matches match expression, where match expression can include the * wildcard character and any of the operators listed in “Logical Operators” and “Comparison Operations.” You must specify key as a string constant. The entitlement dictionary is included in signatures for certain platforms.

Code Directory Hash

The expression

cdhash hash-constant

computes a SHA-1 hash of the program’s CodeDirectory resource and succeeds if the value of this hash exactly equals the specified hash constant.

The CodeDirectory resource is the master directory of the contents of the program. It consists of a versioned header followed by an array of hashes. This array consists of a set of optional special hashes for other resources, plus a vector of hashes for pages of the main executable. The CodeSignature and CodeDirectory resources together make up the signature of the code.

You can use the codesign utility with (at least) three levels of verbosity to obtain the hash constant of a program’s CodeDirectory resource:

    $ codesign -dvvv /bin/ls
    ...
    CodeDirectory v=20001 size=257 flags=0x0(none) hashes=8+2 location=embedded
    CDHash=4bccbc576205de37914a3023cae7e737a0b6a802
    ...

Because the code directory changes whenever the program changes in a nontrivial way, this test can be used to unambiguously identify one specific version of a program. When the operating system signs an otherwise unsigned program (so that the keychain or Parental Controls can recognize the program, for example), it uses this requirement.

Requirement Sets

A requirement set is a collection of distinct requirements, each indexed (tagged) with a type code. The expression

tag => requirement

applies requirement to the type of code indicated by tag, where possible tags are

The primary use of requirement sets is to represent the internal requirements of the signed code. For example:

    codesign -r='host => anchor apple and identifier com.apple.perl designated => anchor /my/root and identifier com.bar.foo'

sets the internal requirements of some code, having a host requirement of anchor apple and identifier com.apple.perl (“I'm a Perl script and I want to be run by Apple's Perl interpreter”) and an explicit designated requirement of anchor /my/root and identifier com.bar.foo. Note that this command sets no guest or library requirements.

You can also put the requirement set in a file and point to the file:

    codesign -r myrequirements.rqset

where the file myrequirements.rqset might contain:

    //internal requirements
      host => anchor apple and identifier com.apple.perl //require Apple's Perl interpreter
      designated => anchor /my/root and identifier com.bar.foo