1# Encryption and Decryption by Segment with an AES Symmetric Key (GCM Mode) (ArkTS)
2
3
4For details about the algorithm specifications, see [AES](crypto-sym-encrypt-decrypt-spec.md#aes).
5
6
7**Encryption**
8
9
101. Use [cryptoFramework.createSymKeyGenerator](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#cryptoframeworkcreatesymkeygenerator) and [SymKeyGenerator.generateSymKey](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#generatesymkey-1) to generate a 128-bit AES symmetric key (**SymKey**).
11
12   In addition to the example in this topic, [AES](crypto-sym-key-generation-conversion-spec.md#aes) and [Randomly Generating a Symmetric Key](crypto-generate-sym-key-randomly.md) may help you better understand how to generate an AES symmetric key. Note that the input parameters in the reference documents may be different from those in the example below.
13
142. Use [cryptoFramework.createCipher](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#cryptoframeworkcreatecipher) with the string parameter **'AES128|GCM|PKCS7'** to create a **Cipher** instance. The key type is **AES128**, block cipher mode is **GCM**, and the padding mode is **PKCS7**.
15
163. Use [Cipher.init](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#init-1) to initialize the **Cipher** instance. In the **Cipher.init** API, set **opMode** to **CryptoMode.ENCRYPT_MODE** (encryption), **key** to **SymKey** (the key for encryption), and **params** to **GcmParamsSpec** corresponding to the GCM mode.
17
184. Set the size of the data to be passed in each time to 20 bytes, and call [Cipher.update](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#update-1) multiple times to pass in the data (plaintext) to be encrypted.
19
20   - Currently, the amount of data to be passed in by a single **update()** is not limited. You can determine how to pass in data based on the data volume.
21   - You are advised to check the result of each **update()**. If the result is not **null**, obtain the data and combine the data segments into complete ciphertext. The **update()** result may vary with the key specifications.
22
23      If a block cipher mode (ECB or CBC) is used, data is encrypted and output based on the block size. That is, if the data of an **update()** operation matches the block size, the ciphertext is output. Otherwise, **null** is output, and the plaintext will be combined with the input data of the next **update()** to form a block. When **doFinal()** is called, the unencrypted data is padded to the block size based on the specified padding mode, and then encrypted. The **update()** API works in the same way in decryption.
24
25      If a stream cipher mode (CTR or OFB) is used, the ciphertext length is usually the same as the plaintext length.
26
275. Use [Cipher.doFinal](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#dofinal-1) to obtain the encrypted data.
28
29   - If data has been passed in by **update()**, pass in **null** in the **data** parameter of **Cipher.doFinal**.
30   - The output of **doFinal** may be **null**. To avoid exceptions, always check whether the result is **null** before accessing specific data.
31
326. Obtain [GcmParamsSpec](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#gcmparamsspec).authTag as the authentication information for decryption.
33
34   In GCM mode, extract the last 16 bytes from the encrypted data as the authentication information for initializing the **Cipher** instance in decryption. In the example, **authTag** is of 16 bytes.
35
36
37**Decryption**
38
39
401. Use [Cipher.init](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#init-1) to initialize the **Cipher** instance. In the **Cipher.init** API, set **opMode** to **CryptoMode.DECRYPT_MODE** (decryption), **key** to **SymKey** (the key for decryption), and **params** to **GcmParamsSpec** corresponding to the GCM mode.
41
422. Set the size of the data to be passed in each time to 20 bytes, and call [Cipher.update](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#update-1) multiple times to pass in the data (ciphertext) to be decrypted.
43
443. Use [Cipher.doFinal](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#dofinal-1) to obtain the decrypted data.
45
46
47- Example (using asynchronous APIs):
48
49  ```ts
50  import { cryptoFramework } from '@kit.CryptoArchitectureKit';
51  import { buffer } from '@kit.ArkTS';
52
53  function generateRandom(len: number) {
54    let rand = cryptoFramework.createRandom();
55    let generateRandSync = rand.generateRandomSync(len);
56    return generateRandSync;
57  }
58
59  function genGcmParamsSpec() {
60    let ivBlob = generateRandom(12);
61    let arr = [1, 2, 3, 4, 5, 6, 7, 8]; // 8 bytes
62    let dataAad = new Uint8Array(arr);
63    let aadBlob: cryptoFramework.DataBlob = { data: dataAad };
64    arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes
65    let dataTag = new Uint8Array(arr);
66    let tagBlob: cryptoFramework.DataBlob = {
67      data: dataTag
68    }; // The GCM authTag is obtained by doFinal() in encryption and passed in params of init() in decryption.
69    let gcmParamsSpec: cryptoFramework.GcmParamsSpec = {
70      iv: ivBlob,
71      aad: aadBlob,
72      authTag: tagBlob,
73      algName: "GcmParamsSpec"
74    };
75    return gcmParamsSpec;
76  }
77  let gcmParams = genGcmParamsSpec();
78  // Encrypt the message by segment.
79  async function encryptMessageUpdateBySegment(symKey: cryptoFramework.SymKey, plainText: cryptoFramework.DataBlob) {
80    let cipher = cryptoFramework.createCipher('AES128|GCM|PKCS7');
81    await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams);
82    let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required.
83    let cipherText = new Uint8Array();
84    for (let i = 0; i < plainText.data.length; i += updateLength) {
85      let updateMessage = plainText.data.subarray(i, i + updateLength);
86      let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage };
87      // Call update() multiple times to pass in data by segment.
88      let updateOutput = await cipher.update(updateMessageBlob);
89      // Combine the result of each update() to obtain the ciphertext. In certain cases, the doFinal() result also needs to be combined, which depends on the cipher block mode
90      // and padding mode you use. In this example, the GCM mode is used, and the doFinal() result contains authTag but not ciphertext. Therefore, there is no need to combine the doFinal() result.
91      let mergeText = new Uint8Array(cipherText.length + updateOutput.data.length);
92      mergeText.set(cipherText);
93      mergeText.set(updateOutput.data, cipherText.length);
94      cipherText = mergeText;
95    }
96    gcmParams.authTag = await cipher.doFinal(null);
97    let cipherBlob: cryptoFramework.DataBlob = { data: cipherText };
98    return cipherBlob;
99  }
100  // Decrypt the message by segment.
101  async function decryptMessagePromise(symKey: cryptoFramework.SymKey, cipherText: cryptoFramework.DataBlob) {
102    let decoder = cryptoFramework.createCipher('AES128|GCM|PKCS7');
103    await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, gcmParams);
104    let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required.
105    let decryptText = new Uint8Array();
106    for (let i = 0; i < cipherText.data.length; i += updateLength) {
107      let updateMessage = cipherText.data.subarray(i, i + updateLength);
108      let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage };
109      // Call update() multiple times to pass in data by segment.
110      let updateOutput = await decoder.update(updateMessageBlob);
111      // Combine the update() results to obtain the plaintext.
112      let mergeText = new Uint8Array(decryptText.length + updateOutput.data.length);
113      mergeText.set(decryptText);
114      mergeText.set(updateOutput.data, decryptText.length);
115      decryptText = mergeText;
116    }
117    let decryptData = await decoder.doFinal(null);
118    if (decryptData == null) {
119      console.info('GCM decrypt success, decryptData is null');
120    }
121    let decryptBlob: cryptoFramework.DataBlob = { data: decryptText };
122    return decryptBlob;
123  }
124  async function genSymKeyByData(symKeyData: Uint8Array) {
125    let symKeyBlob: cryptoFramework.DataBlob = { data: symKeyData };
126    let aesGenerator = cryptoFramework.createSymKeyGenerator('AES128');
127    let symKey = await aesGenerator.convertKey(symKeyBlob);
128    console.info('convertKey success');
129    return symKey;
130  }
131  async function aes() {
132    let keyData = new Uint8Array([83, 217, 231, 76, 28, 113, 23, 219, 250, 71, 209, 210, 205, 97, 32, 159]);
133    let symKey = await genSymKeyByData(keyData);
134    let message = "aaaaa.....bbbbb.....ccccc.....ddddd.....eee"; // The message is of 43 bytes. After decoded in UTF-8 format, the message is also of 43 bytes.
135    let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
136    let encryptText = await encryptMessageUpdateBySegment(symKey, plainText);
137    let decryptText = await decryptMessagePromise(symKey, encryptText);
138    if (plainText.data.toString() === decryptText.data.toString()) {
139      console.info('decrypt ok');
140      console.info('decrypt plainText: ' + buffer.from(decryptText.data).toString('utf-8'));
141    } else {
142      console.error('decrypt failed');
143    }
144  }
145  ```
146
147- Example (using synchronous APIs):
148
149  ```ts
150  import { cryptoFramework } from '@kit.CryptoArchitectureKit';
151  import { buffer } from '@kit.ArkTS';
152
153  function generateRandom(len: number) {
154    let rand = cryptoFramework.createRandom();
155    let generateRandSync = rand.generateRandomSync(len);
156    return generateRandSync;
157  }
158
159  function genGcmParamsSpec() {
160    let ivBlob = generateRandom(12);
161    let arr = [1, 2, 3, 4, 5, 6, 7, 8]; // 8 bytes
162    let dataAad = new Uint8Array(arr);
163    let aadBlob: cryptoFramework.DataBlob = { data: dataAad };
164    arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes
165    let dataTag = new Uint8Array(arr);
166    let tagBlob: cryptoFramework.DataBlob = {
167      data: dataTag
168    }; // The GCM authTag is obtained by doFinal() in encryption and passed in params of init() in decryption.
169    let gcmParamsSpec: cryptoFramework.GcmParamsSpec = {
170      iv: ivBlob,
171      aad: aadBlob,
172      authTag: tagBlob,
173      algName: "GcmParamsSpec"
174    };
175    return gcmParamsSpec;
176  }
177  let gcmParams = genGcmParamsSpec();
178  // Encrypt the message by segment.
179  function encryptMessageUpdateBySegment(symKey: cryptoFramework.SymKey, plainText: cryptoFramework.DataBlob) {
180    let cipher = cryptoFramework.createCipher('AES128|GCM|PKCS7');
181    cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams);
182    let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required.
183    let cipherText = new Uint8Array();
184    for (let i = 0; i < plainText.data.length; i += updateLength) {
185      let updateMessage = plainText.data.subarray(i, i + updateLength);
186      let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage };
187      // Call update() multiple times to pass in data by segment.
188      let updateOutput = cipher.updateSync(updateMessageBlob);
189      // Combine the result of each update() to obtain the ciphertext. In certain cases, the doFinal() result also needs to be combined, which depends on the cipher block mode
190      // and padding mode you use. In this example, the GCM mode is used, and the doFinal() result contains authTag but not ciphertext. Therefore, there is no need to combine the doFinal() result.
191      let mergeText = new Uint8Array(cipherText.length + updateOutput.data.length);
192      mergeText.set(cipherText);
193      mergeText.set(updateOutput.data, cipherText.length);
194      cipherText = mergeText;
195    }
196    gcmParams.authTag = cipher.doFinalSync(null);
197    let cipherBlob: cryptoFramework.DataBlob = { data: cipherText };
198    return cipherBlob;
199  }
200  // Decrypt the message by segment.
201  function decryptMessage(symKey: cryptoFramework.SymKey, cipherText: cryptoFramework.DataBlob) {
202    let decoder = cryptoFramework.createCipher('AES128|GCM|PKCS7');
203    decoder.initSync(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, gcmParams);
204    let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required.
205    let decryptText = new Uint8Array();
206    for (let i = 0; i < cipherText.data.length; i += updateLength) {
207      let updateMessage = cipherText.data.subarray(i, i + updateLength);
208      let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage };
209      // Call update() multiple times to pass in data by segment.
210      let updateOutput = decoder.updateSync(updateMessageBlob);
211      // Combine the update() results to obtain the plaintext.
212      let mergeText = new Uint8Array(decryptText.length + updateOutput.data.length);
213      mergeText.set(decryptText);
214      mergeText.set(updateOutput.data, decryptText.length);
215      decryptText = mergeText;
216    }
217    let decryptData = decoder.doFinalSync(null);
218    if (decryptData == null) {
219      console.info('GCM decrypt success, decryptData is null');
220    }
221    let decryptBlob: cryptoFramework.DataBlob = { data: decryptText };
222    return decryptBlob;
223  }
224  function genSymKeyByData(symKeyData: Uint8Array) {
225    let symKeyBlob: cryptoFramework.DataBlob = { data: symKeyData };
226    let aesGenerator = cryptoFramework.createSymKeyGenerator('AES128');
227    let symKey = aesGenerator.convertKeySync(symKeyBlob);
228    console.info('convertKeySync success');
229    return symKey;
230  }
231  function main() {
232    let keyData = new Uint8Array([83, 217, 231, 76, 28, 113, 23, 219, 250, 71, 209, 210, 205, 97, 32, 159]);
233    let symKey = genSymKeyByData(keyData);
234    let message = "aaaaa.....bbbbb.....ccccc.....ddddd.....eee"; // The message is of 43 bytes. After decoded in UTF-8 format, the message is also of 43 bytes.
235    let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
236    let encryptText = encryptMessageUpdateBySegment(symKey, plainText);
237    let decryptText = decryptMessage(symKey, encryptText);
238    if (plainText.data.toString() === decryptText.data.toString()) {
239      console.info('decrypt ok');
240      console.info('decrypt plainText: ' + buffer.from(decryptText.data).toString('utf-8'));
241    } else {
242      console.error('decrypt failed');
243    }
244  }
245
246  ```
247