//go:build darwin // +build darwin // Package macse provides Go bindings for macOS Secure Enclave operations // using CryptoTokenKit identities created via sc_auth. // Key creation and deletion shell out to sc_auth (which has SE entitlements). // Encrypt/decrypt use Security.framework ECIES directly (works unsigned). package macse /* #cgo CFLAGS: -x objective-c -fobjc-arc #cgo LDFLAGS: -framework Security -framework Foundation -framework CoreFoundation #include #include "secure_enclave.h" */ import "C" import ( "fmt" "unsafe" ) const ( // p256UncompressedKeySize is the size of an uncompressed P-256 public key. p256UncompressedKeySize = 65 // errorBufferSize is the size of the C error message buffer. errorBufferSize = 512 // hashBufferSize is the size of the hash output buffer. hashBufferSize = 128 // maxCiphertextSize is the max buffer for ECIES ciphertext. // ECIES overhead for P-256: 65 (ephemeral pub) + 16 (GCM tag) + 16 (IV) + plaintext. maxCiphertextSize = 8192 // maxPlaintextSize is the max buffer for decrypted plaintext. maxPlaintextSize = 8192 ) // CreateKey creates a new P-256 non-exportable key in the Secure Enclave via sc_auth. // Returns the uncompressed public key bytes (65 bytes) and the identity hash (for deletion). func CreateKey(label string) (publicKey []byte, hash string, err error) { cLabel := C.CString(label) defer C.free(unsafe.Pointer(cLabel)) pubKeyBuf := make([]C.uint8_t, p256UncompressedKeySize) pubKeyLen := C.int(p256UncompressedKeySize) var hashBuf [hashBufferSize]C.char var errBuf [errorBufferSize]C.char result := C.se_create_key(cLabel, &pubKeyBuf[0], &pubKeyLen, &hashBuf[0], C.int(hashBufferSize), &errBuf[0], C.int(errorBufferSize)) if result != 0 { return nil, "", fmt.Errorf("secure enclave: %s", C.GoString(&errBuf[0])) } pk := C.GoBytes(unsafe.Pointer(&pubKeyBuf[0]), pubKeyLen) h := C.GoString(&hashBuf[0]) return pk, h, nil } // Encrypt encrypts plaintext using the SE-backed public key via ECIES // (eciesEncryptionStandardVariableIVX963SHA256AESGCM). // Encryption uses only the public key; no SE interaction required. func Encrypt(label string, plaintext []byte) ([]byte, error) { cLabel := C.CString(label) defer C.free(unsafe.Pointer(cLabel)) ciphertextBuf := make([]C.uint8_t, maxCiphertextSize) ciphertextLen := C.int(maxCiphertextSize) var errBuf [errorBufferSize]C.char result := C.se_encrypt(cLabel, (*C.uint8_t)(unsafe.Pointer(&plaintext[0])), C.int(len(plaintext)), &ciphertextBuf[0], &ciphertextLen, &errBuf[0], C.int(errorBufferSize)) if result != 0 { return nil, fmt.Errorf("secure enclave: %s", C.GoString(&errBuf[0])) } return C.GoBytes(unsafe.Pointer(&ciphertextBuf[0]), ciphertextLen), nil } // Decrypt decrypts ECIES ciphertext using the SE-backed private key. // The ECDH portion of decryption is performed inside the Secure Enclave. func Decrypt(label string, ciphertext []byte) ([]byte, error) { cLabel := C.CString(label) defer C.free(unsafe.Pointer(cLabel)) plaintextBuf := make([]C.uint8_t, maxPlaintextSize) plaintextLen := C.int(maxPlaintextSize) var errBuf [errorBufferSize]C.char result := C.se_decrypt(cLabel, (*C.uint8_t)(unsafe.Pointer(&ciphertext[0])), C.int(len(ciphertext)), &plaintextBuf[0], &plaintextLen, &errBuf[0], C.int(errorBufferSize)) if result != 0 { return nil, fmt.Errorf("secure enclave: %s", C.GoString(&errBuf[0])) } return C.GoBytes(unsafe.Pointer(&plaintextBuf[0]), plaintextLen), nil } // DeleteKey removes a CTK identity from the Secure Enclave via sc_auth. func DeleteKey(hash string) error { cHash := C.CString(hash) defer C.free(unsafe.Pointer(cHash)) var errBuf [errorBufferSize]C.char result := C.se_delete_key(cHash, &errBuf[0], C.int(errorBufferSize)) if result != 0 { return fmt.Errorf("secure enclave: %s", C.GoString(&errBuf[0])) } return nil }