1f86ab8e7SSumit Garg // SPDX-License-Identifier: BSD-2-Clause 2f86ab8e7SSumit Garg /* 3f86ab8e7SSumit Garg * Copyright (C) 2019-2020, Linaro Limited 4f86ab8e7SSumit Garg */ 5f86ab8e7SSumit Garg 6f86ab8e7SSumit Garg #include <assert.h> 7f86ab8e7SSumit Garg #include <pta_system.h> 8f86ab8e7SSumit Garg #include <string.h> 9f86ab8e7SSumit Garg #include <string_ext.h> 10f86ab8e7SSumit Garg #include <tee_internal_api.h> 11f86ab8e7SSumit Garg #include <tee_internal_api_extensions.h> 12f86ab8e7SSumit Garg #include <trusted_keys.h> 13f86ab8e7SSumit Garg #include <util.h> 14f86ab8e7SSumit Garg 15f86ab8e7SSumit Garg #define IV_SIZE 16 16f86ab8e7SSumit Garg #define TAG_SIZE 16 17f86ab8e7SSumit Garg #define MAX_BUF_SIZE 512 18f86ab8e7SSumit Garg 19f86ab8e7SSumit Garg /* 20f86ab8e7SSumit Garg * Acronym: 21f86ab8e7SSumit Garg * 22f86ab8e7SSumit Garg * TK - Trusted Key 23f86ab8e7SSumit Garg */ 24f86ab8e7SSumit Garg 25f86ab8e7SSumit Garg struct tk_blob_hdr { 26f86ab8e7SSumit Garg uint8_t reserved; 27f86ab8e7SSumit Garg uint8_t iv[IV_SIZE]; 28f86ab8e7SSumit Garg uint8_t tag[TAG_SIZE]; 29f86ab8e7SSumit Garg uint8_t enc_key[]; 30f86ab8e7SSumit Garg }; 31f86ab8e7SSumit Garg 32f86ab8e7SSumit Garg static TEE_Result get_random(uint32_t types, TEE_Param params[TEE_NUM_PARAMS]) 33f86ab8e7SSumit Garg { 34*a01ae03aSSumit Garg uint8_t *rng_buf = NULL; 35*a01ae03aSSumit Garg 36f86ab8e7SSumit Garg DMSG("Invoked TA_CMD_GET_RANDOM"); 37f86ab8e7SSumit Garg 38f86ab8e7SSumit Garg if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_OUTPUT, 39f86ab8e7SSumit Garg TEE_PARAM_TYPE_NONE, 40f86ab8e7SSumit Garg TEE_PARAM_TYPE_NONE, 41f86ab8e7SSumit Garg TEE_PARAM_TYPE_NONE)) 42f86ab8e7SSumit Garg return TEE_ERROR_BAD_PARAMETERS; 43f86ab8e7SSumit Garg 44f86ab8e7SSumit Garg if (!params[0].memref.buffer || !params[0].memref.size) 45f86ab8e7SSumit Garg return TEE_ERROR_BAD_PARAMETERS; 46f86ab8e7SSumit Garg 47*a01ae03aSSumit Garg rng_buf = TEE_Malloc(params[0].memref.size, TEE_MALLOC_FILL_ZERO); 48*a01ae03aSSumit Garg if (!rng_buf) 49*a01ae03aSSumit Garg return TEE_ERROR_OUT_OF_MEMORY; 50*a01ae03aSSumit Garg 51*a01ae03aSSumit Garg TEE_GenerateRandom(rng_buf, params[0].memref.size); 52*a01ae03aSSumit Garg memcpy(params[0].memref.buffer, rng_buf, params[0].memref.size); 53*a01ae03aSSumit Garg memzero_explicit(rng_buf, params[0].memref.size); 54*a01ae03aSSumit Garg 55*a01ae03aSSumit Garg TEE_Free(rng_buf); 56f86ab8e7SSumit Garg 57f86ab8e7SSumit Garg return TEE_SUCCESS; 58f86ab8e7SSumit Garg } 59f86ab8e7SSumit Garg 60f86ab8e7SSumit Garg static TEE_Result derive_unique_key(uint8_t *key, uint16_t key_size, 61f86ab8e7SSumit Garg uint8_t *extra, uint16_t extra_size) 62f86ab8e7SSumit Garg { 63f86ab8e7SSumit Garg TEE_TASessionHandle sess = TEE_HANDLE_NULL; 64f86ab8e7SSumit Garg TEE_Param params[TEE_NUM_PARAMS] = { }; 65f86ab8e7SSumit Garg TEE_Result res = TEE_ERROR_GENERIC; 66f86ab8e7SSumit Garg uint32_t ret_orig = 0; 67f86ab8e7SSumit Garg uint32_t param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, 68f86ab8e7SSumit Garg TEE_PARAM_TYPE_MEMREF_OUTPUT, 69f86ab8e7SSumit Garg TEE_PARAM_TYPE_NONE, 70f86ab8e7SSumit Garg TEE_PARAM_TYPE_NONE); 71f86ab8e7SSumit Garg 72f86ab8e7SSumit Garg res = TEE_OpenTASession(&(const TEE_UUID)PTA_SYSTEM_UUID, 73f86ab8e7SSumit Garg TEE_TIMEOUT_INFINITE, 0, NULL, &sess, 74f86ab8e7SSumit Garg &ret_orig); 75f86ab8e7SSumit Garg if (res) 76f86ab8e7SSumit Garg return res; 77f86ab8e7SSumit Garg 78f86ab8e7SSumit Garg if (extra && extra_size) { 79f86ab8e7SSumit Garg params[0].memref.buffer = extra; 80f86ab8e7SSumit Garg params[0].memref.size = extra_size; 81f86ab8e7SSumit Garg } 82f86ab8e7SSumit Garg 83f86ab8e7SSumit Garg params[1].memref.buffer = key; 84f86ab8e7SSumit Garg params[1].memref.size = key_size; 85f86ab8e7SSumit Garg 86f86ab8e7SSumit Garg res = TEE_InvokeTACommand(sess, TEE_TIMEOUT_INFINITE, 87f86ab8e7SSumit Garg PTA_SYSTEM_DERIVE_TA_UNIQUE_KEY, 88f86ab8e7SSumit Garg param_types, params, &ret_orig); 89f86ab8e7SSumit Garg 90f86ab8e7SSumit Garg TEE_CloseTASession(sess); 91f86ab8e7SSumit Garg 92f86ab8e7SSumit Garg return res; 93f86ab8e7SSumit Garg } 94f86ab8e7SSumit Garg 95f86ab8e7SSumit Garg static TEE_Result huk_ae_encrypt(TEE_OperationHandle crypto_op, uint8_t *in, 96f86ab8e7SSumit Garg uint32_t in_sz, uint8_t *out, uint32_t *out_sz) 97f86ab8e7SSumit Garg { 98f86ab8e7SSumit Garg TEE_Result res = TEE_ERROR_GENERIC; 99f86ab8e7SSumit Garg struct tk_blob_hdr *hdr = (struct tk_blob_hdr *)out; 100*a01ae03aSSumit Garg uint8_t iv[IV_SIZE] = { 0 }; 101f86ab8e7SSumit Garg uint32_t enc_key_len = in_sz; 102f86ab8e7SSumit Garg uint32_t tag_len = TAG_SIZE; 103f86ab8e7SSumit Garg 104f86ab8e7SSumit Garg hdr->reserved = 0; 105*a01ae03aSSumit Garg TEE_GenerateRandom(iv, IV_SIZE); 106*a01ae03aSSumit Garg memcpy(hdr->iv, iv, IV_SIZE); 107f86ab8e7SSumit Garg 108f86ab8e7SSumit Garg res = TEE_AEInit(crypto_op, hdr->iv, IV_SIZE, TAG_SIZE * 8, 0, 0); 109f86ab8e7SSumit Garg if (res) 110f86ab8e7SSumit Garg return res; 111f86ab8e7SSumit Garg 112f86ab8e7SSumit Garg res = TEE_AEEncryptFinal(crypto_op, in, in_sz, hdr->enc_key, 113f86ab8e7SSumit Garg &enc_key_len, hdr->tag, &tag_len); 114f86ab8e7SSumit Garg if (res || tag_len != TAG_SIZE) 115f86ab8e7SSumit Garg return TEE_ERROR_SECURITY; 116f86ab8e7SSumit Garg 117f86ab8e7SSumit Garg if (ADD_OVERFLOW(enc_key_len, sizeof(*hdr), out_sz)) 118f86ab8e7SSumit Garg return TEE_ERROR_SECURITY; 119f86ab8e7SSumit Garg 120f86ab8e7SSumit Garg return res; 121f86ab8e7SSumit Garg } 122f86ab8e7SSumit Garg 123f86ab8e7SSumit Garg static TEE_Result huk_ae_decrypt(TEE_OperationHandle crypto_op, uint8_t *in, 124f86ab8e7SSumit Garg uint32_t in_sz, uint8_t *out, uint32_t *out_sz) 125f86ab8e7SSumit Garg { 126f86ab8e7SSumit Garg TEE_Result res = TEE_ERROR_GENERIC; 127f86ab8e7SSumit Garg struct tk_blob_hdr *hdr = (struct tk_blob_hdr *)in; 128*a01ae03aSSumit Garg uint8_t tag[TAG_SIZE] = { 0 }; 129f86ab8e7SSumit Garg uint32_t enc_key_len = 0; 130f86ab8e7SSumit Garg 131f86ab8e7SSumit Garg if (SUB_OVERFLOW(in_sz, sizeof(*hdr), &enc_key_len)) 132f86ab8e7SSumit Garg return TEE_ERROR_SECURITY; 133f86ab8e7SSumit Garg 134f86ab8e7SSumit Garg res = TEE_AEInit(crypto_op, hdr->iv, IV_SIZE, TAG_SIZE * 8, 0, 0); 135f86ab8e7SSumit Garg if (res) 136f86ab8e7SSumit Garg return res; 137f86ab8e7SSumit Garg 138*a01ae03aSSumit Garg memcpy(tag, hdr->tag, TAG_SIZE); 139f86ab8e7SSumit Garg res = TEE_AEDecryptFinal(crypto_op, hdr->enc_key, enc_key_len, out, 140*a01ae03aSSumit Garg out_sz, tag, TAG_SIZE); 141f86ab8e7SSumit Garg if (res) 142f86ab8e7SSumit Garg res = TEE_ERROR_SECURITY; 143f86ab8e7SSumit Garg 144f86ab8e7SSumit Garg return res; 145f86ab8e7SSumit Garg } 146f86ab8e7SSumit Garg 147f86ab8e7SSumit Garg static TEE_Result huk_crypt(TEE_OperationMode mode, uint8_t *in, uint32_t in_sz, 148f86ab8e7SSumit Garg uint8_t *out, uint32_t *out_sz) 149f86ab8e7SSumit Garg { 150f86ab8e7SSumit Garg TEE_Result res = TEE_ERROR_GENERIC; 151f86ab8e7SSumit Garg TEE_OperationHandle crypto_op = TEE_HANDLE_NULL; 152f86ab8e7SSumit Garg TEE_ObjectHandle hkey = TEE_HANDLE_NULL; 153f86ab8e7SSumit Garg uint8_t huk_key[TA_DERIVED_KEY_MAX_SIZE] = { }; 154f86ab8e7SSumit Garg TEE_Attribute attr = { }; 155f86ab8e7SSumit Garg 156f86ab8e7SSumit Garg res = TEE_AllocateOperation(&crypto_op, TEE_ALG_AES_GCM, mode, 157f86ab8e7SSumit Garg sizeof(huk_key) * 8); 158f86ab8e7SSumit Garg if (res) 159f86ab8e7SSumit Garg return res; 160f86ab8e7SSumit Garg 161f86ab8e7SSumit Garg res = derive_unique_key(huk_key, sizeof(huk_key), NULL, 0); 162f86ab8e7SSumit Garg if (res) { 163f86ab8e7SSumit Garg EMSG("derive_unique_key failed: returned %#"PRIx32, res); 164f86ab8e7SSumit Garg goto out_op; 165f86ab8e7SSumit Garg } 166f86ab8e7SSumit Garg 167f86ab8e7SSumit Garg res = TEE_AllocateTransientObject(TEE_TYPE_AES, sizeof(huk_key) * 8, 168f86ab8e7SSumit Garg &hkey); 169f86ab8e7SSumit Garg if (res) 170f86ab8e7SSumit Garg goto out_op; 171f86ab8e7SSumit Garg 172f86ab8e7SSumit Garg attr.attributeID = TEE_ATTR_SECRET_VALUE; 173f86ab8e7SSumit Garg attr.content.ref.buffer = huk_key; 174f86ab8e7SSumit Garg attr.content.ref.length = sizeof(huk_key); 175f86ab8e7SSumit Garg 176f86ab8e7SSumit Garg res = TEE_PopulateTransientObject(hkey, &attr, 1); 177f86ab8e7SSumit Garg if (res) 178f86ab8e7SSumit Garg goto out_key; 179f86ab8e7SSumit Garg 180f86ab8e7SSumit Garg res = TEE_SetOperationKey(crypto_op, hkey); 181f86ab8e7SSumit Garg if (res) 182f86ab8e7SSumit Garg goto out_key; 183f86ab8e7SSumit Garg 184f86ab8e7SSumit Garg if (mode == TEE_MODE_ENCRYPT) { 185f86ab8e7SSumit Garg res = huk_ae_encrypt(crypto_op, in, in_sz, out, out_sz); 186f86ab8e7SSumit Garg if (res) 187f86ab8e7SSumit Garg EMSG("huk_AE_encrypt failed: returned %#"PRIx32, res); 188f86ab8e7SSumit Garg } else if (mode == TEE_MODE_DECRYPT) { 189f86ab8e7SSumit Garg res = huk_ae_decrypt(crypto_op, in, in_sz, out, out_sz); 190f86ab8e7SSumit Garg if (res) 191f86ab8e7SSumit Garg EMSG("huk_AE_decrypt failed: returned %#"PRIx32, res); 192f86ab8e7SSumit Garg } else { 193f86ab8e7SSumit Garg TEE_Panic(0); 194f86ab8e7SSumit Garg } 195f86ab8e7SSumit Garg 196f86ab8e7SSumit Garg out_key: 197f86ab8e7SSumit Garg TEE_FreeTransientObject(hkey); 198f86ab8e7SSumit Garg out_op: 199f86ab8e7SSumit Garg TEE_FreeOperation(crypto_op); 200f86ab8e7SSumit Garg memzero_explicit(huk_key, sizeof(huk_key)); 201f86ab8e7SSumit Garg return res; 202f86ab8e7SSumit Garg } 203f86ab8e7SSumit Garg 204f86ab8e7SSumit Garg static TEE_Result seal_trusted_key(uint32_t types, 205f86ab8e7SSumit Garg TEE_Param params[TEE_NUM_PARAMS]) 206f86ab8e7SSumit Garg { 207f86ab8e7SSumit Garg TEE_Result res = TEE_SUCCESS; 208f86ab8e7SSumit Garg uint8_t *in = NULL; 209f86ab8e7SSumit Garg uint32_t in_sz = 0; 210f86ab8e7SSumit Garg uint8_t *out = NULL; 211f86ab8e7SSumit Garg uint32_t out_sz = 0; 212f86ab8e7SSumit Garg 213f86ab8e7SSumit Garg DMSG("Invoked TA_CMD_SEAL"); 214f86ab8e7SSumit Garg 215f86ab8e7SSumit Garg if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, 216f86ab8e7SSumit Garg TEE_PARAM_TYPE_MEMREF_OUTPUT, 217f86ab8e7SSumit Garg TEE_PARAM_TYPE_NONE, 218f86ab8e7SSumit Garg TEE_PARAM_TYPE_NONE)) 219f86ab8e7SSumit Garg return TEE_ERROR_BAD_PARAMETERS; 220f86ab8e7SSumit Garg 221f86ab8e7SSumit Garg in = params[0].memref.buffer; 222f86ab8e7SSumit Garg in_sz = params[0].memref.size; 223f86ab8e7SSumit Garg out = params[1].memref.buffer; 224f86ab8e7SSumit Garg out_sz = params[1].memref.size; 225f86ab8e7SSumit Garg 226f86ab8e7SSumit Garg if (!in || !in_sz || in_sz > MAX_BUF_SIZE) 227f86ab8e7SSumit Garg return TEE_ERROR_BAD_PARAMETERS; 228f86ab8e7SSumit Garg if ((!out && out_sz) || 229f86ab8e7SSumit Garg (out && !ALIGNMENT_IS_OK(out, struct tk_blob_hdr)) || 230f86ab8e7SSumit Garg out_sz > MAX_BUF_SIZE) 231f86ab8e7SSumit Garg return TEE_ERROR_BAD_PARAMETERS; 232f86ab8e7SSumit Garg 233f86ab8e7SSumit Garg if ((in_sz + sizeof(struct tk_blob_hdr)) > out_sz) { 234f86ab8e7SSumit Garg params[1].memref.size = in_sz + sizeof(struct tk_blob_hdr); 235f86ab8e7SSumit Garg return TEE_ERROR_SHORT_BUFFER; 236f86ab8e7SSumit Garg } 237f86ab8e7SSumit Garg 238f86ab8e7SSumit Garg res = huk_crypt(TEE_MODE_ENCRYPT, in, in_sz, out, &out_sz); 239f86ab8e7SSumit Garg if (res == TEE_SUCCESS) { 240f86ab8e7SSumit Garg assert(out_sz == in_sz + sizeof(struct tk_blob_hdr)); 241f86ab8e7SSumit Garg params[1].memref.size = out_sz; 242f86ab8e7SSumit Garg } 243f86ab8e7SSumit Garg 244f86ab8e7SSumit Garg return res; 245f86ab8e7SSumit Garg } 246f86ab8e7SSumit Garg 247f86ab8e7SSumit Garg static TEE_Result unseal_trusted_key(uint32_t types, 248f86ab8e7SSumit Garg TEE_Param params[TEE_NUM_PARAMS]) 249f86ab8e7SSumit Garg { 250f86ab8e7SSumit Garg TEE_Result res = TEE_SUCCESS; 251f86ab8e7SSumit Garg uint8_t *in = NULL; 252f86ab8e7SSumit Garg uint32_t in_sz = 0; 253f86ab8e7SSumit Garg uint8_t *out = NULL; 254f86ab8e7SSumit Garg uint32_t out_sz = 0; 255f86ab8e7SSumit Garg 256f86ab8e7SSumit Garg DMSG("Invoked TA_CMD_UNSEAL"); 257f86ab8e7SSumit Garg 258f86ab8e7SSumit Garg if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, 259f86ab8e7SSumit Garg TEE_PARAM_TYPE_MEMREF_OUTPUT, 260f86ab8e7SSumit Garg TEE_PARAM_TYPE_NONE, 261f86ab8e7SSumit Garg TEE_PARAM_TYPE_NONE)) 262f86ab8e7SSumit Garg return TEE_ERROR_BAD_PARAMETERS; 263f86ab8e7SSumit Garg 264f86ab8e7SSumit Garg in = params[0].memref.buffer; 265f86ab8e7SSumit Garg in_sz = params[0].memref.size; 266f86ab8e7SSumit Garg out = params[1].memref.buffer; 267f86ab8e7SSumit Garg out_sz = params[1].memref.size; 268f86ab8e7SSumit Garg 269f86ab8e7SSumit Garg if (!in || !ALIGNMENT_IS_OK(in, struct tk_blob_hdr) || 270f86ab8e7SSumit Garg in_sz <= sizeof(struct tk_blob_hdr) || in_sz > MAX_BUF_SIZE) 271f86ab8e7SSumit Garg return TEE_ERROR_BAD_PARAMETERS; 272f86ab8e7SSumit Garg if ((!out && out_sz) || out_sz > MAX_BUF_SIZE) 273f86ab8e7SSumit Garg return TEE_ERROR_BAD_PARAMETERS; 274f86ab8e7SSumit Garg 275f86ab8e7SSumit Garg if (in_sz > (out_sz + sizeof(struct tk_blob_hdr))) { 276f86ab8e7SSumit Garg params[1].memref.size = in_sz - sizeof(struct tk_blob_hdr); 277f86ab8e7SSumit Garg return TEE_ERROR_SHORT_BUFFER; 278f86ab8e7SSumit Garg } 279f86ab8e7SSumit Garg 280f86ab8e7SSumit Garg res = huk_crypt(TEE_MODE_DECRYPT, in, in_sz, out, &out_sz); 281f86ab8e7SSumit Garg if (res == TEE_SUCCESS) { 282f86ab8e7SSumit Garg assert(out_sz == in_sz - sizeof(struct tk_blob_hdr)); 283f86ab8e7SSumit Garg params[1].memref.size = out_sz; 284f86ab8e7SSumit Garg } 285f86ab8e7SSumit Garg 286f86ab8e7SSumit Garg return res; 287f86ab8e7SSumit Garg } 288f86ab8e7SSumit Garg 289f86ab8e7SSumit Garg TEE_Result TA_CreateEntryPoint(void) 290f86ab8e7SSumit Garg { 291f86ab8e7SSumit Garg return TEE_SUCCESS; 292f86ab8e7SSumit Garg } 293f86ab8e7SSumit Garg 294f86ab8e7SSumit Garg void TA_DestroyEntryPoint(void) 295f86ab8e7SSumit Garg { 296f86ab8e7SSumit Garg } 297f86ab8e7SSumit Garg 298f86ab8e7SSumit Garg TEE_Result TA_OpenSessionEntryPoint(uint32_t pt __unused, 299f86ab8e7SSumit Garg TEE_Param params[TEE_NUM_PARAMS] __unused, 300f86ab8e7SSumit Garg void **session __unused) 301f86ab8e7SSumit Garg { 302f86ab8e7SSumit Garg TEE_Result res = TEE_ERROR_GENERIC; 303f86ab8e7SSumit Garg TEE_PropSetHandle h = TEE_HANDLE_NULL; 304f86ab8e7SSumit Garg TEE_Identity id = { }; 305f86ab8e7SSumit Garg 306f86ab8e7SSumit Garg res = TEE_AllocatePropertyEnumerator(&h); 307f86ab8e7SSumit Garg if (res) 308f86ab8e7SSumit Garg goto out; 309f86ab8e7SSumit Garg 310f86ab8e7SSumit Garg TEE_StartPropertyEnumerator(h, TEE_PROPSET_CURRENT_CLIENT); 311f86ab8e7SSumit Garg 312f86ab8e7SSumit Garg res = TEE_GetPropertyAsIdentity(h, NULL, &id); 313f86ab8e7SSumit Garg if (res) 314f86ab8e7SSumit Garg goto out; 315f86ab8e7SSumit Garg 316f86ab8e7SSumit Garg if (id.login != TEE_LOGIN_REE_KERNEL) 317f86ab8e7SSumit Garg res = TEE_ERROR_ACCESS_DENIED; 318f86ab8e7SSumit Garg 319f86ab8e7SSumit Garg out: 320f86ab8e7SSumit Garg if (h) 321f86ab8e7SSumit Garg TEE_FreePropertyEnumerator(h); 322f86ab8e7SSumit Garg return res; 323f86ab8e7SSumit Garg } 324f86ab8e7SSumit Garg 325f86ab8e7SSumit Garg void TA_CloseSessionEntryPoint(void *sess __unused) 326f86ab8e7SSumit Garg { 327f86ab8e7SSumit Garg } 328f86ab8e7SSumit Garg 329f86ab8e7SSumit Garg TEE_Result TA_InvokeCommandEntryPoint(void *sess __unused, uint32_t cmd, 330f86ab8e7SSumit Garg uint32_t pt, 331f86ab8e7SSumit Garg TEE_Param params[TEE_NUM_PARAMS]) 332f86ab8e7SSumit Garg { 333f86ab8e7SSumit Garg switch (cmd) { 334f86ab8e7SSumit Garg case TA_CMD_GET_RANDOM: 335f86ab8e7SSumit Garg return get_random(pt, params); 336f86ab8e7SSumit Garg case TA_CMD_SEAL: 337f86ab8e7SSumit Garg return seal_trusted_key(pt, params); 338f86ab8e7SSumit Garg case TA_CMD_UNSEAL: 339f86ab8e7SSumit Garg return unseal_trusted_key(pt, params); 340f86ab8e7SSumit Garg default: 341f86ab8e7SSumit Garg EMSG("Command ID %#"PRIx32" is not supported", cmd); 342f86ab8e7SSumit Garg return TEE_ERROR_NOT_SUPPORTED; 343f86ab8e7SSumit Garg } 344f86ab8e7SSumit Garg } 345