1# Refined Key Access Control Development
2
3As an extension of the access control based on user identity authentication, the refined key access control provides fine-grained access control capabilities via secondary identity authentication based on biometric features and lock screen passwords. You can set whether identity authentication is required for a key in one or more scenarios such as encryption, decryption, signing, signature verification, key agreement, and key derivation.
4
5For example, a service needs to use a HUKS key to encrypt the account password information. In this scenario, identity authentication is not required in encryption but required in decryption. To achieve this purpose, you can use the refined access control feature provided by HUKS.
6
7To implement this feature, you only need to set **HuksTag** to **HUKS_TAG_KEY_AUTH_PURPOSE**.
8
9> **NOTE**<br>
10> For symmetric encryption and decryption, only the AES/CBC, AES/GCM, and SM4/CBC modes support fine-grained access control.
11
12## How to Develop
13
141. Generate a key, set HuksUserAuthType to fingerprint authentication, and set other parameters including **HUKS_TAG_KEY_AUTH_PURPOSE**.
15
16   ```ts
17   import { huks } from "@kit.UniversalKeystoreKit";
18   /*
19    * Set the key alias and encapsulate the key property set.
20    */
21   let keyAlias = 'test_sm4_key_alias';
22   class throwObject {
23       isThrow: boolean = false;
24   }
25
26   let properties: Array<huks.HuksParam> = [
27       {
28           tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
29           value: huks.HuksKeyAlg.HUKS_ALG_SM4,
30       },
31       {
32           tag: huks.HuksTag.HUKS_TAG_PURPOSE,
33           value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT,
34       },
35       {
36           tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
37           value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
38       },
39       {
40           tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
41           value: huks.HuksCipherMode.HUKS_MODE_CBC,
42       },
43       {
44           tag: huks.HuksTag.HUKS_TAG_PADDING,
45           value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
46       },
47       {
48           tag: huks.HuksTag.HUKS_TAG_USER_AUTH_TYPE,
49           value: huks.HuksUserAuthType.HUKS_USER_AUTH_TYPE_FINGERPRINT
50       },
51       {
52           tag: huks.HuksTag.HUKS_TAG_KEY_AUTH_ACCESS_TYPE,
53           value: huks.HuksAuthAccessType.HUKS_AUTH_ACCESS_INVALID_NEW_BIO_ENROLL
54       },
55       {
56           tag: huks.HuksTag.HUKS_TAG_CHALLENGE_TYPE,
57           value: huks.HuksChallengeType.HUKS_CHALLENGE_TYPE_NORMAL
58       },
59       {
60           tag: huks.HuksTag.HUKS_TAG_KEY_AUTH_PURPOSE,
61           value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT
62       }
63   ];
64
65   let huksOptions: huks.HuksOptions = {
66       properties: properties,
67       inData: new Uint8Array(new Array())
68   }
69   /*
70    * Generate a key.
71    */
72   async function generateKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) {
73       return new Promise<void>((resolve, reject) => {
74           try {
75               huks.generateKeyItem(keyAlias, huksOptions, (error, data) => {
76                   if (error) {
77                       reject(error);
78                   } else {
79                       resolve(data);
80                   }
81               });
82           } catch (error) {
83               throwObject.isThrow = true;
84               throw(error as Error);
85           }
86       });
87   }
88   async function publicGenKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
89       console.info(`enter promise generateKeyItem`);
90       let throwObject: throwObject = {isThrow: false};
91       try {
92           await generateKeyItem(keyAlias, huksOptions, throwObject)
93           .then((data) => {
94               console.info(`promise: generateKeyItem success, data = ${JSON.stringify(data)}`);
95           })
96           .catch((error: Error) => {
97               if (throwObject.isThrow) {
98                   throw(error as Error);
99               } else {
100                   console.error(`promise: generateKeyItem failed` + JSON.stringify(error));
101               }
102           });
103       } catch (error) {
104           console.error(`promise: generateKeyItem input arg invalid` + JSON.stringify(error));
105       }
106   }
107   async function TestGenKeyForFingerprintAccessControl() {
108       await publicGenKeyFunc(keyAlias, huksOptions);
109   }
110   ```
111
1122. Use the key. User identity authentication is not required when the key is used for encryption.
113
114   ```ts
115   import { huks } from "@kit.UniversalKeystoreKit";
116   class HuksProperties {
117       tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM;
118       value: huks.HuksKeyAlg | huks.HuksKeySize | huks.HuksKeyPurpose | huks.HuksKeyPadding | huks.HuksCipherMode
119           | Uint8Array = huks.HuksKeyAlg.HUKS_ALG_ECC;
120   }
121   /*
122    * Set the key alias and encapsulate the key property set.
123    */
124   let cipherInData = 'Hks_SM4_Cipher_Test_101010101010101010110_string'; // Plaintext
125   let IV = '1234567890123456';
126   let handle = 0;
127   let cipherText: Uint8Array; // Ciphertext after encryption.
128   function StringToUint8Array(str: string) {
129       let arr: number[] = [];
130       for (let i = 0, j = str.length; i < j; ++i) {
131           arr.push(str.charCodeAt(i));
132       }
133       return new Uint8Array(arr);
134   }
135   /* Set the key generation parameter set and key encryption parameter set. */
136   let propertiesEncrypt: HuksProperties[] = [
137       {
138           tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
139           value: huks.HuksKeyAlg.HUKS_ALG_SM4,
140       },
141       {
142           tag: huks.HuksTag.HUKS_TAG_PURPOSE,
143           value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT,
144       },
145       {
146           tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
147           value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
148       },
149       {
150           tag: huks.HuksTag.HUKS_TAG_PADDING,
151           value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
152       },
153       {
154           tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
155           value: huks.HuksCipherMode.HUKS_MODE_CBC,
156       },
157       {
158           tag: huks.HuksTag.HUKS_TAG_IV,
159           value: StringToUint8Array(IV),
160       }
161   ];
162   let encryptOptions: huks.HuksOptions = {
163       properties: propertiesEncrypt,
164       inData: new Uint8Array(new Array())
165   }
166   class throwObject1{
167       isThrow: boolean = false;
168   }
169   function initSession(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject1) {
170       return new Promise<huks.HuksSessionHandle>((resolve, reject) => {
171           try {
172               huks.initSession(keyAlias, huksOptions, (error, data) => {
173                   if (error) {
174                       reject(error);
175                   } else {
176                       resolve(data);
177                   }
178               });
179           } catch (error) {
180               throwObject.isThrow = true;
181               throw (error as Error);
182           }
183       });
184   }
185   async function publicInitFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
186       console.info(`enter promise doInit`);
187       let throwObject: throwObject1 = { isThrow: false };
188       try {
189           await initSession(keyAlias, huksOptions, throwObject)
190           .then((data) => {
191               console.info(`promise: doInit success, data = ${JSON.stringify(data)}`);
192               handle = data.handle as number;
193           })
194           .catch((error: Error) => {
195               if (throwObject.isThrow) {
196                   throw (error as Error);
197               } else {
198                   console.error(`promise: doInit failed` + JSON.stringify(error));
199               }
200           });
201       } catch (error) {
202           console.error(`promise: doInit input arg invalid` + JSON.stringify(error));
203       }
204   }
205   function finishSession(handle: number, huksOptions: huks.HuksOptions, throwObject: throwObject1) {
206       return new Promise<huks.HuksReturnResult>((resolve, reject) => {
207           try {
208               huks.finishSession(handle, huksOptions, (error, data) => {
209                   if (error) {
210                       reject(error);
211                   } else {
212                       resolve(data);
213                   }
214               });
215           } catch (error) {
216               throwObject.isThrow = true;
217               throw (error as Error);
218           }
219       });
220   }
221   async function publicFinishFunc(handle: number, huksOptions: huks.HuksOptions) {
222       console.info(`enter promise doFinish`);
223       let throwObject: throwObject1 = { isThrow: false };
224       try {
225           await finishSession(handle, huksOptions, throwObject)
226           .then((data) => {
227               cipherText = data.outData as Uint8Array;
228               console.info(`promise: doFinish success, data = ${JSON.stringify(data)}`);
229           })
230           .catch((error: Error) => {
231               if (throwObject.isThrow) {
232                   throw (error as Error);
233               } else {
234                   console.error(`promise: doFinish failed` + JSON.stringify(error));
235               }
236           });
237       } catch (error) {
238           console.error(`promise: doFinish input arg invalid` + JSON.stringify(error));
239       }
240   }
241   async function testSm4Cipher() {
242       /* Initialize the key session to obtain a challenge. */
243       await publicInitFunc(keyAlias, encryptOptions);
244       /** Encryption */
245       encryptOptions.inData = StringToUint8Array(cipherInData);
246       await publicFinishFunc(handle, encryptOptions);
247   }
248   ```
249
2503. Use the key. User identity authentication is required when the key is used for decryption.
251
252   ```ts
253    import { huks } from "@kit.UniversalKeystoreKit";
254    import { userAuth } from '@kit.UserAuthenticationKit';
255    import { BusinessError } from "@kit.BasicServicesKit"
256
257    let keyAlias = 'test_sm4_key_alias';
258    let IV = '1234567890123456';
259    let handle = 0;
260    let cipherText: Uint8Array; // Data in ciphertext.
261    /*
262    * Determine the key property set to be encapsulated.
263    */
264    let finishOutData: Uint8Array; // Plaintext after decryption.
265    let fingerAuthToken: Uint8Array;
266    let challenge: Uint8Array;
267    let authType = userAuth.UserAuthType.FINGERPRINT;
268    let authTrustLevel = userAuth.AuthTrustLevel.ATL1;
269    class throwObject {
270        isThrow: boolean = false;
271    }
272    function StringToUint8Array(str: string) {
273        let arr: number[] = [];
274        for (let i = 0, j = str.length; i < j; ++i) {
275            arr.push(str.charCodeAt(i));
276        }
277        return new Uint8Array(arr);
278    }
279    /* Set the key generation parameter set and key encryption parameter set. */
280    class propertyDecryptType {
281    tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM
282    value: huks.HuksKeyAlg | huks.HuksKeyPurpose | huks.HuksKeySize | huks.HuksKeyPadding | huks.HuksCipherMode
283        | Uint8Array = huks.HuksKeyAlg.HUKS_ALG_SM4
284    }
285    let propertiesDecrypt: propertyDecryptType[] = [
286        {
287            tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
288            value: huks.HuksKeyAlg.HUKS_ALG_SM4,
289        },
290        {
291            tag: huks.HuksTag.HUKS_TAG_PURPOSE,
292            value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT,
293        },
294        {
295            tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
296            value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
297        },
298        {
299            tag: huks.HuksTag.HUKS_TAG_PADDING,
300            value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
301        },
302        {
303            tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
304            value: huks.HuksCipherMode.HUKS_MODE_CBC,
305        },
306        {
307            tag: huks.HuksTag.HUKS_TAG_IV,
308            value: StringToUint8Array(IV),
309        }
310    ]
311    let decryptOptions: huks.HuksOptions = {
312        properties: propertiesDecrypt,
313        inData: new Uint8Array(new Array())
314    }
315    function initSession(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) {
316        return new Promise<huks.HuksSessionHandle>((resolve, reject) => {
317            try {
318                huks.initSession(keyAlias, huksOptions, (error, data) => {
319                    if (error) {
320                        reject(error);
321                    } else {
322                        resolve(data);
323                    }
324                });
325            } catch (error) {
326                throwObject.isThrow = true;
327                throw(error as Error);
328            }
329        });
330    }
331    async function publicInitFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
332    console.info(`enter promise doInit`);
333    let throwObject: throwObject = {isThrow: false};
334    try {
335        await initSession(keyAlias, huksOptions, throwObject)
336        .then ((data) => {
337            console.info(`promise: doInit success, data = ${JSON.stringify(data)}`);
338            handle = data.handle;
339            challenge = data.challenge as Uint8Array;
340        })
341        .catch((error: BusinessError) => {
342            if (throwObject.isThrow) {
343            throw(error as Error);
344            } else {
345            console.error(`promise: doInit failed` + JSON.stringify(error));
346            }
347        });
348    } catch (error) {
349        console.error(`promise: doInit input arg invalid` + JSON.stringify(error));
350    }
351    }
352    function userIAMAuthFinger(huksChallenge: Uint8Array) {
353    // Obtain an authentication object.
354    let authTypeList:userAuth.UserAuthType[]= [ authType ];
355    const authParam:userAuth.AuthParam = {
356        challenge: huksChallenge,
357        authType: authTypeList,
358        authTrustLevel: userAuth.AuthTrustLevel.ATL1
359    };
360    const widgetParam:userAuth.WidgetParam = {
361        title: 'Enter password',
362    };
363    let auth : userAuth.UserAuthInstance;
364    try {
365        auth = userAuth.getUserAuthInstance(authParam, widgetParam);
366        console.info("get auth instance success");
367    } catch (error) {
368        console.error("get auth instance failed" + JSON.stringify(error));
369        return;
370    }
371    // Subscribe to the authentication result.
372    try {
373        auth.on("result", {
374        onResult(result) {
375            console.info("[HUKS] -> [IAM]  userAuthInstance callback result = " + JSON.stringify(result));
376            fingerAuthToken = result.token;
377        }
378        });
379        console.info("subscribe authentication event success");
380    } catch (error) {
381        console.error("subscribe authentication event failed " + JSON.stringify(error));
382    }
383    // Start user authentication.
384    try {
385        auth.start();
386        console.info("authV9 start auth success");
387    } catch (error) {
388        console.error("authV9 start auth failed, error = " + JSON.stringify(error));
389    }
390    }
391    function finishSession(handle: number, huksOptions: huks.HuksOptions, token: Uint8Array, throwObject: throwObject) {
392    return new Promise<huks.HuksReturnResult>((resolve, reject) => {
393        try {
394        huks.finishSession(handle, huksOptions, token, (error, data) => {
395            if (error) {
396            reject(error);
397            } else {
398            resolve(data);
399            }
400        });
401        } catch (error) {
402        throwObject.isThrow = true;
403        throw(error as Error);
404        }
405    });
406    }
407    async function publicFinishFunc(handle: number, token: Uint8Array, huksOptions: huks.HuksOptions) {
408    console.info(`enter promise doFinish`);
409    let throwObject: throwObject = {isThrow: false};
410    try {
411        await finishSession(handle, huksOptions, token, throwObject)
412        .then ((data) => {
413            finishOutData = data.outData as Uint8Array;
414            console.info(`promise: doFinish success, data = ${JSON.stringify(data)}`);
415        })
416        .catch((error: BusinessError) => {
417            if (throwObject.isThrow) {
418                throw(error as Error);
419            } else {
420                console.error(`promise: doFinish failed` + JSON.stringify(error));
421            }
422        });
423    } catch (error) {
424        console.error(`promise: doFinish input arg invalid` + JSON.stringify(error));
425    }
426    }
427    async function testSm4Cipher() {
428    /* Initialize the key session to obtain a challenge. */
429    await publicInitFunc(keyAlias, decryptOptions);
430    /* Invoke userIAM to perform user identity authentication. */
431    userIAMAuthFinger(challenge);
432    /* Perform decryption after the authentication is successful. The **authToken** value returned after the authentication needs to be passed in. */
433    decryptOptions.inData = StringToUint8Array(cipherText);
434    await publicFinishFunc(handle, fingerAuthToken, decryptOptions);
435    }
436   ```
437