expo-device-crypto 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -26,78 +26,36 @@ If you want to allow Face ID on iOS, add this to your app config:
|
|
|
26
26
|
|
|
27
27
|
## Supported Algorithms
|
|
28
28
|
|
|
29
|
-
### Signature Algorithms
|
|
29
|
+
### ✍️ Signature Algorithms
|
|
30
30
|
|
|
31
31
|
- `ECDSA_SECP256R1_SHA256`
|
|
32
32
|
|
|
33
|
-
**Curve:** P-256 / secp256r1. </br>
|
|
34
|
-
**Hash:** SHA-256. </br>
|
|
33
|
+
**Curve:** P-256 / secp256r1. </br>
|
|
34
|
+
**Hash:** SHA-256. </br>
|
|
35
35
|
|
|
36
|
-
### Encryption Algorithms
|
|
36
|
+
### 🔐 Encryption Algorithms
|
|
37
37
|
|
|
38
38
|
- `RSA_2048_PKCS1`
|
|
39
39
|
|
|
40
|
-
**Key size:** 2048 bits. </br>
|
|
41
|
-
**Padding:** PKCS#1 v1.5. </br>
|
|
40
|
+
**Key size:** 2048 bits. </br>
|
|
41
|
+
**Padding:** PKCS#1 v1.5. </br>
|
|
42
42
|
|
|
43
43
|
- `RSA_2048_OAEP_SHA1`
|
|
44
44
|
|
|
45
|
-
**Key size:** 2048 bits. </br>
|
|
46
|
-
**Padding:** OAEP with SHA-1 and MGF1. </br>
|
|
45
|
+
**Key size:** 2048 bits. </br>
|
|
46
|
+
**Padding:** OAEP with SHA-1 and MGF1. </br>
|
|
47
47
|
|
|
48
48
|
- `ECIES_P256_AES256_GCM`
|
|
49
49
|
|
|
50
|
-
**Curve:** P-256 (secp256r1). </br>
|
|
51
|
-
**Symmetric cipher:** AES-256-GCM. </br>
|
|
52
|
-
**Key derivation:** HKDF-SHA256 (32-byte key from ECDH shared secret). </br>
|
|
50
|
+
**Curve:** P-256 (secp256r1). </br>
|
|
51
|
+
**Symmetric cipher:** AES-256-GCM. </br>
|
|
52
|
+
**Key derivation:** HKDF-SHA256 (32-byte key from ECDH shared secret). </br>
|
|
53
53
|
|
|
54
54
|
**More coming soon...**
|
|
55
55
|
|
|
56
|
-
## Example
|
|
56
|
+
## Example
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
import DeviceCrypto, {
|
|
60
|
-
AuthCheckResult,
|
|
61
|
-
AuthMethod,
|
|
62
|
-
SigningAlgorithm,
|
|
63
|
-
} from "expo-device-crypto";
|
|
64
|
-
|
|
65
|
-
const alias = "user-signing-key";
|
|
66
|
-
const payload = "Sign this challenge payload";
|
|
67
|
-
const algorithmType = SigningAlgorithm.ECDSA_SECP256R1_SHA256;
|
|
68
|
-
const authMethod = AuthMethod.PASSCODE_OR_BIOMETRIC;
|
|
69
|
-
|
|
70
|
-
// 1) Ensure device authentication is configured (passcode at minimum)
|
|
71
|
-
const authStatus = DeviceCrypto.isAuthCheckAvailable();
|
|
72
|
-
if (authStatus !== AuthCheckResult.AVAILABLE) {
|
|
73
|
-
throw new Error(`Authentication unavailable: ${authStatus}`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// 2) Generate an auth-protected ECDSA key pair
|
|
77
|
-
await DeviceCrypto.generateKeyPair(alias, {
|
|
78
|
-
requireAuthentication: true,
|
|
79
|
-
authMethod, // iOS: defined at key generation;
|
|
80
|
-
algorithmType,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// 3) Optional: share public key with backend
|
|
84
|
-
const publicKeyPem = DeviceCrypto.getPublicKey(alias, { format: "PEM" });
|
|
85
|
-
|
|
86
|
-
// 4) Sign with private key (prompts biometric/passcode on protected keys)
|
|
87
|
-
// Note: The data to sign must be in UTF-8 format.
|
|
88
|
-
// Note: This function should display the system user authentication prompt.
|
|
89
|
-
const signature = await DeviceCrypto.sign(alias, payload, {
|
|
90
|
-
algorithmType,
|
|
91
|
-
authMethod, // Android: defined when signing
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// 5) Verify locally (usually done server-side with stored public key)
|
|
95
|
-
const isValid = await DeviceCrypto.verify(alias, payload, signature ?? "", {
|
|
96
|
-
algorithmType,
|
|
97
|
-
});
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Example: RSA with Biometric
|
|
58
|
+
### RSA with Biometric
|
|
101
59
|
|
|
102
60
|
```ts
|
|
103
61
|
import DeviceCrypto, {
|
|
@@ -107,7 +65,7 @@ import DeviceCrypto, {
|
|
|
107
65
|
} from "expo-device-crypto";
|
|
108
66
|
|
|
109
67
|
const alias = "user-encryption-key";
|
|
110
|
-
const
|
|
68
|
+
const dataToEncrypt = "Sensitive data";
|
|
111
69
|
const algorithmType = EncryptionAlgorithm.RSA_2048_PKCS1;
|
|
112
70
|
const authMethod = AuthMethod.PASSCODE_OR_BIOMETRIC;
|
|
113
71
|
|
|
@@ -125,17 +83,17 @@ await DeviceCrypto.generateKeyPair(alias, {
|
|
|
125
83
|
});
|
|
126
84
|
|
|
127
85
|
// 3) Optional: retrieve/share public key with backend
|
|
128
|
-
const publicKeyPem = DeviceCrypto.getPublicKey(alias
|
|
86
|
+
const publicKeyPem = DeviceCrypto.getPublicKey(alias);
|
|
129
87
|
|
|
130
88
|
// 4) Encrypt with public key
|
|
131
|
-
const encrypted = await DeviceCrypto.encrypt(alias,
|
|
89
|
+
const encrypted = await DeviceCrypto.encrypt(alias, dataToEncrypt, {
|
|
132
90
|
algorithmType,
|
|
133
91
|
});
|
|
134
92
|
|
|
135
|
-
// 5) Decrypt with private key
|
|
93
|
+
// 5) Decrypt with private key
|
|
136
94
|
// Note: Data to decrypt must be in Base64 format.
|
|
137
95
|
// Note: This function should display the system user authentication prompt.
|
|
138
|
-
const decrypted = await DeviceCrypto.decrypt(alias, encrypted
|
|
96
|
+
const decrypted = await DeviceCrypto.decrypt(alias, encrypted, {
|
|
139
97
|
algorithmType,
|
|
140
98
|
authMethod, // Android: defined when decrypting
|
|
141
99
|
});
|
|
@@ -143,7 +101,11 @@ const decrypted = await DeviceCrypto.decrypt(alias, encrypted ?? "", {
|
|
|
143
101
|
|
|
144
102
|
> ⚠️ Because iOS Keychain/Secure Enclave binds authentication policy to key creation, `authMethod` must be set in `generateKeyPair`. On Android Keystore, authentication is applied at key usage time, so `authMethod` is provided in operations like `sign` and `decrypt`.
|
|
145
103
|
|
|
146
|
-
###
|
|
104
|
+
### 📚 More examples
|
|
105
|
+
|
|
106
|
+
**You can find additional usage examples in the [`examples` directory](https://github.com/piotr-pietras/expo-device-crypto/tree/master/examples) of the main repository.**
|
|
107
|
+
|
|
108
|
+
## Methods
|
|
147
109
|
|
|
148
110
|
- `isAuthCheckAvailable(): AuthCheckResult`
|
|
149
111
|
- Returns device authentication availability (`AVAILABLE`, `NO_HARDWARE`, `UNAVAILABLE`).
|
|
@@ -211,3 +173,7 @@ If StrongBox is not available, Android falls back to the Trusted Execution Envir
|
|
|
211
173
|
### Secure Enclave support on iOS
|
|
212
174
|
|
|
213
175
|
On iOS, only ECDSA keys can use the [Secure Enclave processor](https://developer.apple.com/documentation/security/ksecattrtokenidsecureenclave?utm_source=chatgpt.com#Discussion). RSA private-key operations are software-backed and handled by Apple’s system cryptographic services, which still provide strong isolation and protection.
|
|
176
|
+
|
|
177
|
+
### ECIES - EC encryption schema
|
|
178
|
+
|
|
179
|
+
For library purposes, ECIES is referred to as an encryption algorithm for simplification, even though it is technically a broader encryption scheme.
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'expo.modules.devicecrypto'
|
|
4
|
-
version = '0.3.
|
|
4
|
+
version = '0.3.2'
|
|
5
5
|
|
|
6
6
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
7
|
apply from: expoModulesCorePlugin
|
|
@@ -35,7 +35,7 @@ android {
|
|
|
35
35
|
namespace "expo.modules.devicecrypto"
|
|
36
36
|
defaultConfig {
|
|
37
37
|
versionCode 1
|
|
38
|
-
versionName "0.3.
|
|
38
|
+
versionName "0.3.2"
|
|
39
39
|
}
|
|
40
40
|
lintOptions {
|
|
41
41
|
abortOnError false
|
|
@@ -38,7 +38,7 @@ export type GenerateKeyPairOptions = {
|
|
|
38
38
|
requireAuthentication?: boolean;
|
|
39
39
|
/**
|
|
40
40
|
* The method of authentication to use.
|
|
41
|
-
* Note that on Android you have to define it when signing.
|
|
41
|
+
* Note that on Android you have to define it when signing or decrypting.
|
|
42
42
|
*
|
|
43
43
|
* If you want to allow to use Face ID, you need to add the following to your app.json config file:
|
|
44
44
|
* ```json
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceCrypto.types.js","sourceRoot":"","sources":["../src/DeviceCrypto.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,qBAIX;AAJD,WAAY,qBAAqB;IAC/B,kEAAyC,CAAA;IACzC,4EAAmD,CAAA;IACnD,wDAA+B,CAAA;AACjC,CAAC,EAJW,qBAAqB,KAArB,qBAAqB,QAIhC;AAED,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,0CAAuB,CAAA;IACvB,8CAA2B,CAAA;IAC3B,8CAA2B,CAAA;AAC7B,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B;AAED,MAAM,CAAN,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,mCAAqB,CAAA;IACrB,6DAA+C,CAAA;AACjD,CAAC,EAHW,UAAU,KAAV,UAAU,QAGrB;AAED,MAAM,CAAN,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IAC1B,qEAAiD,CAAA;AACnD,CAAC,EAFW,gBAAgB,KAAhB,gBAAgB,QAE3B;AAID,MAAM,CAAN,IAAY,mBAIX;AAJD,WAAY,mBAAmB;IAC7B,wDAAiC,CAAA;IACjC,gEAAyC,CAAA;IACzC,sEAA+C,CAAA;AACjD,CAAC,EAJW,mBAAmB,KAAnB,mBAAmB,QAI9B","sourcesContent":["export enum GenerateKeyPairResult {\n KEY_PAIR_GENERATED = \"KEY_PAIR_GENERATED\",\n KEY_PAIR_ALREADY_EXISTS = \"KEY_PAIR_ALREADY_EXISTS\",\n NOT_AVAILABLE = \"NOT_AVAILABLE\",\n}\n\nexport enum AuthCheckResult {\n AVAILABLE = \"AVAILABLE\",\n NO_HARDWARE = \"NO_HARDWARE\",\n UNAVAILABLE = \"UNAVAILABLE\",\n}\n\nexport enum AuthMethod {\n PASSCODE = \"PASSCODE\",\n PASSCODE_OR_BIOMETRIC = \"PASSCODE_OR_BIOMETRIC\",\n}\n\nexport enum SigningAlgorithm {\n ECDSA_SECP256R1_SHA256 = \"ECDSA_SECP256R1_SHA256\",\n}\n\ntype EciesAlgorithm = \"ECIES_P256_AES256_GCM\";\ntype RsaAlgorithm = \"RSA_2048_PKCS1\" | \"RSA_2048_OAEP_SHA1\";\nexport enum EncryptionAlgorithm {\n RSA_2048_PKCS1 = \"RSA_2048_PKCS1\",\n RSA_2048_OAEP_SHA1 = \"RSA_2048_OAEP_SHA1\",\n ECIES_P256_AES256_GCM = \"ECIES_P256_AES256_GCM\",\n}\n\nexport type GenerateKeyPairOptions = {\n /**\n * The algorithm type of the key to generate.\n */\n algorithmType: SigningAlgorithm | EncryptionAlgorithm;\n /**\n * Whether to require authentication to sign.\n * Setting this to true will prompt biometric or passcode authentication before signing.\n *\n * Please check the *`isAuthCheckAvailable`* function to see if authentication is available.\n * If authentication is not available, generating a key pair will throw an error.\n * @default false\n */\n requireAuthentication?: boolean;\n /**\n * The method of authentication to use.\n * Note that on Android you have to define it when signing.\n *\n * If you want to allow to use Face ID, you need to add the following to your app.json config file:\n * ```json\n * \"ios\": {\n * \"infoPlist\": {\n * \"NSFaceIDUsageDescription\": \"We use Face ID to secure your data.\",\n * }\n * }\n * ```\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform ios 🍏\n */\n authMethod?: AuthMethod;\n /**\n * If the Strong Box is available, it will be used instead of the Trusted Execution Environment (TEE).\n * @default false\n * @platform android 🤖\n */\n preferStrongBox?: boolean;\n}\n\ntype BaseAuthOptions = {\n /**\n * The title of the prompt to show when authentication is required.\n * @default \"Unlock\"\n * @platform android 🤖\n */\n promptTitle?: string;\n /**\n * The subtitle of the prompt to show when authentication is required.\n * @default \"Enter your PIN to continue\"\n * @platform android 🤖\n */\n promptSubtitle?: string;\n /**\n * The method of authentication to use.\n * Note that on iOS you have to define it when generating the key pair.\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform android 🤖\n */\n authMethod?: AuthMethod;\n}\n\ntype BaseSigningOptions = {\n /**\n * The algorithm type of the key to use for signing.\n */\n algorithmType: SigningAlgorithm;\n};\ntype BaseVerifyOptions = BaseSigningOptions;\n\ntype BaseRsaEncryptionOptions = {\n /**\n * The algorithm type of the key to use for encrypting.\n */\n algorithmType: RsaAlgorithm;\n};\ntype BaseRsaDecryptionOptions = BaseRsaEncryptionOptions;\n\ntype BaseEciesEncryptionOptions = {\n /**\n * The algorithm type of the key to use for encrypting.\n */\n algorithmType: EciesAlgorithm;\n /**\n * The peer public key to use for ECIES.\n */\n peerPublicKey: string;\n};\ntype BaseEciesDecryptionOptions = BaseEciesEncryptionOptions;\n\nexport type SignOptions = BaseAuthOptions & BaseSigningOptions;\nexport type VerifyOptions = BaseVerifyOptions;\n\nexport type DecryptOptions =\n | (BaseAuthOptions & BaseEciesDecryptionOptions)\n | (BaseAuthOptions & BaseRsaDecryptionOptions);\nexport type EncryptOptions =\n | (BaseAuthOptions & BaseEciesEncryptionOptions)\n | BaseRsaEncryptionOptions;\n\nexport interface GetPublicKeyOptions {\n /**\n * The format of the public key to return.\n * @default \"PEM\" (Base64 of PEM-encoded SubjectPublicKeyInfo (SPKI) for P‑256)\n */\n format?: \"PEM\" | \"BASE64\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"DeviceCrypto.types.js","sourceRoot":"","sources":["../src/DeviceCrypto.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,qBAIX;AAJD,WAAY,qBAAqB;IAC/B,kEAAyC,CAAA;IACzC,4EAAmD,CAAA;IACnD,wDAA+B,CAAA;AACjC,CAAC,EAJW,qBAAqB,KAArB,qBAAqB,QAIhC;AAED,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,0CAAuB,CAAA;IACvB,8CAA2B,CAAA;IAC3B,8CAA2B,CAAA;AAC7B,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B;AAED,MAAM,CAAN,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,mCAAqB,CAAA;IACrB,6DAA+C,CAAA;AACjD,CAAC,EAHW,UAAU,KAAV,UAAU,QAGrB;AAED,MAAM,CAAN,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IAC1B,qEAAiD,CAAA;AACnD,CAAC,EAFW,gBAAgB,KAAhB,gBAAgB,QAE3B;AAID,MAAM,CAAN,IAAY,mBAIX;AAJD,WAAY,mBAAmB;IAC7B,wDAAiC,CAAA;IACjC,gEAAyC,CAAA;IACzC,sEAA+C,CAAA;AACjD,CAAC,EAJW,mBAAmB,KAAnB,mBAAmB,QAI9B","sourcesContent":["export enum GenerateKeyPairResult {\n KEY_PAIR_GENERATED = \"KEY_PAIR_GENERATED\",\n KEY_PAIR_ALREADY_EXISTS = \"KEY_PAIR_ALREADY_EXISTS\",\n NOT_AVAILABLE = \"NOT_AVAILABLE\",\n}\n\nexport enum AuthCheckResult {\n AVAILABLE = \"AVAILABLE\",\n NO_HARDWARE = \"NO_HARDWARE\",\n UNAVAILABLE = \"UNAVAILABLE\",\n}\n\nexport enum AuthMethod {\n PASSCODE = \"PASSCODE\",\n PASSCODE_OR_BIOMETRIC = \"PASSCODE_OR_BIOMETRIC\",\n}\n\nexport enum SigningAlgorithm {\n ECDSA_SECP256R1_SHA256 = \"ECDSA_SECP256R1_SHA256\",\n}\n\ntype EciesAlgorithm = \"ECIES_P256_AES256_GCM\";\ntype RsaAlgorithm = \"RSA_2048_PKCS1\" | \"RSA_2048_OAEP_SHA1\";\nexport enum EncryptionAlgorithm {\n RSA_2048_PKCS1 = \"RSA_2048_PKCS1\",\n RSA_2048_OAEP_SHA1 = \"RSA_2048_OAEP_SHA1\",\n ECIES_P256_AES256_GCM = \"ECIES_P256_AES256_GCM\",\n}\n\nexport type GenerateKeyPairOptions = {\n /**\n * The algorithm type of the key to generate.\n */\n algorithmType: SigningAlgorithm | EncryptionAlgorithm;\n /**\n * Whether to require authentication to sign.\n * Setting this to true will prompt biometric or passcode authentication before signing.\n *\n * Please check the *`isAuthCheckAvailable`* function to see if authentication is available.\n * If authentication is not available, generating a key pair will throw an error.\n * @default false\n */\n requireAuthentication?: boolean;\n /**\n * The method of authentication to use.\n * Note that on Android you have to define it when signing or decrypting.\n *\n * If you want to allow to use Face ID, you need to add the following to your app.json config file:\n * ```json\n * \"ios\": {\n * \"infoPlist\": {\n * \"NSFaceIDUsageDescription\": \"We use Face ID to secure your data.\",\n * }\n * }\n * ```\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform ios 🍏\n */\n authMethod?: AuthMethod;\n /**\n * If the Strong Box is available, it will be used instead of the Trusted Execution Environment (TEE).\n * @default false\n * @platform android 🤖\n */\n preferStrongBox?: boolean;\n}\n\ntype BaseAuthOptions = {\n /**\n * The title of the prompt to show when authentication is required.\n * @default \"Unlock\"\n * @platform android 🤖\n */\n promptTitle?: string;\n /**\n * The subtitle of the prompt to show when authentication is required.\n * @default \"Enter your PIN to continue\"\n * @platform android 🤖\n */\n promptSubtitle?: string;\n /**\n * The method of authentication to use.\n * Note that on iOS you have to define it when generating the key pair.\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform android 🤖\n */\n authMethod?: AuthMethod;\n}\n\ntype BaseSigningOptions = {\n /**\n * The algorithm type of the key to use for signing.\n */\n algorithmType: SigningAlgorithm;\n};\ntype BaseVerifyOptions = BaseSigningOptions;\n\ntype BaseRsaEncryptionOptions = {\n /**\n * The algorithm type of the key to use for encrypting.\n */\n algorithmType: RsaAlgorithm;\n};\ntype BaseRsaDecryptionOptions = BaseRsaEncryptionOptions;\n\ntype BaseEciesEncryptionOptions = {\n /**\n * The algorithm type of the key to use for encrypting.\n */\n algorithmType: EciesAlgorithm;\n /**\n * The peer public key to use for ECIES.\n */\n peerPublicKey: string;\n};\ntype BaseEciesDecryptionOptions = BaseEciesEncryptionOptions;\n\nexport type SignOptions = BaseAuthOptions & BaseSigningOptions;\nexport type VerifyOptions = BaseVerifyOptions;\n\nexport type DecryptOptions =\n | (BaseAuthOptions & BaseEciesDecryptionOptions)\n | (BaseAuthOptions & BaseRsaDecryptionOptions);\nexport type EncryptOptions =\n | (BaseAuthOptions & BaseEciesEncryptionOptions)\n | BaseRsaEncryptionOptions;\n\nexport interface GetPublicKeyOptions {\n /**\n * The format of the public key to return.\n * @default \"PEM\" (Base64 of PEM-encoded SubjectPublicKeyInfo (SPKI) for P‑256)\n */\n format?: \"PEM\" | \"BASE64\";\n}\n"]}
|
package/ios/DeviceCrypto.podspec
CHANGED
package/package.json
CHANGED
|
@@ -43,7 +43,7 @@ export type GenerateKeyPairOptions = {
|
|
|
43
43
|
requireAuthentication?: boolean;
|
|
44
44
|
/**
|
|
45
45
|
* The method of authentication to use.
|
|
46
|
-
* Note that on Android you have to define it when signing.
|
|
46
|
+
* Note that on Android you have to define it when signing or decrypting.
|
|
47
47
|
*
|
|
48
48
|
* If you want to allow to use Face ID, you need to add the following to your app.json config file:
|
|
49
49
|
* ```json
|