mirror of
https://github.com/electron/electron.git
synced 2026-01-08 23:18:06 -05:00
feat: validate integrity of ASAR Integrity dictionary on macOS (#48587)
This commit is contained in:
@@ -194,6 +194,8 @@ filenames = {
|
|||||||
"shell/common/api/electron_api_clipboard_mac.mm",
|
"shell/common/api/electron_api_clipboard_mac.mm",
|
||||||
"shell/common/api/electron_api_native_image_mac.mm",
|
"shell/common/api/electron_api_native_image_mac.mm",
|
||||||
"shell/common/asar/archive_mac.mm",
|
"shell/common/asar/archive_mac.mm",
|
||||||
|
"shell/common/asar/integrity_digest.h",
|
||||||
|
"shell/common/asar/integrity_digest.mm",
|
||||||
"shell/common/application_info_mac.mm",
|
"shell/common/application_info_mac.mm",
|
||||||
"shell/common/language_util_mac.mm",
|
"shell/common/language_util_mac.mm",
|
||||||
"shell/common/mac/main_application_bundle.h",
|
"shell/common/mac/main_application_bundle.h",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "base/files/file_util.h"
|
#include "base/files/file_util.h"
|
||||||
#include "base/strings/sys_string_conversions.h"
|
#include "base/strings/sys_string_conversions.h"
|
||||||
#include "shell/common/asar/asar_util.h"
|
#include "shell/common/asar/asar_util.h"
|
||||||
|
#include "shell/common/asar/integrity_digest.h"
|
||||||
|
|
||||||
namespace asar {
|
namespace asar {
|
||||||
|
|
||||||
@@ -39,6 +40,9 @@ std::optional<IntegrityPayload> Archive::HeaderIntegrity() const {
|
|||||||
NSDictionary* integrity = [[NSBundle mainBundle]
|
NSDictionary* integrity = [[NSBundle mainBundle]
|
||||||
objectForInfoDictionaryKey:@"ElectronAsarIntegrity"];
|
objectForInfoDictionaryKey:@"ElectronAsarIntegrity"];
|
||||||
|
|
||||||
|
if (!IsIntegrityDictionaryValid(integrity))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
// Integrity not provided
|
// Integrity not provided
|
||||||
if (!integrity)
|
if (!integrity)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|||||||
15
shell/common/asar/integrity_digest.h
Normal file
15
shell/common/asar/integrity_digest.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) 2025 Noah Gregory
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef ELECTRON_SHELL_COMMON_ASAR_INTEGRITY_DIGEST_H_
|
||||||
|
#define ELECTRON_SHELL_COMMON_ASAR_INTEGRITY_DIGEST_H_
|
||||||
|
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
namespace asar {
|
||||||
|
|
||||||
|
bool IsIntegrityDictionaryValid(NSDictionary* integrity_dict);
|
||||||
|
|
||||||
|
} // namespace asar
|
||||||
|
|
||||||
|
#endif // ELECTRON_SHELL_COMMON_ASAR_INTEGRITY_DIGEST_H_
|
||||||
74
shell/common/asar/integrity_digest.mm
Normal file
74
shell/common/asar/integrity_digest.mm
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (c) 2025 Noah Gregory
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "shell/common/asar/integrity_digest.h"
|
||||||
|
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include "base/strings/sys_string_conversions.h"
|
||||||
|
#include "crypto/hash.h"
|
||||||
|
|
||||||
|
namespace asar {
|
||||||
|
|
||||||
|
constexpr crypto::hash::HashKind kIntegrityDictionaryHashKind =
|
||||||
|
crypto::hash::HashKind::kSha256;
|
||||||
|
|
||||||
|
constexpr size_t kIntegrityDictionaryDigestSize =
|
||||||
|
DigestSizeForHashKind(kIntegrityDictionaryHashKind);
|
||||||
|
constexpr char kIntegrityDictionaryDigestSentinel[] =
|
||||||
|
"AGbevlPCksUGKNL8TSn7wGmJEuJsXb2A";
|
||||||
|
|
||||||
|
struct IntegrityDictionaryDigestSlot {
|
||||||
|
uint8_t sentinel[sizeof(kIntegrityDictionaryDigestSentinel) - 1];
|
||||||
|
uint8_t used;
|
||||||
|
uint8_t version;
|
||||||
|
uint8_t digest[kIntegrityDictionaryDigestSize];
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr IntegrityDictionaryDigestSlot MakeDigestSlot(
|
||||||
|
const char (&sentinel)[33]) {
|
||||||
|
IntegrityDictionaryDigestSlot slot{};
|
||||||
|
std::span<uint8_t, 32> slot_sentinel_span(slot.sentinel);
|
||||||
|
std::copy_n(sentinel, slot_sentinel_span.size(), slot_sentinel_span.begin());
|
||||||
|
slot.used = false; // to be set at package-time
|
||||||
|
slot.version = 0; // to be set at package-time
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((section("__DATA_CONST,__asar_integrity"), used))
|
||||||
|
const volatile IntegrityDictionaryDigestSlot kIntegrityDictionaryDigest =
|
||||||
|
MakeDigestSlot(kIntegrityDictionaryDigestSentinel);
|
||||||
|
|
||||||
|
bool IsIntegrityDictionaryValid(NSDictionary* integrity) {
|
||||||
|
if (kIntegrityDictionaryDigest.used == false)
|
||||||
|
return true; // No digest to validate against, fail open
|
||||||
|
if (kIntegrityDictionaryDigest.version != 1)
|
||||||
|
return false; // Unknown version, fail closed
|
||||||
|
crypto::hash::Hasher integrity_hasher(kIntegrityDictionaryHashKind);
|
||||||
|
for (NSString *relative_path_key in
|
||||||
|
[[integrity allKeys] sortedArrayUsingComparator:^NSComparisonResult(
|
||||||
|
NSString* s1, NSString* s2) {
|
||||||
|
return [s1 compare:s2 options:NSLiteralSearch];
|
||||||
|
}]) {
|
||||||
|
NSDictionary* file_integrity = [integrity objectForKey:relative_path_key];
|
||||||
|
NSString* algorithm = [file_integrity objectForKey:@"algorithm"];
|
||||||
|
NSString* hash = [file_integrity objectForKey:@"hash"];
|
||||||
|
integrity_hasher.Update(base::SysNSStringToUTF8(relative_path_key));
|
||||||
|
integrity_hasher.Update(base::SysNSStringToUTF8(algorithm));
|
||||||
|
integrity_hasher.Update(base::SysNSStringToUTF8(hash));
|
||||||
|
}
|
||||||
|
std::array<uint8_t, kIntegrityDictionaryDigestSize> digest;
|
||||||
|
integrity_hasher.Finish(digest);
|
||||||
|
if (!std::equal(digest.begin(), digest.end(),
|
||||||
|
kIntegrityDictionaryDigest.digest)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace asar
|
||||||
Reference in New Issue
Block a user