Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Verifying a CMS detached signature in pyobjc on macOS
import objc
from ctypes import create_string_buffer, c_void_p, cast
from Foundation import NSBundle
Security = NSBundle.bundleWithIdentifier_('com.apple.security')
# CMSDecoder.h
kCMSSignerUnsigned = 0
kCMSSignerValid = 1
kCMSSignerNeedsDetachedContent = 2
kCMSSignerInvalidSignature = 3
kCMSSignerInvalidCert = 4
kCMSSignerInvalidIndex = 5
# cssmerr.h - you don't need all of these, only the ones you want to check for
CSSMERR_TP_AUTHENTICATION_FAILED = -2147409657
CSSMERR_TP_CERTGROUP_INCOMPLETE = -2147409656
CSSMERR_TP_CERTIFICATE_CANT_OPERATE = -2147409655
CSSMERR_TP_CERT_EXPIRED = -2147409654
CSSMERR_TP_CERT_NOT_VALID_YET = -2147409653
CSSMERR_TP_CERT_REVOKED = -2147409652
CSSMERR_TP_CERT_SUSPENDED = -2147409651
CSSMERR_TP_CRL_ALREADY_SIGNED = -2147409849
CSSMERR_TP_DEVICE_FAILED = -2147409691
CSSMERR_TP_DEVICE_RESET = -2147409692
CSSMERR_TP_FUNCTION_FAILED = -2147409910
CSSMERR_TP_FUNCTION_NOT_IMPLEMENTED = -2147409913
CSSMERR_TP_INSUFFICIENT_CLIENT_IDENTIFICATION = -2147409693
CSSMERR_TP_INSUFFICIENT_CREDENTIALS = -2147409650
CSSMERR_TP_INTERNAL_ERROR = -2147409919
CSSMERR_TP_INVALID_ACTION = -2147409649
CSSMERR_TP_INVALID_ACTION_DATA = -2147409648
CSSMERR_TP_INVALID_ANCHOR_CERT = -2147409646
CSSMERR_TP_INVALID_AUTHORITY = -2147409645
CSSMERR_TP_INVALID_CALLBACK = -2147409625
CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER = -2147409663
CSSMERR_TP_INVALID_CERTGROUP = -2147409660
CSSMERR_TP_INVALID_CERTGROUP_POINTER = -2147409854
CSSMERR_TP_INVALID_CERTIFICATE = -2147409643
CSSMERR_TP_INVALID_CERT_AUTHORITY = -2147409642
CSSMERR_TP_INVALID_CERT_POINTER = -2147409853
CSSMERR_TP_INVALID_CL_HANDLE = -2147409838
CSSMERR_TP_INVALID_CONTEXT_HANDLE = -2147409856
CSSMERR_TP_INVALID_CRL = -2147409638
CSSMERR_TP_INVALID_CRLGROUP = -2147409659
CSSMERR_TP_INVALID_CRLGROUP_POINTER = -2147409658
CSSMERR_TP_INVALID_CRL_AUTHORITY = -2147409641
CSSMERR_TP_INVALID_CRL_ENCODING = -2147409640
CSSMERR_TP_INVALID_CRL_POINTER = -2147409852
CSSMERR_TP_INVALID_CRL_TYPE = -2147409639
CSSMERR_TP_INVALID_CSP_HANDLE = -2147409840
CSSMERR_TP_INVALID_DATA = -2147409850
CSSMERR_TP_INVALID_DB_HANDLE = -2147409846
CSSMERR_TP_INVALID_DB_LIST = -2147409844
CSSMERR_TP_INVALID_DB_LIST_POINTER = -2147409843
CSSMERR_TP_INVALID_DL_HANDLE = -2147409839
CSSMERR_TP_INVALID_FIELD_POINTER = -2147409851
CSSMERR_TP_INVALID_FORM_TYPE = -2147409637
CSSMERR_TP_INVALID_ID = -2147409636
CSSMERR_TP_INVALID_IDENTIFIER = -2147409635
CSSMERR_TP_INVALID_IDENTIFIER_POINTER = -2147409662
CSSMERR_TP_INVALID_INDEX = -2147409634
CSSMERR_TP_INVALID_INPUT_POINTER = -2147409915
CSSMERR_TP_INVALID_KEYCACHE_HANDLE = -2147409661
CSSMERR_TP_INVALID_NAME = -2147409633
CSSMERR_TP_INVALID_NETWORK_ADDR = -2147409833
CSSMERR_TP_INVALID_NUMBER_OF_FIELDS = -2147409848
CSSMERR_TP_INVALID_OUTPUT_POINTER = -2147409914
CSSMERR_TP_INVALID_PASSTHROUGH_ID = -2147409834
CSSMERR_TP_INVALID_POINTER = -2147409916
CSSMERR_TP_INVALID_POLICY_IDENTIFIERS = -2147409632
CSSMERR_TP_INVALID_REASON = -2147409630
CSSMERR_TP_INVALID_REQUEST_INPUTS = -2147409629
CSSMERR_TP_INVALID_RESPONSE_VECTOR = -2147409628
CSSMERR_TP_INVALID_SIGNATURE = -2147409627
CSSMERR_TP_INVALID_STOP_ON_POLICY = -2147409626
CSSMERR_TP_INVALID_TIMESTRING = -2147409631
CSSMERR_TP_INVALID_TUPLE = -2147409624
CSSMERR_TP_INVALID_TUPLEGROUP = -2147409614
CSSMERR_TP_INVALID_TUPLEGROUP_POINTER = -2147409615
CSSMERR_TP_IN_DARK_WAKE = -2147409690
CSSMERR_TP_MDS_ERROR = -2147409917
CSSMERR_TP_MEMORY_ERROR = -2147409918
CSSMERR_TP_NOT_SIGNER = -2147409623
CSSMERR_TP_NOT_TRUSTED = -2147409622
CSSMERR_TP_NO_DEFAULT_AUTHORITY = -2147409621
CSSMERR_TP_NO_USER_INTERACTION = -2147409696
CSSMERR_TP_OS_ACCESS_DENIED = -2147409911
CSSMERR_TP_REJECTED_FORM = -2147409620
CSSMERR_TP_REQUEST_LOST = -2147409619
CSSMERR_TP_REQUEST_REJECTED = -2147409618
CSSMERR_TP_SELF_CHECK_FAILED = -2147409912
CSSMERR_TP_SERVICE_NOT_AVAILABLE = -2147409694
CSSMERR_TP_UNKNOWN_FORMAT = -2147409842
CSSMERR_TP_UNKNOWN_TAG = -2147409841
CSSMERR_TP_UNSUPPORTED_ADDR_TYPE = -2147409617
CSSMERR_TP_UNSUPPORTED_SERVICE = -2147409616
CSSMERR_TP_USER_CANCELED = -2147409695
CSSMERR_TP_VERIFICATION_FAILURE = -2147409847
CSSMERR_TP_VERIFY_ACTION_FAILED = -2147409644
_ = [
('CMSDecoderGetTypeID', 'Q'),
('SecPolicyGetTypeID', 'Q'),
('SecTrustGetTypeID', 'Q'),
]
objc.loadBundleFunctions(Security, globals(), _)
CMSDecoderRef = objc.registerCFSignature('CMSDecoderRef', '^{_CMSDecoder=}', CMSDecoderGetTypeID())
SecPolicyRef = objc.registerCFSignature('SecPolicyRef', '^{OpaqueSecPolicyRef=}', SecPolicyGetTypeID())
SecTrustRef = objc.registerCFSignature('SecTrustRef', '^{__SecTrust=}', SecTrustGetTypeID())
_ = [
('CMSDecoderCreate', 'io^^{_CMSDecoder}'),
('CMSDecoderSetDetachedContent', 'i^{_CMSDecoder=}^{__CFData=}'),
('CMSDecoderUpdateMessage', 'i^{_CMSDecoder=}^vQ'),
('CMSDecoderFinalizeMessage', 'i^{_CMSDecoder=}'),
('CMSDecoderCopySignerStatus', 'i^{_CMSDecoder=}Q@Bo^Io^^{__SecTrust}o^i'),
('SecPolicyCreateBasicX509', '^{OpaqueSecPolicyRef=}'),
]
objc.loadBundleFunctions(Security, globals(), _)
def check_detached_sig(message_path, signature_path, signer_index=0, trust_policy=None, evaluate=True):
# Read in the message
f = open(message_path, 'rb')
message = f.read()
f.close()
message_bytes = buffer(message)
# Read in the signature
f = open(signature_path, 'rb')
signature = f.read()
f.close()
signature_bytes = cast(create_string_buffer(signature), c_void_p)
result, decoder = CMSDecoderCreate(None)
result = CMSDecoderSetDetachedContent(decoder, message_bytes)
# For some reason CMSDecoderUpdateMessage takes a freaking raw buffer
result = CMSDecoderUpdateMessage(decoder, signature_bytes.value, len(signature))
result = CMSDecoderFinalizeMessage(decoder)
if trust_policy is None:
trust_policy = SecPolicyCreateBasicX509()
# returns: result, signer_status, sec_trust, cert_verify_result
return CMSDecoderCopySignerStatus(decoder, signer_index, trust_policy, evaluate, None, None, None)
# Example usage:
result, signer_status, sec_trust, cert_verify_result = check_detached_sig('test.txt', 'test.txt.sig')
# In testing, I was able to generate a self-signed cert and create a detached signature:
# openssl smime -sign -signer signer_cert.pem -inkey signer_key.pem -binary -in test.txt -outform der -out test.txt.sig
#
# When tested against check_detached_sig, I received kCMSSignerInvalidCert (4) for signer_status
# which indicates that the message was properly signed, but the signer's cert is unknown regarding trust.
#
# Modifying the test.txt (but re-using the same signature), I received kCMSSignerInvalidSignature (3) as expected.
#
# By importing the signer_cert.pem into the Keychain and then marking it as trusted (since it's self-signed), I was
# able to achieve kCMSSignerValid (1).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.