1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/crx_file/crx_file.h"
7 #include "base/base64.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_file.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/numerics/safe_math.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "components/crx_file/constants.h"
16 #include "components/crx_file/id_util.h"
17 #include "crypto/secure_hash.h"
18 #include "crypto/sha2.h"
19 #include "crypto/signature_verifier.h"
25 // The current version of the crx format.
26 static const uint32 kCurrentVersion
= 2;
28 // The current version of the crx diff format.
29 static const uint32 kCurrentDiffVersion
= 0;
31 // The maximum size the crx parser will tolerate for a public key.
32 static const uint32 kMaxPublicKeySize
= 1 << 16;
34 // The maximum size the crx parser will tolerate for a signature.
35 static const uint32 kMaxSignatureSize
= 1 << 16;
37 // Helper function to read bytes into a buffer while also updating a hash with
38 // those bytes. Returns the number of bytes read.
39 size_t ReadAndHash(void* ptr
,
43 crypto::SecureHash
* hash
) {
44 size_t item_count
= fread(ptr
, size
, nmemb
, stream
);
45 base::CheckedNumeric
<size_t> byte_count(item_count
);
47 if (!byte_count
.IsValid())
49 if (item_count
> 0 && hash
) {
50 hash
->Update(ptr
, byte_count
.ValueOrDie());
52 return byte_count
.ValueOrDie();
55 // Helper function to finish computing a hash and return an error if the
56 // result of the hash didn't meet an expected base64-encoded value.
57 CrxFile::ValidateError
FinalizeHash(const std::string
& extension_id
,
58 crypto::SecureHash
* hash
,
59 const std::string
& expected_hash
) {
60 CHECK(hash
!= nullptr);
61 uint8 output
[crypto::kSHA256Length
] = {};
62 hash
->Finish(output
, sizeof(output
));
63 std::string hash_base64
=
64 base::ToLowerASCII(base::HexEncode(output
, sizeof(output
)));
65 if (hash_base64
!= expected_hash
) {
66 LOG(ERROR
) << "Hash check failed for extension: " << extension_id
67 << ", expected " << expected_hash
<< ", got " << hash_base64
;
68 return CrxFile::ValidateError::CRX_HASH_VERIFICATION_FAILED
;
70 return CrxFile::ValidateError::NONE
;
76 // The magic string embedded in the header.
77 const char kCrxFileHeaderMagic
[] = "Cr24";
78 const char kCrxDiffFileHeaderMagic
[] = "CrOD";
80 scoped_ptr
<CrxFile
> CrxFile::Parse(const CrxFile::Header
& header
,
81 CrxFile::Error
* error
) {
82 if (HeaderIsValid(header
, error
))
83 return scoped_ptr
<CrxFile
>(new CrxFile(header
));
84 return scoped_ptr
<CrxFile
>();
87 scoped_ptr
<CrxFile
> CrxFile::Create(const uint32 key_size
,
88 const uint32 signature_size
,
89 CrxFile::Error
* error
) {
90 CrxFile::Header header
;
91 memcpy(&header
.magic
, kCrxFileHeaderMagic
, kCrxFileHeaderMagicSize
);
92 header
.version
= kCurrentVersion
;
93 header
.key_size
= key_size
;
94 header
.signature_size
= signature_size
;
95 if (HeaderIsValid(header
, error
))
96 return scoped_ptr
<CrxFile
>(new CrxFile(header
));
97 return scoped_ptr
<CrxFile
>();
100 bool CrxFile::HeaderIsDelta(const CrxFile::Header
& header
) {
101 return !strncmp(kCrxDiffFileHeaderMagic
, header
.magic
, sizeof(header
.magic
));
105 CrxFile::ValidateError
CrxFile::ValidateSignature(
106 const base::FilePath
& crx_path
,
107 const std::string
& expected_hash
,
108 std::string
* public_key
,
109 std::string
* extension_id
,
110 CrxFile::Header
* header_out
) {
111 base::ScopedFILE
file(base::OpenFile(crx_path
, "rb"));
112 scoped_ptr
<crypto::SecureHash
> hash
;
113 if (!expected_hash
.empty())
114 hash
.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256
));
117 return ValidateError::CRX_FILE_NOT_READABLE
;
119 CrxFile::Header header
;
120 size_t len
= ReadAndHash(&header
, sizeof(header
), 1, file
.get(), hash
.get());
121 if (len
!= sizeof(header
))
122 return ValidateError::CRX_HEADER_INVALID
;
124 *header_out
= header
;
126 CrxFile::Error error
;
127 scoped_ptr
<CrxFile
> crx(CrxFile::Parse(header
, &error
));
130 case CrxFile::kWrongMagic
:
131 return ValidateError::CRX_MAGIC_NUMBER_INVALID
;
132 case CrxFile::kInvalidVersion
:
133 return ValidateError::CRX_VERSION_NUMBER_INVALID
;
135 case CrxFile::kInvalidKeyTooLarge
:
136 case CrxFile::kInvalidSignatureTooLarge
:
137 return ValidateError::CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE
;
139 case CrxFile::kInvalidKeyTooSmall
:
140 return ValidateError::CRX_ZERO_KEY_LENGTH
;
141 case CrxFile::kInvalidSignatureTooSmall
:
142 return ValidateError::CRX_ZERO_SIGNATURE_LENGTH
;
145 return ValidateError::CRX_HEADER_INVALID
;
149 std::vector
<uint8
> key(header
.key_size
);
150 len
= ReadAndHash(&key
.front(), sizeof(uint8
), header
.key_size
, file
.get(),
152 if (len
!= header
.key_size
)
153 return ValidateError::CRX_PUBLIC_KEY_INVALID
;
155 std::vector
<uint8
> signature(header
.signature_size
);
156 len
= ReadAndHash(&signature
.front(), sizeof(uint8
), header
.signature_size
,
157 file
.get(), hash
.get());
158 if (len
< header
.signature_size
)
159 return ValidateError::CRX_SIGNATURE_INVALID
;
161 crypto::SignatureVerifier verifier
;
162 if (!verifier
.VerifyInit(
163 crx_file::kSignatureAlgorithm
, sizeof(crx_file::kSignatureAlgorithm
),
164 &signature
.front(), static_cast<int>(signature
.size()), &key
.front(),
165 static_cast<int>(key
.size()))) {
166 // Signature verification initialization failed. This is most likely
167 // caused by a public key in the wrong format (should encode algorithm).
168 return ValidateError::CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED
;
171 uint8 buf
[1 << 12] = {};
172 while ((len
= ReadAndHash(buf
, sizeof(buf
[0]), arraysize(buf
), file
.get(),
174 verifier
.VerifyUpdate(buf
, static_cast<int>(len
));
176 if (!verifier
.VerifyFinal())
177 return ValidateError::CRX_SIGNATURE_VERIFICATION_FAILED
;
179 std::string public_key_bytes
=
180 std::string(reinterpret_cast<char*>(&key
.front()), key
.size());
182 base::Base64Encode(public_key_bytes
, public_key
);
184 std::string id
= id_util::GenerateId(public_key_bytes
);
188 if (!expected_hash
.empty())
189 return FinalizeHash(id
, hash
.get(), expected_hash
);
191 return ValidateError::NONE
;
194 CrxFile::CrxFile(const Header
& header
) : header_(header
) {}
196 bool CrxFile::HeaderIsValid(const CrxFile::Header
& header
,
197 CrxFile::Error
* error
) {
199 bool diffCrx
= false;
200 if (!strncmp(kCrxDiffFileHeaderMagic
, header
.magic
, sizeof(header
.magic
)))
202 if (strncmp(kCrxFileHeaderMagic
, header
.magic
, sizeof(header
.magic
)) &&
204 *error
= kWrongMagic
;
205 else if (header
.version
!= kCurrentVersion
206 && !(diffCrx
&& header
.version
== kCurrentDiffVersion
))
207 *error
= kInvalidVersion
;
208 else if (header
.key_size
> kMaxPublicKeySize
)
209 *error
= kInvalidKeyTooLarge
;
210 else if (header
.key_size
== 0)
211 *error
= kInvalidKeyTooSmall
;
212 else if (header
.signature_size
> kMaxSignatureSize
)
213 *error
= kInvalidSignatureTooLarge
;
214 else if (header
.signature_size
== 0)
215 *error
= kInvalidSignatureTooSmall
;
221 } // namespace crx_file