1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "heif_format_agent.h"
17 
18 #include "image_log.h"
19 #include "plugin_service.h"
20 #include "string"
21 #include "securec.h"
22 
23 #undef LOG_DOMAIN
24 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_PLUGIN
25 
26 #undef LOG_TAG
27 #define LOG_TAG "HeifFormatAgent"
28 
29 namespace OHOS {
30 namespace ImagePlugin {
31 using namespace MultimediaPlugin;
32 
33 const std::string FORMAT_TYPE = "image/heif";
34 constexpr uint32_t HEADER_SIZE = 32;
35 constexpr uint32_t HEADER_LEAST_SIZE = 8;
36 constexpr size_t HEADER_NEXT_SIZE = 16;
37 constexpr uint32_t OFFSET_SIZE = 8;
38 
39 constexpr uint32_t SHIFT_BASE = 8;
40 constexpr uint32_t TIMES_SEVEN = 7;
41 constexpr uint32_t TIMES_FIVE = 5;
42 constexpr uint32_t TIMES_THREE = 3;
43 constexpr uint32_t TIMES_TWO = 2;
44 constexpr uint32_t MAX_LOOP_SIZE = 5;
45 
GetFormatType()46 std::string HeifFormatAgent::GetFormatType()
47 {
48     return FORMAT_TYPE;
49 }
50 
GetHeaderSize()51 uint32_t HeifFormatAgent::GetHeaderSize()
52 {
53     return HEADER_SIZE;
54 }
55 
CheckFormatHead(const void * headerData,uint32_t dataSize)56 bool CheckFormatHead(const void *headerData, uint32_t dataSize)
57 {
58     if (headerData == nullptr) {
59         IMAGE_LOGE("check format failed: header data is null.");
60         return false;
61     }
62     // Any valid ftyp box should have at least 8 bytes.
63     if (dataSize < HEADER_LEAST_SIZE) {
64         IMAGE_LOGE("data size[%{public}u] less than eight.", dataSize);
65         return false;
66     }
67     return true;
68 }
69 
CheckFormat(const void * headerData,uint32_t dataSize)70 bool HeifFormatAgent::CheckFormat(const void *headerData, uint32_t dataSize)
71 {
72     if (!CheckFormatHead(headerData, dataSize)) {
73         return false;
74     }
75     uint32_t tmpBuff[HEADER_SIZE];
76     if (memcpy_s(tmpBuff, HEADER_SIZE, headerData, dataSize) != 0) {
77         IMAGE_LOGE("memcpy headerData data size:[%{public}d] error.", dataSize);
78         return false;
79     }
80 
81     const uint32_t *ptr = reinterpret_cast<const uint32_t *>(tmpBuff);
82     uint64_t chunkSize = EndianSwap32(ptr[0]);  // first item
83     uint32_t chunkType = EndianSwap32(ptr[1]);  // second item
84     if (chunkType != Fourcc('f', 't', 'y', 'p')) {
85         IMAGE_LOGD("head type is not ftyp.");
86         return false;
87     }
88 
89     int64_t offset = OFFSET_SIZE;
90     if (!IsHeif64(tmpBuff, dataSize, offset, chunkSize)) {
91         return false;
92     }
93     int64_t chunkDataSize = static_cast<int64_t>(chunkSize) - offset;
94     // It should at least have major brand (4-byte) and minor version (4-bytes).
95     // The rest of the chunk (if any) is a list of (4-byte) compatible brands.
96     if (chunkDataSize < HEADER_LEAST_SIZE) {
97         IMAGE_LOGE("chunk data size [%{public}lld] less than eight.", static_cast<long long>(chunkDataSize));
98         return false;
99     }
100     uint32_t numCompatibleBrands = (chunkDataSize - OFFSET_SIZE) / sizeof(uint32_t);
101     if (numCompatibleBrands != 0 && numCompatibleBrands + TIMES_TWO < HEADER_SIZE) {
102         for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {  // need next 2 item
103             if (i == 1) {
104                 // Skip this index, it refers to the minorVersion, not a brand.
105                 continue;
106             }
107             if (i == MAX_LOOP_SIZE) {
108                 // When numCompatibleBrands is 4, i equals 5, and there is no heif brand, it will be read out of bounds.
109                 IMAGE_LOGI("check heif format failed, the number of cycles exceeded expectations");
110                 return false;
111             }
112             auto *brandPtr = static_cast<const uint32_t *>(tmpBuff) + (numCompatibleBrands + i);
113             uint32_t brand = EndianSwap32(*brandPtr);
114             if (brand == Fourcc('m', 'i', 'f', '1') || brand == Fourcc('h', 'e', 'i', 'c') ||
115                 brand == Fourcc('m', 's', 'f', '1') || brand == Fourcc('h', 'e', 'v', 'c')) {
116                 return true;
117             }
118         }
119     }
120     IMAGE_LOGI("check heif format failed.");
121     return false;
122 }
123 
IsHeif64(const void * buffer,const size_t bytesRead,int64_t & offset,uint64_t & chunkSize)124 bool HeifFormatAgent::IsHeif64(const void *buffer, const size_t bytesRead, int64_t &offset, uint64_t &chunkSize)
125 {
126     // If it is 1, a 64-bit check is required.
127     if (chunkSize == 1) {
128         // This indicates that the next 8 bytes represent the chunk size,
129         // and chunk data comes after that.
130         if (bytesRead < HEADER_NEXT_SIZE) {
131             IMAGE_LOGE("bytes read [%{public}zd] less than sixteen.", bytesRead);
132             return false;
133         }
134         auto *chunkSizePtr = static_cast<const uint64_t *>(buffer) + (offset / sizeof(uint64_t));
135         chunkSize = EndianSwap64(*chunkSizePtr);
136         if (chunkSize < HEADER_NEXT_SIZE) {
137             // The smallest valid chunk is 16 bytes long in this case.
138             IMAGE_LOGE("chunk size [%{public}llu] less than sixteen.", static_cast<unsigned long long>(chunkSize));
139             return false;
140         }
141         offset += OFFSET_SIZE;
142     } else if (chunkSize < HEADER_LEAST_SIZE) {
143         // The smallest valid chunk is 8 bytes long.
144         IMAGE_LOGE("chunk size [%{public}llu] less than eight.", static_cast<unsigned long long>(chunkSize));
145         return false;
146     }
147 
148     if (chunkSize > bytesRead) {
149         chunkSize = bytesRead;
150     }
151     return true;
152 }
153 
Fourcc(uint8_t c1,uint8_t c2,uint8_t c3,uint8_t c4)154 uint32_t HeifFormatAgent::Fourcc(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4)
155 {
156     return (c1 << (SHIFT_BASE * TIMES_THREE)) | (c2 << (SHIFT_BASE * TIMES_TWO)) | (c3 << SHIFT_BASE) | (c4);
157 }
158 
EndianSwap32(uint32_t value)159 uint32_t HeifFormatAgent::EndianSwap32(uint32_t value)
160 {
161     return ((value & 0xFF) << (SHIFT_BASE * TIMES_THREE)) | ((value & 0xFF00) << SHIFT_BASE) |
162            ((value & 0xFF0000) >> SHIFT_BASE) | (value >> (SHIFT_BASE * TIMES_THREE));
163 }
164 
EndianSwap64(uint64_t value)165 uint64_t HeifFormatAgent::EndianSwap64(uint64_t value)
166 {
167     return (((value & 0x00000000000000FFULL) << (SHIFT_BASE * TIMES_SEVEN)) |
168             ((value & 0x000000000000FF00ULL) << (SHIFT_BASE * TIMES_FIVE)) |
169             ((value & 0x0000000000FF0000ULL) << (SHIFT_BASE * TIMES_THREE)) |
170             ((value & 0x00000000FF000000ULL) << (SHIFT_BASE)) | ((value & 0x000000FF00000000ULL) >> (SHIFT_BASE)) |
171             ((value & 0x0000FF0000000000ULL) >> (SHIFT_BASE * TIMES_THREE)) |
172             ((value & 0x00FF000000000000ULL) >> (SHIFT_BASE * TIMES_FIVE)) | ((value) >> (SHIFT_BASE * TIMES_SEVEN)));
173 }
174 } // namespace ImagePlugin
175 } // namespace OHOS
176