1a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera/* 2a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * Copyright (C) 2010 The Android Open Source Project 3a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * 4a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * Licensed under the Apache License, Version 2.0 (the "License"); 5a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * you may not use this file except in compliance with the License. 6a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * You may obtain a copy of the License at 7a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * 8a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * http://www.apache.org/licenses/LICENSE-2.0 9a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * 10a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * Unless required by applicable law or agreed to in writing, software 11a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * distributed under the License is distributed on an "AS IS" BASIS, 12a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * See the License for the specific language governing permissions and 14a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * limitations under the License. 15a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera */ 16a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 17a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include <assert.h> 18a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include <errno.h> 19a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include <fcntl.h> 20a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include <pthread.h> 21a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include <stdlib.h> 22a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include <string.h> 23a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include <sys/stat.h> 24a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include <unistd.h> 25a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include <openssl/aes.h> 26a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 27a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#include "FwdLockGlue.h" 28a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 29a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#define TRUE 1 30a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#define FALSE 0 31a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 32a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#define KEY_SIZE 16 33a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera#define KEY_SIZE_IN_BITS (KEY_SIZE * 8) 34a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 35a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeherastatic int isInitialized = FALSE; 36a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 37a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeherastatic const char strKeyFilename[] = "/data/drm/fwdlock/kek.dat"; 38a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 39a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeherastatic AES_KEY encryptionRoundKeys; 40a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeherastatic AES_KEY decryptionRoundKeys; 41a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 42a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera/** 43a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * Creates all directories along the fully qualified path of the given file. 44a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * 45a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * @param[in] path A reference to the fully qualified path of a file. 46a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * @param[in] mode The access mode to use for the directories being created. 47a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * 48a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * @return A Boolean value indicating whether the operation was successful. 49a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera */ 50a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeherastatic int FwdLockGlue_CreateDirectories(const char *path, mode_t mode) { 51a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera int result = TRUE; 52a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t partialPathLength = strlen(path); 53a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera char *partialPath = malloc(partialPathLength + 1); 54a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (partialPath == NULL) { 55a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera result = FALSE; 56a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } else { 57a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t i; 58a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera for (i = 0; i < partialPathLength; ++i) { 59a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (path[i] == '/' && i > 0) { 60a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera partialPath[i] = '\0'; 61a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (mkdir(partialPath, mode) != 0 && errno != EEXIST) { 62a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera result = FALSE; 63a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera break; 64a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 65a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 66a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera partialPath[i] = path[i]; 67a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 68a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera free(partialPath); 69a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 70a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera return result; 71a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera} 72a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 73a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera/** 74a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * Initializes the round keys used for encryption and decryption of session keys. First creates a 75a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * device-unique key-encryption key if none exists yet. 76a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera */ 77a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeherastatic void FwdLockGlue_InitializeRoundKeys() { 78a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera unsigned char keyEncryptionKey[KEY_SIZE]; 79a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera int fileDesc = open(strKeyFilename, O_RDONLY); 80a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (fileDesc >= 0) { 81a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (read(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) { 82a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera isInitialized = TRUE; 83a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 84a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera (void)close(fileDesc); 85a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } else if (errno == ENOENT && 86a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera FwdLockGlue_GetRandomNumber(keyEncryptionKey, KEY_SIZE) && 87a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera FwdLockGlue_CreateDirectories(strKeyFilename, S_IRWXU)) { 88a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera fileDesc = open(strKeyFilename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR); 89a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (fileDesc >= 0) { 90a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (write(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) { 91a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera isInitialized = TRUE; 92a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 93a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera (void)close(fileDesc); 94a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 95a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 96a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (isInitialized) { 97a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (AES_set_encrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &encryptionRoundKeys) != 0 || 98a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera AES_set_decrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &decryptionRoundKeys) != 0) { 99a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera isInitialized = FALSE; 100a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 101a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 102a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data. 103a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera} 104a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 105a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera/** 106a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * Validates the padding of a decrypted key. 107a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * 108a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * @param[in] pData A reference to the buffer containing the decrypted key and padding. 109a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * @param[in] decryptedKeyLength The length in bytes of the decrypted key. 110a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * 111a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera * @return A Boolean value indicating whether the padding was valid. 112a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera */ 113a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeherastatic int FwdLockGlue_ValidatePadding(const unsigned char *pData, size_t decryptedKeyLength) { 114a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t i; 115a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t padding = AES_BLOCK_SIZE - (decryptedKeyLength % AES_BLOCK_SIZE); 116a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera pData += decryptedKeyLength; 117a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera for (i = 0; i < padding; ++i) { 118a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if ((size_t)*pData != padding) { 119a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera return FALSE; 120a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 121a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera ++pData; 122a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 123a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera return TRUE; 124a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera} 125a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 126a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeheraint FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes) { 127a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera // Generate 'cryptographically secure' random bytes by reading them from "/dev/urandom" (the 128a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera // non-blocking version of "/dev/random"). 129a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera ssize_t numBytesRead = 0; 130a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera int fileDesc = open("/dev/urandom", O_RDONLY); 131a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (fileDesc >= 0) { 132a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera numBytesRead = read(fileDesc, pBuffer, numBytes); 133a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera (void)close(fileDesc); 134a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 135a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera return numBytesRead >= 0 && (size_t)numBytesRead == numBytes; 136a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera} 137a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 138a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeheraint FwdLockGlue_InitializeKeyEncryption() { 139a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera static pthread_once_t once = PTHREAD_ONCE_INIT; 140a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera pthread_once(&once, FwdLockGlue_InitializeRoundKeys); 141a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera return isInitialized; 142a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera} 143a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 144a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeherasize_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) { 145a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE; 146a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera} 147a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 148a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeheraint FwdLockGlue_EncryptKey(const void *pPlaintextKey, 149a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t plaintextKeyLength, 150a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera void *pEncryptedKey, 151a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t encryptedKeyLength) { 152a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera int result = FALSE; 153a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(plaintextKeyLength)); 154a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (FwdLockGlue_InitializeKeyEncryption()) { 155a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera unsigned char initVector[AES_BLOCK_SIZE]; 156a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (FwdLockGlue_GetRandomNumber(initVector, AES_BLOCK_SIZE)) { 157a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t padding = AES_BLOCK_SIZE - (plaintextKeyLength % AES_BLOCK_SIZE); 158a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE; 159a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera memcpy(pEncryptedKey, pPlaintextKey, plaintextKeyLength); 160a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera memset((unsigned char *)pEncryptedKey + plaintextKeyLength, (int)padding, padding); 161a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera memcpy((unsigned char *)pEncryptedKey + dataLength, initVector, AES_BLOCK_SIZE); 162a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera AES_cbc_encrypt(pEncryptedKey, pEncryptedKey, dataLength, &encryptionRoundKeys, 163a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera initVector, AES_ENCRYPT); 164a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera result = TRUE; 165a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 166a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 167a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera return result; 168a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera} 169a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera 170a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbeheraint FwdLockGlue_DecryptKey(const void *pEncryptedKey, 171a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t encryptedKeyLength, 172a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera void *pDecryptedKey, 173a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t decryptedKeyLength) { 174a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera int result = FALSE; 175a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(decryptedKeyLength)); 176a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (FwdLockGlue_InitializeKeyEncryption()) { 177a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE; 178a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera unsigned char *pData = malloc(dataLength); 179a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera if (pData != NULL) { 180a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera unsigned char initVector[AES_BLOCK_SIZE]; 181a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera memcpy(pData, pEncryptedKey, dataLength); 182a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera memcpy(initVector, (const unsigned char *)pEncryptedKey + dataLength, AES_BLOCK_SIZE); 183a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera AES_cbc_encrypt(pData, pData, dataLength, &decryptionRoundKeys, initVector, 184a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera AES_DECRYPT); 185a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera memcpy(pDecryptedKey, pData, decryptedKeyLength); 186a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera result = FwdLockGlue_ValidatePadding(pData, decryptedKeyLength); 187a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera free(pData); 188a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 189a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera } 190a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera return result; 191a3e96bfa89964cbc431080e1b9f4ad8981b5426bPravat Dalbehera} 192