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 = chip::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 : ReturnErrorCodeIf(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 : ReturnErrorCodeIf(fileIdentifier != kOTAImageFileIdentifier, CHIP_ERROR_INVALID_FILE_IDENTIFIER); 120 : // Safety check against malicious headers. 121 7 : ReturnErrorCodeIf(mHeaderTlvSize > kMaxHeaderSize, CHIP_ERROR_NO_MEMORY); 122 7 : ReturnErrorCodeIf(!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 : ReturnErrorCodeIf(mBufferOffset < mHeaderTlvSize, CHIP_ERROR_BUFFER_TOO_SMALL); 133 : 134 : 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 : ReturnErrorCodeIf(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 : ReturnErrorCodeIf(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