Complete API documentation for the Vigil framework.
- Vigil (Main Interface)
- HashEngine
- SEKeyManager
- AttestationStore
- VigilValidatorProtocol
- Types and Constants
- Error Codes
- Swift Interface
The primary class for performing integrity validation.
Initializes Vigil, generating Secure Enclave keys and establishing connection with the validator.
+ (void)initializeWithCompletion:(void (^)(BOOL success, NSError *error))completion;Parameters:
completion: Called when initialization completes.successisYESif initialization succeeded.
Notes:
- Must be called before any validation attempts
- Generates SE key pair if not already present
- Performs initial key exchange with validator if needed
- Thread-safe; can be called from any thread
Example:
[Vigil initializeWithCompletion:^(BOOL success, NSError *error) {
if (success) {
NSLog(@"Vigil initialized successfully");
} else {
NSLog(@"Initialization failed: %@", error.localizedDescription);
}
}];Performs integrity validation with the default timeout (5 seconds).
+ (void)validateWithCompletion:(void (^)(VigilResult result))completion;Parameters:
completion: Called with the validation result.
Example:
[Vigil validateWithCompletion:^(VigilResult result) {
if (result == VigilResultValid) {
// Proceed with normal operation
}
}];Performs integrity validation with a custom timeout.
+ (void)validateWithTimeout:(NSTimeInterval)timeout
completion:(void (^)(VigilResult result))completion;Parameters:
timeout: Maximum time to wait for validator response (in seconds)completion: Called with the validation result
Notes:
- If timeout elapses before response, returns
VigilResultTimeout - Recommended timeout: 3-10 seconds
- Completion always called on main thread
Example:
[Vigil validateWithTimeout:3.0 completion:^(VigilResult result) {
switch (result) {
case VigilResultValid:
break;
case VigilResultTampered:
case VigilResultTimeout:
[self handleCompromise];
break;
case VigilResultError:
[self retryValidation];
break;
}
}];Performs synchronous validation (blocks calling thread).
+ (VigilResult)validateSync;Returns: VigilResult indicating validation outcome.
Warning: Do not call on main thread. Will block until validation completes or times out.
Example:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
VigilResult result = [Vigil validateSync];
dispatch_async(dispatch_get_main_queue(), ^{
[self handleResult:result];
});
});Checks if Vigil has been initialized.
+ (BOOL)isInitialized;Returns: YES if initializeWithCompletion: has completed successfully.
Sets the logging verbosity.
+ (void)setLogLevel:(VigilLogLevel)level;Parameters:
level: One ofVigilLogLevelNone,VigilLogLevelError,VigilLogLevelWarning,VigilLogLevelInfo,VigilLogLevelDebug
Example:
#ifdef DEBUG
[Vigil setLogLevel:VigilLogLevelDebug];
#else
[Vigil setLogLevel:VigilLogLevelError];
#endifReturns the current status of the validator process.
+ (VigilValidatorStatus)validatorStatus;Returns: One of:
VigilValidatorStatusUnknown: Status not yet determinedVigilValidatorStatusRunning: Validator is running and responsiveVigilValidatorStatusNotRunning: Validator is not runningVigilValidatorStatusNotConfigured: Validator not set up (iOS: Content Filter disabled)
Computes cryptographic hashes of executable code segments.
Computes SHA-256 hash of the current process's __TEXT segments.
+ (NSData *)computeTextHash;Returns: 32-byte NSData containing SHA-256 hash, or nil on error.
Notes:
- Includes all Mach-O images within the app bundle
- Excludes system frameworks and dylibs
- Hash is deterministic for identical binaries
Example:
NSData *hash = [HashEngine computeTextHash];
NSLog(@"Current hash: %@", [hash base64EncodedStringWithOptions:0]);Computes SHA-256 hash of a binary at the specified path.
+ (NSData *)computeTextHashForPath:(NSString *)path;Parameters:
path: Absolute path to a Mach-O binary
Returns: 32-byte NSData containing SHA-256 hash, or nil on error.
Notes:
- Used primarily for build-time hash computation
- Can hash any Mach-O binary, not just the current process
Combines multiple hashes into a single digest.
+ (NSData *)combineHashes:(NSArray<NSData *> *)hashes;Parameters:
hashes: Array of hashNSDataobjects
Returns: Combined 32-byte SHA-256 hash.
Notes:
- Order-dependent:
[A, B]produces different result than[B, A] - Used for multi-binary validation scenarios
Converts hash data to hexadecimal string.
+ (NSString *)hexStringFromData:(NSData *)data;Parameters:
data: Binary data to convert
Returns: Lowercase hexadecimal string representation.
Example:
NSData *hash = [HashEngine computeTextHash];
NSString *hexHash = [HashEngine hexStringFromData:hash];
// "a3f2b1c4..."Manages Secure Enclave key pairs for cryptographic operations.
Returns the singleton instance.
+ (instancetype)sharedManager;Checks if Secure Enclave is available on this device.
+ (BOOL)isSecureEnclaveAvailable;Returns: YES if SE is available (physical device with SE hardware).
Notes:
- Returns
NOon simulators - Returns
NOon devices without SE (pre-iPhone 5s, some iPads)
Generates a new Secure Enclave key pair.
- (BOOL)generateKeyPairWithTag:(NSString *)tag error:(NSError **)error;Parameters:
tag: Unique identifier for the key pair (e.g.,"com.myapp.vigil")error: Pointer to receive error information
Returns: YES if key generation succeeded.
Notes:
- Keys are permanent and persist across app launches
- Keys are device-specific and non-exportable
- Returns
NOif key with this tag already exists
Example:
NSError *error;
BOOL success = [[SEKeyManager sharedManager]
generateKeyPairWithTag:@"com.myapp.vigil" error:&error];
if (!success) {
NSLog(@"Key generation failed: %@", error);
}Retrieves the public key for a key pair.
- (NSData *)publicKeyDataForTag:(NSString *)tag;Parameters:
tag: Tag of the key pair
Returns: Public key in X.509 SubjectPublicKeyInfo format, or nil if not found.
Notes:
- Public key can be freely shared
- Format is compatible with standard crypto libraries
Signs data using the Secure Enclave private key.
- (NSData *)signData:(NSData *)data
withKeyTag:(NSString *)tag
error:(NSError **)error;Parameters:
data: Data to signtag: Tag of the key pair to useerror: Pointer to receive error information
Returns: ECDSA signature in DER format, or nil on error.
Notes:
- Signing operation occurs inside Secure Enclave
- Private key never leaves SE hardware
- Uses SHA-256 for digest
Example:
NSData *message = [@"Hello, World!" dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSData *signature = [[SEKeyManager sharedManager]
signData:message withKeyTag:@"com.myapp.vigil" error:&error];Verifies an ECDSA signature.
- (BOOL)verifySignature:(NSData *)signature
forData:(NSData *)data
withPublicKey:(NSData *)publicKey
error:(NSError **)error;Parameters:
signature: ECDSA signature in DER formatdata: Original signed datapublicKey: Public key in X.509 formaterror: Pointer to receive error information
Returns: YES if signature is valid.
Notes:
- Does not require Secure Enclave (verification uses public key only)
- Works on simulators for testing
Deletes a key pair from the Secure Enclave.
- (BOOL)deleteKeyPairWithTag:(NSString *)tag error:(NSError **)error;Parameters:
tag: Tag of the key pair to deleteerror: Pointer to receive error information
Returns: YES if deletion succeeded.
Warning: Deleted keys cannot be recovered.
Persistent storage for exchanged public keys.
Returns the singleton instance.
+ (instancetype)sharedStore;Stores the app's public key (called by validator).
- (BOOL)storeAppPublicKey:(NSData *)publicKey error:(NSError **)error;Parameters:
publicKey: App's public key dataerror: Pointer to receive error information
Returns: YES if storage succeeded.
Stores the validator's public key (called by app).
- (BOOL)storeValidatorPublicKey:(NSData *)publicKey error:(NSError **)error;Parameters:
publicKey: Validator's public key dataerror: Pointer to receive error information
Returns: YES if storage succeeded.
Retrieves the stored app public key.
- (NSData *)appPublicKey;Returns: App's public key, or nil if not stored.
Retrieves the stored validator public key.
- (NSData *)validatorPublicKey;Returns: Validator's public key, or nil if not stored.
Checks if initial key exchange has been completed.
- (BOOL)isAttestationConfigured;Returns: YES if both app and validator public keys are stored.
Removes all stored keys.
- (BOOL)clearStore:(NSError **)error;Parameters:
error: Pointer to receive error information
Returns: YES if clearing succeeded.
Warning: Requires re-running key exchange after clearing.
Protocol implemented by the validator (XPC Service or Network Extension).
@protocol VigilValidatorProtocol <NSObject>
- (void)validateHash:(NSData *)hash
signature:(NSData *)signature
publicKey:(NSData *)publicKey
nonce:(NSData *)nonce
withReply:(void (^)(BOOL valid,
NSData *validatorHash,
NSData *responseSignature,
NSData *validatorPublicKey))reply;
- (void)exchangePublicKey:(NSData *)appPublicKey
withReply:(void (^)(NSData *validatorPublicKey))reply;
- (void)ping:(void (^)(BOOL alive))reply;
@endValidates the app's integrity claim.
Parameters:
hash: SHA-256 hash of app's__TEXTsegmentsignature: SE signature of(hash + nonce)publicKey: App's SE public keynonce: Random challenge noncereply: Completion block with validation result
Reply Parameters:
valid:YESif hash matches expected value and signature is validvalidatorHash: Validator's own__TEXThashresponseSignature: SE signature of responsevalidatorPublicKey: Validator's SE public key
Exchanges public keys during initial setup.
Parameters:
appPublicKey: App's SE public keyreply: Completion block with validator's public key
Checks if validator is alive and responsive.
Parameters:
reply: Completion block with alive status
typedef NS_ENUM(NSInteger, VigilResult) {
VigilResultValid = 0, // Integrity verified
VigilResultTampered = 1, // Tampering detected
VigilResultTimeout = 2, // Validator unresponsive
VigilResultError = 3 // Validation error occurred
};typedef NS_ENUM(NSInteger, VigilValidatorStatus) {
VigilValidatorStatusUnknown = 0,
VigilValidatorStatusRunning = 1,
VigilValidatorStatusNotRunning = 2,
VigilValidatorStatusNotConfigured = 3
};typedef NS_ENUM(NSInteger, VigilLogLevel) {
VigilLogLevelNone = 0,
VigilLogLevelError = 1,
VigilLogLevelWarning = 2,
VigilLogLevelInfo = 3,
VigilLogLevelDebug = 4
};// Default validation timeout
extern NSTimeInterval const VigilDefaultTimeout; // 5.0 seconds
// Notification posted when validation completes
extern NSString *const VigilValidationDidCompleteNotification;
// UserInfo keys for notification
extern NSString *const VigilResultKey; // NSNumber containing VigilResultextern NSString *const VigilErrorDomain;typedef NS_ENUM(NSInteger, VigilErrorCode) {
// Initialization errors
VigilErrorSecureEnclaveUnavailable = 1000,
VigilErrorKeyGenerationFailed = 1001,
VigilErrorKeyExchangeFailed = 1002,
// Validation errors
VigilErrorValidatorNotRunning = 2000,
VigilErrorValidatorNotConfigured = 2001,
VigilErrorIPCFailure = 2002,
VigilErrorSignatureInvalid = 2003,
VigilErrorHashMismatch = 2004,
VigilErrorPublicKeyMismatch = 2005,
// Storage errors
VigilErrorKeychainAccessFailed = 3000,
VigilErrorStorageCorrupted = 3001,
// Internal errors
VigilErrorInternalFailure = 9000
};Vigil provides a native Swift API.
public final class Vigil {
/// Initialize Vigil (must call before validation)
public static func initialize() async throws
/// Validate integrity with default timeout
public static func validate() async -> VigilResult
/// Validate integrity with custom timeout
public static func validate(timeout: TimeInterval) async -> VigilResult
/// Check if Vigil is initialized
public static var isInitialized: Bool { get }
/// Current validator status
public static var validatorStatus: VigilValidatorStatus { get }
/// Set log level
public static var logLevel: VigilLogLevel { get set }
}public enum VigilResult: Int, Sendable {
case valid = 0
case tampered = 1
case timeout = 2
case error = 3
}import Vigil
@main
struct MyApp: App {
init() {
Task {
do {
try await Vigil.initialize()
let result = await Vigil.validate(timeout: 5.0)
switch result {
case .valid:
print("Integrity verified")
case .tampered, .timeout:
fatalError("App integrity compromised")
case .error:
print("Validation error - retrying")
}
} catch {
print("Vigil initialization failed: \(error)")
}
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}import Combine
extension Vigil {
/// Publisher for validation results
public static var validationPublisher: AnyPublisher<VigilResult, Never> {
NotificationCenter.default
.publisher(for: .vigilValidationDidComplete)
.compactMap { notification in
notification.userInfo?[Vigil.resultKey] as? VigilResult
}
.eraseToAnyPublisher()
}
}public enum VigilError: Error {
case secureEnclaveUnavailable
case keyGenerationFailed(underlying: Error?)
case keyExchangeFailed(underlying: Error?)
case validatorNotRunning
case validatorNotConfigured
case ipcFailure(underlying: Error?)
case signatureInvalid
case hashMismatch
case publicKeyMismatch
case keychainAccessFailed(status: OSStatus)
case storageCorrupted
case internalFailure(message: String)
}All Vigil APIs are thread-safe unless otherwise noted:
Vigilclass methods: Thread-safeHashEngineclass methods: Thread-safeSEKeyManagermethods: Thread-safe (but SE operations are serialized internally)AttestationStoremethods: Thread-safe (uses Keychain, which is thread-safe)
Completion handlers are always called on the main thread unless using the Sync variants.
Vigil uses ARC. No manual memory management required.
Key objects (SEKeyManager, AttestationStore) are singletons with static lifetime.
None currently. Future deprecations will be marked with __deprecated_msg() and announced in release notes.