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/ScopedBuffer.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 112 : if (mState == State::kInitialized)
84 : {
85 24 : Append(buffer, kFixedHeaderSize - mBufferOffset);
86 24 : error = DecodeFixed();
87 : }
88 :
89 112 : if (mState == State::kTlv)
90 : {
91 95 : Append(buffer, mHeaderTlvSize - mBufferOffset);
92 95 : error = DecodeTlv(header);
93 : }
94 :
95 112 : if (error != CHIP_NO_ERROR && error != CHIP_ERROR_BUFFER_TOO_SMALL)
96 : {
97 2 : Clear();
98 : }
99 :
100 112 : return error;
101 : }
102 :
103 119 : void OTAImageHeaderParser::Append(ByteSpan & buffer, uint32_t numBytes)
104 : {
105 119 : numBytes = std::min(numBytes, static_cast<uint32_t>(buffer.size()));
106 119 : memcpy(&mBuffer[mBufferOffset], buffer.data(), numBytes);
107 119 : mBufferOffset += numBytes;
108 119 : buffer = buffer.SubSpan(numBytes);
109 119 : }
110 :
111 24 : CHIP_ERROR OTAImageHeaderParser::DecodeFixed()
112 : {
113 24 : VerifyOrReturnError(mBufferOffset >= kFixedHeaderSize, CHIP_ERROR_BUFFER_TOO_SMALL);
114 :
115 8 : Encoding::LittleEndian::Reader reader(mBuffer.Get(), mBufferOffset);
116 : uint32_t fileIdentifier;
117 : uint64_t totalSize;
118 8 : ReturnErrorOnFailure(reader.Read32(&fileIdentifier).Read64(&totalSize).Read32(&mHeaderTlvSize).StatusCode());
119 8 : VerifyOrReturnError(fileIdentifier == kOTAImageFileIdentifier, CHIP_ERROR_INVALID_FILE_IDENTIFIER);
120 : // Safety check against malicious headers.
121 7 : VerifyOrReturnError(mHeaderTlvSize <= kMaxHeaderSize, CHIP_ERROR_NO_MEMORY);
122 7 : VerifyOrReturnError(mBuffer.Alloc(mHeaderTlvSize), CHIP_ERROR_NO_MEMORY);
123 :
124 7 : mState = State::kTlv;
125 7 : mBufferOffset = 0;
126 :
127 7 : return CHIP_NO_ERROR;
128 : }
129 :
130 95 : CHIP_ERROR OTAImageHeaderParser::DecodeTlv(OTAImageHeader & header)
131 : {
132 95 : VerifyOrReturnError(mBufferOffset >= mHeaderTlvSize, CHIP_ERROR_BUFFER_TOO_SMALL);
133 :
134 6 : TLV::TLVReader tlvReader;
135 6 : tlvReader.Init(mBuffer.Get(), mBufferOffset);
136 6 : ReturnErrorOnFailure(tlvReader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag()));
137 :
138 : TLV::TLVType outerType;
139 6 : ReturnErrorOnFailure(tlvReader.EnterContainer(outerType));
140 :
141 6 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kVendorId)));
142 5 : ReturnErrorOnFailure(tlvReader.Get(header.mVendorId));
143 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kProductId)));
144 5 : ReturnErrorOnFailure(tlvReader.Get(header.mProductId));
145 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kSoftwareVersion)));
146 5 : ReturnErrorOnFailure(tlvReader.Get(header.mSoftwareVersion));
147 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kSoftwareVersionString)));
148 5 : ReturnErrorOnFailure(tlvReader.Get(header.mSoftwareVersionString));
149 5 : VerifyOrReturnError(header.mSoftwareVersionString.size() <= kMaxSoftwareVersionStringSize, CHIP_ERROR_INVALID_STRING_LENGTH);
150 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kPayloadSize)));
151 5 : ReturnErrorOnFailure(tlvReader.Get(header.mPayloadSize));
152 5 : ReturnErrorOnFailure(tlvReader.Next());
153 :
154 5 : if (tlvReader.GetTag() == TLV::ContextTag(Tag::kMinApplicableVersion))
155 : {
156 4 : ReturnErrorOnFailure(tlvReader.Get(header.mMinApplicableVersion.Emplace()));
157 4 : ReturnErrorOnFailure(tlvReader.Next());
158 : }
159 :
160 5 : if (tlvReader.GetTag() == TLV::ContextTag(Tag::kMaxApplicableVersion))
161 : {
162 4 : ReturnErrorOnFailure(tlvReader.Get(header.mMaxApplicableVersion.Emplace()));
163 4 : ReturnErrorOnFailure(tlvReader.Next());
164 : }
165 :
166 5 : if (tlvReader.GetTag() == TLV::ContextTag(Tag::kReleaseNotesURL))
167 : {
168 4 : ReturnErrorOnFailure(tlvReader.Get(header.mReleaseNotesURL));
169 4 : VerifyOrReturnError(header.mReleaseNotesURL.size() <= kMaxReleaseNotesURLSize, CHIP_ERROR_INVALID_STRING_LENGTH);
170 4 : ReturnErrorOnFailure(tlvReader.Next());
171 : }
172 :
173 5 : ReturnErrorOnFailure(tlvReader.Expect(TLV::ContextTag(Tag::kImageDigestType)));
174 5 : ReturnErrorOnFailure(tlvReader.Get(header.mImageDigestType));
175 5 : ReturnErrorOnFailure(tlvReader.Next(TLV::ContextTag(Tag::kImageDigest)));
176 5 : ReturnErrorOnFailure(tlvReader.Get(header.mImageDigest));
177 :
178 5 : ReturnErrorOnFailure(tlvReader.ExitContainer(outerType));
179 :
180 5 : return CHIP_NO_ERROR;
181 : }
182 :
183 : } // namespace chip
|