secure_enclave

Apple Secure Enclave implementaton for Flutter

What is a Secure Enclave? 👮

The Secure Enclave is a dedicated secure subsystem integrated into Apple systems on chip (SoCs). The Secure Enclave is isolated from the main processor to provide an extra layer of security and is designed to keep sensitive user data secure even when the Application Processor kernel becomes compromised. https://support.apple.com/en-ie/guide/security/sec59b0b31ff/web

Feature Set ✨

✅ Check tag status

✅ Generate Key Pair

✅ Get Public Key

✅ Encrypt

✅ Encrypt with Public Key

✅ Decrypt

✅ Sign

✅ Verify

✅ Flags (reference)

  • devicePasscode ✅
  • biometryAny ✅
  • biometryCurrentSet ✅
  • userPresence ✅
  • watch ✅
  • and ✅
  • or ✅
  • applicationPassword ✅
  • privateKeyUsage ✅

🚧 Accessible (reference)

  • kSecAttrAccessibleWhenUnlockedThisDeviceOnly ✅
  • kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly ⌛
  • kSecAttrAccessibleWhenUnlocked ⌛
  • kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly ⌛
  • kSecAttrAccessibleAfterFirstUnlock ⌛

🚧 Algorithm (reference)

  • eciesEncryptionCofactorVariableIVX963SHA256AESGCM ✅
  • ecdsaSignatureMessageX962SHA256 ✅
  • others ... ⌛

How to Use 🚀

📈 Check tag status :

final _secureEnclavePlugin = SecureEnclave();
final bool status = (await _secureEnclavePlugin.isKeyCreated(tag: 'kota')).value;

🔑 Generate Key Pair :

final _secureEnclavePlugin = SecureEnclave();

ResultModel res = await _secureEnclavePlugin.generateKeyPair(
    accessControl: AccessControlModel(
      password: 'jakarta123', // Fill this password if you want custom pop up dialog of .applicationPassword.

      options: [
        AccessControlOption.applicationPassword,
        AccessControlOption.privateKeyUsage,
      ],
      tag: 'kota',
    ),
);

if (res.error != null) {
	print(res.error!.desc.toString());
} else {
	print(res.value);
}
 

📢 Get Public Key :

final _secureEnclavePlugin = SecureEnclave();

ResultModel res = await _secureEnclavePlugin.getPublicKey(tag: 'kota');

if (res.error != null) {
	print(res.error!.desc.toString());
} else {
	print(res.value);
}
 

🔒 Encrypt :

final _secureEnclavePlugin = SecureEnclave();

ResultModel res = await _secureEnclavePlugin.encrypt(
    message: 'hello jakarta',
    tag: 'kota',
    password: 'jakarta123',
);

if (res.error != null) {
	print(res.error!.desc.toString());
} else {
	print(res.value); // Uint8List
}

🔐 Encrypt with Public Key:

final _secureEnclavePlugin = SecureEnclave();

ResultModel res = await _secureEnclavePlugin.encrypt(
    message: 'hello jakarta',
    publicKey: 'T57xZkDf2WPN8BT2Qlg2LiaBEVCRDw1Xq8aWQQfil' // base64 encode
);

if (res.error != null) {
	print(res.error!.desc.toString());
} else {
	print(res.value); // Uint8List
}

🔓 Decrypt :

final _secureEnclavePlugin = SecureEnclave();

ResultModel res = await _secureEnclavePlugin.decrypt(
    message: Uint8List.fromList(hex.decode('iasjfoiaj2EL3EL')), // hex => Uint8List
    tag: 'kota',
    password: 'jakarta123',
);

if (res.error != null) {
	print(res.error!.desc.toString());
} else {
	print(res.value);
}

🔏 Sign :

final _secureEnclavePlugin = SecureEnclave();

ResultModel res = await _secureEnclavePlugin.sign(
    message: Uint8List.fromList('hello jakarta'.codeUnits), // String => Uint8List
    tag: 'kota',
    password: 'jakarta123',
);

if (res.error != null) {
	print(res.error!.desc.toString());
} else {
	print(res.value);
}

Verify :

final _secureEnclavePlugin = SecureEnclave();

ResultModel res = await _secureEnclavePlugin.verify(
	plainText: 'hello jakarta',
    signature: 'fDrPlGl48R8DPCGNTsAticYfx3RoWPKxEHQ2pHWrBDGk887UwWYGVTSSUj6LciietChBULEs ',
    tag: 'kota',
    password: 'jakarta123',
);

if (res.error != null) {
	print(res.error!.desc.toString());
} else {
	print(res.value);
}