1# Key Agreement (ArkTS) 2 3 4This topic walks you through on how to agree on a 256-bit X25519 key that is used only in HUKS. For details about the scenarios and supported algorithms, see [Supported Algorithms](huks-key-generation-overview.md#supported-algorithms). 5 6 7## How to Develop 8 9**Key Generation** 10 11Generate an asymmetric key for device A and device B each. For details, see [Key Generation](huks-key-generation-overview.md) or [Key Import](huks-key-import-overview.md). 12 13The optional parameter **HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG** specifies how a derived key or a key generated after key agreement is stored. 14 15- **HUKS_STORAGE_ONLY_USED_IN_HUKS**: The key is stored and managed by HUKS. 16 17- **HUKS_STORAGE_KEY_EXPORT_ALLOWED** (default): The key is directly exported to the service and not managed by HUKS. 18 19**Key Export** 20 21Export the public key of the asymmetric key pair of device A and device B. For details, see [Key Export](huks-export-key-arkts.md). 22 23**Key Agreement** 24 25Perform key agreement using the public key of the peer device and private key of the local device (that is, public key of device B and private key of device A for device A, and public key of device A and private key of device B for device B) to produce a shared secrete key. 26 27**Key Deletion** 28 29Delete the keys from device A and device B when the keys are not required. For details, see [Deleting a Key](huks-delete-key-arkts.md). 30 31```ts 32/* 33 * Agree on a 256-bit X25519 key. This example uses promise-based APIs. 34 */ 35 import huks from '@ohos.security.huks'; 36 import { BusinessError } from '@ohos.base'; 37 /* 38 * Set the key alias and encapsulate the key property set. 39 */ 40 let srcKeyAliasFirst = "AgreeX25519KeyFirstAlias"; 41 let srcKeyAliasSecond = "AgreeX25519KeySecondAlias"; 42 let agreeX25519InData = 'AgreeX25519TestIndata'; 43 let finishOutData: Uint8Array; 44 let handle: number; 45 let exportKey: Uint8Array; 46 let exportKeyFrist: Uint8Array; 47 let exportKeySecond: Uint8Array; 48 /* Set the parameter set used for generating the key. */ 49 let properties: Array<huks.HuksParam> = new Array(); 50 properties[0] = { 51 tag: huks.HuksTag.HUKS_TAG_ALGORITHM, 52 value: huks.HuksKeyAlg.HUKS_ALG_X25519, 53 } 54 properties[1] = { 55 tag: huks.HuksTag.HUKS_TAG_PURPOSE, 56 value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE, 57 } 58 properties[2] = { 59 tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, 60 value: huks.HuksKeySize.HUKS_CURVE25519_KEY_SIZE_256, 61 } 62 properties[3] = { 63 tag: huks.HuksTag.HUKS_TAG_DIGEST, 64 value: huks.HuksKeyDigest.HUKS_DIGEST_NONE, 65 } 66 properties[4] = { 67 tag: huks.HuksTag.HUKS_TAG_PADDING, 68 value: huks.HuksKeyPadding.HUKS_PADDING_NONE, 69 } 70 properties[5] = { 71 tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, 72 value: huks.HuksCipherMode.HUKS_MODE_CBC, 73 } 74 properties[6] = { 75 tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, 76 value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, 77 } 78 let HuksOptions: huks.HuksOptions = { 79 properties: properties, 80 inData: new Uint8Array(new Array()) 81 } 82 /* Set the parameter set for the first key agreement. */ 83 let finishProperties: Array<huks.HuksParam> = new Array(); 84 finishProperties[0] = { 85 tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, 86 value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, 87 } 88 finishProperties[1] = { 89 tag: huks.HuksTag.HUKS_TAG_IS_KEY_ALIAS, 90 value: true 91 } 92 finishProperties[2] = { 93 tag: huks.HuksTag.HUKS_TAG_ALGORITHM, 94 value: huks.HuksKeyAlg.HUKS_ALG_AES, 95 } 96 finishProperties[3] = { 97 tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, 98 value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256, 99 } 100 finishProperties[4] = { 101 tag: huks.HuksTag.HUKS_TAG_PURPOSE, 102 value: 103 huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | 104 huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT, 105 } 106 finishProperties[5] = { 107 tag: huks.HuksTag.HUKS_TAG_DIGEST, 108 value: huks.HuksKeyDigest.HUKS_DIGEST_NONE, 109 } 110 finishProperties[6] = { 111 tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS, 112 value: StringToUint8Array(srcKeyAliasFirst+ 'final'), 113 } 114 finishProperties[7] = { 115 tag: huks.HuksTag.HUKS_TAG_PADDING, 116 value: huks.HuksKeyPadding.HUKS_PADDING_NONE, 117 } 118 finishProperties[8] = { 119 tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, 120 value: huks.HuksCipherMode.HUKS_MODE_ECB, 121 } 122 let finishOptionsFrist: huks.HuksOptions = { 123 properties: finishProperties, 124 inData: StringToUint8Array(agreeX25519InData) 125 } 126 /* Set the parameter set for the second key agreement. */ 127 let finishPropertiesSecond = [...finishProperties] 128 finishPropertiesSecond[6] = { 129 tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS, 130 value: StringToUint8Array(srcKeyAliasSecond + 'final'), 131 } 132 let finishOptionsSecond: huks.HuksOptions = { 133 properties: finishPropertiesSecond, 134 inData: StringToUint8Array(agreeX25519InData) 135 } 136 function StringToUint8Array(str:string) { 137 let arr: number[] = new Array(); 138 for (let i = 0, j = str.length; i < j; ++i) { 139 arr.push(str.charCodeAt(i)); 140 } 141 return new Uint8Array(arr); 142 } 143 class throwObject { 144 isThrow: boolean = false 145 } 146 /* Generate a key. */ 147 function generateKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) { 148 return new Promise<void>((resolve, reject) => { 149 try { 150 huks.generateKeyItem(keyAlias, huksOptions, (error, data) => { 151 if (error) { 152 reject(error); 153 } else { 154 resolve(data); 155 } 156 }); 157 } catch (error) { 158 throwObject.isThrow = true; 159 throw(error as Error); 160 } 161 }); 162 } 163 /* Call generateKeyItem to generate a key. */ 164 async function publicGenKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) { 165 console.info(`enter promise generateKeyItem`); 166 let throwObject:throwObject = {isThrow: false}; 167 try { 168 await generateKeyItem(keyAlias, huksOptions, throwObject) 169 .then((data) => { 170 console.info(`promise: generateKeyItem success, data = ${JSON.stringify(data)}`); 171 }) 172 .catch((error: BusinessError) => { 173 if (throwObject.isThrow) { 174 throw(error as Error); 175 } else { 176 console.error(`promise: generateKeyItem failed` + error); 177 } 178 }); 179 } catch (error) { 180 console.error(`promise: generateKeyItem input arg invalid` + error); 181 } 182 } 183 /* Initializes a key session, which returns a session handle (mandatory) and a challenge (optional). */ 184 function initSession(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) { 185 return new Promise<huks.HuksSessionHandle>((resolve, reject) => { 186 try { 187 huks.initSession(keyAlias, huksOptions, (error, data) => { 188 if (error) { 189 reject(error); 190 } else { 191 resolve(data); 192 } 193 }); 194 } catch (error) { 195 throwObject.isThrow = true; 196 throw(error as Error); 197 } 198 }); 199 } 200 /* Call initSession. A session handle is returned. */ 201 async function publicInitFunc(keyAlias: string, huksOptions: huks.HuksOptions) { 202 console.info(`enter promise doInit`); 203 let throwObject: throwObject = {isThrow: false}; 204 try { 205 await initSession(keyAlias, huksOptions, throwObject) 206 .then ((data) => { 207 console.info(`promise: doInit success, data = ${JSON.stringify(data)}`); 208 handle = data.handle; 209 }) 210 .catch((error: BusinessError) => { 211 if (throwObject.isThrow) { 212 throw(error as Error); 213 } else { 214 console.error(`promise: doInit failed` + error); 215 } 216 }); 217 } catch (error) { 218 console.error(`promise: doInit input arg invalid` + error); 219 } 220 } 221 /* Call updateSession multiple times to process data by segment and output the processed data. */ 222 function updateSession(handle: number, huksOptions: huks.HuksOptions, throwObject: throwObject) { 223 return new Promise<huks.HuksReturnResult>((resolve, reject) => { 224 try { 225 huks.updateSession(handle, huksOptions, (error, data) => { 226 if (error) { 227 reject(error); 228 } else { 229 resolve(data); 230 } 231 }); 232 } catch (error) { 233 throwObject.isThrow = true; 234 throw(error as Error); 235 } 236 }); 237 } 238 /* Call updateSession to perform key agreement. */ 239 async function publicUpdateFunc(handle: number, huksOptions: huks.HuksOptions) { 240 console.info(`enter promise doUpdate`); 241 let throwObject: throwObject = {isThrow: false}; 242 try { 243 await updateSession(handle, huksOptions, throwObject) 244 .then ((data) => { 245 console.info(`promise: doUpdate success, data = ${JSON.stringify(data)}`); 246 }) 247 .catch((error: BusinessError) => { 248 if (throwObject.isThrow) { 249 throw(error as Error); 250 } else { 251 console.error(`promise: doUpdate failed` + error); 252 } 253 }); 254 } catch (error) { 255 console.error(`promise: doUpdate input arg invalid` + error); 256 } 257 } 258 /* Finish the key session to output the shared secret key. */ 259 function finishSession(handle: number, huksOptions: huks.HuksOptions, throwObject: throwObject) { 260 return new Promise<huks.HuksReturnResult>((resolve, reject) => { 261 try { 262 huks.finishSession(handle, huksOptions, (error, data) =>{ 263 if (error) { 264 reject(error); 265 } else { 266 resolve(data); 267 } 268 }); 269 } catch (error) { 270 throwObject.isThrow = true; 271 throw(error as Error); 272 } 273 }); 274 } 275 /* Call finishSession to finish the operation. */ 276 async function publicFinishFunc(handle: number, huksOptions: huks.HuksOptions) { 277 console.info(`enter promise doFinish`); 278 let throwObject: throwObject = {isThrow: false}; 279 try { 280 await finishSession(handle, huksOptions, throwObject) 281 .then ((data) => { 282 finishOutData = data.outData as Uint8Array; 283 console.info(`promise: doFinish success, data = ${JSON.stringify(data)}`); 284 }) 285 .catch((error: BusinessError) => { 286 if (throwObject.isThrow) { 287 throw(error as Error); 288 } else { 289 console.error(`promise: doFinish failed` + error); 290 } 291 }); 292 } catch (error) { 293 console.error(`promise: doFinish input arg invalid` + error); 294 } 295 } 296 /* Export a key. */ 297 function exportKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) { 298 return new Promise<huks.HuksReturnResult>((resolve, reject) => { 299 try { 300 huks.exportKeyItem(keyAlias, huksOptions, (error, data) => { 301 if (error) { 302 reject(error); 303 } else { 304 resolve(data); 305 } 306 }); 307 } catch (error) { 308 throwObject.isThrow = true; 309 throw(error as Error); 310 } 311 }); 312 } 313 /* Call exportKeyItem to export the public key. */ 314 async function publicExportKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) { 315 console.info(`enter promise export`); 316 let throwObject: throwObject = {isThrow: false}; 317 try { 318 await exportKeyItem(keyAlias, huksOptions, throwObject) 319 .then ((data) => { 320 console.info(`promise: exportKeyItem success, data = ${JSON.stringify(data)}`); 321 exportKey = data.outData as Uint8Array; 322 }) 323 .catch((error: BusinessError) => { 324 if (throwObject.isThrow) { 325 throw(error as Error); 326 } else { 327 console.error(`promise: exportKeyItem failed` + error); 328 } 329 }); 330 } catch (error) { 331 console.error(`promise: exportKeyItem input arg invalid` + error); 332 } 333 } 334 /* Delete the keys. */ 335 function deleteKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) { 336 return new Promise<void>((resolve, reject) => { 337 try { 338 huks.deleteKeyItem(keyAlias, huksOptions, (error, data) => { 339 if (error) { 340 reject(error); 341 } else { 342 resolve(data); 343 } 344 }); 345 } catch (error) { 346 throwObject.isThrow = true; 347 throw(error as Error); 348 } 349 }); 350 } 351 /* Call deleteKeyItem to delete a key. */ 352 async function publicDeleteKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) { 353 console.info(`enter promise deleteKeyItem`); 354 let throwObject: throwObject = {isThrow: false}; 355 try { 356 await deleteKeyItem(keyAlias, huksOptions, throwObject) 357 .then ((data) => { 358 console.info(`promise: deleteKeyItem key success, data = ${JSON.stringify(data)}`); 359 }) 360 .catch((error :BusinessError) => { 361 if (throwObject.isThrow) { 362 throw(error as Error); 363 } else { 364 console.error(`promise: deleteKeyItem failed` + error); 365 } 366 }); 367 } catch (error) { 368 console.error(`promise: deletKeeyItem input arg invalid` + error); 369 } 370 } 371 async function testAgree() { 372 /* 1. Set the key alias srcKeyAliasFirst for device A and srcKeyAliasSecond for device B, and parameters for generating the key pairs. */ 373 /* 2. Generate an asymmetric key pair for device A. */ 374 await publicGenKeyFunc(srcKeyAliasFirst, HuksOptions); 375 /* 3. Generate an asymmetric key pair for device B. */ 376 await publicGenKeyFunc(srcKeyAliasSecond, HuksOptions); 377 /* 4. Export the public keys of the key pairs of device A and device B. */ 378 await publicExportKeyFunc(srcKeyAliasFirst, HuksOptions); 379 exportKeyFrist = exportKey; 380 await publicExportKeyFunc(srcKeyAliasFirst, HuksOptions); 381 exportKeySecond = exportKey; 382 /* 5. Perform key agreement (Init-Update-Finish) for device A. */ 383 await publicInitFunc(srcKeyAliasFirst, HuksOptions); 384 HuksOptions.inData = exportKeySecond; 385 await publicUpdateFunc(handle, HuksOptions); 386 await publicFinishFunc(handle, finishOptionsFrist); 387 /* 5. Perform key agreement (Init-Update-Finish) for device B. */ 388 await publicInitFunc(srcKeyAliasSecond, HuksOptions); 389 HuksOptions.inData = exportKeyFrist; 390 await publicUpdateFunc(handle, HuksOptions); 391 await publicFinishFunc(handle, finishOptionsSecond); 392 /* 6. Delete keys from device A and device B. */ 393 await publicDeleteKeyFunc(srcKeyAliasFirst, HuksOptions); 394 await publicDeleteKeyFunc(srcKeyAliasSecond, HuksOptions); 395 } 396``` 397