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