Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2022 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 : #include <lib/core/OTAImageHeader.h>
18 :
19 : #include <lib/core/CHIPError.h>
20 : #include <lib/core/Optional.h>
21 : #include <lib/core/TLVReader.h>
22 : #include <lib/core/TLVTags.h>
23 : #include <lib/core/TLVTypes.h>
24 : #include <lib/support/BufferReader.h>
25 : #include <lib/support/CodeUtils.h>
26 : #include <lib/support/ScopedMemoryBuffer.h>
27 : #include <lib/support/Span.h>
28 :
29 : #include <string.h>
30 :
31 : namespace chip {
32 :
33 : namespace {
34 :
35 : enum class Tag : uint8_t
36 : {
37 : kVendorId = 0,
38 : kProductId = 1,
39 : kSoftwareVersion = 2,
40 : kSoftwareVersionString = 3,
41 : kPayloadSize = 4,
42 : kMinApplicableVersion = 5,
43 : kMaxApplicableVersion = 6,
44 : kReleaseNotesURL = 7,
45 : kImageDigestType = 8,
46 : kImageDigest = 9,
47 : };
48 :
49 : /// Length of the fixed portion of the Matter OTA image header: FileIdentifier (4B), TotalSize (8B) and HeaderSize (4B)
50 : constexpr uint32_t kFixedHeaderSize = 16;
51 :
52 : /// Maximum supported Matter OTA image header size
53 : constexpr uint32_t kMaxHeaderSize = 1024;
54 :
55 : /// Maximum size of the software version string
56 : constexpr size_t kMaxSoftwareVersionStringSize = 64;
57 :
58 : /// Maximum size of the release notes URL
59 : constexpr size_t kMaxReleaseNotesURLSize = 256;
60 :
61 : } // namespace
62 :
63 9 : void OTAImageHeaderParser::Init()
64 : {
65 9 : mState = State::kInitialized;
66 9 : mBufferOffset = 0;
67 9 : mHeaderTlvSize = 0;
68 9 : mBuffer.Alloc(kFixedHeaderSize);
69 9 : }
70 :
71 2 : void OTAImageHeaderParser::Clear()
72 : {
73 2 : mState = State::kNotInitialized;
74 2 : mBufferOffset = 0;
75 2 : mHeaderTlvSize = 0;
76 2 : mBuffer.Free();
77 2 : }
78 :
79 112 : CHIP_ERROR OTAImageHeaderParser::AccumulateAndDecode(ByteSpan & buffer, OTAImageHeader & header)
80 : {
81 112 : CHIP_ERROR error = CHIP_NO_ERROR;
82 :
83 : // Init() may have left mBuffer null on alloc failure.
84 112 : if (mState != State::kNotInitialized && mBuffer.Get() == nullptr)
85 : {
86 0 : Clear();
87 0 : return CHIP_ERROR_NO_MEMORY;
88 : }
89 :
90 112 : if (mState == State::kInitialized)
91 : {
92 24 : Append(buffer, kFixedHeaderSize - mBufferOffset);
93 24 : error = DecodeFixed();
94 : }
95 :
96 112 : if (mState == State::kTlv)
97 : {
98 95 : Append(buffer, mHeaderTlvSize - mBufferOffset);
99 95 : error = DecodeTlv(header);
100 : }
101 :
102 331 : if (error != CHIP_NO_ERROR && error != CHIP_ERROR_BUFFER_TOO_SMALL)
103 : {
104 2 : Clear();
105 : }
106 :
107 112 : return error;
108 : }
109 :
110 119 : void OTAImageHeaderParser::Append(ByteSpan & buffer, uint32_t numBytes)
111 : {
112 119 : numBytes = std::min(numBytes, static_cast<uint32_t>(buffer.size()));
113 119 : memcpy(&mBuffer[mBufferOffset], buffer.data(), numBytes);
114 119 : mBufferOffset += numBytes;
115 119 : buffer = buffer.SubSpan(numBytes);
116 119 : }
117 :
118 24 : CHIP_ERROR OTAImageHeaderParser::DecodeFixed()
119 : {
120 24 : VerifyOrReturnError(mBufferOffset >= kFixedHeaderSize, CHIP_ERROR_BUFFER_TOO_SMALL);
121 :
122 8 : Encoding::LittleEndian::Reader reader(mBuffer.Get(), mBufferOffset);
123 : uint32_t fileIdentifier;
124 : uint64_t totalSize;
125 8 : ReturnErrorOnFailure(reader.Read32(&fileIdentifier).Read64(&totalSize).Read32(&mHeaderTlvSize).StatusCode());
126 8 : VerifyOrReturnError(fileIdentifier == kOTAImageFileIdentifier, CHIP_ERROR_INVALID_FILE_IDENTIFIER);
127 : // Safety check against malicious headers.
128 7 : VerifyOrReturnError(mHeaderTlvSize <= kMaxHeaderSize, CHIP_ERROR_NO_MEMORY);
129 7 : VerifyOrReturnError(mBuffer.Alloc(mHeaderTlvSize), CHIP_ERROR_NO_MEMORY);
130 :
131 7 : mState = State::kTlv;
132 7 : mBufferOffset = 0;
133 :
134 7 : return CHIP_NO_ERROR;
135 : }
136 :
137 95 : CHIP_ERROR OTAImageHeaderParser::DecodeTlv(OTAImageHeader & header)
138 : {
139 95 : VerifyOrReturnError(mBufferOffset >= mHeaderTlvSize, CHIP_ERROR_BUFFER_TOO_SMALL);
140 :
141 6 : TLV::TLVReader tlvReader;
142 6 : tlvReader.Init(mBuffer.Get(), mBufferOffset);
143 6 : ReturnErrorOnFailure(tlvReader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag()));
144 :
145 : TLV::TLVType outerType;
146 6 : ReturnErrorOnFailure(tlvReader.EnterContainer(outerType));
147 :
148 6 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kVendorId)));
149 5 : ReturnErrorOnFailure(tlvReader.Get(header.mVendorId));
150 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kProductId)));
151 5 : ReturnErrorOnFailure(tlvReader.Get(header.mProductId));
152 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kSoftwareVersion)));
153 5 : ReturnErrorOnFailure(tlvReader.Get(header.mSoftwareVersion));
154 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kSoftwareVersionString)));
155 5 : ReturnErrorOnFailure(tlvReader.Get(header.mSoftwareVersionString));
156 5 : VerifyOrReturnError(header.mSoftwareVersionString.size() <= kMaxSoftwareVersionStringSize, CHIP_ERROR_INVALID_STRING_LENGTH);
157 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kPayloadSize)));
158 5 : ReturnErrorOnFailure(tlvReader.Get(header.mPayloadSize));
159 5 : ReturnErrorOnFailure(tlvReader.Next());
160 :
161 5 : if (tlvReader.GetTag() == TLV::ContextTag(Tag::kMinApplicableVersion))
162 : {
163 4 : ReturnErrorOnFailure(tlvReader.Get(header.mMinApplicableVersion.Emplace()));
164 4 : ReturnErrorOnFailure(tlvReader.Next());
165 : }
166 :
167 5 : if (tlvReader.GetTag() == TLV::ContextTag(Tag::kMaxApplicableVersion))
168 : {
169 4 : ReturnErrorOnFailure(tlvReader.Get(header.mMaxApplicableVersion.Emplace()));
170 4 : ReturnErrorOnFailure(tlvReader.Next());
171 : }
172 :
173 5 : if (tlvReader.GetTag() == TLV::ContextTag(Tag::kReleaseNotesURL))
174 : {
175 4 : ReturnErrorOnFailure(tlvReader.Get(header.mReleaseNotesURL));
176 4 : VerifyOrReturnError(header.mReleaseNotesURL.size() <= kMaxReleaseNotesURLSize, CHIP_ERROR_INVALID_STRING_LENGTH);
177 4 : ReturnErrorOnFailure(tlvReader.Next());
178 : }
179 :
180 5 : ReturnErrorOnFailure(tlvReader.Expect(TLV::ContextTag(Tag::kImageDigestType)));
181 5 : ReturnErrorOnFailure(tlvReader.Get(header.mImageDigestType));
182 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kImageDigest)));
183 5 : ReturnErrorOnFailure(tlvReader.Get(header.mImageDigest));
184 :
185 5 : ReturnErrorOnFailure(tlvReader.ExitContainer(outerType));
186 :
187 5 : return CHIP_NO_ERROR;
188 : }
189 :
190 : } // namespace chip
|