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