snej@8: //
snej@8: //  CryptoDecoder.h
snej@8: //  Cloudy
snej@8: //
snej@8: //  Created by Jens Alfke on 1/16/08.
snej@8: //  Copyright 2008 Jens Alfke. All rights reserved.
snej@8: //
snej@8: 
snej@8: #import <Cocoa/Cocoa.h>
snej@8: #import <Security/CMSDecoder.h>
snej@8: 
snej@8: @class MYKeychain, MYCertificate;
snej@8: 
snej@8: 
snej@8: /** Decodes a CMS-formatted message into the original data, and identifies & verifies signatures. */
snej@8: @interface MYDecoder : NSObject 
snej@8: {
snej@8:     @private
snej@8:     CMSDecoderRef _decoder;
snej@8:     OSStatus _error;
snej@8:     SecPolicyRef _policy;
snej@8: }
snej@8: 
snej@8: /** Initializes a new decoder. */
snej@8: - (id) init;
snej@8: 
snej@8: /** Initializes a new decoder and reads the entire message data. */
snej@8: - (id) initWithData: (NSData*)data error: (NSError**)outError;
snej@8: 
snej@8: /** Specifies a keychain to use to look up certificates and keys, instead of the default
snej@8:     keychain search path. */
snej@8: - (BOOL) useKeychain: (MYKeychain*)keychain;
snej@8: 
snej@8: /** Adds data to the decoder. You can add the entire data at once, or in bits and pieces
snej@8:     (if you're reading it from a stream). */
snej@8: - (BOOL) addData: (NSData*)data;
snej@8: 
snej@8: /** The error, if any, that occurred while decoding the content.
snej@8:     If -addData: returns NO, read this property to find out what went wrong.
snej@8:     The most likely error is (NSOSStatusErrorDomain, errSecUnknownFormat). */
snej@8: @property (readonly) NSError *error;
snej@8: 
snej@8: /** If the message content is detached (stored separately from the encoded message),
snej@8:     you must copy it to this property before calling -finish, so that the decoder can use it
snej@8:     to verify signatures. */
snej@8: @property (retain) NSData *detachedContent;
snej@8: 
snej@8: /** Tells the decoder that all of the data has been read, after the last call to -addData:. 
snej@8:     You must call this before accessing the message content or metadata. */
snej@8: - (BOOL) finish;
snej@8: 
snej@8: /** The decoded message content. */
snej@8: @property (readonly) NSData* content;
snej@8: 
snej@8: /** YES if the message was signed. (Use the signers property to see who signed it.) */
snej@8: @property (readonly) BOOL isSigned;
snej@8: 
snej@8: /** YES if the message was encrypted. */
snej@8: @property (readonly) BOOL isEncrypted;
snej@8: 
snej@8: /** An array of MYSigner objects representing the identities who signed the message.
snej@8:     Nil if the message is unsigned. */
snej@8: @property (readonly) NSArray* signers;
snej@8:  
snej@8: /** All of the certificates (as MYCertificate objects) that were attached to the message. */
snej@8: @property (readonly) NSArray* certificates;
snej@8: 
snej@8: 
snej@8: /** @name Expert
snej@8:  *  Advanced methods. 
snej@8:  */
snej@8: //@{
snej@8: 
snej@8: /** The X.509 content-type of the message contents.
snej@8:     The Data field points to autoreleased memory: do not free it yourself, and do not
snej@8:     expect it to remain valid after the calling method returns. */
snej@8: @property (readonly) CSSM_OID contentType;
snej@8: 
snej@8: /** The Policy that will be used to evaluate trust when calling MYSigner.copyTrust.
snej@8:     NULL by default. */
snej@8: @property (assign) SecPolicyRef policy;
snej@8: 
snej@8: /** Returns a string with detailed information about the message metadata.
snej@8:     Not user-presentable; used for debugging. */
snej@8: - (NSString*) dump;
snej@8: 
snej@8: //@}
snej@8: 
snej@8: @end
snej@8: 
snej@8: 
snej@8: /** Represents a signer of a CMS message, as returned by the MYDecoder.signers property. */
snej@8: @interface MYSigner : NSObject
snej@8: {
snej@8:     @private
snej@8:     CMSDecoderRef _decoder;
snej@8:     size_t _index;
snej@8:     CFTypeRef _policy;
snej@8:     CMSSignerStatus _status;
snej@8:     OSStatus _verifyResult;
snej@8:     SecTrustRef _trust;
snej@8: }
snej@8: 
snej@8: /** The status of the signature, i.e. whether it's valid or not.
snej@8:  *  Values include:
snej@8:  *	  kCMSSignerValid               :both signature and signer certificate verified OK.
snej@8:  *	  kCMSSignerNeedsDetachedContent:the MYDecoder's detachedContent property must be set,
snej@8:  *                                   to ascertain the signature status.
snej@8:  *	  kCMSSignerInvalidSignature    :bad signature -- either the content or the signature
snej@8:  *                                   data were tampered with after the message was encoded.
snej@8:  *	  kCMSSignerInvalidCert         :an error occurred verifying the signer's certificate.
snej@8:  *							         Further information available via the verifyResult
snej@8:  *                                   and copyTrust methods.
snej@8:  */
snej@8: @property (readonly) CMSSignerStatus status;
snej@8: 
snej@8: /** The signer's certificate.
snej@8:     You should check the status property first, to see whether the signature and certificate
snej@8:     are valid.
snej@8:     For safety purposes, if you haven't checked status yet, this method will return nil
snej@8:     if the signer status is not kCMSSignerValid. */
snej@8: @property (readonly) MYCertificate *certificate;
snej@8: 
snej@8: /** The signer's email address (if any), as stored in the certificate. */
snej@8: @property (readonly) NSString* emailAddress;
snej@8: 
snej@8: /** @name Expert
snej@8:  *  Advanced methods. 
snej@8:  */
snej@8: //@{
snej@8: 
snej@8: /** Returns the SecTrustRef that was used to verify the certificate.
snej@8:     You can use this object to get more detailed information about how the verification was done.
snej@8:     If you set the parent decoder's policy property, then that SecPolicy will be used to evaluate
snej@9:     trust; otherwise you'll need to do it yourself using the SecTrust object. */
snej@9: @property (readonly) SecTrustRef trust;
snej@8: 
snej@8: /** The result of certificate verification, as a CSSM_RESULT code; 
snej@8:  *  a nonzero value indicates an error.
snej@8:  *
snej@8:  * Some of the most common and interesting errors are:
snej@8:  *
snej@8:  * CSSMERR_TP_INVALID_ANCHOR_CERT : The cert was verified back to a 
snej@8:  *		self-signed (root) cert which was present in the message, but 
snej@8:  *		that root cert is not a known, trusted root cert. 
snej@8:  * CSSMERR_TP_NOT_TRUSTED: The cert could not be verified back to 
snej@8:  *		a root cert.
snej@8:  * CSSMERR_TP_VERIFICATION_FAILURE: A root cert was found which does
snej@8:  *   	not self-verify. 
snej@8:  * CSSMERR_TP_VERIFY_ACTION_FAILED: Indicates a failure of the requested 
snej@8:  *		policy action. 
snej@8:  * CSSMERR_TP_INVALID_CERTIFICATE: Indicates a bad leaf cert. 
snej@8:  * CSSMERR_TP_CERT_EXPIRED: A cert in the chain was expired at the time of
snej@8:  *		verification.
snej@8:  * CSSMERR_TP_CERT_NOT_VALID_YET: A cert in the chain was not yet valie at 
snej@8:  *		the time of	verification.
snej@8:  */
snej@8: @property (readonly) OSStatus verifyResult;
snej@8: 
snej@8: //@}
snej@8: 
snej@8: @end
snej@8: 
snej@8: 
snej@8: enum {
snej@8:     /** Returned from MYSigner.status to indicate a failure (non-noErr return value)
snej@8:      of the underlying CMSDecoderCopySignerStatus call. Should never occur. */
snej@8:     kMYSignerStatusCheckFailed = 666
snej@8: };