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