summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Vakulenko <avakulenko@google.com>2016-01-22 16:52:43 -0800
committerAlex Vakulenko <avakulenko@google.com>2016-01-22 17:02:32 -0800
commitf6024733c0d1eed88f68520b5e6a20b96e212ad6 (patch)
treefed955593d9995a027a33cd46b41260882ddd3ea
parent340b8dd38ca56de409ca4f790b45b1c314c544dd (diff)
downloadlibchrome-f6024733c0d1eed88f68520b5e6a20b96e212ad6.tar.gz
libchrome: Revert deleted files needed by Chrome OS
Some of the "unused" files were apparently needed for building libchrome on Chrome OS. Reverting deletion of these files. Change-Id: I02e32f112d16480206f43ca0087342a9de7f1e1b
-rw-r--r--base/allocator/allocator_extension.cc56
-rw-r--r--base/native_library_posix.cc57
-rw-r--r--base/timer/hi_res_timer_manager_posix.cc24
-rw-r--r--base/timer/mock_timer.cc63
-rw-r--r--base/timer/mock_timer.h41
-rw-r--r--base/timer/mock_timer_unittest.cc82
-rw-r--r--crypto/hmac.cc57
-rw-r--r--crypto/hmac.h93
-rw-r--r--crypto/hmac_nss.cc117
-rw-r--r--crypto/hmac_openssl.cc56
-rw-r--r--crypto/hmac_unittest.cc295
-rw-r--r--crypto/hmac_win.cc209
-rw-r--r--crypto/nss_key_util.cc163
-rw-r--r--crypto/nss_key_util.h58
-rw-r--r--crypto/nss_key_util_unittest.cc87
-rw-r--r--crypto/nss_util.cc1131
-rw-r--r--crypto/nss_util.h155
-rw-r--r--crypto/nss_util_unittest.cc43
-rw-r--r--crypto/openssl_bio_string.cc77
-rw-r--r--crypto/openssl_bio_string.h29
-rw-r--r--crypto/openssl_bio_string_unittest.cc63
-rw-r--r--crypto/openssl_util.cc132
-rw-r--r--crypto/openssl_util.h90
-rw-r--r--crypto/p224.cc758
-rw-r--r--crypto/p224.h60
-rw-r--r--crypto/p224_spake.cc268
-rw-r--r--crypto/p224_spake.h126
-rw-r--r--crypto/p224_spake_unittest.cc174
-rw-r--r--crypto/p224_unittest.cc824
-rw-r--r--crypto/random.cc19
-rw-r--r--crypto/random.h21
-rw-r--r--crypto/random_unittest.cc27
-rw-r--r--crypto/rsa_private_key.cc384
-rw-r--r--crypto/rsa_private_key.h231
-rw-r--r--crypto/rsa_private_key_nss.cc150
-rw-r--r--crypto/rsa_private_key_openssl.cc138
-rw-r--r--crypto/rsa_private_key_unittest.cc470
-rw-r--r--crypto/scoped_test_nss_chromeos_user.cc37
-rw-r--r--crypto/scoped_test_nss_chromeos_user.h43
-rw-r--r--crypto/scoped_test_nss_db.cc53
-rw-r--r--crypto/scoped_test_nss_db.h35
-rw-r--r--crypto/scoped_test_system_nss_key_slot.cc32
-rw-r--r--crypto/scoped_test_system_nss_key_slot.h43
-rw-r--r--crypto/secure_hash_default.cc94
-rw-r--r--crypto/secure_hash_openssl.cc102
-rw-r--r--crypto/secure_hash_unittest.cc75
-rw-r--r--crypto/secure_util.cc19
-rw-r--r--crypto/secure_util.h29
-rw-r--r--crypto/sha2.cc25
-rw-r--r--crypto/sha2.h33
-rw-r--r--crypto/sha2_unittest.cc100
-rw-r--r--crypto/signature_creator_nss.cc118
-rw-r--r--crypto/signature_creator_openssl.cc114
-rw-r--r--crypto/signature_creator_unittest.cc143
-rw-r--r--crypto/signature_verifier_nss.cc226
-rw-r--r--crypto/signature_verifier_openssl.cc160
-rw-r--r--crypto/signature_verifier_unittest.cc1167
-rw-r--r--crypto/symmetric_key_nss.cc149
-rw-r--r--crypto/symmetric_key_openssl.cc104
-rw-r--r--crypto/symmetric_key_unittest.cc225
-rw-r--r--crypto/symmetric_key_win.cc536
-rw-r--r--crypto/third_party/nss/pk11akey.cc98
-rw-r--r--crypto/third_party/nss/secsign.cc132
-rw-r--r--crypto/third_party/nss/sha512.cc1391
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl.cc363
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl.h317
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc486
-rw-r--r--sandbox/linux/bpf_dsl/codegen.cc159
-rw-r--r--sandbox/linux/bpf_dsl/codegen.h123
-rw-r--r--sandbox/linux/bpf_dsl/codegen_unittest.cc402
-rw-r--r--sandbox/linux/bpf_dsl/dump_bpf.cc109
-rw-r--r--sandbox/linux/bpf_dsl/dump_bpf.h18
-rw-r--r--sandbox/linux/bpf_dsl/policy.cc19
-rw-r--r--sandbox/linux/bpf_dsl/policy.h37
-rw-r--r--sandbox/linux/bpf_dsl/policy_compiler.cc499
-rw-r--r--sandbox/linux/bpf_dsl/policy_compiler.h159
-rw-r--r--sandbox/linux/bpf_dsl/syscall_set.cc144
-rw-r--r--sandbox/linux/bpf_dsl/syscall_set.h103
-rw-r--r--sandbox/linux/bpf_dsl/syscall_set_unittest.cc124
-rw-r--r--sandbox/linux/bpf_dsl/verifier.cc396
-rw-r--r--sandbox/linux/bpf_dsl/verifier.h57
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc270
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/baseline_policy.h48
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc334
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc297
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h82
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc319
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h100
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc282
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc1060
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_sets.h112
-rw-r--r--sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc153
-rw-r--r--sandbox/linux/seccomp-bpf/die.cc93
-rw-r--r--sandbox/linux/seccomp-bpf/die.h68
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.cc279
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.h118
-rw-r--r--sandbox/linux/seccomp-bpf/syscall.cc421
-rw-r--r--sandbox/linux/seccomp-bpf/syscall.h166
-rw-r--r--sandbox/linux/seccomp-bpf/syscall_unittest.cc240
-rw-r--r--sandbox/linux/seccomp-bpf/trap.cc390
-rw-r--r--sandbox/linux/seccomp-bpf/trap.h85
-rw-r--r--sandbox/linux/seccomp-bpf/trap_unittest.cc28
-rw-r--r--sandbox/linux/services/credentials.cc299
-rw-r--r--sandbox/linux/services/credentials.h104
-rw-r--r--sandbox/linux/services/credentials_unittest.cc242
-rw-r--r--sandbox/linux/services/init_process_reaper.cc101
-rw-r--r--sandbox/linux/services/init_process_reaper.h25
-rw-r--r--sandbox/linux/services/namespace_sandbox.cc208
-rw-r--r--sandbox/linux/services/namespace_sandbox.h101
-rw-r--r--sandbox/linux/services/namespace_sandbox_unittest.cc217
-rw-r--r--sandbox/linux/services/namespace_utils.cc117
-rw-r--r--sandbox/linux/services/namespace_utils.h53
-rw-r--r--sandbox/linux/services/namespace_utils_unittest.cc72
-rw-r--r--sandbox/linux/services/proc_util.cc119
-rw-r--r--sandbox/linux/services/proc_util.h42
-rw-r--r--sandbox/linux/services/proc_util_unittest.cc62
-rw-r--r--sandbox/linux/services/resource_limits.cc26
-rw-r--r--sandbox/linux/services/resource_limits.h29
-rw-r--r--sandbox/linux/services/resource_limits_unittests.cc43
-rw-r--r--sandbox/linux/services/scoped_process.cc119
-rw-r--r--sandbox/linux/services/scoped_process.h55
-rw-r--r--sandbox/linux/services/scoped_process_unittest.cc130
-rw-r--r--sandbox/linux/services/syscall_wrappers.cc246
-rw-r--r--sandbox/linux/services/syscall_wrappers.h83
-rw-r--r--sandbox/linux/services/syscall_wrappers_unittest.cc99
-rw-r--r--sandbox/linux/services/thread_helpers.cc157
-rw-r--r--sandbox/linux/services/thread_helpers.h43
-rw-r--r--sandbox/linux/services/thread_helpers_unittests.cc147
-rw-r--r--sandbox/linux/services/yama.cc115
-rw-r--r--sandbox/linux/services/yama.h57
-rw-r--r--sandbox/linux/services/yama_unittests.cc172
-rw-r--r--sandbox/linux/syscall_broker/broker_channel.cc35
-rw-r--r--sandbox/linux/syscall_broker/broker_channel.h31
-rw-r--r--sandbox/linux/syscall_broker/broker_client.cc144
-rw-r--r--sandbox/linux/syscall_broker/broker_client.h75
-rw-r--r--sandbox/linux/syscall_broker/broker_file_permission.cc243
-rw-r--r--sandbox/linux/syscall_broker/broker_file_permission.h119
-rw-r--r--sandbox/linux/syscall_broker/broker_file_permission_unittest.cc262
-rw-r--r--sandbox/linux/syscall_broker/broker_host.cc231
-rw-r--r--sandbox/linux/syscall_broker/broker_host.h41
-rw-r--r--sandbox/linux/syscall_broker/broker_policy.cc99
-rw-r--r--sandbox/linux/syscall_broker/broker_policy.h87
-rw-r--r--sandbox/linux/syscall_broker/broker_process.cc120
-rw-r--r--sandbox/linux/syscall_broker/broker_process.h94
-rw-r--r--sandbox/linux/syscall_broker/broker_process_unittest.cc656
145 files changed, 26091 insertions, 0 deletions
diff --git a/base/allocator/allocator_extension.cc b/base/allocator/allocator_extension.cc
new file mode 100644
index 0000000000..83e460ac82
--- /dev/null
+++ b/base/allocator/allocator_extension.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/allocator/allocator_extension.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace allocator {
+
+bool GetAllocatorWasteSize(size_t* size) {
+ thunks::GetAllocatorWasteSizeFunction get_allocator_waste_size_function =
+ thunks::GetGetAllocatorWasteSizeFunction();
+ return get_allocator_waste_size_function != NULL &&
+ get_allocator_waste_size_function(size);
+}
+
+void GetStats(char* buffer, int buffer_length) {
+ DCHECK_GT(buffer_length, 0);
+ thunks::GetStatsFunction get_stats_function = thunks::GetGetStatsFunction();
+ if (get_stats_function)
+ get_stats_function(buffer, buffer_length);
+ else
+ buffer[0] = '\0';
+}
+
+void ReleaseFreeMemory() {
+ thunks::ReleaseFreeMemoryFunction release_free_memory_function =
+ thunks::GetReleaseFreeMemoryFunction();
+ if (release_free_memory_function)
+ release_free_memory_function();
+}
+
+void SetGetAllocatorWasteSizeFunction(
+ thunks::GetAllocatorWasteSizeFunction get_allocator_waste_size_function) {
+ DCHECK_EQ(thunks::GetGetAllocatorWasteSizeFunction(),
+ reinterpret_cast<thunks::GetAllocatorWasteSizeFunction>(NULL));
+ thunks::SetGetAllocatorWasteSizeFunction(get_allocator_waste_size_function);
+}
+
+void SetGetStatsFunction(thunks::GetStatsFunction get_stats_function) {
+ DCHECK_EQ(thunks::GetGetStatsFunction(),
+ reinterpret_cast<thunks::GetStatsFunction>(NULL));
+ thunks::SetGetStatsFunction(get_stats_function);
+}
+
+void SetReleaseFreeMemoryFunction(
+ thunks::ReleaseFreeMemoryFunction release_free_memory_function) {
+ DCHECK_EQ(thunks::GetReleaseFreeMemoryFunction(),
+ reinterpret_cast<thunks::ReleaseFreeMemoryFunction>(NULL));
+ thunks::SetReleaseFreeMemoryFunction(release_free_memory_function);
+}
+
+} // namespace allocator
+} // namespace base
diff --git a/base/native_library_posix.cc b/base/native_library_posix.cc
new file mode 100644
index 0000000000..3179a93833
--- /dev/null
+++ b/base/native_library_posix.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/native_library.h"
+
+#include <dlfcn.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+std::string NativeLibraryLoadError::ToString() const {
+ return message;
+}
+
+// static
+NativeLibrary LoadNativeLibrary(const FilePath& library_path,
+ NativeLibraryLoadError* error) {
+ // dlopen() opens the file off disk.
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // We deliberately do not use RTLD_DEEPBIND. For the history why, please
+ // refer to the bug tracker. Some useful bug reports to read include:
+ // http://crbug.com/17943, http://crbug.com/17557, http://crbug.com/36892,
+ // and http://crbug.com/40794.
+ void* dl = dlopen(library_path.value().c_str(), RTLD_LAZY);
+ if (!dl && error)
+ error->message = dlerror();
+
+ return dl;
+}
+
+// static
+void UnloadNativeLibrary(NativeLibrary library) {
+ int ret = dlclose(library);
+ if (ret < 0) {
+ DLOG(ERROR) << "dlclose failed: " << dlerror();
+ NOTREACHED();
+ }
+}
+
+// static
+void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
+ const char* name) {
+ return dlsym(library, name);
+}
+
+// static
+string16 GetNativeLibraryName(const string16& name) {
+ return ASCIIToUTF16("lib") + name + ASCIIToUTF16(".so");
+}
+
+} // namespace base
diff --git a/base/timer/hi_res_timer_manager_posix.cc b/base/timer/hi_res_timer_manager_posix.cc
new file mode 100644
index 0000000000..d2f152c8c0
--- /dev/null
+++ b/base/timer/hi_res_timer_manager_posix.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/timer/hi_res_timer_manager.h"
+
+// On POSIX we don't need to do anything special with the system timer.
+
+namespace base {
+
+HighResolutionTimerManager::HighResolutionTimerManager()
+ : hi_res_clock_available_(false) {
+}
+
+HighResolutionTimerManager::~HighResolutionTimerManager() {
+}
+
+void HighResolutionTimerManager::OnPowerStateChange(bool on_battery_power) {
+}
+
+void HighResolutionTimerManager::UseHiResClock(bool use) {
+}
+
+} // namespace base
diff --git a/base/timer/mock_timer.cc b/base/timer/mock_timer.cc
new file mode 100644
index 0000000000..296071e8e3
--- /dev/null
+++ b/base/timer/mock_timer.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/timer/mock_timer.h"
+
+namespace base {
+
+MockTimer::MockTimer(bool retain_user_task, bool is_repeating)
+ : Timer(retain_user_task, is_repeating),
+ is_running_(false) {
+}
+
+MockTimer::MockTimer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating)
+ : Timer(true, is_repeating),
+ delay_(delay),
+ is_running_(false) {
+}
+
+MockTimer::~MockTimer() {
+}
+
+bool MockTimer::IsRunning() const {
+ return is_running_;
+}
+
+base::TimeDelta MockTimer::GetCurrentDelay() const {
+ return delay_;
+}
+
+void MockTimer::Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task) {
+ delay_ = delay;
+ user_task_ = user_task;
+ Reset();
+}
+
+void MockTimer::Stop() {
+ is_running_ = false;
+ if (!retain_user_task())
+ user_task_.Reset();
+}
+
+void MockTimer::Reset() {
+ DCHECK(!user_task_.is_null());
+ is_running_ = true;
+}
+
+void MockTimer::Fire() {
+ DCHECK(is_running_);
+ base::Closure old_task = user_task_;
+ if (is_repeating())
+ Reset();
+ else
+ Stop();
+ old_task.Run();
+}
+
+} // namespace base
diff --git a/base/timer/mock_timer.h b/base/timer/mock_timer.h
new file mode 100644
index 0000000000..e18a5c0489
--- /dev/null
+++ b/base/timer/mock_timer.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TIMER_MOCK_TIMER_H_
+#define BASE_TIMER_MOCK_TIMER_H_
+
+#include "base/timer/timer.h"
+
+namespace base {
+
+class BASE_EXPORT MockTimer : public Timer {
+ public:
+ MockTimer(bool retain_user_task, bool is_repeating);
+ MockTimer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating);
+ ~MockTimer() override;
+
+ // base::Timer implementation.
+ bool IsRunning() const override;
+ base::TimeDelta GetCurrentDelay() const override;
+ void Start(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task) override;
+ void Stop() override;
+ void Reset() override;
+
+ // Testing methods.
+ void Fire();
+
+ private:
+ base::Closure user_task_;
+ TimeDelta delay_;
+ bool is_running_;
+};
+
+} // namespace base
+
+#endif // BASE_TIMER_MOCK_TIMER_H_
diff --git a/base/timer/mock_timer_unittest.cc b/base/timer/mock_timer_unittest.cc
new file mode 100644
index 0000000000..f6b6953648
--- /dev/null
+++ b/base/timer/mock_timer_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/timer/mock_timer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void CallMeMaybe(int *number) {
+ (*number)++;
+}
+
+TEST(MockTimerTest, FiresOnce) {
+ int calls = 0;
+ base::MockTimer timer(false, false);
+ base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
+ timer.Start(FROM_HERE, delay,
+ base::Bind(&CallMeMaybe,
+ base::Unretained(&calls)));
+ EXPECT_EQ(delay, timer.GetCurrentDelay());
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Fire();
+ EXPECT_FALSE(timer.IsRunning());
+ EXPECT_EQ(1, calls);
+}
+
+TEST(MockTimerTest, FiresRepeatedly) {
+ int calls = 0;
+ base::MockTimer timer(true, true);
+ base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
+ timer.Start(FROM_HERE, delay,
+ base::Bind(&CallMeMaybe,
+ base::Unretained(&calls)));
+ timer.Fire();
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Fire();
+ timer.Fire();
+ EXPECT_TRUE(timer.IsRunning());
+ EXPECT_EQ(3, calls);
+}
+
+TEST(MockTimerTest, Stops) {
+ int calls = 0;
+ base::MockTimer timer(true, true);
+ base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
+ timer.Start(FROM_HERE, delay,
+ base::Bind(&CallMeMaybe,
+ base::Unretained(&calls)));
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+}
+
+class HasWeakPtr : public base::SupportsWeakPtr<HasWeakPtr> {
+ public:
+ HasWeakPtr() {}
+ virtual ~HasWeakPtr() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HasWeakPtr);
+};
+
+void DoNothingWithWeakPtr(HasWeakPtr* has_weak_ptr) {
+}
+
+TEST(MockTimerTest, DoesNotRetainClosure) {
+ HasWeakPtr *has_weak_ptr = new HasWeakPtr();
+ base::WeakPtr<HasWeakPtr> weak_ptr(has_weak_ptr->AsWeakPtr());
+ base::MockTimer timer(false, false);
+ base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
+ ASSERT_TRUE(weak_ptr.get());
+ timer.Start(FROM_HERE, delay,
+ base::Bind(&DoNothingWithWeakPtr,
+ base::Owned(has_weak_ptr)));
+ ASSERT_TRUE(weak_ptr.get());
+ timer.Fire();
+ ASSERT_FALSE(weak_ptr.get());
+}
+
+} // namespace
diff --git a/crypto/hmac.cc b/crypto/hmac.cc
new file mode 100644
index 0000000000..c9a2b74d98
--- /dev/null
+++ b/crypto/hmac.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/hmac.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "crypto/secure_util.h"
+#include "crypto/symmetric_key.h"
+
+namespace crypto {
+
+bool HMAC::Init(SymmetricKey* key) {
+ std::string raw_key;
+ bool result = key->GetRawKey(&raw_key) && Init(raw_key);
+ // Zero out key copy. This might get optimized away, but one can hope.
+ // Using std::string to store key info at all is a larger problem.
+ std::fill(raw_key.begin(), raw_key.end(), 0);
+ return result;
+}
+
+size_t HMAC::DigestLength() const {
+ switch (hash_alg_) {
+ case SHA1:
+ return 20;
+ case SHA256:
+ return 32;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+bool HMAC::Verify(const base::StringPiece& data,
+ const base::StringPiece& digest) const {
+ if (digest.size() != DigestLength())
+ return false;
+ return VerifyTruncated(data, digest);
+}
+
+bool HMAC::VerifyTruncated(const base::StringPiece& data,
+ const base::StringPiece& digest) const {
+ if (digest.empty())
+ return false;
+ size_t digest_length = DigestLength();
+ scoped_ptr<unsigned char[]> computed_digest(
+ new unsigned char[digest_length]);
+ if (!Sign(data, computed_digest.get(), digest_length))
+ return false;
+
+ return SecureMemEqual(digest.data(), computed_digest.get(),
+ std::min(digest.size(), digest_length));
+}
+
+} // namespace crypto
diff --git a/crypto/hmac.h b/crypto/hmac.h
new file mode 100644
index 0000000000..c7b22fa1bd
--- /dev/null
+++ b/crypto/hmac.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Utility class for calculating the HMAC for a given message. We currently
+// only support SHA1 for the hash algorithm, but this can be extended easily.
+
+#ifndef CRYPTO_HMAC_H_
+#define CRYPTO_HMAC_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "crypto/crypto_export.h"
+
+namespace crypto {
+
+// Simplify the interface and reduce includes by abstracting out the internals.
+struct HMACPlatformData;
+class SymmetricKey;
+
+class CRYPTO_EXPORT HMAC {
+ public:
+ // The set of supported hash functions. Extend as required.
+ enum HashAlgorithm {
+ SHA1,
+ SHA256,
+ };
+
+ explicit HMAC(HashAlgorithm hash_alg);
+ ~HMAC();
+
+ // Returns the length of digest that this HMAC will create.
+ size_t DigestLength() const;
+
+ // TODO(abarth): Add a PreferredKeyLength() member function.
+
+ // Initializes this instance using |key| of the length |key_length|. Call Init
+ // only once. It returns false on the second or later calls.
+ //
+ // NOTE: the US Federal crypto standard FIPS 198, Section 3 says:
+ // The size of the key, K, shall be equal to or greater than L/2, where L
+ // is the size of the hash function output.
+ // In FIPS 198-1 (and SP-800-107, which describes key size recommendations),
+ // this requirement is gone. But a system crypto library may still enforce
+ // this old requirement. If the key is shorter than this recommended value,
+ // Init() may fail.
+ bool Init(const unsigned char* key, size_t key_length) WARN_UNUSED_RESULT;
+
+ // Initializes this instance using |key|. Call Init
+ // only once. It returns false on the second or later calls.
+ bool Init(SymmetricKey* key) WARN_UNUSED_RESULT;
+
+ // Initializes this instance using |key|. Call Init only once. It returns
+ // false on the second or later calls.
+ bool Init(const base::StringPiece& key) WARN_UNUSED_RESULT {
+ return Init(reinterpret_cast<const unsigned char*>(key.data()),
+ key.size());
+ }
+
+ // Calculates the HMAC for the message in |data| using the algorithm supplied
+ // to the constructor and the key supplied to the Init method. The HMAC is
+ // returned in |digest|, which has |digest_length| bytes of storage available.
+ bool Sign(const base::StringPiece& data, unsigned char* digest,
+ size_t digest_length) const WARN_UNUSED_RESULT;
+
+ // Verifies that the HMAC for the message in |data| equals the HMAC provided
+ // in |digest|, using the algorithm supplied to the constructor and the key
+ // supplied to the Init method. Use of this method is strongly recommended
+ // over using Sign() with a manual comparison (such as memcmp), as such
+ // comparisons may result in side-channel disclosures, such as timing, that
+ // undermine the cryptographic integrity. |digest| must be exactly
+ // |DigestLength()| bytes long.
+ bool Verify(const base::StringPiece& data,
+ const base::StringPiece& digest) const WARN_UNUSED_RESULT;
+
+ // Verifies a truncated HMAC, behaving identical to Verify(), except
+ // that |digest| is allowed to be smaller than |DigestLength()|.
+ bool VerifyTruncated(
+ const base::StringPiece& data,
+ const base::StringPiece& digest) const WARN_UNUSED_RESULT;
+
+ private:
+ HashAlgorithm hash_alg_;
+ scoped_ptr<HMACPlatformData> plat_;
+
+ DISALLOW_COPY_AND_ASSIGN(HMAC);
+};
+
+} // namespace crypto
+
+#endif // CRYPTO_HMAC_H_
diff --git a/crypto/hmac_nss.cc b/crypto/hmac_nss.cc
new file mode 100644
index 0000000000..e14282c916
--- /dev/null
+++ b/crypto/hmac_nss.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/hmac.h"
+
+#include <nss.h>
+#include <pk11pub.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+
+namespace crypto {
+
+struct HMACPlatformData {
+ CK_MECHANISM_TYPE mechanism_;
+ ScopedPK11Slot slot_;
+ ScopedPK11SymKey sym_key_;
+};
+
+HMAC::HMAC(HashAlgorithm hash_alg)
+ : hash_alg_(hash_alg), plat_(new HMACPlatformData()) {
+ // Only SHA-1 and SHA-256 hash algorithms are supported.
+ switch (hash_alg_) {
+ case SHA1:
+ plat_->mechanism_ = CKM_SHA_1_HMAC;
+ break;
+ case SHA256:
+ plat_->mechanism_ = CKM_SHA256_HMAC;
+ break;
+ default:
+ NOTREACHED() << "Unsupported hash algorithm";
+ break;
+ }
+}
+
+HMAC::~HMAC() {
+}
+
+bool HMAC::Init(const unsigned char *key, size_t key_length) {
+ EnsureNSSInit();
+
+ if (plat_->slot_.get()) {
+ // Init must not be called more than twice on the same HMAC object.
+ NOTREACHED();
+ return false;
+ }
+
+ plat_->slot_.reset(PK11_GetInternalSlot());
+ if (!plat_->slot_.get()) {
+ NOTREACHED();
+ return false;
+ }
+
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = const_cast<unsigned char*>(key); // NSS API isn't const.
+ key_item.len = key_length;
+
+ plat_->sym_key_.reset(PK11_ImportSymKey(plat_->slot_.get(),
+ plat_->mechanism_,
+ PK11_OriginUnwrap,
+ CKA_SIGN,
+ &key_item,
+ NULL));
+ if (!plat_->sym_key_.get()) {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+bool HMAC::Sign(const base::StringPiece& data,
+ unsigned char* digest,
+ size_t digest_length) const {
+ if (!plat_->sym_key_.get()) {
+ // Init has not been called before Sign.
+ NOTREACHED();
+ return false;
+ }
+
+ SECItem param = { siBuffer, NULL, 0 };
+ ScopedPK11Context context(PK11_CreateContextBySymKey(plat_->mechanism_,
+ CKA_SIGN,
+ plat_->sym_key_.get(),
+ &param));
+ if (!context.get()) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (PK11_DigestBegin(context.get()) != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (PK11_DigestOp(context.get(),
+ reinterpret_cast<const unsigned char*>(data.data()),
+ data.length()) != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ unsigned int len = 0;
+ if (PK11_DigestFinal(context.get(),
+ digest, &len, digest_length) != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace crypto
diff --git a/crypto/hmac_openssl.cc b/crypto/hmac_openssl.cc
new file mode 100644
index 0000000000..ef20290e22
--- /dev/null
+++ b/crypto/hmac_openssl.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/hmac.h"
+
+#include <openssl/hmac.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "crypto/openssl_util.h"
+
+namespace crypto {
+
+struct HMACPlatformData {
+ std::vector<unsigned char> key;
+};
+
+HMAC::HMAC(HashAlgorithm hash_alg) : hash_alg_(hash_alg) {
+ // Only SHA-1 and SHA-256 hash algorithms are supported now.
+ DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256);
+}
+
+bool HMAC::Init(const unsigned char* key, size_t key_length) {
+ // Init must not be called more than once on the same HMAC object.
+ DCHECK(!plat_);
+ plat_.reset(new HMACPlatformData());
+ plat_->key.assign(key, key + key_length);
+ return true;
+}
+
+HMAC::~HMAC() {
+ if (plat_) {
+ // Zero out key copy.
+ plat_->key.assign(plat_->key.size(), 0);
+ STLClearObject(&plat_->key);
+ }
+}
+
+bool HMAC::Sign(const base::StringPiece& data,
+ unsigned char* digest,
+ size_t digest_length) const {
+ DCHECK(plat_); // Init must be called before Sign.
+
+ ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> result(digest, digest_length);
+ return !!::HMAC(hash_alg_ == SHA1 ? EVP_sha1() : EVP_sha256(),
+ vector_as_array(&plat_->key), plat_->key.size(),
+ reinterpret_cast<const unsigned char*>(data.data()),
+ data.size(), result.safe_buffer(), NULL);
+}
+
+} // namespace crypto
diff --git a/crypto/hmac_unittest.cc b/crypto/hmac_unittest.cc
new file mode 100644
index 0000000000..91eccd68e2
--- /dev/null
+++ b/crypto/hmac_unittest.cc
@@ -0,0 +1,295 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "crypto/hmac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+static const size_t kSHA1DigestSize = 20;
+static const size_t kSHA256DigestSize = 32;
+
+static const char* kSimpleKey =
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA";
+static const size_t kSimpleKeyLength = 80;
+
+static const struct {
+ const char *data;
+ const int data_len;
+ const char *digest;
+} kSimpleHmacCases[] = {
+ { "Test Using Larger Than Block-Size Key - Hash Key First", 54,
+ "\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55"
+ "\xED\x40\x21\x12" },
+ { "Test Using Larger Than Block-Size Key and Larger "
+ "Than One Block-Size Data", 73,
+ "\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08"
+ "\xBB\xFF\x1A\x91" }
+};
+
+TEST(HMACTest, HmacSafeBrowsingResponseTest) {
+ const int kKeySize = 16;
+
+ // Client key.
+ const unsigned char kClientKey[kKeySize] =
+ { 0xbf, 0xf6, 0x83, 0x4b, 0x3e, 0xa3, 0x23, 0xdd,
+ 0x96, 0x78, 0x70, 0x8e, 0xa1, 0x9d, 0x3b, 0x40 };
+
+ // Expected HMAC result using kMessage and kClientKey.
+ const unsigned char kReceivedHmac[kSHA1DigestSize] =
+ { 0xb9, 0x3c, 0xd6, 0xf0, 0x49, 0x47, 0xe2, 0x52,
+ 0x59, 0x7a, 0xbd, 0x1f, 0x2b, 0x4c, 0x83, 0xad,
+ 0x86, 0xd2, 0x48, 0x85 };
+
+ const char kMessage[] =
+"n:1896\ni:goog-malware-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav"
+"ar_s_445-450\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_439-444\nu:s"
+".ytimg.com/safebrowsing/rd/goog-malware-shavar_s_437\nu:s.ytimg.com/safebrowsi"
+"ng/rd/goog-malware-shavar_s_436\nu:s.ytimg.com/safebrowsing/rd/goog-malware-sh"
+"avar_s_433-435\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_431\nu:s.y"
+"timg.com/safebrowsing/rd/goog-malware-shavar_s_430\nu:s.ytimg.com/safebrowsing"
+"/rd/goog-malware-shavar_s_429\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav"
+"ar_s_428\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_426\nu:s.ytimg.c"
+"om/safebrowsing/rd/goog-malware-shavar_s_424\nu:s.ytimg.com/safebrowsing/rd/go"
+"og-malware-shavar_s_423\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_4"
+"22\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_420\nu:s.ytimg.com/saf"
+"ebrowsing/rd/goog-malware-shavar_s_419\nu:s.ytimg.com/safebrowsing/rd/goog-mal"
+"ware-shavar_s_414\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_409-411"
+"\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_405\nu:s.ytimg.com/safeb"
+"rowsing/rd/goog-malware-shavar_s_404\nu:s.ytimg.com/safebrowsing/rd/goog-malwa"
+"re-shavar_s_402\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_401\nu:s."
+"ytimg.com/safebrowsing/rd/goog-malware-shavar_a_973-978\nu:s.ytimg.com/safebro"
+"wsing/rd/goog-malware-shavar_a_937-972\nu:s.ytimg.com/safebrowsing/rd/goog-mal"
+"ware-shavar_a_931-936\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_925"
+"-930\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_919-924\ni:goog-phis"
+"h-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2633\nu:s.ytimg.co"
+"m/safebrowsing/rd/goog-phish-shavar_a_2632\nu:s.ytimg.com/safebrowsing/rd/goog"
+"-phish-shavar_a_2629-2631\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2"
+"626-2628\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2625\n";
+
+ std::string message_data(kMessage);
+
+ crypto::HMAC hmac(crypto::HMAC::SHA1);
+ ASSERT_TRUE(hmac.Init(kClientKey, kKeySize));
+ unsigned char calculated_hmac[kSHA1DigestSize];
+
+ EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kSHA1DigestSize));
+ EXPECT_EQ(0, memcmp(kReceivedHmac, calculated_hmac, kSHA1DigestSize));
+}
+
+// Test cases from RFC 2202 section 3
+TEST(HMACTest, RFC2202TestCases) {
+ const struct {
+ const char *key;
+ const int key_len;
+ const char *data;
+ const int data_len;
+ const char *digest;
+ } cases[] = {
+ { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B"
+ "\x0B\x0B\x0B\x0B", 20,
+ "Hi There", 8,
+ "\xB6\x17\x31\x86\x55\x05\x72\x64\xE2\x8B\xC0\xB6\xFB\x37\x8C\x8E"
+ "\xF1\x46\xBE\x00" },
+ { "Jefe", 4,
+ "what do ya want for nothing?", 28,
+ "\xEF\xFC\xDF\x6A\xE5\xEB\x2F\xA2\xD2\x74\x16\xD5\xF1\x84\xDF\x9C"
+ "\x25\x9A\x7C\x79" },
+ { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA", 20,
+ "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
+ "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
+ "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
+ "\xDD\xDD", 50,
+ "\x12\x5D\x73\x42\xB9\xAC\x11\xCD\x91\xA3\x9A\xF4\x8A\xA1\x7B\x4F"
+ "\x63\xF1\x75\xD3" },
+ { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10"
+ "\x11\x12\x13\x14\x15\x16\x17\x18\x19", 25,
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD", 50,
+ "\x4C\x90\x07\xF4\x02\x62\x50\xC6\xBC\x84\x14\xF9\xBF\x50\xC8\x6C"
+ "\x2D\x72\x35\xDA" },
+ { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C"
+ "\x0C\x0C\x0C\x0C", 20,
+ "Test With Truncation", 20,
+ "\x4C\x1A\x03\x42\x4B\x55\xE0\x7F\xE7\xF2\x7B\xE1\xD5\x8B\xB9\x32"
+ "\x4A\x9A\x5A\x04" },
+ { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ 80,
+ "Test Using Larger Than Block-Size Key - Hash Key First", 54,
+ "\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55"
+ "\xED\x40\x21\x12" },
+ { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ 80,
+ "Test Using Larger Than Block-Size Key and Larger "
+ "Than One Block-Size Data", 73,
+ "\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08"
+ "\xBB\xFF\x1A\x91" }
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ crypto::HMAC hmac(crypto::HMAC::SHA1);
+ ASSERT_TRUE(hmac.Init(reinterpret_cast<const unsigned char*>(cases[i].key),
+ cases[i].key_len));
+ std::string data_string(cases[i].data, cases[i].data_len);
+ unsigned char digest[kSHA1DigestSize];
+ EXPECT_TRUE(hmac.Sign(data_string, digest, kSHA1DigestSize));
+ EXPECT_EQ(0, memcmp(cases[i].digest, digest, kSHA1DigestSize));
+ }
+}
+
+// TODO(wtc): add other test vectors from RFC 4231.
+TEST(HMACTest, RFC4231TestCase6) {
+ unsigned char key[131];
+ for (size_t i = 0; i < sizeof(key); ++i)
+ key[i] = 0xaa;
+
+ std::string data = "Test Using Larger Than Block-Size Key - Hash Key First";
+ ASSERT_EQ(54U, data.size());
+
+ static unsigned char kKnownHMACSHA256[] = {
+ 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f,
+ 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f,
+ 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14,
+ 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54
+ };
+
+ crypto::HMAC hmac(crypto::HMAC::SHA256);
+ ASSERT_TRUE(hmac.Init(key, sizeof(key)));
+ unsigned char calculated_hmac[kSHA256DigestSize];
+
+ EXPECT_EQ(kSHA256DigestSize, hmac.DigestLength());
+ EXPECT_TRUE(hmac.Sign(data, calculated_hmac, kSHA256DigestSize));
+ EXPECT_EQ(0, memcmp(kKnownHMACSHA256, calculated_hmac, kSHA256DigestSize));
+}
+
+// Based on NSS's FIPS HMAC power-up self-test.
+TEST(HMACTest, NSSFIPSPowerUpSelfTest) {
+ static const char kKnownMessage[] =
+ "The test message for the MD2, MD5, and SHA-1 hashing algorithms.";
+
+ static const unsigned char kKnownSecretKey[] = {
+ 0x46, 0x69, 0x72, 0x65, 0x66, 0x6f, 0x78, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x54, 0x68, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x42, 0x69, 0x72, 0x64, 0x20,
+ 0x61, 0x72, 0x65, 0x20, 0x61, 0x77, 0x65, 0x73,
+ 0x6f, 0x6d, 0x65, 0x21, 0x00
+ };
+
+ static const size_t kKnownSecretKeySize = sizeof(kKnownSecretKey);
+
+ // HMAC-SHA-1 known answer (20 bytes).
+ static const unsigned char kKnownHMACSHA1[] = {
+ 0xd5, 0x85, 0xf6, 0x5b, 0x39, 0xfa, 0xb9, 0x05,
+ 0x3b, 0x57, 0x1d, 0x61, 0xe7, 0xb8, 0x84, 0x1e,
+ 0x5d, 0x0e, 0x1e, 0x11
+ };
+
+ // HMAC-SHA-256 known answer (32 bytes).
+ static const unsigned char kKnownHMACSHA256[] = {
+ 0x05, 0x75, 0x9a, 0x9e, 0x70, 0x5e, 0xe7, 0x44,
+ 0xe2, 0x46, 0x4b, 0x92, 0x22, 0x14, 0x22, 0xe0,
+ 0x1b, 0x92, 0x8a, 0x0c, 0xfe, 0xf5, 0x49, 0xe9,
+ 0xa7, 0x1b, 0x56, 0x7d, 0x1d, 0x29, 0x40, 0x48
+ };
+
+ std::string message_data(kKnownMessage);
+
+ crypto::HMAC hmac(crypto::HMAC::SHA1);
+ ASSERT_TRUE(hmac.Init(kKnownSecretKey, kKnownSecretKeySize));
+ unsigned char calculated_hmac[kSHA1DigestSize];
+
+ EXPECT_EQ(kSHA1DigestSize, hmac.DigestLength());
+ EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kSHA1DigestSize));
+ EXPECT_EQ(0, memcmp(kKnownHMACSHA1, calculated_hmac, kSHA1DigestSize));
+ EXPECT_TRUE(hmac.Verify(
+ message_data,
+ base::StringPiece(reinterpret_cast<const char*>(kKnownHMACSHA1),
+ kSHA1DigestSize)));
+ EXPECT_TRUE(hmac.VerifyTruncated(
+ message_data,
+ base::StringPiece(reinterpret_cast<const char*>(kKnownHMACSHA1),
+ kSHA1DigestSize / 2)));
+
+ crypto::HMAC hmac2(crypto::HMAC::SHA256);
+ ASSERT_TRUE(hmac2.Init(kKnownSecretKey, kKnownSecretKeySize));
+ unsigned char calculated_hmac2[kSHA256DigestSize];
+
+ EXPECT_TRUE(hmac2.Sign(message_data, calculated_hmac2, kSHA256DigestSize));
+ EXPECT_EQ(0, memcmp(kKnownHMACSHA256, calculated_hmac2, kSHA256DigestSize));
+}
+
+TEST(HMACTest, HMACObjectReuse) {
+ crypto::HMAC hmac(crypto::HMAC::SHA1);
+ ASSERT_TRUE(
+ hmac.Init(reinterpret_cast<const unsigned char*>(kSimpleKey),
+ kSimpleKeyLength));
+ for (size_t i = 0; i < arraysize(kSimpleHmacCases); ++i) {
+ std::string data_string(kSimpleHmacCases[i].data,
+ kSimpleHmacCases[i].data_len);
+ unsigned char digest[kSHA1DigestSize];
+ EXPECT_TRUE(hmac.Sign(data_string, digest, kSHA1DigestSize));
+ EXPECT_EQ(0, memcmp(kSimpleHmacCases[i].digest, digest, kSHA1DigestSize));
+ }
+}
+
+TEST(HMACTest, Verify) {
+ crypto::HMAC hmac(crypto::HMAC::SHA1);
+ ASSERT_TRUE(
+ hmac.Init(reinterpret_cast<const unsigned char*>(kSimpleKey),
+ kSimpleKeyLength));
+ const char empty_digest[kSHA1DigestSize] = { 0 };
+ for (size_t i = 0; i < arraysize(kSimpleHmacCases); ++i) {
+ // Expected results
+ EXPECT_TRUE(hmac.Verify(
+ base::StringPiece(kSimpleHmacCases[i].data,
+ kSimpleHmacCases[i].data_len),
+ base::StringPiece(kSimpleHmacCases[i].digest,
+ kSHA1DigestSize)));
+ // Mismatched size
+ EXPECT_FALSE(hmac.Verify(
+ base::StringPiece(kSimpleHmacCases[i].data,
+ kSimpleHmacCases[i].data_len),
+ base::StringPiece(kSimpleHmacCases[i].data,
+ kSimpleHmacCases[i].data_len)));
+
+ // Expected size, mismatched data
+ EXPECT_FALSE(hmac.Verify(
+ base::StringPiece(kSimpleHmacCases[i].data,
+ kSimpleHmacCases[i].data_len),
+ base::StringPiece(empty_digest, kSHA1DigestSize)));
+ }
+}
+
+TEST(HMACTest, EmptyKey) {
+ // Test vector from https://en.wikipedia.org/wiki/HMAC
+ const char* kExpectedDigest =
+ "\xFB\xDB\x1D\x1B\x18\xAA\x6C\x08\x32\x4B\x7D\x64\xB7\x1F\xB7\x63"
+ "\x70\x69\x0E\x1D";
+ base::StringPiece data("");
+
+ crypto::HMAC hmac(crypto::HMAC::SHA1);
+ ASSERT_TRUE(hmac.Init(NULL, 0));
+
+ unsigned char digest[kSHA1DigestSize];
+ EXPECT_TRUE(hmac.Sign(data, digest, kSHA1DigestSize));
+ EXPECT_EQ(0, memcmp(kExpectedDigest, digest, kSHA1DigestSize));
+
+ EXPECT_TRUE(hmac.Verify(
+ data, base::StringPiece(kExpectedDigest, kSHA1DigestSize)));
+}
diff --git a/crypto/hmac_win.cc b/crypto/hmac_win.cc
new file mode 100644
index 0000000000..99b3a60d57
--- /dev/null
+++ b/crypto/hmac_win.cc
@@ -0,0 +1,209 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/hmac.h"
+
+#include <windows.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+#include "crypto/scoped_capi_types.h"
+#include "crypto/third_party/nss/chromium-blapi.h"
+#include "crypto/third_party/nss/chromium-sha256.h"
+#include "crypto/wincrypt_shim.h"
+
+namespace crypto {
+
+namespace {
+
+// Implementation of HMAC-SHA-256:
+//
+// SHA-256 is supported in Windows XP SP3 or later. We still need to support
+// Windows XP SP2, so unfortunately we have to implement HMAC-SHA-256 here.
+
+enum {
+ SHA256_BLOCK_SIZE = 64 // Block size (in bytes) of the input to SHA-256.
+};
+
+// NSS doesn't accept size_t for text size, divide the data into smaller
+// chunks as needed.
+void Wrapped_SHA256_Update(SHA256Context* ctx, const unsigned char* text,
+ size_t text_len) {
+ const unsigned int kChunkSize = 1 << 30;
+ while (text_len > kChunkSize) {
+ SHA256_Update(ctx, text, kChunkSize);
+ text += kChunkSize;
+ text_len -= kChunkSize;
+ }
+ SHA256_Update(ctx, text, (unsigned int)text_len);
+}
+
+// See FIPS 198: The Keyed-Hash Message Authentication Code (HMAC).
+void ComputeHMACSHA256(const unsigned char* key, size_t key_len,
+ const unsigned char* text, size_t text_len,
+ unsigned char* output, size_t output_len) {
+ SHA256Context ctx;
+
+ // Pre-process the key, if necessary.
+ unsigned char key0[SHA256_BLOCK_SIZE];
+ if (key_len > SHA256_BLOCK_SIZE) {
+ SHA256_Begin(&ctx);
+ Wrapped_SHA256_Update(&ctx, key, key_len);
+ SHA256_End(&ctx, key0, NULL, SHA256_LENGTH);
+ memset(key0 + SHA256_LENGTH, 0, SHA256_BLOCK_SIZE - SHA256_LENGTH);
+ } else {
+ memcpy(key0, key, key_len);
+ if (key_len < SHA256_BLOCK_SIZE)
+ memset(key0 + key_len, 0, SHA256_BLOCK_SIZE - key_len);
+ }
+
+ unsigned char padded_key[SHA256_BLOCK_SIZE];
+ unsigned char inner_hash[SHA256_LENGTH];
+
+ // XOR key0 with ipad.
+ for (int i = 0; i < SHA256_BLOCK_SIZE; ++i)
+ padded_key[i] = key0[i] ^ 0x36;
+
+ // Compute the inner hash.
+ SHA256_Begin(&ctx);
+ SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE);
+ Wrapped_SHA256_Update(&ctx, text, text_len);
+ SHA256_End(&ctx, inner_hash, NULL, SHA256_LENGTH);
+
+ // XOR key0 with opad.
+ for (int i = 0; i < SHA256_BLOCK_SIZE; ++i)
+ padded_key[i] = key0[i] ^ 0x5c;
+
+ // Compute the outer hash.
+ SHA256_Begin(&ctx);
+ SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE);
+ SHA256_Update(&ctx, inner_hash, SHA256_LENGTH);
+ SHA256_End(&ctx, output, NULL, (unsigned int) output_len);
+}
+
+} // namespace
+
+struct HMACPlatformData {
+ ~HMACPlatformData() {
+ if (!raw_key_.empty()) {
+ SecureZeroMemory(&raw_key_[0], raw_key_.size());
+ }
+
+ // Destroy the key before releasing the provider.
+ key_.reset();
+ }
+
+ ScopedHCRYPTPROV provider_;
+ ScopedHCRYPTKEY key_;
+
+ // For HMAC-SHA-256 only.
+ std::vector<unsigned char> raw_key_;
+};
+
+HMAC::HMAC(HashAlgorithm hash_alg)
+ : hash_alg_(hash_alg), plat_(new HMACPlatformData()) {
+ // Only SHA-1 and SHA-256 hash algorithms are supported now.
+ DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256);
+}
+
+bool HMAC::Init(const unsigned char* key, size_t key_length) {
+ if (plat_->provider_ || plat_->key_ || !plat_->raw_key_.empty()) {
+ // Init must not be called more than once on the same HMAC object.
+ NOTREACHED();
+ return false;
+ }
+
+ if (hash_alg_ == SHA256) {
+ plat_->raw_key_.assign(key, key + key_length);
+ return true;
+ }
+
+ if (!CryptAcquireContext(plat_->provider_.receive(), NULL, NULL,
+ PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+ NOTREACHED();
+ return false;
+ }
+
+ // This code doesn't work on Win2k because PLAINTEXTKEYBLOB and
+ // CRYPT_IPSEC_HMAC_KEY are not supported on Windows 2000. PLAINTEXTKEYBLOB
+ // allows the import of an unencrypted key. For Win2k support, a cubmbersome
+ // exponent-of-one key procedure must be used:
+ // http://support.microsoft.com/kb/228786/en-us
+ // CRYPT_IPSEC_HMAC_KEY allows keys longer than 16 bytes.
+
+ struct KeyBlob {
+ BLOBHEADER header;
+ DWORD key_size;
+ BYTE key_data[1];
+ };
+ size_t key_blob_size = std::max(offsetof(KeyBlob, key_data) + key_length,
+ sizeof(KeyBlob));
+ std::vector<BYTE> key_blob_storage = std::vector<BYTE>(key_blob_size);
+ KeyBlob* key_blob = reinterpret_cast<KeyBlob*>(&key_blob_storage[0]);
+ key_blob->header.bType = PLAINTEXTKEYBLOB;
+ key_blob->header.bVersion = CUR_BLOB_VERSION;
+ key_blob->header.reserved = 0;
+ key_blob->header.aiKeyAlg = CALG_RC2;
+ key_blob->key_size = static_cast<DWORD>(key_length);
+ memcpy(key_blob->key_data, key, key_length);
+
+ if (!CryptImportKey(plat_->provider_, &key_blob_storage[0],
+ (DWORD)key_blob_storage.size(), 0,
+ CRYPT_IPSEC_HMAC_KEY, plat_->key_.receive())) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Destroy the copy of the key.
+ SecureZeroMemory(key_blob->key_data, key_length);
+
+ return true;
+}
+
+HMAC::~HMAC() {
+}
+
+bool HMAC::Sign(const base::StringPiece& data,
+ unsigned char* digest,
+ size_t digest_length) const {
+ if (hash_alg_ == SHA256) {
+ if (plat_->raw_key_.empty())
+ return false;
+ ComputeHMACSHA256(&plat_->raw_key_[0], plat_->raw_key_.size(),
+ reinterpret_cast<const unsigned char*>(data.data()),
+ data.size(), digest, digest_length);
+ return true;
+ }
+
+ if (!plat_->provider_ || !plat_->key_)
+ return false;
+
+ if (hash_alg_ != SHA1) {
+ NOTREACHED();
+ return false;
+ }
+
+ ScopedHCRYPTHASH hash;
+ if (!CryptCreateHash(plat_->provider_, CALG_HMAC, plat_->key_, 0,
+ hash.receive()))
+ return false;
+
+ HMAC_INFO hmac_info;
+ memset(&hmac_info, 0, sizeof(hmac_info));
+ hmac_info.HashAlgid = CALG_SHA1;
+ if (!CryptSetHashParam(hash, HP_HMAC_INFO,
+ reinterpret_cast<BYTE*>(&hmac_info), 0))
+ return false;
+
+ if (!CryptHashData(hash, reinterpret_cast<const BYTE*>(data.data()),
+ static_cast<DWORD>(data.size()), 0))
+ return false;
+
+ DWORD sha1_size = static_cast<DWORD>(digest_length);
+ return !!CryptGetHashParam(hash, HP_HASHVAL, digest, &sha1_size, 0);
+}
+
+} // namespace crypto
diff --git a/crypto/nss_key_util.cc b/crypto/nss_key_util.cc
new file mode 100644
index 0000000000..77435fba36
--- /dev/null
+++ b/crypto/nss_key_util.cc
@@ -0,0 +1,163 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/nss_key_util.h"
+
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "crypto/nss_util.h"
+
+#if defined(USE_NSS_CERTS)
+#include <secmod.h>
+#include "crypto/nss_util_internal.h"
+#endif
+
+namespace crypto {
+
+namespace {
+
+#if defined(USE_NSS_CERTS)
+
+struct PublicKeyInfoDeleter {
+ inline void operator()(CERTSubjectPublicKeyInfo* spki) {
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+ }
+};
+
+typedef scoped_ptr<CERTSubjectPublicKeyInfo, PublicKeyInfoDeleter>
+ ScopedPublicKeyInfo;
+
+// Decodes |input| as a SubjectPublicKeyInfo and returns a SECItem containing
+// the CKA_ID of that public key or nullptr on error.
+ScopedSECItem MakeIDFromSPKI(const std::vector<uint8_t>& input) {
+ // First, decode and save the public key.
+ SECItem key_der;
+ key_der.type = siBuffer;
+ key_der.data = const_cast<unsigned char*>(vector_as_array(&input));
+ key_der.len = input.size();
+
+ ScopedPublicKeyInfo spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&key_der));
+ if (!spki)
+ return nullptr;
+
+ ScopedSECKEYPublicKey result(SECKEY_ExtractPublicKey(spki.get()));
+ if (!result)
+ return nullptr;
+
+ // See pk11_MakeIDFromPublicKey from NSS. For now, only RSA keys are
+ // supported.
+ if (SECKEY_GetPublicKeyType(result.get()) != rsaKey)
+ return nullptr;
+
+ return ScopedSECItem(PK11_MakeIDFromPubKey(&result->u.rsa.modulus));
+}
+
+#endif // defined(USE_NSS_CERTS)
+
+} // namespace
+
+bool GenerateRSAKeyPairNSS(PK11SlotInfo* slot,
+ uint16_t num_bits,
+ bool permanent,
+ ScopedSECKEYPublicKey* public_key,
+ ScopedSECKEYPrivateKey* private_key) {
+ DCHECK(slot);
+
+ PK11RSAGenParams param;
+ param.keySizeInBits = num_bits;
+ param.pe = 65537L;
+ SECKEYPublicKey* public_key_raw = nullptr;
+ private_key->reset(PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN,
+ &param, &public_key_raw, permanent,
+ permanent /* sensitive */, nullptr));
+ if (!*private_key)
+ return false;
+
+ public_key->reset(public_key_raw);
+ return true;
+}
+
+ScopedSECKEYPrivateKey ImportNSSKeyFromPrivateKeyInfo(
+ PK11SlotInfo* slot,
+ const std::vector<uint8_t>& input,
+ bool permanent) {
+ DCHECK(slot);
+
+ ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ DCHECK(arena);
+
+ // Excess data is illegal, but NSS silently accepts it, so first ensure that
+ // |input| consists of a single ASN.1 element.
+ SECItem input_item;
+ input_item.data = const_cast<unsigned char*>(vector_as_array(&input));
+ input_item.len = input.size();
+ SECItem der_private_key_info;
+ SECStatus rv =
+ SEC_QuickDERDecodeItem(arena.get(), &der_private_key_info,
+ SEC_ASN1_GET(SEC_AnyTemplate), &input_item);
+ if (rv != SECSuccess)
+ return nullptr;
+
+ // Allow the private key to be used for key unwrapping, data decryption,
+ // and signature generation.
+ const unsigned int key_usage =
+ KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | KU_DIGITAL_SIGNATURE;
+ SECKEYPrivateKey* key_raw = nullptr;
+ rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ slot, &der_private_key_info, nullptr, nullptr, permanent,
+ permanent /* sensitive */, key_usage, &key_raw, nullptr);
+ if (rv != SECSuccess)
+ return nullptr;
+ return ScopedSECKEYPrivateKey(key_raw);
+}
+
+#if defined(USE_NSS_CERTS)
+
+ScopedSECKEYPrivateKey FindNSSKeyFromPublicKeyInfo(
+ const std::vector<uint8_t>& input) {
+ EnsureNSSInit();
+
+ ScopedSECItem cka_id(MakeIDFromSPKI(input));
+ if (!cka_id)
+ return nullptr;
+
+ // Search all slots in all modules for the key with the given ID.
+ AutoSECMODListReadLock auto_lock;
+ const SECMODModuleList* head = SECMOD_GetDefaultModuleList();
+ for (const SECMODModuleList* item = head; item != nullptr;
+ item = item->next) {
+ int slot_count = item->module->loaded ? item->module->slotCount : 0;
+ for (int i = 0; i < slot_count; i++) {
+ // Look for the key in slot |i|.
+ ScopedSECKEYPrivateKey key(
+ PK11_FindKeyByKeyID(item->module->slots[i], cka_id.get(), nullptr));
+ if (key)
+ return key.Pass();
+ }
+ }
+
+ // The key wasn't found in any module.
+ return nullptr;
+}
+
+ScopedSECKEYPrivateKey FindNSSKeyFromPublicKeyInfoInSlot(
+ const std::vector<uint8_t>& input,
+ PK11SlotInfo* slot) {
+ DCHECK(slot);
+
+ ScopedSECItem cka_id(MakeIDFromSPKI(input));
+ if (!cka_id)
+ return nullptr;
+
+ return ScopedSECKEYPrivateKey(
+ PK11_FindKeyByKeyID(slot, cka_id.get(), nullptr));
+}
+
+#endif // defined(USE_NSS_CERTS)
+
+} // namespace crypto
diff --git a/crypto/nss_key_util.h b/crypto/nss_key_util.h
new file mode 100644
index 0000000000..12b948d25b
--- /dev/null
+++ b/crypto/nss_key_util.h
@@ -0,0 +1,58 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_NSS_KEY_UTIL_H_
+#define CRYPTO_NSS_KEY_UTIL_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "build/build_config.h"
+#include "crypto/crypto_export.h"
+#include "crypto/scoped_nss_types.h"
+
+typedef struct PK11SlotInfoStr PK11SlotInfo;
+
+namespace crypto {
+
+// Generates a new RSA keypair of size |num_bits| in |slot|. Returns true on
+// success and false on failure. If |permanent| is true, the resulting key is
+// permanent and is not exportable in plaintext form.
+CRYPTO_EXPORT bool GenerateRSAKeyPairNSS(
+ PK11SlotInfo* slot,
+ uint16_t num_bits,
+ bool permanent,
+ ScopedSECKEYPublicKey* out_public_key,
+ ScopedSECKEYPrivateKey* out_private_key);
+
+// Imports a private key from |input| into |slot|. |input| is interpreted as a
+// DER-encoded PrivateKeyInfo block from PKCS #8. Returns nullptr on error. If
+// |permanent| is true, the resulting key is permanent and is not exportable in
+// plaintext form.
+CRYPTO_EXPORT ScopedSECKEYPrivateKey
+ImportNSSKeyFromPrivateKeyInfo(PK11SlotInfo* slot,
+ const std::vector<uint8_t>& input,
+ bool permanent);
+
+#if defined(USE_NSS_CERTS)
+
+// Decodes |input| as a DER-encoded X.509 SubjectPublicKeyInfo and searches for
+// the private key half in the key database. Returns the private key on success
+// or nullptr on error.
+CRYPTO_EXPORT ScopedSECKEYPrivateKey
+FindNSSKeyFromPublicKeyInfo(const std::vector<uint8_t>& input);
+
+// Decodes |input| as a DER-encoded X.509 SubjectPublicKeyInfo and searches for
+// the private key half in the slot specified by |slot|. Returns the private key
+// on success or nullptr on error.
+CRYPTO_EXPORT ScopedSECKEYPrivateKey
+FindNSSKeyFromPublicKeyInfoInSlot(const std::vector<uint8_t>& input,
+ PK11SlotInfo* slot);
+
+#endif // defined(USE_NSS_CERTS)
+
+} // namespace crypto
+
+#endif // CRYPTO_NSS_KEY_UTIL_H_
diff --git a/crypto/nss_key_util_unittest.cc b/crypto/nss_key_util_unittest.cc
new file mode 100644
index 0000000000..ff4d55a146
--- /dev/null
+++ b/crypto/nss_key_util_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/nss_key_util.h"
+
+#include <keyhi.h>
+#include <pk11pub.h>
+
+#include <vector>
+
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crypto {
+
+class NSSKeyUtilTest : public testing::Test {
+ public:
+ void SetUp() override {
+ EnsureNSSInit();
+
+ internal_slot_.reset(PK11_GetInternalSlot());
+ ASSERT_TRUE(internal_slot_);
+ }
+
+ PK11SlotInfo* internal_slot() { return internal_slot_.get(); }
+
+ private:
+ ScopedPK11Slot internal_slot_;
+};
+
+TEST_F(NSSKeyUtilTest, GenerateRSAKeyPairNSS) {
+ const int kKeySizeBits = 1024;
+
+ ScopedSECKEYPublicKey public_key;
+ ScopedSECKEYPrivateKey private_key;
+ ASSERT_TRUE(GenerateRSAKeyPairNSS(internal_slot(), kKeySizeBits,
+ false /* not permanent */, &public_key,
+ &private_key));
+
+ EXPECT_EQ(rsaKey, SECKEY_GetPublicKeyType(public_key.get()));
+ EXPECT_EQ(rsaKey, SECKEY_GetPrivateKeyType(private_key.get()));
+ EXPECT_EQ((kKeySizeBits + 7) / 8,
+ PK11_GetPrivateModulusLen(private_key.get()));
+}
+
+#if defined(USE_NSS_CERTS)
+TEST_F(NSSKeyUtilTest, FindNSSKeyFromPublicKeyInfo) {
+ // Create an NSS keypair, which will put the keys in the user's NSSDB.
+ ScopedSECKEYPublicKey public_key;
+ ScopedSECKEYPrivateKey private_key;
+ ASSERT_TRUE(GenerateRSAKeyPairNSS(internal_slot(), 512,
+ false /* not permanent */, &public_key,
+ &private_key));
+
+ ScopedSECItem item(SECKEY_EncodeDERSubjectPublicKeyInfo(public_key.get()));
+ ASSERT_TRUE(item);
+ std::vector<uint8_t> public_key_der(item->data, item->data + item->len);
+
+ ScopedSECKEYPrivateKey private_key2 =
+ FindNSSKeyFromPublicKeyInfo(public_key_der);
+ ASSERT_TRUE(private_key2);
+ EXPECT_EQ(private_key->pkcs11ID, private_key2->pkcs11ID);
+}
+
+TEST_F(NSSKeyUtilTest, FailedFindNSSKeyFromPublicKeyInfo) {
+ // Create an NSS keypair, which will put the keys in the user's NSSDB.
+ ScopedSECKEYPublicKey public_key;
+ ScopedSECKEYPrivateKey private_key;
+ ASSERT_TRUE(GenerateRSAKeyPairNSS(internal_slot(), 512,
+ false /* not permanent */, &public_key,
+ &private_key));
+
+ ScopedSECItem item(SECKEY_EncodeDERSubjectPublicKeyInfo(public_key.get()));
+ ASSERT_TRUE(item);
+ std::vector<uint8_t> public_key_der(item->data, item->data + item->len);
+
+ // Remove the keys from the DB, and make sure we can't find them again.
+ PK11_DestroyTokenObject(private_key->pkcs11Slot, private_key->pkcs11ID);
+ PK11_DestroyTokenObject(public_key->pkcs11Slot, public_key->pkcs11ID);
+
+ EXPECT_FALSE(FindNSSKeyFromPublicKeyInfo(public_key_der));
+}
+#endif // defined(USE_NSS_CERTS)
+
+} // namespace crypto
diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc
new file mode 100644
index 0000000000..125591c73f
--- /dev/null
+++ b/crypto/nss_util.cc
@@ -0,0 +1,1131 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+
+#include <nss.h>
+#include <pk11pub.h>
+#include <plarena.h>
+#include <prerror.h>
+#include <prinit.h>
+#include <prtime.h>
+#include <secmod.h>
+
+#if defined(OS_OPENBSD)
+#include <sys/mount.h>
+#include <sys/param.h>
+#endif
+
+#if defined(OS_CHROMEOS)
+#include <dlfcn.h>
+#endif
+
+#include <map>
+#include <vector>
+
+#include "base/base_paths.h"
+#include "base/bind.h"
+#include "base/cpu.h"
+#include "base/debug/alias.h"
+#include "base/debug/stack_trace.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/native_library.h"
+#include "base/path_service.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/worker_pool.h"
+#include "build/build_config.h"
+
+// USE_NSS_CERTS means NSS is used for certificates and platform integration.
+// This requires additional support to manage the platform certificate and key
+// stores.
+#if defined(USE_NSS_CERTS)
+#include "base/synchronization/lock.h"
+#include "crypto/nss_crypto_module_delegate.h"
+#endif // defined(USE_NSS_CERTS)
+
+namespace crypto {
+
+namespace {
+
+#if defined(OS_CHROMEOS)
+const char kUserNSSDatabaseName[] = "UserNSSDB";
+
+// Constants for loading the Chrome OS TPM-backed PKCS #11 library.
+const char kChapsModuleName[] = "Chaps";
+const char kChapsPath[] = "libchaps.so";
+
+// Fake certificate authority database used for testing.
+static const base::FilePath::CharType kReadOnlyCertDB[] =
+ FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
+#endif // defined(OS_CHROMEOS)
+
+std::string GetNSSErrorMessage() {
+ std::string result;
+ if (PR_GetErrorTextLength()) {
+ scoped_ptr<char[]> error_text(new char[PR_GetErrorTextLength() + 1]);
+ PRInt32 copied = PR_GetErrorText(error_text.get());
+ result = std::string(error_text.get(), copied);
+ } else {
+ result = base::StringPrintf("NSS error code: %d", PR_GetError());
+ }
+ return result;
+}
+
+#if defined(USE_NSS_CERTS)
+#if !defined(OS_CHROMEOS)
+base::FilePath GetDefaultConfigDirectory() {
+ base::FilePath dir;
+ PathService::Get(base::DIR_HOME, &dir);
+ if (dir.empty()) {
+ LOG(ERROR) << "Failed to get home directory.";
+ return dir;
+ }
+ dir = dir.AppendASCII(".pki").AppendASCII("nssdb");
+ if (!base::CreateDirectory(dir)) {
+ LOG(ERROR) << "Failed to create " << dir.value() << " directory.";
+ dir.clear();
+ }
+ DVLOG(2) << "DefaultConfigDirectory: " << dir.value();
+ return dir;
+}
+#endif // !defined(IS_CHROMEOS)
+
+// On non-Chrome OS platforms, return the default config directory. On Chrome OS
+// test images, return a read-only directory with fake root CA certs (which are
+// used by the local Google Accounts server mock we use when testing our login
+// code). On Chrome OS non-test images (where the read-only directory doesn't
+// exist), return an empty path.
+base::FilePath GetInitialConfigDirectory() {
+#if defined(OS_CHROMEOS)
+ base::FilePath database_dir = base::FilePath(kReadOnlyCertDB);
+ if (!base::PathExists(database_dir))
+ database_dir.clear();
+ return database_dir;
+#else
+ return GetDefaultConfigDirectory();
+#endif // defined(OS_CHROMEOS)
+}
+
+// This callback for NSS forwards all requests to a caller-specified
+// CryptoModuleBlockingPasswordDelegate object.
+char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) {
+ crypto::CryptoModuleBlockingPasswordDelegate* delegate =
+ reinterpret_cast<crypto::CryptoModuleBlockingPasswordDelegate*>(arg);
+ if (delegate) {
+ bool cancelled = false;
+ std::string password = delegate->RequestPassword(PK11_GetTokenName(slot),
+ retry != PR_FALSE,
+ &cancelled);
+ if (cancelled)
+ return NULL;
+ char* result = PORT_Strdup(password.c_str());
+ password.replace(0, password.size(), password.size(), 0);
+ return result;
+ }
+ DLOG(ERROR) << "PK11 password requested with NULL arg";
+ return NULL;
+}
+
+// NSS creates a local cache of the sqlite database if it detects that the
+// filesystem the database is on is much slower than the local disk. The
+// detection doesn't work with the latest versions of sqlite, such as 3.6.22
+// (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561). So we set
+// the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's
+// detection when database_dir is on NFS. See http://crbug.com/48585.
+//
+// TODO(wtc): port this function to other USE_NSS_CERTS platforms. It is
+// defined only for OS_LINUX and OS_OPENBSD simply because the statfs structure
+// is OS-specific.
+//
+// Because this function sets an environment variable it must be run before we
+// go multi-threaded.
+void UseLocalCacheOfNSSDatabaseIfNFS(const base::FilePath& database_dir) {
+ bool db_on_nfs = false;
+#if defined(OS_LINUX)
+ base::FileSystemType fs_type = base::FILE_SYSTEM_UNKNOWN;
+ if (base::GetFileSystemType(database_dir, &fs_type))
+ db_on_nfs = (fs_type == base::FILE_SYSTEM_NFS);
+#elif defined(OS_OPENBSD)
+ struct statfs buf;
+ if (statfs(database_dir.value().c_str(), &buf) == 0)
+ db_on_nfs = (strcmp(buf.f_fstypename, MOUNT_NFS) == 0);
+#else
+ NOTIMPLEMENTED();
+#endif
+
+ if (db_on_nfs) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ static const char kUseCacheEnvVar[] = "NSS_SDB_USE_CACHE";
+ if (!env->HasVar(kUseCacheEnvVar))
+ env->SetVar(kUseCacheEnvVar, "yes");
+ }
+}
+
+#endif // defined(USE_NSS_CERTS)
+
+// A singleton to initialize/deinitialize NSPR.
+// Separate from the NSS singleton because we initialize NSPR on the UI thread.
+// Now that we're leaking the singleton, we could merge back with the NSS
+// singleton.
+class NSPRInitSingleton {
+ private:
+ friend struct base::DefaultLazyInstanceTraits<NSPRInitSingleton>;
+
+ NSPRInitSingleton() {
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ }
+
+ // NOTE(willchan): We don't actually execute this code since we leak NSS to
+ // prevent non-joinable threads from using NSS after it's already been shut
+ // down.
+ ~NSPRInitSingleton() {
+ PL_ArenaFinish();
+ PRStatus prstatus = PR_Cleanup();
+ if (prstatus != PR_SUCCESS)
+ LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?";
+ }
+};
+
+base::LazyInstance<NSPRInitSingleton>::Leaky
+ g_nspr_singleton = LAZY_INSTANCE_INITIALIZER;
+
+// Force a crash with error info on NSS_NoDB_Init failure.
+void CrashOnNSSInitFailure() {
+ int nss_error = PR_GetError();
+ int os_error = PR_GetOSError();
+ base::debug::Alias(&nss_error);
+ base::debug::Alias(&os_error);
+ LOG(ERROR) << "Error initializing NSS without a persistent database: "
+ << GetNSSErrorMessage();
+ LOG(FATAL) << "nss_error=" << nss_error << ", os_error=" << os_error;
+}
+
+#if defined(OS_CHROMEOS)
+class ChromeOSUserData {
+ public:
+ explicit ChromeOSUserData(ScopedPK11Slot public_slot)
+ : public_slot_(public_slot.Pass()),
+ private_slot_initialization_started_(false) {}
+ ~ChromeOSUserData() {
+ if (public_slot_) {
+ SECStatus status = SECMOD_CloseUserDB(public_slot_.get());
+ if (status != SECSuccess)
+ PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
+ }
+ }
+
+ ScopedPK11Slot GetPublicSlot() {
+ return ScopedPK11Slot(
+ public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) : NULL);
+ }
+
+ ScopedPK11Slot GetPrivateSlot(
+ const base::Callback<void(ScopedPK11Slot)>& callback) {
+ if (private_slot_)
+ return ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
+ if (!callback.is_null())
+ tpm_ready_callback_list_.push_back(callback);
+ return ScopedPK11Slot();
+ }
+
+ void SetPrivateSlot(ScopedPK11Slot private_slot) {
+ DCHECK(!private_slot_);
+ private_slot_ = private_slot.Pass();
+
+ SlotReadyCallbackList callback_list;
+ callback_list.swap(tpm_ready_callback_list_);
+ for (SlotReadyCallbackList::iterator i = callback_list.begin();
+ i != callback_list.end();
+ ++i) {
+ (*i).Run(ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get())));
+ }
+ }
+
+ bool private_slot_initialization_started() const {
+ return private_slot_initialization_started_;
+ }
+
+ void set_private_slot_initialization_started() {
+ private_slot_initialization_started_ = true;
+ }
+
+ private:
+ ScopedPK11Slot public_slot_;
+ ScopedPK11Slot private_slot_;
+
+ bool private_slot_initialization_started_;
+
+ typedef std::vector<base::Callback<void(ScopedPK11Slot)> >
+ SlotReadyCallbackList;
+ SlotReadyCallbackList tpm_ready_callback_list_;
+};
+
+class ScopedChapsLoadFixup {
+ public:
+ ScopedChapsLoadFixup();
+ ~ScopedChapsLoadFixup();
+
+ private:
+#if defined(COMPONENT_BUILD)
+ void *chaps_handle_;
+#endif
+};
+
+#if defined(COMPONENT_BUILD)
+
+ScopedChapsLoadFixup::ScopedChapsLoadFixup() {
+ // HACK: libchaps links the system protobuf and there are symbol conflicts
+ // with the bundled copy. Load chaps with RTLD_DEEPBIND to workaround.
+ chaps_handle_ = dlopen(kChapsPath, RTLD_LOCAL | RTLD_NOW | RTLD_DEEPBIND);
+}
+
+ScopedChapsLoadFixup::~ScopedChapsLoadFixup() {
+ // LoadModule() will have taken a 2nd reference.
+ if (chaps_handle_)
+ dlclose(chaps_handle_);
+}
+
+#else
+
+ScopedChapsLoadFixup::ScopedChapsLoadFixup() {}
+ScopedChapsLoadFixup::~ScopedChapsLoadFixup() {}
+
+#endif // defined(COMPONENT_BUILD)
+#endif // defined(OS_CHROMEOS)
+
+class NSSInitSingleton {
+ public:
+#if defined(OS_CHROMEOS)
+ // Used with PostTaskAndReply to pass handles to worker thread and back.
+ struct TPMModuleAndSlot {
+ explicit TPMModuleAndSlot(SECMODModule* init_chaps_module)
+ : chaps_module(init_chaps_module) {}
+ SECMODModule* chaps_module;
+ crypto::ScopedPK11Slot tpm_slot;
+ };
+
+ ScopedPK11Slot OpenPersistentNSSDBForPath(const std::string& db_name,
+ const base::FilePath& path) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // NSS is allowed to do IO on the current thread since dispatching
+ // to a dedicated thread would still have the affect of blocking
+ // the current thread, due to NSS's internal locking requirements
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ base::FilePath nssdb_path = path.AppendASCII(".pki").AppendASCII("nssdb");
+ if (!base::CreateDirectory(nssdb_path)) {
+ LOG(ERROR) << "Failed to create " << nssdb_path.value() << " directory.";
+ return ScopedPK11Slot();
+ }
+ return OpenSoftwareNSSDB(nssdb_path, db_name);
+ }
+
+ void EnableTPMTokenForNSS() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // If this gets set, then we'll use the TPM for certs with
+ // private keys, otherwise we'll fall back to the software
+ // implementation.
+ tpm_token_enabled_for_nss_ = true;
+ }
+
+ bool IsTPMTokenEnabledForNSS() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return tpm_token_enabled_for_nss_;
+ }
+
+ void InitializeTPMTokenAndSystemSlot(
+ int system_slot_id,
+ const base::Callback<void(bool)>& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // Should not be called while there is already an initialization in
+ // progress.
+ DCHECK(!initializing_tpm_token_);
+ // If EnableTPMTokenForNSS hasn't been called, return false.
+ if (!tpm_token_enabled_for_nss_) {
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, false));
+ return;
+ }
+
+ // If everything is already initialized, then return true.
+ // Note that only |tpm_slot_| is checked, since |chaps_module_| could be
+ // NULL in tests while |tpm_slot_| has been set to the test DB.
+ if (tpm_slot_) {
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, true));
+ return;
+ }
+
+ // Note that a reference is not taken to chaps_module_. This is safe since
+ // NSSInitSingleton is Leaky, so the reference it holds is never released.
+ scoped_ptr<TPMModuleAndSlot> tpm_args(new TPMModuleAndSlot(chaps_module_));
+ TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
+ if (base::WorkerPool::PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&NSSInitSingleton::InitializeTPMTokenOnWorkerThread,
+ system_slot_id,
+ tpm_args_ptr),
+ base::Bind(&NSSInitSingleton::OnInitializedTPMTokenAndSystemSlot,
+ base::Unretained(this), // NSSInitSingleton is leaky
+ callback,
+ base::Passed(&tpm_args)),
+ true /* task_is_slow */
+ )) {
+ initializing_tpm_token_ = true;
+ } else {
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, false));
+ }
+ }
+
+ static void InitializeTPMTokenOnWorkerThread(CK_SLOT_ID token_slot_id,
+ TPMModuleAndSlot* tpm_args) {
+ // This tries to load the Chaps module so NSS can talk to the hardware
+ // TPM.
+ if (!tpm_args->chaps_module) {
+ ScopedChapsLoadFixup chaps_loader;
+
+ DVLOG(3) << "Loading chaps...";
+ tpm_args->chaps_module = LoadModule(
+ kChapsModuleName,
+ kChapsPath,
+ // For more details on these parameters, see:
+ // https://developer.mozilla.org/en/PKCS11_Module_Specs
+ // slotFlags=[PublicCerts] -- Certificates and public keys can be
+ // read from this slot without requiring a call to C_Login.
+ // askpw=only -- Only authenticate to the token when necessary.
+ "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\"");
+ }
+ if (tpm_args->chaps_module) {
+ tpm_args->tpm_slot =
+ GetTPMSlotForIdOnWorkerThread(tpm_args->chaps_module, token_slot_id);
+ }
+ }
+
+ void OnInitializedTPMTokenAndSystemSlot(
+ const base::Callback<void(bool)>& callback,
+ scoped_ptr<TPMModuleAndSlot> tpm_args) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(2) << "Loaded chaps: " << !!tpm_args->chaps_module
+ << ", got tpm slot: " << !!tpm_args->tpm_slot;
+
+ chaps_module_ = tpm_args->chaps_module;
+ tpm_slot_ = tpm_args->tpm_slot.Pass();
+ if (!chaps_module_ && test_system_slot_) {
+ // chromeos_unittests try to test the TPM initialization process. If we
+ // have a test DB open, pretend that it is the TPM slot.
+ tpm_slot_.reset(PK11_ReferenceSlot(test_system_slot_.get()));
+ }
+ initializing_tpm_token_ = false;
+
+ if (tpm_slot_)
+ RunAndClearTPMReadyCallbackList();
+
+ callback.Run(!!tpm_slot_);
+ }
+
+ void RunAndClearTPMReadyCallbackList() {
+ TPMReadyCallbackList callback_list;
+ callback_list.swap(tpm_ready_callback_list_);
+ for (TPMReadyCallbackList::iterator i = callback_list.begin();
+ i != callback_list.end();
+ ++i) {
+ i->Run();
+ }
+ }
+
+ bool IsTPMTokenReady(const base::Closure& callback) {
+ if (!callback.is_null()) {
+ // Cannot DCHECK in the general case yet, but since the callback is
+ // a new addition to the API, DCHECK to make sure at least the new uses
+ // don't regress.
+ DCHECK(thread_checker_.CalledOnValidThread());
+ } else if (!thread_checker_.CalledOnValidThread()) {
+ // TODO(mattm): Change to DCHECK when callers have been fixed.
+ DVLOG(1) << "Called on wrong thread.\n"
+ << base::debug::StackTrace().ToString();
+ }
+
+ if (tpm_slot_)
+ return true;
+
+ if (!callback.is_null())
+ tpm_ready_callback_list_.push_back(callback);
+
+ return false;
+ }
+
+ // Note that CK_SLOT_ID is an unsigned long, but cryptohome gives us the slot
+ // id as an int. This should be safe since this is only used with chaps, which
+ // we also control.
+ static crypto::ScopedPK11Slot GetTPMSlotForIdOnWorkerThread(
+ SECMODModule* chaps_module,
+ CK_SLOT_ID slot_id) {
+ DCHECK(chaps_module);
+
+ DVLOG(3) << "Poking chaps module.";
+ SECStatus rv = SECMOD_UpdateSlotList(chaps_module);
+ if (rv != SECSuccess)
+ PLOG(ERROR) << "SECMOD_UpdateSlotList failed: " << PORT_GetError();
+
+ PK11SlotInfo* slot = SECMOD_LookupSlot(chaps_module->moduleID, slot_id);
+ if (!slot)
+ LOG(ERROR) << "TPM slot " << slot_id << " not found.";
+ return crypto::ScopedPK11Slot(slot);
+ }
+
+ bool InitializeNSSForChromeOSUser(const std::string& username_hash,
+ const base::FilePath& path) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()) {
+ // This user already exists in our mapping.
+ DVLOG(2) << username_hash << " already initialized.";
+ return false;
+ }
+
+ DVLOG(2) << "Opening NSS DB " << path.value();
+ std::string db_name = base::StringPrintf(
+ "%s %s", kUserNSSDatabaseName, username_hash.c_str());
+ ScopedPK11Slot public_slot(OpenPersistentNSSDBForPath(db_name, path));
+ chromeos_user_map_[username_hash] =
+ new ChromeOSUserData(public_slot.Pass());
+ return true;
+ }
+
+ bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
+
+ return !chromeos_user_map_[username_hash]
+ ->private_slot_initialization_started();
+ }
+
+ void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
+
+ chromeos_user_map_[username_hash]
+ ->set_private_slot_initialization_started();
+ }
+
+ void InitializeTPMForChromeOSUser(const std::string& username_hash,
+ CK_SLOT_ID slot_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
+ DCHECK(chromeos_user_map_[username_hash]->
+ private_slot_initialization_started());
+
+ if (!chaps_module_)
+ return;
+
+ // Note that a reference is not taken to chaps_module_. This is safe since
+ // NSSInitSingleton is Leaky, so the reference it holds is never released.
+ scoped_ptr<TPMModuleAndSlot> tpm_args(new TPMModuleAndSlot(chaps_module_));
+ TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
+ base::WorkerPool::PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&NSSInitSingleton::InitializeTPMTokenOnWorkerThread,
+ slot_id,
+ tpm_args_ptr),
+ base::Bind(&NSSInitSingleton::OnInitializedTPMForChromeOSUser,
+ base::Unretained(this), // NSSInitSingleton is leaky
+ username_hash,
+ base::Passed(&tpm_args)),
+ true /* task_is_slow */
+ );
+ }
+
+ void OnInitializedTPMForChromeOSUser(const std::string& username_hash,
+ scoped_ptr<TPMModuleAndSlot> tpm_args) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(2) << "Got tpm slot for " << username_hash << " "
+ << !!tpm_args->tpm_slot;
+ chromeos_user_map_[username_hash]->SetPrivateSlot(
+ tpm_args->tpm_slot.Pass());
+ }
+
+ void InitializePrivateSoftwareSlotForChromeOSUser(
+ const std::string& username_hash) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ VLOG(1) << "using software private slot for " << username_hash;
+ DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
+ DCHECK(chromeos_user_map_[username_hash]->
+ private_slot_initialization_started());
+
+ chromeos_user_map_[username_hash]->SetPrivateSlot(
+ chromeos_user_map_[username_hash]->GetPublicSlot());
+ }
+
+ ScopedPK11Slot GetPublicSlotForChromeOSUser(
+ const std::string& username_hash) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (username_hash.empty()) {
+ DVLOG(2) << "empty username_hash";
+ return ScopedPK11Slot();
+ }
+
+ if (chromeos_user_map_.find(username_hash) == chromeos_user_map_.end()) {
+ LOG(ERROR) << username_hash << " not initialized.";
+ return ScopedPK11Slot();
+ }
+ return chromeos_user_map_[username_hash]->GetPublicSlot();
+ }
+
+ ScopedPK11Slot GetPrivateSlotForChromeOSUser(
+ const std::string& username_hash,
+ const base::Callback<void(ScopedPK11Slot)>& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (username_hash.empty()) {
+ DVLOG(2) << "empty username_hash";
+ if (!callback.is_null()) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, base::Passed(ScopedPK11Slot())));
+ }
+ return ScopedPK11Slot();
+ }
+
+ DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
+
+ return chromeos_user_map_[username_hash]->GetPrivateSlot(callback);
+ }
+
+ void CloseChromeOSUserForTesting(const std::string& username_hash) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ChromeOSUserMap::iterator i = chromeos_user_map_.find(username_hash);
+ DCHECK(i != chromeos_user_map_.end());
+ delete i->second;
+ chromeos_user_map_.erase(i);
+ }
+
+ void SetSystemKeySlotForTesting(ScopedPK11Slot slot) {
+ // Ensure that a previous value of test_system_slot_ is not overwritten.
+ // Unsetting, i.e. setting a NULL, however is allowed.
+ DCHECK(!slot || !test_system_slot_);
+ test_system_slot_ = slot.Pass();
+ if (test_system_slot_) {
+ tpm_slot_.reset(PK11_ReferenceSlot(test_system_slot_.get()));
+ RunAndClearTPMReadyCallbackList();
+ } else {
+ tpm_slot_.reset();
+ }
+ }
+#endif // defined(OS_CHROMEOS)
+
+#if !defined(OS_CHROMEOS)
+ PK11SlotInfo* GetPersistentNSSKeySlot() {
+ // TODO(mattm): Change to DCHECK when callers have been fixed.
+ if (!thread_checker_.CalledOnValidThread()) {
+ DVLOG(1) << "Called on wrong thread.\n"
+ << base::debug::StackTrace().ToString();
+ }
+
+ return PK11_GetInternalKeySlot();
+ }
+#endif
+
+#if defined(OS_CHROMEOS)
+ void GetSystemNSSKeySlotCallback(
+ const base::Callback<void(ScopedPK11Slot)>& callback) {
+ callback.Run(ScopedPK11Slot(PK11_ReferenceSlot(tpm_slot_.get())));
+ }
+
+ ScopedPK11Slot GetSystemNSSKeySlot(
+ const base::Callback<void(ScopedPK11Slot)>& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // TODO(mattm): chromeos::TPMTokenloader always calls
+ // InitializeTPMTokenAndSystemSlot with slot 0. If the system slot is
+ // disabled, tpm_slot_ will be the first user's slot instead. Can that be
+ // detected and return NULL instead?
+
+ base::Closure wrapped_callback;
+ if (!callback.is_null()) {
+ wrapped_callback =
+ base::Bind(&NSSInitSingleton::GetSystemNSSKeySlotCallback,
+ base::Unretained(this) /* singleton is leaky */,
+ callback);
+ }
+ if (IsTPMTokenReady(wrapped_callback))
+ return ScopedPK11Slot(PK11_ReferenceSlot(tpm_slot_.get()));
+ return ScopedPK11Slot();
+ }
+#endif
+
+#if defined(USE_NSS_CERTS)
+ base::Lock* write_lock() {
+ return &write_lock_;
+ }
+#endif // defined(USE_NSS_CERTS)
+
+ // This method is used to force NSS to be initialized without a DB.
+ // Call this method before NSSInitSingleton() is constructed.
+ static void ForceNoDBInit() {
+ force_nodb_init_ = true;
+ }
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<NSSInitSingleton>;
+
+ NSSInitSingleton()
+ : tpm_token_enabled_for_nss_(false),
+ initializing_tpm_token_(false),
+ chaps_module_(NULL),
+ root_(NULL) {
+ // It's safe to construct on any thread, since LazyInstance will prevent any
+ // other threads from accessing until the constructor is done.
+ thread_checker_.DetachFromThread();
+
+ DisableAESNIIfNeeded();
+
+ EnsureNSPRInit();
+
+ // We *must* have NSS >= 3.14.3.
+ static_assert(
+ (NSS_VMAJOR == 3 && NSS_VMINOR == 14 && NSS_VPATCH >= 3) ||
+ (NSS_VMAJOR == 3 && NSS_VMINOR > 14) ||
+ (NSS_VMAJOR > 3),
+ "nss version check failed");
+ // Also check the run-time NSS version.
+ // NSS_VersionCheck is a >= check, not strict equality.
+ if (!NSS_VersionCheck("3.14.3")) {
+ LOG(FATAL) << "NSS_VersionCheck(\"3.14.3\") failed. NSS >= 3.14.3 is "
+ "required. Please upgrade to the latest NSS, and if you "
+ "still get this error, contact your distribution "
+ "maintainer.";
+ }
+
+ SECStatus status = SECFailure;
+ bool nodb_init = force_nodb_init_;
+
+#if !defined(USE_NSS_CERTS)
+ // Use the system certificate store, so initialize NSS without database.
+ nodb_init = true;
+#endif
+
+ if (nodb_init) {
+ status = NSS_NoDB_Init(NULL);
+ if (status != SECSuccess) {
+ CrashOnNSSInitFailure();
+ return;
+ }
+#if defined(OS_IOS)
+ root_ = InitDefaultRootCerts();
+#endif // defined(OS_IOS)
+ } else {
+#if defined(USE_NSS_CERTS)
+ base::FilePath database_dir = GetInitialConfigDirectory();
+ if (!database_dir.empty()) {
+ // This duplicates the work which should have been done in
+ // EarlySetupForNSSInit. However, this function is idempotent so
+ // there's no harm done.
+ UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
+
+ // Initialize with a persistent database (likely, ~/.pki/nssdb).
+ // Use "sql:" which can be shared by multiple processes safely.
+ std::string nss_config_dir =
+ base::StringPrintf("sql:%s", database_dir.value().c_str());
+#if defined(OS_CHROMEOS)
+ status = NSS_Init(nss_config_dir.c_str());
+#else
+ status = NSS_InitReadWrite(nss_config_dir.c_str());
+#endif
+ if (status != SECSuccess) {
+ LOG(ERROR) << "Error initializing NSS with a persistent "
+ "database (" << nss_config_dir
+ << "): " << GetNSSErrorMessage();
+ }
+ }
+ if (status != SECSuccess) {
+ VLOG(1) << "Initializing NSS without a persistent database.";
+ status = NSS_NoDB_Init(NULL);
+ if (status != SECSuccess) {
+ CrashOnNSSInitFailure();
+ return;
+ }
+ }
+
+ PK11_SetPasswordFunc(PKCS11PasswordFunc);
+
+ // If we haven't initialized the password for the NSS databases,
+ // initialize an empty-string password so that we don't need to
+ // log in.
+ PK11SlotInfo* slot = PK11_GetInternalKeySlot();
+ if (slot) {
+ // PK11_InitPin may write to the keyDB, but no other thread can use NSS
+ // yet, so we don't need to lock.
+ if (PK11_NeedUserInit(slot))
+ PK11_InitPin(slot, NULL, NULL);
+ PK11_FreeSlot(slot);
+ }
+
+ root_ = InitDefaultRootCerts();
+#endif // defined(USE_NSS_CERTS)
+ }
+
+ // Disable MD5 certificate signatures. (They are disabled by default in
+ // NSS 3.14.)
+ NSS_SetAlgorithmPolicy(SEC_OID_MD5, 0, NSS_USE_ALG_IN_CERT_SIGNATURE);
+ NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
+ 0, NSS_USE_ALG_IN_CERT_SIGNATURE);
+ }
+
+ // NOTE(willchan): We don't actually execute this code since we leak NSS to
+ // prevent non-joinable threads from using NSS after it's already been shut
+ // down.
+ ~NSSInitSingleton() {
+#if defined(OS_CHROMEOS)
+ STLDeleteValues(&chromeos_user_map_);
+#endif
+ tpm_slot_.reset();
+ if (root_) {
+ SECMOD_UnloadUserModule(root_);
+ SECMOD_DestroyModule(root_);
+ root_ = NULL;
+ }
+ if (chaps_module_) {
+ SECMOD_UnloadUserModule(chaps_module_);
+ SECMOD_DestroyModule(chaps_module_);
+ chaps_module_ = NULL;
+ }
+
+ SECStatus status = NSS_Shutdown();
+ if (status != SECSuccess) {
+ // We VLOG(1) because this failure is relatively harmless (leaking, but
+ // we're shutting down anyway).
+ VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609";
+ }
+ }
+
+#if defined(USE_NSS_CERTS) || defined(OS_IOS)
+ // Load nss's built-in root certs.
+ SECMODModule* InitDefaultRootCerts() {
+ SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL);
+ if (root)
+ return root;
+
+ // Aw, snap. Can't find/load root cert shared library.
+ // This will make it hard to talk to anybody via https.
+ // TODO(mattm): Re-add the NOTREACHED here when crbug.com/310972 is fixed.
+ return NULL;
+ }
+
+ // Load the given module for this NSS session.
+ static SECMODModule* LoadModule(const char* name,
+ const char* library_path,
+ const char* params) {
+ std::string modparams = base::StringPrintf(
+ "name=\"%s\" library=\"%s\" %s",
+ name, library_path, params ? params : "");
+
+ // Shouldn't need to const_cast here, but SECMOD doesn't properly
+ // declare input string arguments as const. Bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed
+ // on NSS codebase to address this.
+ SECMODModule* module = SECMOD_LoadUserModule(
+ const_cast<char*>(modparams.c_str()), NULL, PR_FALSE);
+ if (!module) {
+ LOG(ERROR) << "Error loading " << name << " module into NSS: "
+ << GetNSSErrorMessage();
+ return NULL;
+ }
+ if (!module->loaded) {
+ LOG(ERROR) << "After loading " << name << ", loaded==false: "
+ << GetNSSErrorMessage();
+ SECMOD_DestroyModule(module);
+ return NULL;
+ }
+ return module;
+ }
+#endif
+
+ static void DisableAESNIIfNeeded() {
+ if (NSS_VersionCheck("3.15") && !NSS_VersionCheck("3.15.4")) {
+ // Some versions of NSS have a bug that causes AVX instructions to be
+ // used without testing whether XSAVE is enabled by the operating system.
+ // In order to work around this, we disable AES-NI in NSS when we find
+ // that |has_avx()| is false (which includes the XSAVE test). See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=940794
+ base::CPU cpu;
+
+ if (cpu.has_avx_hardware() && !cpu.has_avx()) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ env->SetVar("NSS_DISABLE_HW_AES", "1");
+ }
+ }
+ }
+
+ // If this is set to true NSS is forced to be initialized without a DB.
+ static bool force_nodb_init_;
+
+ bool tpm_token_enabled_for_nss_;
+ bool initializing_tpm_token_;
+ typedef std::vector<base::Closure> TPMReadyCallbackList;
+ TPMReadyCallbackList tpm_ready_callback_list_;
+ SECMODModule* chaps_module_;
+ crypto::ScopedPK11Slot tpm_slot_;
+ SECMODModule* root_;
+#if defined(OS_CHROMEOS)
+ typedef std::map<std::string, ChromeOSUserData*> ChromeOSUserMap;
+ ChromeOSUserMap chromeos_user_map_;
+ ScopedPK11Slot test_system_slot_;
+#endif
+#if defined(USE_NSS_CERTS)
+ // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011
+ // is fixed, we will no longer need the lock.
+ base::Lock write_lock_;
+#endif // defined(USE_NSS_CERTS)
+
+ base::ThreadChecker thread_checker_;
+};
+
+// static
+bool NSSInitSingleton::force_nodb_init_ = false;
+
+base::LazyInstance<NSSInitSingleton>::Leaky
+ g_nss_singleton = LAZY_INSTANCE_INITIALIZER;
+} // namespace
+
+#if defined(USE_NSS_CERTS)
+ScopedPK11Slot OpenSoftwareNSSDB(const base::FilePath& path,
+ const std::string& description) {
+ const std::string modspec =
+ base::StringPrintf("configDir='sql:%s' tokenDescription='%s'",
+ path.value().c_str(),
+ description.c_str());
+ PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
+ if (db_slot) {
+ if (PK11_NeedUserInit(db_slot))
+ PK11_InitPin(db_slot, NULL, NULL);
+ } else {
+ LOG(ERROR) << "Error opening persistent database (" << modspec
+ << "): " << GetNSSErrorMessage();
+ }
+ return ScopedPK11Slot(db_slot);
+}
+
+void EarlySetupForNSSInit() {
+ base::FilePath database_dir = GetInitialConfigDirectory();
+ if (!database_dir.empty())
+ UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
+}
+#endif
+
+void EnsureNSPRInit() {
+ g_nspr_singleton.Get();
+}
+
+void InitNSSSafely() {
+ // We might fork, but we haven't loaded any security modules.
+ DisableNSSForkCheck();
+ // If we're sandboxed, we shouldn't be able to open user security modules,
+ // but it's more correct to tell NSS to not even try.
+ // Loading user security modules would have security implications.
+ ForceNSSNoDBInit();
+ // Initialize NSS.
+ EnsureNSSInit();
+}
+
+void EnsureNSSInit() {
+ // Initializing SSL causes us to do blocking IO.
+ // Temporarily allow it until we fix
+ // http://code.google.com/p/chromium/issues/detail?id=59847
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ g_nss_singleton.Get();
+}
+
+void ForceNSSNoDBInit() {
+ NSSInitSingleton::ForceNoDBInit();
+}
+
+void DisableNSSForkCheck() {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ env->SetVar("NSS_STRICT_NOFORK", "DISABLED");
+}
+
+void LoadNSSLibraries() {
+ // Some NSS libraries are linked dynamically so load them here.
+#if defined(USE_NSS_CERTS)
+ // Try to search for multiple directories to load the libraries.
+ std::vector<base::FilePath> paths;
+
+ // Use relative path to Search PATH for the library files.
+ paths.push_back(base::FilePath());
+
+ // For Debian derivatives NSS libraries are located here.
+ paths.push_back(base::FilePath("/usr/lib/nss"));
+
+ // Ubuntu 11.10 (Oneiric) and Debian Wheezy place the libraries here.
+#if defined(ARCH_CPU_X86_64)
+ paths.push_back(base::FilePath("/usr/lib/x86_64-linux-gnu/nss"));
+#elif defined(ARCH_CPU_X86)
+ paths.push_back(base::FilePath("/usr/lib/i386-linux-gnu/nss"));
+#elif defined(ARCH_CPU_ARMEL)
+#if defined(__ARM_PCS_VFP)
+ paths.push_back(base::FilePath("/usr/lib/arm-linux-gnueabihf/nss"));
+#else
+ paths.push_back(base::FilePath("/usr/lib/arm-linux-gnueabi/nss"));
+#endif // defined(__ARM_PCS_VFP)
+#elif defined(ARCH_CPU_MIPSEL)
+ paths.push_back(base::FilePath("/usr/lib/mipsel-linux-gnu/nss"));
+#endif // defined(ARCH_CPU_X86_64)
+
+ // A list of library files to load.
+ std::vector<std::string> libs;
+ libs.push_back("libsoftokn3.so");
+ libs.push_back("libfreebl3.so");
+
+ // For each combination of library file and path, check for existence and
+ // then load.
+ size_t loaded = 0;
+ for (size_t i = 0; i < libs.size(); ++i) {
+ for (size_t j = 0; j < paths.size(); ++j) {
+ base::FilePath path = paths[j].Append(libs[i]);
+ base::NativeLibrary lib = base::LoadNativeLibrary(path, NULL);
+ if (lib) {
+ ++loaded;
+ break;
+ }
+ }
+ }
+
+ if (loaded == libs.size()) {
+ VLOG(3) << "NSS libraries loaded.";
+ } else {
+ LOG(ERROR) << "Failed to load NSS libraries.";
+ }
+#endif // defined(USE_NSS_CERTS)
+}
+
+bool CheckNSSVersion(const char* version) {
+ return !!NSS_VersionCheck(version);
+}
+
+#if defined(USE_NSS_CERTS)
+base::Lock* GetNSSWriteLock() {
+ return g_nss_singleton.Get().write_lock();
+}
+
+AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) {
+ // May be NULL if the lock is not needed in our version of NSS.
+ if (lock_)
+ lock_->Acquire();
+}
+
+AutoNSSWriteLock::~AutoNSSWriteLock() {
+ if (lock_) {
+ lock_->AssertAcquired();
+ lock_->Release();
+ }
+}
+
+AutoSECMODListReadLock::AutoSECMODListReadLock()
+ : lock_(SECMOD_GetDefaultModuleListLock()) {
+ SECMOD_GetReadLock(lock_);
+ }
+
+AutoSECMODListReadLock::~AutoSECMODListReadLock() {
+ SECMOD_ReleaseReadLock(lock_);
+}
+#endif // defined(USE_NSS_CERTS)
+
+#if defined(OS_CHROMEOS)
+ScopedPK11Slot GetSystemNSSKeySlot(
+ const base::Callback<void(ScopedPK11Slot)>& callback) {
+ return g_nss_singleton.Get().GetSystemNSSKeySlot(callback);
+}
+
+void SetSystemKeySlotForTesting(ScopedPK11Slot slot) {
+ g_nss_singleton.Get().SetSystemKeySlotForTesting(slot.Pass());
+}
+
+void EnableTPMTokenForNSS() {
+ g_nss_singleton.Get().EnableTPMTokenForNSS();
+}
+
+bool IsTPMTokenEnabledForNSS() {
+ return g_nss_singleton.Get().IsTPMTokenEnabledForNSS();
+}
+
+bool IsTPMTokenReady(const base::Closure& callback) {
+ return g_nss_singleton.Get().IsTPMTokenReady(callback);
+}
+
+void InitializeTPMTokenAndSystemSlot(
+ int token_slot_id,
+ const base::Callback<void(bool)>& callback) {
+ g_nss_singleton.Get().InitializeTPMTokenAndSystemSlot(token_slot_id,
+ callback);
+}
+
+bool InitializeNSSForChromeOSUser(const std::string& username_hash,
+ const base::FilePath& path) {
+ return g_nss_singleton.Get().InitializeNSSForChromeOSUser(username_hash,
+ path);
+}
+
+bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
+ return g_nss_singleton.Get().ShouldInitializeTPMForChromeOSUser(
+ username_hash);
+}
+
+void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
+ g_nss_singleton.Get().WillInitializeTPMForChromeOSUser(username_hash);
+}
+
+void InitializeTPMForChromeOSUser(
+ const std::string& username_hash,
+ CK_SLOT_ID slot_id) {
+ g_nss_singleton.Get().InitializeTPMForChromeOSUser(username_hash, slot_id);
+}
+
+void InitializePrivateSoftwareSlotForChromeOSUser(
+ const std::string& username_hash) {
+ g_nss_singleton.Get().InitializePrivateSoftwareSlotForChromeOSUser(
+ username_hash);
+}
+
+ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) {
+ return g_nss_singleton.Get().GetPublicSlotForChromeOSUser(username_hash);
+}
+
+ScopedPK11Slot GetPrivateSlotForChromeOSUser(
+ const std::string& username_hash,
+ const base::Callback<void(ScopedPK11Slot)>& callback) {
+ return g_nss_singleton.Get().GetPrivateSlotForChromeOSUser(username_hash,
+ callback);
+}
+
+void CloseChromeOSUserForTesting(const std::string& username_hash) {
+ g_nss_singleton.Get().CloseChromeOSUserForTesting(username_hash);
+}
+#endif // defined(OS_CHROMEOS)
+
+base::Time PRTimeToBaseTime(PRTime prtime) {
+ return base::Time::FromInternalValue(
+ prtime + base::Time::UnixEpoch().ToInternalValue());
+}
+
+PRTime BaseTimeToPRTime(base::Time time) {
+ return time.ToInternalValue() - base::Time::UnixEpoch().ToInternalValue();
+}
+
+#if !defined(OS_CHROMEOS)
+PK11SlotInfo* GetPersistentNSSKeySlot() {
+ return g_nss_singleton.Get().GetPersistentNSSKeySlot();
+}
+#endif
+
+} // namespace crypto
diff --git a/crypto/nss_util.h b/crypto/nss_util.h
new file mode 100644
index 0000000000..1ca0de3e77
--- /dev/null
+++ b/crypto/nss_util.h
@@ -0,0 +1,155 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_NSS_UTIL_H_
+#define CRYPTO_NSS_UTIL_H_
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "crypto/crypto_export.h"
+
+namespace base {
+class FilePath;
+class Lock;
+class Time;
+} // namespace base
+
+// This file specifically doesn't depend on any NSS or NSPR headers because it
+// is included by various (non-crypto) parts of chrome to call the
+// initialization functions.
+namespace crypto {
+
+#if defined(USE_NSS_CERTS)
+// EarlySetupForNSSInit performs lightweight setup which must occur before the
+// process goes multithreaded. This does not initialise NSS. For test, see
+// EnsureNSSInit.
+CRYPTO_EXPORT void EarlySetupForNSSInit();
+#endif
+
+// Initialize NRPR if it isn't already initialized. This function is
+// thread-safe, and NSPR will only ever be initialized once.
+CRYPTO_EXPORT void EnsureNSPRInit();
+
+// Initialize NSS safely for strict sandboxing. This function tells NSS to not
+// load user security modules, and makes sure NSS will have proper entropy in a
+// restricted, sandboxed environment.
+//
+// As a defense in depth measure, this function should be called in a sandboxed
+// environment. That way, in the event of a bug, NSS will still not be able to
+// load security modules that could expose private data and keys.
+//
+// Make sure to get an LGTM from the Chrome Security Team if you use this.
+CRYPTO_EXPORT void InitNSSSafely();
+
+// Initialize NSS if it isn't already initialized. This must be called before
+// any other NSS functions. This function is thread-safe, and NSS will only
+// ever be initialized once.
+CRYPTO_EXPORT void EnsureNSSInit();
+
+// Call this before calling EnsureNSSInit() will force NSS to initialize
+// without a persistent DB. This is used for the special case where access of
+// persistent DB is prohibited.
+//
+// TODO(hclam): Isolate loading default root certs.
+//
+// NSS will be initialized without loading any user security modules, including
+// the built-in root certificates module. User security modules need to be
+// loaded manually after NSS initialization.
+//
+// If EnsureNSSInit() is called before then this function has no effect.
+//
+// Calling this method only has effect on Linux.
+//
+// WARNING: Use this with caution.
+CRYPTO_EXPORT void ForceNSSNoDBInit();
+
+// This method is used to disable checks in NSS when used in a forked process.
+// NSS checks whether it is running a forked process to avoid problems when
+// using user security modules in a forked process. However if we are sure
+// there are no modules loaded before the process is forked then there is no
+// harm disabling the check.
+//
+// This method must be called before EnsureNSSInit() to take effect.
+//
+// WARNING: Use this with caution.
+CRYPTO_EXPORT void DisableNSSForkCheck();
+
+// Load NSS library files. This function has no effect on Mac and Windows.
+// This loads the necessary NSS library files so that NSS can be initialized
+// after loading additional library files is disallowed, for example when the
+// sandbox is active.
+//
+// Note that this does not load libnssckbi.so which contains the root
+// certificates.
+CRYPTO_EXPORT void LoadNSSLibraries();
+
+// Check if the current NSS version is greater than or equals to |version|.
+// A sample version string is "3.12.3".
+bool CheckNSSVersion(const char* version);
+
+#if defined(OS_CHROMEOS)
+// Indicates that NSS should use the Chaps library so that we
+// can access the TPM through NSS. InitializeTPMTokenAndSystemSlot and
+// InitializeTPMForChromeOSUser must still be called to load the slots.
+CRYPTO_EXPORT void EnableTPMTokenForNSS();
+
+// Returns true if EnableTPMTokenForNSS has been called.
+CRYPTO_EXPORT bool IsTPMTokenEnabledForNSS();
+
+// Returns true if the TPM is owned and PKCS#11 initialized with the
+// user and security officer PINs, and has been enabled in NSS by
+// calling EnableTPMForNSS, and Chaps has been successfully
+// loaded into NSS.
+// If |callback| is non-null and the function returns false, the |callback| will
+// be run once the TPM is ready. |callback| will never be run if the function
+// returns true.
+CRYPTO_EXPORT bool IsTPMTokenReady(const base::Closure& callback)
+ WARN_UNUSED_RESULT;
+
+// Initialize the TPM token and system slot. The |callback| will run on the same
+// thread with true if the token and slot were successfully loaded or were
+// already initialized. |callback| will be passed false if loading failed. Once
+// called, InitializeTPMTokenAndSystemSlot must not be called again until the
+// |callback| has been run.
+CRYPTO_EXPORT void InitializeTPMTokenAndSystemSlot(
+ int system_slot_id,
+ const base::Callback<void(bool)>& callback);
+#endif
+
+// Convert a NSS PRTime value into a base::Time object.
+// We use a int64 instead of PRTime here to avoid depending on NSPR headers.
+CRYPTO_EXPORT base::Time PRTimeToBaseTime(int64 prtime);
+
+// Convert a base::Time object into a PRTime value.
+// We use a int64 instead of PRTime here to avoid depending on NSPR headers.
+CRYPTO_EXPORT int64 BaseTimeToPRTime(base::Time time);
+
+#if defined(USE_NSS_CERTS)
+// NSS has a bug which can cause a deadlock or stall in some cases when writing
+// to the certDB and keyDB. It also has a bug which causes concurrent key pair
+// generations to scribble over each other. To work around this, we synchronize
+// writes to the NSS databases with a global lock. The lock is hidden beneath a
+// function for easy disabling when the bug is fixed. Callers should allow for
+// it to return NULL in the future.
+//
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=564011
+base::Lock* GetNSSWriteLock();
+
+// A helper class that acquires the NSS write Lock while the AutoNSSWriteLock
+// is in scope.
+class CRYPTO_EXPORT AutoNSSWriteLock {
+ public:
+ AutoNSSWriteLock();
+ ~AutoNSSWriteLock();
+ private:
+ base::Lock *lock_;
+ DISALLOW_COPY_AND_ASSIGN(AutoNSSWriteLock);
+};
+#endif // defined(USE_NSS_CERTS)
+
+} // namespace crypto
+
+#endif // CRYPTO_NSS_UTIL_H_
diff --git a/crypto/nss_util_unittest.cc b/crypto/nss_util_unittest.cc
new file mode 100644
index 0000000000..28591916d3
--- /dev/null
+++ b/crypto/nss_util_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/nss_util.h"
+
+#include <prtime.h>
+
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crypto {
+
+TEST(NSSUtilTest, PRTimeConversion) {
+ EXPECT_EQ(base::Time::UnixEpoch(), PRTimeToBaseTime(0));
+ EXPECT_EQ(0, BaseTimeToPRTime(base::Time::UnixEpoch()));
+
+ PRExplodedTime prxtime;
+ prxtime.tm_params.tp_gmt_offset = 0;
+ prxtime.tm_params.tp_dst_offset = 0;
+ base::Time::Exploded exploded;
+ exploded.year = prxtime.tm_year = 2011;
+ exploded.month = 12;
+ prxtime.tm_month = 11;
+ // PRExplodedTime::tm_wday is a smaller type than Exploded::day_of_week, so
+ // assigning the two in this order instead of the reverse avoids potential
+ // warnings about type downcasting.
+ exploded.day_of_week = prxtime.tm_wday = 0; // Should be unused.
+ exploded.day_of_month = prxtime.tm_mday = 10;
+ exploded.hour = prxtime.tm_hour = 2;
+ exploded.minute = prxtime.tm_min = 52;
+ exploded.second = prxtime.tm_sec = 19;
+ exploded.millisecond = 342;
+ prxtime.tm_usec = 342000;
+
+ PRTime pr_time = PR_ImplodeTime(&prxtime);
+ base::Time base_time = base::Time::FromUTCExploded(exploded);
+
+ EXPECT_EQ(base_time, PRTimeToBaseTime(pr_time));
+ EXPECT_EQ(pr_time, BaseTimeToPRTime(base_time));
+}
+
+} // namespace crypto
diff --git a/crypto/openssl_bio_string.cc b/crypto/openssl_bio_string.cc
new file mode 100644
index 0000000000..48805001ef
--- /dev/null
+++ b/crypto/openssl_bio_string.cc
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/openssl_bio_string.h"
+
+#include <openssl/bio.h>
+#include <string.h>
+
+namespace crypto {
+
+namespace {
+
+int bio_string_write(BIO* bio, const char* data, int len) {
+ reinterpret_cast<std::string*>(bio->ptr)->append(data, len);
+ return len;
+}
+
+int bio_string_puts(BIO* bio, const char* data) {
+ // Note: unlike puts(), BIO_puts does not add a newline.
+ return bio_string_write(bio, data, strlen(data));
+}
+
+long bio_string_ctrl(BIO* bio, int cmd, long num, void* ptr) {
+ std::string* str = reinterpret_cast<std::string*>(bio->ptr);
+ switch (cmd) {
+ case BIO_CTRL_RESET:
+ str->clear();
+ return 1;
+ case BIO_C_FILE_SEEK:
+ return -1;
+ case BIO_C_FILE_TELL:
+ return str->size();
+ case BIO_CTRL_FLUSH:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int bio_string_new(BIO* bio) {
+ bio->ptr = NULL;
+ bio->init = 0;
+ return 1;
+}
+
+int bio_string_free(BIO* bio) {
+ // The string is owned by the caller, so there's nothing to do here.
+ return bio != NULL;
+}
+
+BIO_METHOD bio_string_methods = {
+ // TODO(mattm): Should add some type number too? (bio.h uses 1-24)
+ BIO_TYPE_SOURCE_SINK,
+ "bio_string",
+ bio_string_write,
+ NULL, /* read */
+ bio_string_puts,
+ NULL, /* gets */
+ bio_string_ctrl,
+ bio_string_new,
+ bio_string_free,
+ NULL, /* callback_ctrl */
+};
+
+} // namespace
+
+BIO* BIO_new_string(std::string* out) {
+ BIO* bio = BIO_new(&bio_string_methods);
+ if (!bio)
+ return bio;
+ bio->ptr = out;
+ bio->init = 1;
+ return bio;
+}
+
+} // namespace crypto
diff --git a/crypto/openssl_bio_string.h b/crypto/openssl_bio_string.h
new file mode 100644
index 0000000000..ca46c12de8
--- /dev/null
+++ b/crypto/openssl_bio_string.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_OPENSSL_BIO_STRING_H_
+#define CRYPTO_OPENSSL_BIO_STRING_H_
+
+#include <string>
+
+#include "crypto/crypto_export.h"
+
+// From <openssl/bio.h>
+typedef struct bio_st BIO;
+
+namespace crypto {
+
+// Creates a new BIO that can be used with OpenSSL's various output functions,
+// and which will write all output directly into |out|. This is primarily
+// intended as a utility to reduce the amount of copying and separate
+// allocations when performing extensive string modifications or streaming
+// within OpenSSL.
+//
+// Note: |out| must remain valid for the duration of the BIO.
+CRYPTO_EXPORT BIO* BIO_new_string(std::string* out);
+
+} // namespace crypto
+
+#endif // CRYPTO_OPENSSL_BIO_STRING_H_
+
diff --git a/crypto/openssl_bio_string_unittest.cc b/crypto/openssl_bio_string_unittest.cc
new file mode 100644
index 0000000000..9dfa0e70f7
--- /dev/null
+++ b/crypto/openssl_bio_string_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/openssl_bio_string.h"
+
+#include <openssl/bio.h>
+
+#include "crypto/scoped_openssl_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crypto {
+
+TEST(OpenSSLBIOString, TestWrite) {
+ std::string s;
+ const std::string expected1("a one\nb 2\n");
+ const std::string expected2("c d e f");
+ const std::string expected3("g h i");
+ {
+ ScopedBIO bio(BIO_new_string(&s));
+ ASSERT_TRUE(bio.get());
+
+ EXPECT_EQ(static_cast<int>(expected1.size()),
+ BIO_printf(bio.get(), "a %s\nb %i\n", "one", 2));
+ EXPECT_EQ(expected1, s);
+
+ EXPECT_EQ(1, BIO_flush(bio.get()));
+ EXPECT_EQ(expected1, s);
+
+ EXPECT_EQ(static_cast<int>(expected2.size()),
+ BIO_write(bio.get(), expected2.data(), expected2.size()));
+ EXPECT_EQ(expected1 + expected2, s);
+
+ EXPECT_EQ(static_cast<int>(expected3.size()),
+ BIO_puts(bio.get(), expected3.c_str()));
+ EXPECT_EQ(expected1 + expected2 + expected3, s);
+ }
+ EXPECT_EQ(expected1 + expected2 + expected3, s);
+}
+
+TEST(OpenSSLBIOString, TestReset) {
+ std::string s;
+ const std::string expected1("a b c\n");
+ const std::string expected2("d e f g\n");
+ {
+ ScopedBIO bio(BIO_new_string(&s));
+ ASSERT_TRUE(bio.get());
+
+ EXPECT_EQ(static_cast<int>(expected1.size()),
+ BIO_write(bio.get(), expected1.data(), expected1.size()));
+ EXPECT_EQ(expected1, s);
+
+ EXPECT_EQ(1, BIO_reset(bio.get()));
+ EXPECT_EQ(std::string(), s);
+
+ EXPECT_EQ(static_cast<int>(expected2.size()),
+ BIO_write(bio.get(), expected2.data(), expected2.size()));
+ EXPECT_EQ(expected2, s);
+ }
+ EXPECT_EQ(expected2, s);
+}
+
+} // namespace crypto
diff --git a/crypto/openssl_util.cc b/crypto/openssl_util.cc
new file mode 100644
index 0000000000..964d83b843
--- /dev/null
+++ b/crypto/openssl_util.cc
@@ -0,0 +1,132 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/openssl_util.h"
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <openssl/cpu.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "build/build_config.h"
+
+#if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL)
+#include <cpu-features.h>
+#include "base/cpu.h"
+#endif
+
+namespace crypto {
+
+namespace {
+
+void CurrentThreadId(CRYPTO_THREADID* id) {
+ CRYPTO_THREADID_set_numeric(
+ id, static_cast<unsigned long>(base::PlatformThread::CurrentId()));
+}
+
+// Singleton for initializing and cleaning up the OpenSSL library.
+class OpenSSLInitSingleton {
+ public:
+ static OpenSSLInitSingleton* GetInstance() {
+ // We allow the SSL environment to leak for multiple reasons:
+ // - it is used from a non-joinable worker thread that is not stopped on
+ // shutdown, hence may still be using OpenSSL library after the AtExit
+ // runner has completed.
+ // - There are other OpenSSL related singletons (e.g. the client socket
+ // context) who's cleanup depends on the global environment here, but
+ // we can't control the order the AtExit handlers will run in so
+ // allowing the global environment to leak at least ensures it is
+ // available for those other singletons to reliably cleanup.
+ return Singleton<OpenSSLInitSingleton,
+ LeakySingletonTraits<OpenSSLInitSingleton> >::get();
+ }
+ private:
+ friend struct DefaultSingletonTraits<OpenSSLInitSingleton>;
+ OpenSSLInitSingleton() {
+#if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL)
+ const bool has_neon =
+ (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
+ // CRYPTO_set_NEON_capable is called before |SSL_library_init| because this
+ // stops BoringSSL from probing for NEON support via SIGILL in the case
+ // that getauxval isn't present.
+ CRYPTO_set_NEON_capable(has_neon);
+ // See https://code.google.com/p/chromium/issues/detail?id=341598
+ base::CPU cpu;
+ CRYPTO_set_NEON_functional(!cpu.has_broken_neon());
+#endif
+
+ SSL_load_error_strings();
+ SSL_library_init();
+ int num_locks = CRYPTO_num_locks();
+ locks_.reserve(num_locks);
+ for (int i = 0; i < num_locks; ++i)
+ locks_.push_back(new base::Lock());
+ CRYPTO_set_locking_callback(LockingCallback);
+ CRYPTO_THREADID_set_callback(CurrentThreadId);
+ }
+
+ ~OpenSSLInitSingleton() {
+ CRYPTO_set_locking_callback(NULL);
+ EVP_cleanup();
+ ERR_free_strings();
+ }
+
+ static void LockingCallback(int mode, int n, const char* file, int line) {
+ OpenSSLInitSingleton::GetInstance()->OnLockingCallback(mode, n, file, line);
+ }
+
+ void OnLockingCallback(int mode, int n, const char* file, int line) {
+ CHECK_LT(static_cast<size_t>(n), locks_.size());
+ if (mode & CRYPTO_LOCK)
+ locks_[n]->Acquire();
+ else
+ locks_[n]->Release();
+ }
+
+ // These locks are used and managed by OpenSSL via LockingCallback().
+ ScopedVector<base::Lock> locks_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpenSSLInitSingleton);
+};
+
+// Callback routine for OpenSSL to print error messages. |str| is a
+// NULL-terminated string of length |len| containing diagnostic information
+// such as the library, function and reason for the error, the file and line
+// where the error originated, plus potentially any context-specific
+// information about the error. |context| contains a pointer to user-supplied
+// data, which is currently unused.
+// If this callback returns a value <= 0, OpenSSL will stop processing the
+// error queue and return, otherwise it will continue calling this function
+// until all errors have been removed from the queue.
+int OpenSSLErrorCallback(const char* str, size_t len, void* context) {
+ DVLOG(1) << "\t" << base::StringPiece(str, len);
+ return 1;
+}
+
+} // namespace
+
+void EnsureOpenSSLInit() {
+ (void)OpenSSLInitSingleton::GetInstance();
+}
+
+void ClearOpenSSLERRStack(const tracked_objects::Location& location) {
+ if (logging::DEBUG_MODE && VLOG_IS_ON(1)) {
+ uint32_t error_num = ERR_peek_error();
+ if (error_num == 0)
+ return;
+
+ std::string message;
+ location.Write(true, true, &message);
+ DVLOG(1) << "OpenSSL ERR_get_error stack from " << message;
+ ERR_print_errors_cb(&OpenSSLErrorCallback, NULL);
+ } else {
+ ERR_clear_error();
+ }
+}
+
+} // namespace crypto
diff --git a/crypto/openssl_util.h b/crypto/openssl_util.h
new file mode 100644
index 0000000000..2743883dc1
--- /dev/null
+++ b/crypto/openssl_util.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_OPENSSL_UTIL_H_
+#define CRYPTO_OPENSSL_UTIL_H_
+
+#include "base/basictypes.h"
+#include "base/location.h"
+#include "crypto/crypto_export.h"
+
+namespace crypto {
+
+// Provides a buffer of at least MIN_SIZE bytes, for use when calling OpenSSL's
+// SHA256, HMAC, etc functions, adapting the buffer sizing rules to meet those
+// of the our base wrapper APIs.
+// This allows the library to write directly to the caller's buffer if it is of
+// sufficient size, but if not it will write to temporary |min_sized_buffer_|
+// of required size and then its content is automatically copied out on
+// destruction, with truncation as appropriate.
+template<int MIN_SIZE>
+class ScopedOpenSSLSafeSizeBuffer {
+ public:
+ ScopedOpenSSLSafeSizeBuffer(unsigned char* output, size_t output_len)
+ : output_(output),
+ output_len_(output_len) {
+ }
+
+ ~ScopedOpenSSLSafeSizeBuffer() {
+ if (output_len_ < MIN_SIZE) {
+ // Copy the temporary buffer out, truncating as needed.
+ memcpy(output_, min_sized_buffer_, output_len_);
+ }
+ // else... any writing already happened directly into |output_|.
+ }
+
+ unsigned char* safe_buffer() {
+ return output_len_ < MIN_SIZE ? min_sized_buffer_ : output_;
+ }
+
+ private:
+ // Pointer to the caller's data area and its associated size, where data
+ // written via safe_buffer() will [eventually] end up.
+ unsigned char* output_;
+ size_t output_len_;
+
+ // Temporary buffer writen into in the case where the caller's
+ // buffer is not of sufficient size.
+ unsigned char min_sized_buffer_[MIN_SIZE];
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedOpenSSLSafeSizeBuffer);
+};
+
+// Initialize OpenSSL if it isn't already initialized. This must be called
+// before any other OpenSSL functions though it is safe and cheap to call this
+// multiple times.
+// This function is thread-safe, and OpenSSL will only ever be initialized once.
+// OpenSSL will be properly shut down on program exit.
+void CRYPTO_EXPORT EnsureOpenSSLInit();
+
+// Drains the OpenSSL ERR_get_error stack. On a debug build the error codes
+// are send to VLOG(1), on a release build they are disregarded. In most
+// cases you should pass FROM_HERE as the |location|.
+void CRYPTO_EXPORT ClearOpenSSLERRStack(
+ const tracked_objects::Location& location);
+
+// Place an instance of this class on the call stack to automatically clear
+// the OpenSSL error stack on function exit.
+class OpenSSLErrStackTracer {
+ public:
+ // Pass FROM_HERE as |location|, to help track the source of OpenSSL error
+ // messages. Note any diagnostic emitted will be tagged with the location of
+ // the constructor call as it's not possible to trace a destructor's callsite.
+ explicit OpenSSLErrStackTracer(const tracked_objects::Location& location)
+ : location_(location) {
+ EnsureOpenSSLInit();
+ }
+ ~OpenSSLErrStackTracer() {
+ ClearOpenSSLERRStack(location_);
+ }
+
+ private:
+ const tracked_objects::Location location_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(OpenSSLErrStackTracer);
+};
+
+} // namespace crypto
+
+#endif // CRYPTO_OPENSSL_UTIL_H_
diff --git a/crypto/p224.cc b/crypto/p224.cc
new file mode 100644
index 0000000000..11946a9413
--- /dev/null
+++ b/crypto/p224.cc
@@ -0,0 +1,758 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is an implementation of the P224 elliptic curve group. It's written to
+// be short and simple rather than fast, although it's still constant-time.
+//
+// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
+
+#include "crypto/p224.h"
+
+#include <string.h>
+
+#include "base/sys_byteorder.h"
+
+namespace {
+
+using base::HostToNet32;
+using base::NetToHost32;
+
+// Field element functions.
+//
+// The field that we're dealing with is ℤ/pℤ where p = 2**224 - 2**96 + 1.
+//
+// Field elements are represented by a FieldElement, which is a typedef to an
+// array of 8 uint32's. The value of a FieldElement, a, is:
+// a[0] + 2**28·a[1] + 2**56·a[1] + ... + 2**196·a[7]
+//
+// Using 28-bit limbs means that there's only 4 bits of headroom, which is less
+// than we would really like. But it has the useful feature that we hit 2**224
+// exactly, making the reflections during a reduce much nicer.
+
+using crypto::p224::FieldElement;
+
+// kP is the P224 prime.
+const FieldElement kP = {
+ 1, 0, 0, 268431360,
+ 268435455, 268435455, 268435455, 268435455,
+};
+
+void Contract(FieldElement* inout);
+
+// IsZero returns 0xffffffff if a == 0 mod p and 0 otherwise.
+uint32 IsZero(const FieldElement& a) {
+ FieldElement minimal;
+ memcpy(&minimal, &a, sizeof(minimal));
+ Contract(&minimal);
+
+ uint32 is_zero = 0, is_p = 0;
+ for (unsigned i = 0; i < 8; i++) {
+ is_zero |= minimal[i];
+ is_p |= minimal[i] - kP[i];
+ }
+
+ // If either is_zero or is_p is 0, then we should return 1.
+ is_zero |= is_zero >> 16;
+ is_zero |= is_zero >> 8;
+ is_zero |= is_zero >> 4;
+ is_zero |= is_zero >> 2;
+ is_zero |= is_zero >> 1;
+
+ is_p |= is_p >> 16;
+ is_p |= is_p >> 8;
+ is_p |= is_p >> 4;
+ is_p |= is_p >> 2;
+ is_p |= is_p >> 1;
+
+ // For is_zero and is_p, the LSB is 0 iff all the bits are zero.
+ is_zero &= is_p & 1;
+ is_zero = (~is_zero) << 31;
+ is_zero = static_cast<int32>(is_zero) >> 31;
+ return is_zero;
+}
+
+// Add computes *out = a+b
+//
+// a[i] + b[i] < 2**32
+void Add(FieldElement* out, const FieldElement& a, const FieldElement& b) {
+ for (int i = 0; i < 8; i++) {
+ (*out)[i] = a[i] + b[i];
+ }
+}
+
+static const uint32 kTwo31p3 = (1u<<31) + (1u<<3);
+static const uint32 kTwo31m3 = (1u<<31) - (1u<<3);
+static const uint32 kTwo31m15m3 = (1u<<31) - (1u<<15) - (1u<<3);
+// kZero31ModP is 0 mod p where bit 31 is set in all limbs so that we can
+// subtract smaller amounts without underflow. See the section "Subtraction" in
+// [1] for why.
+static const FieldElement kZero31ModP = {
+ kTwo31p3, kTwo31m3, kTwo31m3, kTwo31m15m3,
+ kTwo31m3, kTwo31m3, kTwo31m3, kTwo31m3
+};
+
+// Subtract computes *out = a-b
+//
+// a[i], b[i] < 2**30
+// out[i] < 2**32
+void Subtract(FieldElement* out, const FieldElement& a, const FieldElement& b) {
+ for (int i = 0; i < 8; i++) {
+ // See the section on "Subtraction" in [1] for details.
+ (*out)[i] = a[i] + kZero31ModP[i] - b[i];
+ }
+}
+
+static const uint64 kTwo63p35 = (1ull<<63) + (1ull<<35);
+static const uint64 kTwo63m35 = (1ull<<63) - (1ull<<35);
+static const uint64 kTwo63m35m19 = (1ull<<63) - (1ull<<35) - (1ull<<19);
+// kZero63ModP is 0 mod p where bit 63 is set in all limbs. See the section
+// "Subtraction" in [1] for why.
+static const uint64 kZero63ModP[8] = {
+ kTwo63p35, kTwo63m35, kTwo63m35, kTwo63m35,
+ kTwo63m35m19, kTwo63m35, kTwo63m35, kTwo63m35,
+};
+
+static const uint32 kBottom28Bits = 0xfffffff;
+
+// LargeFieldElement also represents an element of the field. The limbs are
+// still spaced 28-bits apart and in little-endian order. So the limbs are at
+// 0, 28, 56, ..., 392 bits, each 64-bits wide.
+typedef uint64 LargeFieldElement[15];
+
+// ReduceLarge converts a LargeFieldElement to a FieldElement.
+//
+// in[i] < 2**62
+
+// GCC 4.9 incorrectly vectorizes the first coefficient elimination loop, so
+// disable that optimization via pragma. Don't use the pragma under Clang, since
+// clang doesn't understand it.
+// TODO(wez): Remove this when crbug.com/439566 is fixed.
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC optimize("no-tree-vectorize")
+#endif
+
+void ReduceLarge(FieldElement* out, LargeFieldElement* inptr) {
+ LargeFieldElement& in(*inptr);
+
+ for (int i = 0; i < 8; i++) {
+ in[i] += kZero63ModP[i];
+ }
+
+ // Eliminate the coefficients at 2**224 and greater while maintaining the
+ // same value mod p.
+ for (int i = 14; i >= 8; i--) {
+ in[i-8] -= in[i]; // reflection off the "+1" term of p.
+ in[i-5] += (in[i] & 0xffff) << 12; // part of the "-2**96" reflection.
+ in[i-4] += in[i] >> 16; // the rest of the "-2**96" reflection.
+ }
+ in[8] = 0;
+ // in[0..8] < 2**64
+
+ // As the values become small enough, we start to store them in |out| and use
+ // 32-bit operations.
+ for (int i = 1; i < 8; i++) {
+ in[i+1] += in[i] >> 28;
+ (*out)[i] = static_cast<uint32>(in[i] & kBottom28Bits);
+ }
+ // Eliminate the term at 2*224 that we introduced while keeping the same
+ // value mod p.
+ in[0] -= in[8]; // reflection off the "+1" term of p.
+ (*out)[3] += static_cast<uint32>(in[8] & 0xffff) << 12; // "-2**96" term
+ (*out)[4] += static_cast<uint32>(in[8] >> 16); // rest of "-2**96" term
+ // in[0] < 2**64
+ // out[3] < 2**29
+ // out[4] < 2**29
+ // out[1,2,5..7] < 2**28
+
+ (*out)[0] = static_cast<uint32>(in[0] & kBottom28Bits);
+ (*out)[1] += static_cast<uint32>((in[0] >> 28) & kBottom28Bits);
+ (*out)[2] += static_cast<uint32>(in[0] >> 56);
+ // out[0] < 2**28
+ // out[1..4] < 2**29
+ // out[5..7] < 2**28
+}
+
+// TODO(wez): Remove this when crbug.com/439566 is fixed.
+#if defined(__GNUC__) && !defined(__clang__)
+// Reenable "tree-vectorize" optimization if it got disabled for ReduceLarge.
+#pragma GCC reset_options
+#endif
+
+// Mul computes *out = a*b
+//
+// a[i] < 2**29, b[i] < 2**30 (or vice versa)
+// out[i] < 2**29
+void Mul(FieldElement* out, const FieldElement& a, const FieldElement& b) {
+ LargeFieldElement tmp;
+ memset(&tmp, 0, sizeof(tmp));
+
+ for (int i = 0; i < 8; i++) {
+ for (int j = 0; j < 8; j++) {
+ tmp[i+j] += static_cast<uint64>(a[i]) * static_cast<uint64>(b[j]);
+ }
+ }
+
+ ReduceLarge(out, &tmp);
+}
+
+// Square computes *out = a*a
+//
+// a[i] < 2**29
+// out[i] < 2**29
+void Square(FieldElement* out, const FieldElement& a) {
+ LargeFieldElement tmp;
+ memset(&tmp, 0, sizeof(tmp));
+
+ for (int i = 0; i < 8; i++) {
+ for (int j = 0; j <= i; j++) {
+ uint64 r = static_cast<uint64>(a[i]) * static_cast<uint64>(a[j]);
+ if (i == j) {
+ tmp[i+j] += r;
+ } else {
+ tmp[i+j] += r << 1;
+ }
+ }
+ }
+
+ ReduceLarge(out, &tmp);
+}
+
+// Reduce reduces the coefficients of in_out to smaller bounds.
+//
+// On entry: a[i] < 2**31 + 2**30
+// On exit: a[i] < 2**29
+void Reduce(FieldElement* in_out) {
+ FieldElement& a = *in_out;
+
+ for (int i = 0; i < 7; i++) {
+ a[i+1] += a[i] >> 28;
+ a[i] &= kBottom28Bits;
+ }
+ uint32 top = a[7] >> 28;
+ a[7] &= kBottom28Bits;
+
+ // top < 2**4
+ // Constant-time: mask = (top != 0) ? 0xffffffff : 0
+ uint32 mask = top;
+ mask |= mask >> 2;
+ mask |= mask >> 1;
+ mask <<= 31;
+ mask = static_cast<uint32>(static_cast<int32>(mask) >> 31);
+
+ // Eliminate top while maintaining the same value mod p.
+ a[0] -= top;
+ a[3] += top << 12;
+
+ // We may have just made a[0] negative but, if we did, then we must
+ // have added something to a[3], thus it's > 2**12. Therefore we can
+ // carry down to a[0].
+ a[3] -= 1 & mask;
+ a[2] += mask & ((1<<28) - 1);
+ a[1] += mask & ((1<<28) - 1);
+ a[0] += mask & (1<<28);
+}
+
+// Invert calcuates *out = in**-1 by computing in**(2**224 - 2**96 - 1), i.e.
+// Fermat's little theorem.
+void Invert(FieldElement* out, const FieldElement& in) {
+ FieldElement f1, f2, f3, f4;
+
+ Square(&f1, in); // 2
+ Mul(&f1, f1, in); // 2**2 - 1
+ Square(&f1, f1); // 2**3 - 2
+ Mul(&f1, f1, in); // 2**3 - 1
+ Square(&f2, f1); // 2**4 - 2
+ Square(&f2, f2); // 2**5 - 4
+ Square(&f2, f2); // 2**6 - 8
+ Mul(&f1, f1, f2); // 2**6 - 1
+ Square(&f2, f1); // 2**7 - 2
+ for (int i = 0; i < 5; i++) { // 2**12 - 2**6
+ Square(&f2, f2);
+ }
+ Mul(&f2, f2, f1); // 2**12 - 1
+ Square(&f3, f2); // 2**13 - 2
+ for (int i = 0; i < 11; i++) { // 2**24 - 2**12
+ Square(&f3, f3);
+ }
+ Mul(&f2, f3, f2); // 2**24 - 1
+ Square(&f3, f2); // 2**25 - 2
+ for (int i = 0; i < 23; i++) { // 2**48 - 2**24
+ Square(&f3, f3);
+ }
+ Mul(&f3, f3, f2); // 2**48 - 1
+ Square(&f4, f3); // 2**49 - 2
+ for (int i = 0; i < 47; i++) { // 2**96 - 2**48
+ Square(&f4, f4);
+ }
+ Mul(&f3, f3, f4); // 2**96 - 1
+ Square(&f4, f3); // 2**97 - 2
+ for (int i = 0; i < 23; i++) { // 2**120 - 2**24
+ Square(&f4, f4);
+ }
+ Mul(&f2, f4, f2); // 2**120 - 1
+ for (int i = 0; i < 6; i++) { // 2**126 - 2**6
+ Square(&f2, f2);
+ }
+ Mul(&f1, f1, f2); // 2**126 - 1
+ Square(&f1, f1); // 2**127 - 2
+ Mul(&f1, f1, in); // 2**127 - 1
+ for (int i = 0; i < 97; i++) { // 2**224 - 2**97
+ Square(&f1, f1);
+ }
+ Mul(out, f1, f3); // 2**224 - 2**96 - 1
+}
+
+// Contract converts a FieldElement to its minimal, distinguished form.
+//
+// On entry, in[i] < 2**29
+// On exit, in[i] < 2**28
+void Contract(FieldElement* inout) {
+ FieldElement& out = *inout;
+
+ // Reduce the coefficients to < 2**28.
+ for (int i = 0; i < 7; i++) {
+ out[i+1] += out[i] >> 28;
+ out[i] &= kBottom28Bits;
+ }
+ uint32 top = out[7] >> 28;
+ out[7] &= kBottom28Bits;
+
+ // Eliminate top while maintaining the same value mod p.
+ out[0] -= top;
+ out[3] += top << 12;
+
+ // We may just have made out[0] negative. So we carry down. If we made
+ // out[0] negative then we know that out[3] is sufficiently positive
+ // because we just added to it.
+ for (int i = 0; i < 3; i++) {
+ uint32 mask = static_cast<uint32>(static_cast<int32>(out[i]) >> 31);
+ out[i] += (1 << 28) & mask;
+ out[i+1] -= 1 & mask;
+ }
+
+ // We might have pushed out[3] over 2**28 so we perform another, partial
+ // carry chain.
+ for (int i = 3; i < 7; i++) {
+ out[i+1] += out[i] >> 28;
+ out[i] &= kBottom28Bits;
+ }
+ top = out[7] >> 28;
+ out[7] &= kBottom28Bits;
+
+ // Eliminate top while maintaining the same value mod p.
+ out[0] -= top;
+ out[3] += top << 12;
+
+ // There are two cases to consider for out[3]:
+ // 1) The first time that we eliminated top, we didn't push out[3] over
+ // 2**28. In this case, the partial carry chain didn't change any values
+ // and top is zero.
+ // 2) We did push out[3] over 2**28 the first time that we eliminated top.
+ // The first value of top was in [0..16), therefore, prior to eliminating
+ // the first top, 0xfff1000 <= out[3] <= 0xfffffff. Therefore, after
+ // overflowing and being reduced by the second carry chain, out[3] <=
+ // 0xf000. Thus it cannot have overflowed when we eliminated top for the
+ // second time.
+
+ // Again, we may just have made out[0] negative, so do the same carry down.
+ // As before, if we made out[0] negative then we know that out[3] is
+ // sufficiently positive.
+ for (int i = 0; i < 3; i++) {
+ uint32 mask = static_cast<uint32>(static_cast<int32>(out[i]) >> 31);
+ out[i] += (1 << 28) & mask;
+ out[i+1] -= 1 & mask;
+ }
+
+ // The value is < 2**224, but maybe greater than p. In order to reduce to a
+ // unique, minimal value we see if the value is >= p and, if so, subtract p.
+
+ // First we build a mask from the top four limbs, which must all be
+ // equal to bottom28Bits if the whole value is >= p. If top_4_all_ones
+ // ends up with any zero bits in the bottom 28 bits, then this wasn't
+ // true.
+ uint32 top_4_all_ones = 0xffffffffu;
+ for (int i = 4; i < 8; i++) {
+ top_4_all_ones &= out[i];
+ }
+ top_4_all_ones |= 0xf0000000;
+ // Now we replicate any zero bits to all the bits in top_4_all_ones.
+ top_4_all_ones &= top_4_all_ones >> 16;
+ top_4_all_ones &= top_4_all_ones >> 8;
+ top_4_all_ones &= top_4_all_ones >> 4;
+ top_4_all_ones &= top_4_all_ones >> 2;
+ top_4_all_ones &= top_4_all_ones >> 1;
+ top_4_all_ones =
+ static_cast<uint32>(static_cast<int32>(top_4_all_ones << 31) >> 31);
+
+ // Now we test whether the bottom three limbs are non-zero.
+ uint32 bottom_3_non_zero = out[0] | out[1] | out[2];
+ bottom_3_non_zero |= bottom_3_non_zero >> 16;
+ bottom_3_non_zero |= bottom_3_non_zero >> 8;
+ bottom_3_non_zero |= bottom_3_non_zero >> 4;
+ bottom_3_non_zero |= bottom_3_non_zero >> 2;
+ bottom_3_non_zero |= bottom_3_non_zero >> 1;
+ bottom_3_non_zero =
+ static_cast<uint32>(static_cast<int32>(bottom_3_non_zero) >> 31);
+
+ // Everything depends on the value of out[3].
+ // If it's > 0xffff000 and top_4_all_ones != 0 then the whole value is >= p
+ // If it's = 0xffff000 and top_4_all_ones != 0 and bottom_3_non_zero != 0,
+ // then the whole value is >= p
+ // If it's < 0xffff000, then the whole value is < p
+ uint32 n = out[3] - 0xffff000;
+ uint32 out_3_equal = n;
+ out_3_equal |= out_3_equal >> 16;
+ out_3_equal |= out_3_equal >> 8;
+ out_3_equal |= out_3_equal >> 4;
+ out_3_equal |= out_3_equal >> 2;
+ out_3_equal |= out_3_equal >> 1;
+ out_3_equal =
+ ~static_cast<uint32>(static_cast<int32>(out_3_equal << 31) >> 31);
+
+ // If out[3] > 0xffff000 then n's MSB will be zero.
+ uint32 out_3_gt = ~static_cast<uint32>(static_cast<int32>(n << 31) >> 31);
+
+ uint32 mask = top_4_all_ones & ((out_3_equal & bottom_3_non_zero) | out_3_gt);
+ out[0] -= 1 & mask;
+ out[3] -= 0xffff000 & mask;
+ out[4] -= 0xfffffff & mask;
+ out[5] -= 0xfffffff & mask;
+ out[6] -= 0xfffffff & mask;
+ out[7] -= 0xfffffff & mask;
+}
+
+
+// Group element functions.
+//
+// These functions deal with group elements. The group is an elliptic curve
+// group with a = -3 defined in FIPS 186-3, section D.2.2.
+
+using crypto::p224::Point;
+
+// kB is parameter of the elliptic curve.
+const FieldElement kB = {
+ 55967668, 11768882, 265861671, 185302395,
+ 39211076, 180311059, 84673715, 188764328,
+};
+
+void CopyConditional(Point* out, const Point& a, uint32 mask);
+void DoubleJacobian(Point* out, const Point& a);
+
+// AddJacobian computes *out = a+b where a != b.
+void AddJacobian(Point *out,
+ const Point& a,
+ const Point& b) {
+ // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl
+ FieldElement z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v;
+
+ uint32 z1_is_zero = IsZero(a.z);
+ uint32 z2_is_zero = IsZero(b.z);
+
+ // Z1Z1 = Z1²
+ Square(&z1z1, a.z);
+
+ // Z2Z2 = Z2²
+ Square(&z2z2, b.z);
+
+ // U1 = X1*Z2Z2
+ Mul(&u1, a.x, z2z2);
+
+ // U2 = X2*Z1Z1
+ Mul(&u2, b.x, z1z1);
+
+ // S1 = Y1*Z2*Z2Z2
+ Mul(&s1, b.z, z2z2);
+ Mul(&s1, a.y, s1);
+
+ // S2 = Y2*Z1*Z1Z1
+ Mul(&s2, a.z, z1z1);
+ Mul(&s2, b.y, s2);
+
+ // H = U2-U1
+ Subtract(&h, u2, u1);
+ Reduce(&h);
+ uint32 x_equal = IsZero(h);
+
+ // I = (2*H)²
+ for (int k = 0; k < 8; k++) {
+ i[k] = h[k] << 1;
+ }
+ Reduce(&i);
+ Square(&i, i);
+
+ // J = H*I
+ Mul(&j, h, i);
+ // r = 2*(S2-S1)
+ Subtract(&r, s2, s1);
+ Reduce(&r);
+ uint32 y_equal = IsZero(r);
+
+ if (x_equal && y_equal && !z1_is_zero && !z2_is_zero) {
+ // The two input points are the same therefore we must use the dedicated
+ // doubling function as the slope of the line is undefined.
+ DoubleJacobian(out, a);
+ return;
+ }
+
+ for (int k = 0; k < 8; k++) {
+ r[k] <<= 1;
+ }
+ Reduce(&r);
+
+ // V = U1*I
+ Mul(&v, u1, i);
+
+ // Z3 = ((Z1+Z2)²-Z1Z1-Z2Z2)*H
+ Add(&z1z1, z1z1, z2z2);
+ Add(&z2z2, a.z, b.z);
+ Reduce(&z2z2);
+ Square(&z2z2, z2z2);
+ Subtract(&out->z, z2z2, z1z1);
+ Reduce(&out->z);
+ Mul(&out->z, out->z, h);
+
+ // X3 = r²-J-2*V
+ for (int k = 0; k < 8; k++) {
+ z1z1[k] = v[k] << 1;
+ }
+ Add(&z1z1, j, z1z1);
+ Reduce(&z1z1);
+ Square(&out->x, r);
+ Subtract(&out->x, out->x, z1z1);
+ Reduce(&out->x);
+
+ // Y3 = r*(V-X3)-2*S1*J
+ for (int k = 0; k < 8; k++) {
+ s1[k] <<= 1;
+ }
+ Mul(&s1, s1, j);
+ Subtract(&z1z1, v, out->x);
+ Reduce(&z1z1);
+ Mul(&z1z1, z1z1, r);
+ Subtract(&out->y, z1z1, s1);
+ Reduce(&out->y);
+
+ CopyConditional(out, a, z2_is_zero);
+ CopyConditional(out, b, z1_is_zero);
+}
+
+// DoubleJacobian computes *out = a+a.
+void DoubleJacobian(Point* out, const Point& a) {
+ // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
+ FieldElement delta, gamma, beta, alpha, t;
+
+ Square(&delta, a.z);
+ Square(&gamma, a.y);
+ Mul(&beta, a.x, gamma);
+
+ // alpha = 3*(X1-delta)*(X1+delta)
+ Add(&t, a.x, delta);
+ for (int i = 0; i < 8; i++) {
+ t[i] += t[i] << 1;
+ }
+ Reduce(&t);
+ Subtract(&alpha, a.x, delta);
+ Reduce(&alpha);
+ Mul(&alpha, alpha, t);
+
+ // Z3 = (Y1+Z1)²-gamma-delta
+ Add(&out->z, a.y, a.z);
+ Reduce(&out->z);
+ Square(&out->z, out->z);
+ Subtract(&out->z, out->z, gamma);
+ Reduce(&out->z);
+ Subtract(&out->z, out->z, delta);
+ Reduce(&out->z);
+
+ // X3 = alpha²-8*beta
+ for (int i = 0; i < 8; i++) {
+ delta[i] = beta[i] << 3;
+ }
+ Reduce(&delta);
+ Square(&out->x, alpha);
+ Subtract(&out->x, out->x, delta);
+ Reduce(&out->x);
+
+ // Y3 = alpha*(4*beta-X3)-8*gamma²
+ for (int i = 0; i < 8; i++) {
+ beta[i] <<= 2;
+ }
+ Reduce(&beta);
+ Subtract(&beta, beta, out->x);
+ Reduce(&beta);
+ Square(&gamma, gamma);
+ for (int i = 0; i < 8; i++) {
+ gamma[i] <<= 3;
+ }
+ Reduce(&gamma);
+ Mul(&out->y, alpha, beta);
+ Subtract(&out->y, out->y, gamma);
+ Reduce(&out->y);
+}
+
+// CopyConditional sets *out=a if mask is 0xffffffff. mask must be either 0 of
+// 0xffffffff.
+void CopyConditional(Point* out,
+ const Point& a,
+ uint32 mask) {
+ for (int i = 0; i < 8; i++) {
+ out->x[i] ^= mask & (a.x[i] ^ out->x[i]);
+ out->y[i] ^= mask & (a.y[i] ^ out->y[i]);
+ out->z[i] ^= mask & (a.z[i] ^ out->z[i]);
+ }
+}
+
+// ScalarMult calculates *out = a*scalar where scalar is a big-endian number of
+// length scalar_len and != 0.
+void ScalarMult(Point* out, const Point& a,
+ const uint8* scalar, size_t scalar_len) {
+ memset(out, 0, sizeof(*out));
+ Point tmp;
+
+ for (size_t i = 0; i < scalar_len; i++) {
+ for (unsigned int bit_num = 0; bit_num < 8; bit_num++) {
+ DoubleJacobian(out, *out);
+ uint32 bit = static_cast<uint32>(static_cast<int32>(
+ (((scalar[i] >> (7 - bit_num)) & 1) << 31) >> 31));
+ AddJacobian(&tmp, a, *out);
+ CopyConditional(out, tmp, bit);
+ }
+ }
+}
+
+// Get224Bits reads 7 words from in and scatters their contents in
+// little-endian form into 8 words at out, 28 bits per output word.
+void Get224Bits(uint32* out, const uint32* in) {
+ out[0] = NetToHost32(in[6]) & kBottom28Bits;
+ out[1] = ((NetToHost32(in[5]) << 4) |
+ (NetToHost32(in[6]) >> 28)) & kBottom28Bits;
+ out[2] = ((NetToHost32(in[4]) << 8) |
+ (NetToHost32(in[5]) >> 24)) & kBottom28Bits;
+ out[3] = ((NetToHost32(in[3]) << 12) |
+ (NetToHost32(in[4]) >> 20)) & kBottom28Bits;
+ out[4] = ((NetToHost32(in[2]) << 16) |
+ (NetToHost32(in[3]) >> 16)) & kBottom28Bits;
+ out[5] = ((NetToHost32(in[1]) << 20) |
+ (NetToHost32(in[2]) >> 12)) & kBottom28Bits;
+ out[6] = ((NetToHost32(in[0]) << 24) |
+ (NetToHost32(in[1]) >> 8)) & kBottom28Bits;
+ out[7] = (NetToHost32(in[0]) >> 4) & kBottom28Bits;
+}
+
+// Put224Bits performs the inverse operation to Get224Bits: taking 28 bits from
+// each of 8 input words and writing them in big-endian order to 7 words at
+// out.
+void Put224Bits(uint32* out, const uint32* in) {
+ out[6] = HostToNet32((in[0] >> 0) | (in[1] << 28));
+ out[5] = HostToNet32((in[1] >> 4) | (in[2] << 24));
+ out[4] = HostToNet32((in[2] >> 8) | (in[3] << 20));
+ out[3] = HostToNet32((in[3] >> 12) | (in[4] << 16));
+ out[2] = HostToNet32((in[4] >> 16) | (in[5] << 12));
+ out[1] = HostToNet32((in[5] >> 20) | (in[6] << 8));
+ out[0] = HostToNet32((in[6] >> 24) | (in[7] << 4));
+}
+
+} // anonymous namespace
+
+namespace crypto {
+
+namespace p224 {
+
+bool Point::SetFromString(const base::StringPiece& in) {
+ if (in.size() != 2*28)
+ return false;
+ const uint32* inwords = reinterpret_cast<const uint32*>(in.data());
+ Get224Bits(x, inwords);
+ Get224Bits(y, inwords + 7);
+ memset(&z, 0, sizeof(z));
+ z[0] = 1;
+
+ // Check that the point is on the curve, i.e. that y² = x³ - 3x + b.
+ FieldElement lhs;
+ Square(&lhs, y);
+ Contract(&lhs);
+
+ FieldElement rhs;
+ Square(&rhs, x);
+ Mul(&rhs, x, rhs);
+
+ FieldElement three_x;
+ for (int i = 0; i < 8; i++) {
+ three_x[i] = x[i] * 3;
+ }
+ Reduce(&three_x);
+ Subtract(&rhs, rhs, three_x);
+ Reduce(&rhs);
+
+ ::Add(&rhs, rhs, kB);
+ Contract(&rhs);
+ return memcmp(&lhs, &rhs, sizeof(lhs)) == 0;
+}
+
+std::string Point::ToString() const {
+ FieldElement zinv, zinv_sq, xx, yy;
+
+ // If this is the point at infinity we return a string of all zeros.
+ if (IsZero(this->z)) {
+ static const char zeros[56] = {0};
+ return std::string(zeros, sizeof(zeros));
+ }
+
+ Invert(&zinv, this->z);
+ Square(&zinv_sq, zinv);
+ Mul(&xx, x, zinv_sq);
+ Mul(&zinv_sq, zinv_sq, zinv);
+ Mul(&yy, y, zinv_sq);
+
+ Contract(&xx);
+ Contract(&yy);
+
+ uint32 outwords[14];
+ Put224Bits(outwords, xx);
+ Put224Bits(outwords + 7, yy);
+ return std::string(reinterpret_cast<const char*>(outwords), sizeof(outwords));
+}
+
+void ScalarMult(const Point& in, const uint8* scalar, Point* out) {
+ ::ScalarMult(out, in, scalar, 28);
+}
+
+// kBasePoint is the base point (generator) of the elliptic curve group.
+static const Point kBasePoint = {
+ {22813985, 52956513, 34677300, 203240812,
+ 12143107, 133374265, 225162431, 191946955},
+ {83918388, 223877528, 122119236, 123340192,
+ 266784067, 263504429, 146143011, 198407736},
+ {1, 0, 0, 0, 0, 0, 0, 0},
+};
+
+void ScalarBaseMult(const uint8* scalar, Point* out) {
+ ::ScalarMult(out, kBasePoint, scalar, 28);
+}
+
+void Add(const Point& a, const Point& b, Point* out) {
+ AddJacobian(out, a, b);
+}
+
+void Negate(const Point& in, Point* out) {
+ // Guide to elliptic curve cryptography, page 89 suggests that (X : X+Y : Z)
+ // is the negative in Jacobian coordinates, but it doesn't actually appear to
+ // be true in testing so this performs the negation in affine coordinates.
+ FieldElement zinv, zinv_sq, y;
+ Invert(&zinv, in.z);
+ Square(&zinv_sq, zinv);
+ Mul(&out->x, in.x, zinv_sq);
+ Mul(&zinv_sq, zinv_sq, zinv);
+ Mul(&y, in.y, zinv_sq);
+
+ Subtract(&out->y, kP, y);
+ Reduce(&out->y);
+
+ memset(&out->z, 0, sizeof(out->z));
+ out->z[0] = 1;
+}
+
+} // namespace p224
+
+} // namespace crypto
diff --git a/crypto/p224.h b/crypto/p224.h
new file mode 100644
index 0000000000..2efecfab55
--- /dev/null
+++ b/crypto/p224.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_P224_H_
+#define CRYPTO_P224_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "crypto/crypto_export.h"
+
+namespace crypto {
+
+// P224 implements an elliptic curve group, commonly known as P224 and defined
+// in FIPS 186-3, section D.2.2.
+namespace p224 {
+
+// An element of the field (ℤ/pℤ) is represented with 8, 28-bit limbs in
+// little endian order.
+typedef uint32 FieldElement[8];
+
+struct CRYPTO_EXPORT Point {
+ // SetFromString the value of the point from the 56 byte, external
+ // representation. The external point representation is an (x, y) pair of a
+ // point on the curve. Each field element is represented as a big-endian
+ // number < p.
+ bool SetFromString(const base::StringPiece& in);
+
+ // ToString returns an external representation of the Point.
+ std::string ToString() const;
+
+ // An Point is represented in Jacobian form (x/z², y/z³).
+ FieldElement x, y, z;
+};
+
+// kScalarBytes is the number of bytes needed to represent an element of the
+// P224 field.
+static const size_t kScalarBytes = 28;
+
+// ScalarMult computes *out = in*scalar where scalar is a 28-byte, big-endian
+// number.
+void CRYPTO_EXPORT ScalarMult(const Point& in, const uint8* scalar, Point* out);
+
+// ScalarBaseMult computes *out = g*scalar where g is the base point of the
+// curve and scalar is a 28-byte, big-endian number.
+void CRYPTO_EXPORT ScalarBaseMult(const uint8* scalar, Point* out);
+
+// Add computes *out = a+b.
+void CRYPTO_EXPORT Add(const Point& a, const Point& b, Point* out);
+
+// Negate calculates out = -a;
+void CRYPTO_EXPORT Negate(const Point& a, Point* out);
+
+} // namespace p224
+
+} // namespace crypto
+
+#endif // CRYPTO_P224_H_
diff --git a/crypto/p224_spake.cc b/crypto/p224_spake.cc
new file mode 100644
index 0000000000..a6dec40568
--- /dev/null
+++ b/crypto/p224_spake.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This code implements SPAKE2, a variant of EKE:
+// http://www.di.ens.fr/~pointche/pub.php?reference=AbPo04
+
+#include <crypto/p224_spake.h>
+
+#include <algorithm>
+
+#include <base/logging.h>
+#include <crypto/p224.h>
+#include <crypto/random.h>
+#include <crypto/secure_util.h>
+
+namespace {
+
+// The following two points (M and N in the protocol) are verifiable random
+// points on the curve and can be generated with the following code:
+
+// #include <stdint.h>
+// #include <stdio.h>
+// #include <string.h>
+//
+// #include <openssl/ec.h>
+// #include <openssl/obj_mac.h>
+// #include <openssl/sha.h>
+//
+// static const char kSeed1[] = "P224 point generation seed (M)";
+// static const char kSeed2[] = "P224 point generation seed (N)";
+//
+// void find_seed(const char* seed) {
+// SHA256_CTX sha256;
+// uint8_t digest[SHA256_DIGEST_LENGTH];
+//
+// SHA256_Init(&sha256);
+// SHA256_Update(&sha256, seed, strlen(seed));
+// SHA256_Final(digest, &sha256);
+//
+// BIGNUM x, y;
+// EC_GROUP* p224 = EC_GROUP_new_by_curve_name(NID_secp224r1);
+// EC_POINT* p = EC_POINT_new(p224);
+//
+// for (unsigned i = 0;; i++) {
+// BN_init(&x);
+// BN_bin2bn(digest, 28, &x);
+//
+// if (EC_POINT_set_compressed_coordinates_GFp(
+// p224, p, &x, digest[28] & 1, NULL)) {
+// BN_init(&y);
+// EC_POINT_get_affine_coordinates_GFp(p224, p, &x, &y, NULL);
+// char* x_str = BN_bn2hex(&x);
+// char* y_str = BN_bn2hex(&y);
+// printf("Found after %u iterations:\n%s\n%s\n", i, x_str, y_str);
+// OPENSSL_free(x_str);
+// OPENSSL_free(y_str);
+// BN_free(&x);
+// BN_free(&y);
+// break;
+// }
+//
+// SHA256_Init(&sha256);
+// SHA256_Update(&sha256, digest, sizeof(digest));
+// SHA256_Final(digest, &sha256);
+//
+// BN_free(&x);
+// }
+//
+// EC_POINT_free(p);
+// EC_GROUP_free(p224);
+// }
+//
+// int main() {
+// find_seed(kSeed1);
+// find_seed(kSeed2);
+// return 0;
+// }
+
+const crypto::p224::Point kM = {
+ {174237515, 77186811, 235213682, 33849492,
+ 33188520, 48266885, 177021753, 81038478},
+ {104523827, 245682244, 266509668, 236196369,
+ 28372046, 145351378, 198520366, 113345994},
+ {1, 0, 0, 0, 0, 0, 0, 0},
+};
+
+const crypto::p224::Point kN = {
+ {136176322, 263523628, 251628795, 229292285,
+ 5034302, 185981975, 171998428, 11653062},
+ {197567436, 51226044, 60372156, 175772188,
+ 42075930, 8083165, 160827401, 65097570},
+ {1, 0, 0, 0, 0, 0, 0, 0},
+};
+
+} // anonymous namespace
+
+namespace crypto {
+
+P224EncryptedKeyExchange::P224EncryptedKeyExchange(
+ PeerType peer_type, const base::StringPiece& password)
+ : state_(kStateInitial),
+ is_server_(peer_type == kPeerTypeServer) {
+ memset(&x_, 0, sizeof(x_));
+ memset(&expected_authenticator_, 0, sizeof(expected_authenticator_));
+
+ // x_ is a random scalar.
+ RandBytes(x_, sizeof(x_));
+
+ // Calculate |password| hash to get SPAKE password value.
+ SHA256HashString(std::string(password.data(), password.length()),
+ pw_, sizeof(pw_));
+
+ Init();
+}
+
+void P224EncryptedKeyExchange::Init() {
+ // X = g**x_
+ p224::Point X;
+ p224::ScalarBaseMult(x_, &X);
+
+ // The client masks the Diffie-Hellman value, X, by adding M**pw and the
+ // server uses N**pw.
+ p224::Point MNpw;
+ p224::ScalarMult(is_server_ ? kN : kM, pw_, &MNpw);
+
+ // X* = X + (N|M)**pw
+ p224::Point Xstar;
+ p224::Add(X, MNpw, &Xstar);
+
+ next_message_ = Xstar.ToString();
+}
+
+const std::string& P224EncryptedKeyExchange::GetNextMessage() {
+ if (state_ == kStateInitial) {
+ state_ = kStateRecvDH;
+ return next_message_;
+ } else if (state_ == kStateSendHash) {
+ state_ = kStateRecvHash;
+ return next_message_;
+ }
+
+ LOG(FATAL) << "P224EncryptedKeyExchange::GetNextMessage called in"
+ " bad state " << state_;
+ next_message_ = "";
+ return next_message_;
+}
+
+P224EncryptedKeyExchange::Result P224EncryptedKeyExchange::ProcessMessage(
+ const base::StringPiece& message) {
+ if (state_ == kStateRecvHash) {
+ // This is the final state of the protocol: we are reading the peer's
+ // authentication hash and checking that it matches the one that we expect.
+ if (message.size() != sizeof(expected_authenticator_)) {
+ error_ = "peer's hash had an incorrect size";
+ return kResultFailed;
+ }
+ if (!SecureMemEqual(message.data(), expected_authenticator_,
+ message.size())) {
+ error_ = "peer's hash had incorrect value";
+ return kResultFailed;
+ }
+ state_ = kStateDone;
+ return kResultSuccess;
+ }
+
+ if (state_ != kStateRecvDH) {
+ LOG(FATAL) << "P224EncryptedKeyExchange::ProcessMessage called in"
+ " bad state " << state_;
+ error_ = "internal error";
+ return kResultFailed;
+ }
+
+ // Y* is the other party's masked, Diffie-Hellman value.
+ p224::Point Ystar;
+ if (!Ystar.SetFromString(message)) {
+ error_ = "failed to parse peer's masked Diffie-Hellman value";
+ return kResultFailed;
+ }
+
+ // We calculate the mask value: (N|M)**pw
+ p224::Point MNpw, minus_MNpw, Y, k;
+ p224::ScalarMult(is_server_ ? kM : kN, pw_, &MNpw);
+ p224::Negate(MNpw, &minus_MNpw);
+
+ // Y = Y* - (N|M)**pw
+ p224::Add(Ystar, minus_MNpw, &Y);
+
+ // K = Y**x_
+ p224::ScalarMult(Y, x_, &k);
+
+ // If everything worked out, then K is the same for both parties.
+ key_ = k.ToString();
+
+ std::string client_masked_dh, server_masked_dh;
+ if (is_server_) {
+ client_masked_dh = message.as_string();
+ server_masked_dh = next_message_;
+ } else {
+ client_masked_dh = next_message_;
+ server_masked_dh = message.as_string();
+ }
+
+ // Now we calculate the hashes that each side will use to prove to the other
+ // that they derived the correct value for K.
+ uint8 client_hash[kSHA256Length], server_hash[kSHA256Length];
+ CalculateHash(kPeerTypeClient, client_masked_dh, server_masked_dh, key_,
+ client_hash);
+ CalculateHash(kPeerTypeServer, client_masked_dh, server_masked_dh, key_,
+ server_hash);
+
+ const uint8* my_hash = is_server_ ? server_hash : client_hash;
+ const uint8* their_hash = is_server_ ? client_hash : server_hash;
+
+ next_message_ =
+ std::string(reinterpret_cast<const char*>(my_hash), kSHA256Length);
+ memcpy(expected_authenticator_, their_hash, kSHA256Length);
+ state_ = kStateSendHash;
+ return kResultPending;
+}
+
+void P224EncryptedKeyExchange::CalculateHash(
+ PeerType peer_type,
+ const std::string& client_masked_dh,
+ const std::string& server_masked_dh,
+ const std::string& k,
+ uint8* out_digest) {
+ std::string hash_contents;
+
+ if (peer_type == kPeerTypeServer) {
+ hash_contents = "server";
+ } else {
+ hash_contents = "client";
+ }
+
+ hash_contents += client_masked_dh;
+ hash_contents += server_masked_dh;
+ hash_contents +=
+ std::string(reinterpret_cast<const char *>(pw_), sizeof(pw_));
+ hash_contents += k;
+
+ SHA256HashString(hash_contents, out_digest, kSHA256Length);
+}
+
+const std::string& P224EncryptedKeyExchange::error() const {
+ return error_;
+}
+
+const std::string& P224EncryptedKeyExchange::GetKey() const {
+ DCHECK_EQ(state_, kStateDone);
+ return GetUnverifiedKey();
+}
+
+const std::string& P224EncryptedKeyExchange::GetUnverifiedKey() const {
+ // Key is already final when state is kStateSendHash. Subsequent states are
+ // used only for verification of the key. Some users may combine verification
+ // with sending verifiable data instead of |expected_authenticator_|.
+ DCHECK_GE(state_, kStateSendHash);
+ return key_;
+}
+
+void P224EncryptedKeyExchange::SetXForTesting(const std::string& x) {
+ memset(&x_, 0, sizeof(x_));
+ memcpy(&x_, x.data(), std::min(x.size(), sizeof(x_)));
+ Init();
+}
+
+} // namespace crypto
diff --git a/crypto/p224_spake.h b/crypto/p224_spake.h
new file mode 100644
index 0000000000..556b15cd09
--- /dev/null
+++ b/crypto/p224_spake.h
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_P224_SPAKE_H_
+#define CRYPTO_P224_SPAKE_H_
+
+#include <base/gtest_prod_util.h>
+#include <base/strings/string_piece.h>
+#include <crypto/p224.h>
+#include <crypto/sha2.h>
+
+namespace crypto {
+
+// P224EncryptedKeyExchange implements SPAKE2, a variant of Encrypted
+// Key Exchange. It allows two parties that have a secret common
+// password to establish a common secure key by exchanging messages
+// over an insecure channel without disclosing the password.
+//
+// The password can be low entropy as authenticating with an attacker only
+// gives the attacker a one-shot password oracle. No other information about
+// the password is leaked. (However, you must be sure to limit the number of
+// permitted authentication attempts otherwise they get many one-shot oracles.)
+//
+// The protocol requires several RTTs (actually two, but you shouldn't assume
+// that.) To use the object, call GetNextMessage() and pass that message to the
+// peer. Get a message from the peer and feed it into ProcessMessage. Then
+// examine the return value of ProcessMessage:
+// kResultPending: Another round is required. Call GetNextMessage and repeat.
+// kResultFailed: The authentication has failed. You can get a human readable
+// error message by calling error().
+// kResultSuccess: The authentication was successful.
+//
+// In each exchange, each peer always sends a message.
+class CRYPTO_EXPORT P224EncryptedKeyExchange {
+ public:
+ enum Result {
+ kResultPending,
+ kResultFailed,
+ kResultSuccess,
+ };
+
+ // PeerType's values are named client and server due to convention. But
+ // they could be called "A" and "B" as far as the protocol is concerned so
+ // long as the two parties don't both get the same label.
+ enum PeerType {
+ kPeerTypeClient,
+ kPeerTypeServer,
+ };
+
+ // peer_type: the type of the local authentication party.
+ // password: secret session password. Both parties to the
+ // authentication must pass the same value. For the case of a
+ // TLS connection, see RFC 5705.
+ P224EncryptedKeyExchange(PeerType peer_type,
+ const base::StringPiece& password);
+
+ // GetNextMessage returns a byte string which must be passed to the other
+ // party in the authentication.
+ const std::string& GetNextMessage();
+
+ // ProcessMessage processes a message which must have been generated by a
+ // call to GetNextMessage() by the other party.
+ Result ProcessMessage(const base::StringPiece& message);
+
+ // In the event that ProcessMessage() returns kResultFailed, error will
+ // return a human readable error message.
+ const std::string& error() const;
+
+ // The key established as result of the key exchange. Must be called
+ // at then end after ProcessMessage() returns kResultSuccess.
+ const std::string& GetKey() const;
+
+ // The key established as result of the key exchange. Can be called after
+ // the first ProcessMessage()
+ const std::string& GetUnverifiedKey() const;
+
+ private:
+ // The authentication state machine is very simple and each party proceeds
+ // through each of these states, in order.
+ enum State {
+ kStateInitial,
+ kStateRecvDH,
+ kStateSendHash,
+ kStateRecvHash,
+ kStateDone,
+ };
+
+ FRIEND_TEST_ALL_PREFIXES(MutualAuth, ExpectedValues);
+
+ void Init();
+
+ // Sets internal random scalar. Should be used by tests only.
+ void SetXForTesting(const std::string& x);
+
+ State state_;
+ const bool is_server_;
+ // next_message_ contains a value for GetNextMessage() to return.
+ std::string next_message_;
+ std::string error_;
+
+ // CalculateHash computes the verification hash for the given peer and writes
+ // |kSHA256Length| bytes at |out_digest|.
+ void CalculateHash(
+ PeerType peer_type,
+ const std::string& client_masked_dh,
+ const std::string& server_masked_dh,
+ const std::string& k,
+ uint8* out_digest);
+
+ // x_ is the secret Diffie-Hellman exponent (see paper referenced in .cc
+ // file).
+ uint8 x_[p224::kScalarBytes];
+ // pw_ is SHA256(P(password), P(session))[:28] where P() prepends a uint32,
+ // big-endian length prefix (see paper referenced in .cc file).
+ uint8 pw_[p224::kScalarBytes];
+ // expected_authenticator_ is used to store the hash value expected from the
+ // other party.
+ uint8 expected_authenticator_[kSHA256Length];
+
+ std::string key_;
+};
+
+} // namespace crypto
+
+#endif // CRYPTO_P224_SPAKE_H_
diff --git a/crypto/p224_spake_unittest.cc b/crypto/p224_spake_unittest.cc
new file mode 100644
index 0000000000..15b5be2684
--- /dev/null
+++ b/crypto/p224_spake_unittest.cc
@@ -0,0 +1,174 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/p224_spake.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crypto {
+
+namespace {
+
+std::string HexEncodeString(const std::string& binary_data) {
+ return base::HexEncode(binary_data.c_str(), binary_data.size());
+}
+
+bool RunExchange(P224EncryptedKeyExchange* client,
+ P224EncryptedKeyExchange* server,
+ bool is_password_same) {
+ for (;;) {
+ std::string client_message, server_message;
+ client_message = client->GetNextMessage();
+ server_message = server->GetNextMessage();
+
+ P224EncryptedKeyExchange::Result client_result, server_result;
+ client_result = client->ProcessMessage(server_message);
+ server_result = server->ProcessMessage(client_message);
+
+ // Check that we never hit the case where only one succeeds.
+ EXPECT_EQ(client_result == P224EncryptedKeyExchange::kResultSuccess,
+ server_result == P224EncryptedKeyExchange::kResultSuccess);
+
+ if (client_result == P224EncryptedKeyExchange::kResultFailed ||
+ server_result == P224EncryptedKeyExchange::kResultFailed) {
+ return false;
+ }
+
+ EXPECT_EQ(is_password_same,
+ client->GetUnverifiedKey() == server->GetUnverifiedKey());
+
+ if (client_result == P224EncryptedKeyExchange::kResultSuccess &&
+ server_result == P224EncryptedKeyExchange::kResultSuccess) {
+ return true;
+ }
+
+ EXPECT_EQ(P224EncryptedKeyExchange::kResultPending, client_result);
+ EXPECT_EQ(P224EncryptedKeyExchange::kResultPending, server_result);
+ }
+}
+
+const char kPassword[] = "foo";
+
+} // namespace
+
+TEST(MutualAuth, CorrectAuth) {
+ P224EncryptedKeyExchange client(
+ P224EncryptedKeyExchange::kPeerTypeClient, kPassword);
+ P224EncryptedKeyExchange server(
+ P224EncryptedKeyExchange::kPeerTypeServer, kPassword);
+
+ EXPECT_TRUE(RunExchange(&client, &server, true));
+ EXPECT_EQ(client.GetKey(), server.GetKey());
+}
+
+TEST(MutualAuth, IncorrectPassword) {
+ P224EncryptedKeyExchange client(
+ P224EncryptedKeyExchange::kPeerTypeClient,
+ kPassword);
+ P224EncryptedKeyExchange server(
+ P224EncryptedKeyExchange::kPeerTypeServer,
+ "wrongpassword");
+
+ EXPECT_FALSE(RunExchange(&client, &server, false));
+}
+
+TEST(MutualAuth, ExpectedValues) {
+ P224EncryptedKeyExchange client(P224EncryptedKeyExchange::kPeerTypeClient,
+ kPassword);
+ client.SetXForTesting("Client x");
+ P224EncryptedKeyExchange server(P224EncryptedKeyExchange::kPeerTypeServer,
+ kPassword);
+ server.SetXForTesting("Server x");
+
+ std::string client_message = client.GetNextMessage();
+ EXPECT_EQ(
+ "3508EF7DECC8AB9F9C439FBB0154288BBECC0A82E8448F4CF29554EB"
+ "BE9D486686226255EAD1D077C635B1A41F46AC91D7F7F32CED9EC3E0",
+ HexEncodeString(client_message));
+
+ std::string server_message = server.GetNextMessage();
+ EXPECT_EQ(
+ "A3088C18B75D2C2B107105661AEC85424777475EB29F1DDFB8C14AFB"
+ "F1603D0DF38413A00F420ACF2059E7997C935F5A957A193D09A2B584",
+ HexEncodeString(server_message));
+
+ EXPECT_EQ(P224EncryptedKeyExchange::kResultPending,
+ client.ProcessMessage(server_message));
+ EXPECT_EQ(P224EncryptedKeyExchange::kResultPending,
+ server.ProcessMessage(client_message));
+
+ EXPECT_EQ(client.GetUnverifiedKey(), server.GetUnverifiedKey());
+ // Must stay the same. External implementations should be able to pair with.
+ EXPECT_EQ(
+ "CE7CCFC435CDA4F01EC8826788B1F8B82EF7D550A34696B371096E64"
+ "C487D4FE193F7D1A6FF6820BC7F807796BA3889E8F999BBDEFC32FFA",
+ HexEncodeString(server.GetUnverifiedKey()));
+
+ EXPECT_TRUE(RunExchange(&client, &server, true));
+ EXPECT_EQ(client.GetKey(), server.GetKey());
+}
+
+TEST(MutualAuth, Fuzz) {
+ static const unsigned kIterations = 40;
+
+ for (unsigned i = 0; i < kIterations; i++) {
+ P224EncryptedKeyExchange client(
+ P224EncryptedKeyExchange::kPeerTypeClient, kPassword);
+ P224EncryptedKeyExchange server(
+ P224EncryptedKeyExchange::kPeerTypeServer, kPassword);
+
+ // We'll only be testing small values of i, but we don't want that to bias
+ // the test coverage. So we disperse the value of i by multiplying by the
+ // FNV, 32-bit prime, producing a poor-man's PRNG.
+ const uint32 rand = i * 16777619;
+
+ for (unsigned round = 0;; round++) {
+ std::string client_message, server_message;
+ client_message = client.GetNextMessage();
+ server_message = server.GetNextMessage();
+
+ if ((rand & 1) == round) {
+ const bool server_or_client = rand & 2;
+ std::string* m = server_or_client ? &server_message : &client_message;
+ if (rand & 4) {
+ // Truncate
+ *m = m->substr(0, (i >> 3) % m->size());
+ } else {
+ // Corrupt
+ const size_t bits = m->size() * 8;
+ const size_t bit_to_corrupt = (rand >> 3) % bits;
+ const_cast<char*>(m->data())[bit_to_corrupt / 8] ^=
+ 1 << (bit_to_corrupt % 8);
+ }
+ }
+
+ P224EncryptedKeyExchange::Result client_result, server_result;
+ client_result = client.ProcessMessage(server_message);
+ server_result = server.ProcessMessage(client_message);
+
+ // If we have corrupted anything, we expect the authentication to fail,
+ // although one side can succeed if we happen to corrupt the second round
+ // message to the other.
+ ASSERT_FALSE(
+ client_result == P224EncryptedKeyExchange::kResultSuccess &&
+ server_result == P224EncryptedKeyExchange::kResultSuccess);
+
+ if (client_result == P224EncryptedKeyExchange::kResultFailed ||
+ server_result == P224EncryptedKeyExchange::kResultFailed) {
+ break;
+ }
+
+ ASSERT_EQ(P224EncryptedKeyExchange::kResultPending,
+ client_result);
+ ASSERT_EQ(P224EncryptedKeyExchange::kResultPending,
+ server_result);
+ }
+ }
+}
+
+} // namespace crypto
diff --git a/crypto/p224_unittest.cc b/crypto/p224_unittest.cc
new file mode 100644
index 0000000000..aaf5f59f47
--- /dev/null
+++ b/crypto/p224_unittest.cc
@@ -0,0 +1,824 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+#include <stdio.h>
+
+#include "crypto/p224.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crypto {
+
+using p224::Point;
+
+// kBasePointExternal is the P224 base point in external representation.
+static const uint8 kBasePointExternal[56] = {
+ 0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f,
+ 0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03, 0xc1, 0xd3,
+ 0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6,
+ 0x11, 0x5c, 0x1d, 0x21, 0xbd, 0x37, 0x63, 0x88,
+ 0xb5, 0xf7, 0x23, 0xfb, 0x4c, 0x22, 0xdf, 0xe6,
+ 0xcd, 0x43, 0x75, 0xa0, 0x5a, 0x07, 0x47, 0x64,
+ 0x44, 0xd5, 0x81, 0x99, 0x85, 0x00, 0x7e, 0x34,
+};
+
+// TestVector represents a test of scalar multiplication of the base point.
+// |scalar| is a big-endian scalar and |affine| is the external representation
+// of g*scalar.
+struct TestVector {
+ uint8 scalar[28];
+ uint8 affine[28*2];
+};
+
+static const int kNumNISTTestVectors = 52;
+
+// kNISTTestVectors are the NIST test vectors for P224.
+static const TestVector kNISTTestVectors[kNumNISTTestVectors] = {
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01},
+ {0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f,
+ 0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03, 0xc1, 0xd3,
+ 0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6,
+ 0x11, 0x5c, 0x1d, 0x21, 0xbd, 0x37, 0x63, 0x88,
+ 0xb5, 0xf7, 0x23, 0xfb, 0x4c, 0x22, 0xdf, 0xe6,
+ 0xcd, 0x43, 0x75, 0xa0, 0x5a, 0x07, 0x47, 0x64,
+ 0x44, 0xd5, 0x81, 0x99, 0x85, 0x00, 0x7e, 0x34
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, },
+
+ {0x70, 0x6a, 0x46, 0xdc, 0x76, 0xdc, 0xb7, 0x67,
+ 0x98, 0xe6, 0x0e, 0x6d, 0x89, 0x47, 0x47, 0x88,
+ 0xd1, 0x6d, 0xc1, 0x80, 0x32, 0xd2, 0x68, 0xfd,
+ 0x1a, 0x70, 0x4f, 0xa6, 0x1c, 0x2b, 0x76, 0xa7,
+ 0xbc, 0x25, 0xe7, 0x70, 0x2a, 0x70, 0x4f, 0xa9,
+ 0x86, 0x89, 0x28, 0x49, 0xfc, 0xa6, 0x29, 0x48,
+ 0x7a, 0xcf, 0x37, 0x09, 0xd2, 0xe4, 0xe8, 0xbb,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, },
+ {0xdf, 0x1b, 0x1d, 0x66, 0xa5, 0x51, 0xd0, 0xd3,
+ 0x1e, 0xff, 0x82, 0x25, 0x58, 0xb9, 0xd2, 0xcc,
+ 0x75, 0xc2, 0x18, 0x02, 0x79, 0xfe, 0x0d, 0x08,
+ 0xfd, 0x89, 0x6d, 0x04, 0xa3, 0xf7, 0xf0, 0x3c,
+ 0xad, 0xd0, 0xbe, 0x44, 0x4c, 0x0a, 0xa5, 0x68,
+ 0x30, 0x13, 0x0d, 0xdf, 0x77, 0xd3, 0x17, 0x34,
+ 0x4e, 0x1a, 0xf3, 0x59, 0x19, 0x81, 0xa9, 0x25,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, },
+ {0xae, 0x99, 0xfe, 0xeb, 0xb5, 0xd2, 0x69, 0x45,
+ 0xb5, 0x48, 0x92, 0x09, 0x2a, 0x8a, 0xee, 0x02,
+ 0x91, 0x29, 0x30, 0xfa, 0x41, 0xcd, 0x11, 0x4e,
+ 0x40, 0x44, 0x73, 0x01, 0x04, 0x82, 0x58, 0x0a,
+ 0x0e, 0xc5, 0xbc, 0x47, 0xe8, 0x8b, 0xc8, 0xc3,
+ 0x78, 0x63, 0x2c, 0xd1, 0x96, 0xcb, 0x3f, 0xa0,
+ 0x58, 0xa7, 0x11, 0x4e, 0xb0, 0x30, 0x54, 0xc9,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x05, },
+ {0x31, 0xc4, 0x9a, 0xe7, 0x5b, 0xce, 0x78, 0x07,
+ 0xcd, 0xff, 0x22, 0x05, 0x5d, 0x94, 0xee, 0x90,
+ 0x21, 0xfe, 0xdb, 0xb5, 0xab, 0x51, 0xc5, 0x75,
+ 0x26, 0xf0, 0x11, 0xaa, 0x27, 0xe8, 0xbf, 0xf1,
+ 0x74, 0x56, 0x35, 0xec, 0x5b, 0xa0, 0xc9, 0xf1,
+ 0xc2, 0xed, 0xe1, 0x54, 0x14, 0xc6, 0x50, 0x7d,
+ 0x29, 0xff, 0xe3, 0x7e, 0x79, 0x0a, 0x07, 0x9b,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, },
+ {0x1f, 0x24, 0x83, 0xf8, 0x25, 0x72, 0x25, 0x1f,
+ 0xca, 0x97, 0x5f, 0xea, 0x40, 0xdb, 0x82, 0x1d,
+ 0xf8, 0xad, 0x82, 0xa3, 0xc0, 0x02, 0xee, 0x6c,
+ 0x57, 0x11, 0x24, 0x08, 0x89, 0xfa, 0xf0, 0xcc,
+ 0xb7, 0x50, 0xd9, 0x9b, 0x55, 0x3c, 0x57, 0x4f,
+ 0xad, 0x7e, 0xcf, 0xb0, 0x43, 0x85, 0x86, 0xeb,
+ 0x39, 0x52, 0xaf, 0x5b, 0x4b, 0x15, 0x3c, 0x7e,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, },
+ {0xdb, 0x2f, 0x6b, 0xe6, 0x30, 0xe2, 0x46, 0xa5,
+ 0xcf, 0x7d, 0x99, 0xb8, 0x51, 0x94, 0xb1, 0x23,
+ 0xd4, 0x87, 0xe2, 0xd4, 0x66, 0xb9, 0x4b, 0x24,
+ 0xa0, 0x3c, 0x3e, 0x28, 0x0f, 0x3a, 0x30, 0x08,
+ 0x54, 0x97, 0xf2, 0xf6, 0x11, 0xee, 0x25, 0x17,
+ 0xb1, 0x63, 0xef, 0x8c, 0x53, 0xb7, 0x15, 0xd1,
+ 0x8b, 0xb4, 0xe4, 0x80, 0x8d, 0x02, 0xb9, 0x63,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, },
+ {0x85, 0x8e, 0x6f, 0x9c, 0xc6, 0xc1, 0x2c, 0x31,
+ 0xf5, 0xdf, 0x12, 0x4a, 0xa7, 0x77, 0x67, 0xb0,
+ 0x5c, 0x8b, 0xc0, 0x21, 0xbd, 0x68, 0x3d, 0x2b,
+ 0x55, 0x57, 0x15, 0x50, 0x04, 0x6d, 0xcd, 0x3e,
+ 0xa5, 0xc4, 0x38, 0x98, 0xc5, 0xc5, 0xfc, 0x4f,
+ 0xda, 0xc7, 0xdb, 0x39, 0xc2, 0xf0, 0x2e, 0xbe,
+ 0xe4, 0xe3, 0x54, 0x1d, 0x1e, 0x78, 0x04, 0x7a,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x09, },
+ {0x2f, 0xdc, 0xcc, 0xfe, 0xe7, 0x20, 0xa7, 0x7e,
+ 0xf6, 0xcb, 0x3b, 0xfb, 0xb4, 0x47, 0xf9, 0x38,
+ 0x31, 0x17, 0xe3, 0xda, 0xa4, 0xa0, 0x7e, 0x36,
+ 0xed, 0x15, 0xf7, 0x8d, 0x37, 0x17, 0x32, 0xe4,
+ 0xf4, 0x1b, 0xf4, 0xf7, 0x88, 0x30, 0x35, 0xe6,
+ 0xa7, 0x9f, 0xce, 0xdc, 0x0e, 0x19, 0x6e, 0xb0,
+ 0x7b, 0x48, 0x17, 0x16, 0x97, 0x51, 0x74, 0x63,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0a, },
+ {0xae, 0xa9, 0xe1, 0x7a, 0x30, 0x65, 0x17, 0xeb,
+ 0x89, 0x15, 0x2a, 0xa7, 0x09, 0x6d, 0x2c, 0x38,
+ 0x1e, 0xc8, 0x13, 0xc5, 0x1a, 0xa8, 0x80, 0xe7,
+ 0xbe, 0xe2, 0xc0, 0xfd, 0x39, 0xbb, 0x30, 0xea,
+ 0xb3, 0x37, 0xe0, 0xa5, 0x21, 0xb6, 0xcb, 0xa1,
+ 0xab, 0xe4, 0xb2, 0xb3, 0xa3, 0xe5, 0x24, 0xc1,
+ 0x4a, 0x3f, 0xe3, 0xeb, 0x11, 0x6b, 0x65, 0x5f,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0b, },
+ {0xef, 0x53, 0xb6, 0x29, 0x4a, 0xca, 0x43, 0x1f,
+ 0x0f, 0x3c, 0x22, 0xdc, 0x82, 0xeb, 0x90, 0x50,
+ 0x32, 0x4f, 0x1d, 0x88, 0xd3, 0x77, 0xe7, 0x16,
+ 0x44, 0x8e, 0x50, 0x7c, 0x20, 0xb5, 0x10, 0x00,
+ 0x40, 0x92, 0xe9, 0x66, 0x36, 0xcf, 0xb7, 0xe3,
+ 0x2e, 0xfd, 0xed, 0x82, 0x65, 0xc2, 0x66, 0xdf,
+ 0xb7, 0x54, 0xfa, 0x6d, 0x64, 0x91, 0xa6, 0xda,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, },
+ {0x6e, 0x31, 0xee, 0x1d, 0xc1, 0x37, 0xf8, 0x1b,
+ 0x05, 0x67, 0x52, 0xe4, 0xde, 0xab, 0x14, 0x43,
+ 0xa4, 0x81, 0x03, 0x3e, 0x9b, 0x4c, 0x93, 0xa3,
+ 0x04, 0x4f, 0x4f, 0x7a, 0x20, 0x7d, 0xdd, 0xf0,
+ 0x38, 0x5b, 0xfd, 0xea, 0xb6, 0xe9, 0xac, 0xda,
+ 0x8d, 0xa0, 0x6b, 0x3b, 0xbe, 0xf2, 0x24, 0xa9,
+ 0x3a, 0xb1, 0xe9, 0xe0, 0x36, 0x10, 0x9d, 0x13,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0d, },
+ {0x34, 0xe8, 0xe1, 0x7a, 0x43, 0x0e, 0x43, 0x28,
+ 0x97, 0x93, 0xc3, 0x83, 0xfa, 0xc9, 0x77, 0x42,
+ 0x47, 0xb4, 0x0e, 0x9e, 0xbd, 0x33, 0x66, 0x98,
+ 0x1f, 0xcf, 0xae, 0xca, 0x25, 0x28, 0x19, 0xf7,
+ 0x1c, 0x7f, 0xb7, 0xfb, 0xcb, 0x15, 0x9b, 0xe3,
+ 0x37, 0xd3, 0x7d, 0x33, 0x36, 0xd7, 0xfe, 0xb9,
+ 0x63, 0x72, 0x4f, 0xdf, 0xb0, 0xec, 0xb7, 0x67,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0e, },
+ {0xa5, 0x36, 0x40, 0xc8, 0x3d, 0xc2, 0x08, 0x60,
+ 0x3d, 0xed, 0x83, 0xe4, 0xec, 0xf7, 0x58, 0xf2,
+ 0x4c, 0x35, 0x7d, 0x7c, 0xf4, 0x80, 0x88, 0xb2,
+ 0xce, 0x01, 0xe9, 0xfa, 0xd5, 0x81, 0x4c, 0xd7,
+ 0x24, 0x19, 0x9c, 0x4a, 0x5b, 0x97, 0x4a, 0x43,
+ 0x68, 0x5f, 0xbf, 0x5b, 0x8b, 0xac, 0x69, 0x45,
+ 0x9c, 0x94, 0x69, 0xbc, 0x8f, 0x23, 0xcc, 0xaf,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0f, },
+ {0xba, 0xa4, 0xd8, 0x63, 0x55, 0x11, 0xa7, 0xd2,
+ 0x88, 0xae, 0xbe, 0xed, 0xd1, 0x2c, 0xe5, 0x29,
+ 0xff, 0x10, 0x2c, 0x91, 0xf9, 0x7f, 0x86, 0x7e,
+ 0x21, 0x91, 0x6b, 0xf9, 0x97, 0x9a, 0x5f, 0x47,
+ 0x59, 0xf8, 0x0f, 0x4f, 0xb4, 0xec, 0x2e, 0x34,
+ 0xf5, 0x56, 0x6d, 0x59, 0x56, 0x80, 0xa1, 0x17,
+ 0x35, 0xe7, 0xb6, 0x10, 0x46, 0x12, 0x79, 0x89,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, },
+ {0x0b, 0x6e, 0xc4, 0xfe, 0x17, 0x77, 0x38, 0x24,
+ 0x04, 0xef, 0x67, 0x99, 0x97, 0xba, 0x8d, 0x1c,
+ 0xc5, 0xcd, 0x8e, 0x85, 0x34, 0x92, 0x59, 0xf5,
+ 0x90, 0xc4, 0xc6, 0x6d, 0x33, 0x99, 0xd4, 0x64,
+ 0x34, 0x59, 0x06, 0xb1, 0x1b, 0x00, 0xe3, 0x63,
+ 0xef, 0x42, 0x92, 0x21, 0xf2, 0xec, 0x72, 0x0d,
+ 0x2f, 0x66, 0x5d, 0x7d, 0xea, 0xd5, 0xb4, 0x82,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x11, },
+ {0xb8, 0x35, 0x7c, 0x3a, 0x6c, 0xee, 0xf2, 0x88,
+ 0x31, 0x0e, 0x17, 0xb8, 0xbf, 0xef, 0xf9, 0x20,
+ 0x08, 0x46, 0xca, 0x8c, 0x19, 0x42, 0x49, 0x7c,
+ 0x48, 0x44, 0x03, 0xbc, 0xff, 0x14, 0x9e, 0xfa,
+ 0x66, 0x06, 0xa6, 0xbd, 0x20, 0xef, 0x7d, 0x1b,
+ 0x06, 0xbd, 0x92, 0xf6, 0x90, 0x46, 0x39, 0xdc,
+ 0xe5, 0x17, 0x4d, 0xb6, 0xcc, 0x55, 0x4a, 0x26,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x12, },
+ {0xc9, 0xff, 0x61, 0xb0, 0x40, 0x87, 0x4c, 0x05,
+ 0x68, 0x47, 0x92, 0x16, 0x82, 0x4a, 0x15, 0xea,
+ 0xb1, 0xa8, 0x38, 0xa7, 0x97, 0xd1, 0x89, 0x74,
+ 0x62, 0x26, 0xe4, 0xcc, 0xea, 0x98, 0xd6, 0x0e,
+ 0x5f, 0xfc, 0x9b, 0x8f, 0xcf, 0x99, 0x9f, 0xab,
+ 0x1d, 0xf7, 0xe7, 0xef, 0x70, 0x84, 0xf2, 0x0d,
+ 0xdb, 0x61, 0xbb, 0x04, 0x5a, 0x6c, 0xe0, 0x02,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x13, },
+ {0xa1, 0xe8, 0x1c, 0x04, 0xf3, 0x0c, 0xe2, 0x01,
+ 0xc7, 0xc9, 0xac, 0xe7, 0x85, 0xed, 0x44, 0xcc,
+ 0x33, 0xb4, 0x55, 0xa0, 0x22, 0xf2, 0xac, 0xdb,
+ 0xc6, 0xca, 0xe8, 0x3c, 0xdc, 0xf1, 0xf6, 0xc3,
+ 0xdb, 0x09, 0xc7, 0x0a, 0xcc, 0x25, 0x39, 0x1d,
+ 0x49, 0x2f, 0xe2, 0x5b, 0x4a, 0x18, 0x0b, 0xab,
+ 0xd6, 0xce, 0xa3, 0x56, 0xc0, 0x47, 0x19, 0xcd,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x14, },
+ {0xfc, 0xc7, 0xf2, 0xb4, 0x5d, 0xf1, 0xcd, 0x5a,
+ 0x3c, 0x0c, 0x07, 0x31, 0xca, 0x47, 0xa8, 0xaf,
+ 0x75, 0xcf, 0xb0, 0x34, 0x7e, 0x83, 0x54, 0xee,
+ 0xfe, 0x78, 0x24, 0x55, 0x0d, 0x5d, 0x71, 0x10,
+ 0x27, 0x4c, 0xba, 0x7c, 0xde, 0xe9, 0x0e, 0x1a,
+ 0x8b, 0x0d, 0x39, 0x4c, 0x37, 0x6a, 0x55, 0x73,
+ 0xdb, 0x6b, 0xe0, 0xbf, 0x27, 0x47, 0xf5, 0x30,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x8e, 0xbb, 0xb9,
+ 0x5e, 0xed, 0x0e, 0x13, },
+ {0x61, 0xf0, 0x77, 0xc6, 0xf6, 0x2e, 0xd8, 0x02,
+ 0xda, 0xd7, 0xc2, 0xf3, 0x8f, 0x5c, 0x67, 0xf2,
+ 0xcc, 0x45, 0x36, 0x01, 0xe6, 0x1b, 0xd0, 0x76,
+ 0xbb, 0x46, 0x17, 0x9e, 0x22, 0x72, 0xf9, 0xe9,
+ 0xf5, 0x93, 0x3e, 0x70, 0x38, 0x8e, 0xe6, 0x52,
+ 0x51, 0x34, 0x43, 0xb5, 0xe2, 0x89, 0xdd, 0x13,
+ 0x5d, 0xcc, 0x0d, 0x02, 0x99, 0xb2, 0x25, 0xe4,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x9d, 0x89,
+ 0x3d, 0x4c, 0xdd, 0x74, 0x72, 0x46, 0xcd, 0xca,
+ 0x43, 0x59, 0x0e, 0x13, },
+ {0x02, 0x98, 0x95, 0xf0, 0xaf, 0x49, 0x6b, 0xfc,
+ 0x62, 0xb6, 0xef, 0x8d, 0x8a, 0x65, 0xc8, 0x8c,
+ 0x61, 0x39, 0x49, 0xb0, 0x36, 0x68, 0xaa, 0xb4,
+ 0xf0, 0x42, 0x9e, 0x35, 0x3e, 0xa6, 0xe5, 0x3f,
+ 0x9a, 0x84, 0x1f, 0x20, 0x19, 0xec, 0x24, 0xbd,
+ 0xe1, 0xa7, 0x56, 0x77, 0xaa, 0x9b, 0x59, 0x02,
+ 0xe6, 0x10, 0x81, 0xc0, 0x10, 0x64, 0xde, 0x93,
+ },
+ },
+ {
+ {0x41, 0xff, 0xc1, 0xff, 0xff, 0xfe, 0x01, 0xff,
+ 0xfc, 0x00, 0x03, 0xff, 0xfe, 0x00, 0x07, 0xc0,
+ 0x01, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xf0, 0x7f,
+ 0xfe, 0x00, 0x07, 0xc0, },
+ {0xab, 0x68, 0x99, 0x30, 0xbc, 0xae, 0x4a, 0x4a,
+ 0xa5, 0xf5, 0xcb, 0x08, 0x5e, 0x82, 0x3e, 0x8a,
+ 0xe3, 0x0f, 0xd3, 0x65, 0xeb, 0x1d, 0xa4, 0xab,
+ 0xa9, 0xcf, 0x03, 0x79, 0x33, 0x45, 0xa1, 0x21,
+ 0xbb, 0xd2, 0x33, 0x54, 0x8a, 0xf0, 0xd2, 0x10,
+ 0x65, 0x4e, 0xb4, 0x0b, 0xab, 0x78, 0x8a, 0x03,
+ 0x66, 0x64, 0x19, 0xbe, 0x6f, 0xbd, 0x34, 0xe7,
+ },
+ },
+ {
+ {0x7f, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xc0, 0x03,
+ 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, 0x00, 0x00,
+ 0x00, 0x0e, 0x00, 0xff, },
+ {0xbd, 0xb6, 0xa8, 0x81, 0x7c, 0x1f, 0x89, 0xda,
+ 0x1c, 0x2f, 0x3d, 0xd8, 0xe9, 0x7f, 0xeb, 0x44,
+ 0x94, 0xf2, 0xed, 0x30, 0x2a, 0x4c, 0xe2, 0xbc,
+ 0x7f, 0x5f, 0x40, 0x25, 0x4c, 0x70, 0x20, 0xd5,
+ 0x7c, 0x00, 0x41, 0x18, 0x89, 0x46, 0x2d, 0x77,
+ 0xa5, 0x43, 0x8b, 0xb4, 0xe9, 0x7d, 0x17, 0x77,
+ 0x00, 0xbf, 0x72, 0x43, 0xa0, 0x7f, 0x16, 0x80,
+ },
+ },
+ {
+ {0x7f, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xf8, 0xff, 0xff,
+ 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00,
+ 0x00, 0x0f, 0xff, 0xff, },
+ {0xd5, 0x8b, 0x61, 0xaa, 0x41, 0xc3, 0x2d, 0xd5,
+ 0xeb, 0xa4, 0x62, 0x64, 0x7d, 0xba, 0x75, 0xc5,
+ 0xd6, 0x7c, 0x83, 0x60, 0x6c, 0x0a, 0xf2, 0xbd,
+ 0x92, 0x84, 0x46, 0xa9, 0xd2, 0x4b, 0xa6, 0xa8,
+ 0x37, 0xbe, 0x04, 0x60, 0xdd, 0x10, 0x7a, 0xe7,
+ 0x77, 0x25, 0x69, 0x6d, 0x21, 0x14, 0x46, 0xc5,
+ 0x60, 0x9b, 0x45, 0x95, 0x97, 0x6b, 0x16, 0xbd,
+ },
+ },
+ {
+ {0x7f, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xfe, 0x3f,
+ 0xff, 0xfc, 0x10, 0x00, 0x00, 0x20, 0x00, 0x3f,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00,
+ 0x3f, 0xff, 0xff, 0xff, },
+ {0xdc, 0x9f, 0xa7, 0x79, 0x78, 0xa0, 0x05, 0x51,
+ 0x09, 0x80, 0xe9, 0x29, 0xa1, 0x48, 0x5f, 0x63,
+ 0x71, 0x6d, 0xf6, 0x95, 0xd7, 0xa0, 0xc1, 0x8b,
+ 0xb5, 0x18, 0xdf, 0x03, 0xed, 0xe2, 0xb0, 0x16,
+ 0xf2, 0xdd, 0xff, 0xc2, 0xa8, 0xc0, 0x15, 0xb1,
+ 0x34, 0x92, 0x82, 0x75, 0xce, 0x09, 0xe5, 0x66,
+ 0x1b, 0x7a, 0xb1, 0x4c, 0xe0, 0xd1, 0xd4, 0x03,
+ },
+ },
+ {
+ {0x70, 0x01, 0xf0, 0x00, 0x1c, 0x00, 0x01, 0xc0,
+ 0x00, 0x00, 0x1f, 0xff, 0xff, 0xfc, 0x00, 0x00,
+ 0x1f, 0xff, 0xff, 0xf8, 0x00, 0x0f, 0xc0, 0x00,
+ 0x00, 0x01, 0xfc, 0x00, },
+ {0x49, 0x9d, 0x8b, 0x28, 0x29, 0xcf, 0xb8, 0x79,
+ 0xc9, 0x01, 0xf7, 0xd8, 0x5d, 0x35, 0x70, 0x45,
+ 0xed, 0xab, 0x55, 0x02, 0x88, 0x24, 0xd0, 0xf0,
+ 0x5b, 0xa2, 0x79, 0xba, 0xbf, 0x92, 0x95, 0x37,
+ 0xb0, 0x6e, 0x40, 0x15, 0x91, 0x96, 0x39, 0xd9,
+ 0x4f, 0x57, 0x83, 0x8f, 0xa3, 0x3f, 0xc3, 0xd9,
+ 0x52, 0x59, 0x8d, 0xcd, 0xbb, 0x44, 0xd6, 0x38,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00,
+ 0x00, 0xff, 0xf0, 0x30, 0x00, 0x1f, 0x00, 0x00,
+ 0xff, 0xff, 0xf0, 0x00, 0x00, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, },
+ {0x82, 0x46, 0xc9, 0x99, 0x13, 0x71, 0x86, 0x63,
+ 0x2c, 0x5f, 0x9e, 0xdd, 0xf3, 0xb1, 0xb0, 0xe1,
+ 0x76, 0x4c, 0x5e, 0x8b, 0xd0, 0xe0, 0xd8, 0xa5,
+ 0x54, 0xb9, 0xcb, 0x77, 0xe8, 0x0e, 0xd8, 0x66,
+ 0x0b, 0xc1, 0xcb, 0x17, 0xac, 0x7d, 0x84, 0x5b,
+ 0xe4, 0x0a, 0x7a, 0x02, 0x2d, 0x33, 0x06, 0xf1,
+ 0x16, 0xae, 0x9f, 0x81, 0xfe, 0xa6, 0x59, 0x47,
+ },
+ },
+ {
+ {0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xfe, 0x08, 0x00, 0x00, 0x1f,
+ 0xf0, 0x00, 0x1f, 0xff, },
+ {0x66, 0x70, 0xc2, 0x0a, 0xfc, 0xce, 0xae, 0xa6,
+ 0x72, 0xc9, 0x7f, 0x75, 0xe2, 0xe9, 0xdd, 0x5c,
+ 0x84, 0x60, 0xe5, 0x4b, 0xb3, 0x85, 0x38, 0xeb,
+ 0xb4, 0xbd, 0x30, 0xeb, 0xf2, 0x80, 0xd8, 0x00,
+ 0x8d, 0x07, 0xa4, 0xca, 0xf5, 0x42, 0x71, 0xf9,
+ 0x93, 0x52, 0x7d, 0x46, 0xff, 0x3f, 0xf4, 0x6f,
+ 0xd1, 0x19, 0x0a, 0x3f, 0x1f, 0xaa, 0x4f, 0x74,
+ },
+ },
+ {
+ {0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xc0, 0x00, 0x07, 0xff, 0xff, 0xe0, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0xff,
+ 0xff, 0xff, 0xff, 0xff, },
+ {0x00, 0x0e, 0xca, 0x93, 0x42, 0x47, 0x42, 0x5c,
+ 0xfd, 0x94, 0x9b, 0x79, 0x5c, 0xb5, 0xce, 0x1e,
+ 0xff, 0x40, 0x15, 0x50, 0x38, 0x6e, 0x28, 0xd1,
+ 0xa4, 0xc5, 0xa8, 0xeb, 0xd4, 0xc0, 0x10, 0x40,
+ 0xdb, 0xa1, 0x96, 0x28, 0x93, 0x1b, 0xc8, 0x85,
+ 0x53, 0x70, 0x31, 0x7c, 0x72, 0x2c, 0xbd, 0x9c,
+ 0xa6, 0x15, 0x69, 0x85, 0xf1, 0xc2, 0xe9, 0xce,
+ },
+ },
+ {
+ {0x7f, 0xff, 0xfc, 0x03, 0xff, 0x80, 0x7f, 0xff,
+ 0xe0, 0x00, 0x1f, 0xff, 0xff, 0x80, 0x0f, 0xff,
+ 0x80, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01, 0xff,
+ 0xff, 0xfe, 0x00, 0x1f, },
+ {0xef, 0x35, 0x3b, 0xf5, 0xc7, 0x3c, 0xd5, 0x51,
+ 0xb9, 0x6d, 0x59, 0x6f, 0xbc, 0x9a, 0x67, 0xf1,
+ 0x6d, 0x61, 0xdd, 0x9f, 0xe5, 0x6a, 0xf1, 0x9d,
+ 0xe1, 0xfb, 0xa9, 0xcd, 0x21, 0x77, 0x1b, 0x9c,
+ 0xdc, 0xe3, 0xe8, 0x43, 0x0c, 0x09, 0xb3, 0x83,
+ 0x8b, 0xe7, 0x0b, 0x48, 0xc2, 0x1e, 0x15, 0xbc,
+ 0x09, 0xee, 0x1f, 0x2d, 0x79, 0x45, 0xb9, 0x1f,
+ },
+ },
+ {
+ {0x00, 0x00, 0x00, 0x07, 0xff, 0xc0, 0x7f, 0xff,
+ 0xff, 0xff, 0x01, 0xff, 0xfe, 0x03, 0xff, 0xfe,
+ 0x40, 0x00, 0x38, 0x00, 0x07, 0xe0, 0x00, 0x3f,
+ 0xfe, 0x00, 0x00, 0x00, },
+ {0x40, 0x36, 0x05, 0x2a, 0x30, 0x91, 0xeb, 0x48,
+ 0x10, 0x46, 0xad, 0x32, 0x89, 0xc9, 0x5d, 0x3a,
+ 0xc9, 0x05, 0xca, 0x00, 0x23, 0xde, 0x2c, 0x03,
+ 0xec, 0xd4, 0x51, 0xcf, 0xd7, 0x68, 0x16, 0x5a,
+ 0x38, 0xa2, 0xb9, 0x6f, 0x81, 0x25, 0x86, 0xa9,
+ 0xd5, 0x9d, 0x41, 0x36, 0x03, 0x5d, 0x9c, 0x85,
+ 0x3a, 0x5b, 0xf2, 0xe1, 0xc8, 0x6a, 0x49, 0x93,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x29, },
+ {0xfc, 0xc7, 0xf2, 0xb4, 0x5d, 0xf1, 0xcd, 0x5a,
+ 0x3c, 0x0c, 0x07, 0x31, 0xca, 0x47, 0xa8, 0xaf,
+ 0x75, 0xcf, 0xb0, 0x34, 0x7e, 0x83, 0x54, 0xee,
+ 0xfe, 0x78, 0x24, 0x55, 0xf2, 0xa2, 0x8e, 0xef,
+ 0xd8, 0xb3, 0x45, 0x83, 0x21, 0x16, 0xf1, 0xe5,
+ 0x74, 0xf2, 0xc6, 0xb2, 0xc8, 0x95, 0xaa, 0x8c,
+ 0x24, 0x94, 0x1f, 0x40, 0xd8, 0xb8, 0x0a, 0xd1,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x2a, },
+ {0xa1, 0xe8, 0x1c, 0x04, 0xf3, 0x0c, 0xe2, 0x01,
+ 0xc7, 0xc9, 0xac, 0xe7, 0x85, 0xed, 0x44, 0xcc,
+ 0x33, 0xb4, 0x55, 0xa0, 0x22, 0xf2, 0xac, 0xdb,
+ 0xc6, 0xca, 0xe8, 0x3c, 0x23, 0x0e, 0x09, 0x3c,
+ 0x24, 0xf6, 0x38, 0xf5, 0x33, 0xda, 0xc6, 0xe2,
+ 0xb6, 0xd0, 0x1d, 0xa3, 0xb5, 0xe7, 0xf4, 0x54,
+ 0x29, 0x31, 0x5c, 0xa9, 0x3f, 0xb8, 0xe6, 0x34,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x2b, },
+ {0xc9, 0xff, 0x61, 0xb0, 0x40, 0x87, 0x4c, 0x05,
+ 0x68, 0x47, 0x92, 0x16, 0x82, 0x4a, 0x15, 0xea,
+ 0xb1, 0xa8, 0x38, 0xa7, 0x97, 0xd1, 0x89, 0x74,
+ 0x62, 0x26, 0xe4, 0xcc, 0x15, 0x67, 0x29, 0xf1,
+ 0xa0, 0x03, 0x64, 0x70, 0x30, 0x66, 0x60, 0x54,
+ 0xe2, 0x08, 0x18, 0x0f, 0x8f, 0x7b, 0x0d, 0xf2,
+ 0x24, 0x9e, 0x44, 0xfb, 0xa5, 0x93, 0x1f, 0xff,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x2c, },
+ {0xb8, 0x35, 0x7c, 0x3a, 0x6c, 0xee, 0xf2, 0x88,
+ 0x31, 0x0e, 0x17, 0xb8, 0xbf, 0xef, 0xf9, 0x20,
+ 0x08, 0x46, 0xca, 0x8c, 0x19, 0x42, 0x49, 0x7c,
+ 0x48, 0x44, 0x03, 0xbc, 0x00, 0xeb, 0x61, 0x05,
+ 0x99, 0xf9, 0x59, 0x42, 0xdf, 0x10, 0x82, 0xe4,
+ 0xf9, 0x42, 0x6d, 0x08, 0x6f, 0xb9, 0xc6, 0x23,
+ 0x1a, 0xe8, 0xb2, 0x49, 0x33, 0xaa, 0xb5, 0xdb,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x2d, },
+ {0x0b, 0x6e, 0xc4, 0xfe, 0x17, 0x77, 0x38, 0x24,
+ 0x04, 0xef, 0x67, 0x99, 0x97, 0xba, 0x8d, 0x1c,
+ 0xc5, 0xcd, 0x8e, 0x85, 0x34, 0x92, 0x59, 0xf5,
+ 0x90, 0xc4, 0xc6, 0x6d, 0xcc, 0x66, 0x2b, 0x9b,
+ 0xcb, 0xa6, 0xf9, 0x4e, 0xe4, 0xff, 0x1c, 0x9c,
+ 0x10, 0xbd, 0x6d, 0xdd, 0x0d, 0x13, 0x8d, 0xf2,
+ 0xd0, 0x99, 0xa2, 0x82, 0x15, 0x2a, 0x4b, 0x7f,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x2e, },
+ {0xba, 0xa4, 0xd8, 0x63, 0x55, 0x11, 0xa7, 0xd2,
+ 0x88, 0xae, 0xbe, 0xed, 0xd1, 0x2c, 0xe5, 0x29,
+ 0xff, 0x10, 0x2c, 0x91, 0xf9, 0x7f, 0x86, 0x7e,
+ 0x21, 0x91, 0x6b, 0xf9, 0x68, 0x65, 0xa0, 0xb8,
+ 0xa6, 0x07, 0xf0, 0xb0, 0x4b, 0x13, 0xd1, 0xcb,
+ 0x0a, 0xa9, 0x92, 0xa5, 0xa9, 0x7f, 0x5e, 0xe8,
+ 0xca, 0x18, 0x49, 0xef, 0xb9, 0xed, 0x86, 0x78,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x2f, },
+ {0xa5, 0x36, 0x40, 0xc8, 0x3d, 0xc2, 0x08, 0x60,
+ 0x3d, 0xed, 0x83, 0xe4, 0xec, 0xf7, 0x58, 0xf2,
+ 0x4c, 0x35, 0x7d, 0x7c, 0xf4, 0x80, 0x88, 0xb2,
+ 0xce, 0x01, 0xe9, 0xfa, 0x2a, 0x7e, 0xb3, 0x28,
+ 0xdb, 0xe6, 0x63, 0xb5, 0xa4, 0x68, 0xb5, 0xbc,
+ 0x97, 0xa0, 0x40, 0xa3, 0x74, 0x53, 0x96, 0xba,
+ 0x63, 0x6b, 0x96, 0x43, 0x70, 0xdc, 0x33, 0x52,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x30, },
+ {0x34, 0xe8, 0xe1, 0x7a, 0x43, 0x0e, 0x43, 0x28,
+ 0x97, 0x93, 0xc3, 0x83, 0xfa, 0xc9, 0x77, 0x42,
+ 0x47, 0xb4, 0x0e, 0x9e, 0xbd, 0x33, 0x66, 0x98,
+ 0x1f, 0xcf, 0xae, 0xca, 0xda, 0xd7, 0xe6, 0x08,
+ 0xe3, 0x80, 0x48, 0x04, 0x34, 0xea, 0x64, 0x1c,
+ 0xc8, 0x2c, 0x82, 0xcb, 0xc9, 0x28, 0x01, 0x46,
+ 0x9c, 0x8d, 0xb0, 0x20, 0x4f, 0x13, 0x48, 0x9a,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x31, },
+ {0x6e, 0x31, 0xee, 0x1d, 0xc1, 0x37, 0xf8, 0x1b,
+ 0x05, 0x67, 0x52, 0xe4, 0xde, 0xab, 0x14, 0x43,
+ 0xa4, 0x81, 0x03, 0x3e, 0x9b, 0x4c, 0x93, 0xa3,
+ 0x04, 0x4f, 0x4f, 0x7a, 0xdf, 0x82, 0x22, 0x0f,
+ 0xc7, 0xa4, 0x02, 0x15, 0x49, 0x16, 0x53, 0x25,
+ 0x72, 0x5f, 0x94, 0xc3, 0x41, 0x0d, 0xdb, 0x56,
+ 0xc5, 0x4e, 0x16, 0x1f, 0xc9, 0xef, 0x62, 0xee,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x32, },
+ {0xef, 0x53, 0xb6, 0x29, 0x4a, 0xca, 0x43, 0x1f,
+ 0x0f, 0x3c, 0x22, 0xdc, 0x82, 0xeb, 0x90, 0x50,
+ 0x32, 0x4f, 0x1d, 0x88, 0xd3, 0x77, 0xe7, 0x16,
+ 0x44, 0x8e, 0x50, 0x7c, 0xdf, 0x4a, 0xef, 0xff,
+ 0xbf, 0x6d, 0x16, 0x99, 0xc9, 0x30, 0x48, 0x1c,
+ 0xd1, 0x02, 0x12, 0x7c, 0x9a, 0x3d, 0x99, 0x20,
+ 0x48, 0xab, 0x05, 0x92, 0x9b, 0x6e, 0x59, 0x27,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x33, },
+ {0xae, 0xa9, 0xe1, 0x7a, 0x30, 0x65, 0x17, 0xeb,
+ 0x89, 0x15, 0x2a, 0xa7, 0x09, 0x6d, 0x2c, 0x38,
+ 0x1e, 0xc8, 0x13, 0xc5, 0x1a, 0xa8, 0x80, 0xe7,
+ 0xbe, 0xe2, 0xc0, 0xfd, 0xc6, 0x44, 0xcf, 0x15,
+ 0x4c, 0xc8, 0x1f, 0x5a, 0xde, 0x49, 0x34, 0x5e,
+ 0x54, 0x1b, 0x4d, 0x4b, 0x5c, 0x1a, 0xdb, 0x3e,
+ 0xb5, 0xc0, 0x1c, 0x14, 0xee, 0x94, 0x9a, 0xa2,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x34, },
+ {0x2f, 0xdc, 0xcc, 0xfe, 0xe7, 0x20, 0xa7, 0x7e,
+ 0xf6, 0xcb, 0x3b, 0xfb, 0xb4, 0x47, 0xf9, 0x38,
+ 0x31, 0x17, 0xe3, 0xda, 0xa4, 0xa0, 0x7e, 0x36,
+ 0xed, 0x15, 0xf7, 0x8d, 0xc8, 0xe8, 0xcd, 0x1b,
+ 0x0b, 0xe4, 0x0b, 0x08, 0x77, 0xcf, 0xca, 0x19,
+ 0x58, 0x60, 0x31, 0x22, 0xf1, 0xe6, 0x91, 0x4f,
+ 0x84, 0xb7, 0xe8, 0xe9, 0x68, 0xae, 0x8b, 0x9e,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x35, },
+ {0x85, 0x8e, 0x6f, 0x9c, 0xc6, 0xc1, 0x2c, 0x31,
+ 0xf5, 0xdf, 0x12, 0x4a, 0xa7, 0x77, 0x67, 0xb0,
+ 0x5c, 0x8b, 0xc0, 0x21, 0xbd, 0x68, 0x3d, 0x2b,
+ 0x55, 0x57, 0x15, 0x50, 0xfb, 0x92, 0x32, 0xc1,
+ 0x5a, 0x3b, 0xc7, 0x67, 0x3a, 0x3a, 0x03, 0xb0,
+ 0x25, 0x38, 0x24, 0xc5, 0x3d, 0x0f, 0xd1, 0x41,
+ 0x1b, 0x1c, 0xab, 0xe2, 0xe1, 0x87, 0xfb, 0x87,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x36, },
+ {0xdb, 0x2f, 0x6b, 0xe6, 0x30, 0xe2, 0x46, 0xa5,
+ 0xcf, 0x7d, 0x99, 0xb8, 0x51, 0x94, 0xb1, 0x23,
+ 0xd4, 0x87, 0xe2, 0xd4, 0x66, 0xb9, 0x4b, 0x24,
+ 0xa0, 0x3c, 0x3e, 0x28, 0xf0, 0xc5, 0xcf, 0xf7,
+ 0xab, 0x68, 0x0d, 0x09, 0xee, 0x11, 0xda, 0xe8,
+ 0x4e, 0x9c, 0x10, 0x72, 0xac, 0x48, 0xea, 0x2e,
+ 0x74, 0x4b, 0x1b, 0x7f, 0x72, 0xfd, 0x46, 0x9e,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x37, },
+ {0x1f, 0x24, 0x83, 0xf8, 0x25, 0x72, 0x25, 0x1f,
+ 0xca, 0x97, 0x5f, 0xea, 0x40, 0xdb, 0x82, 0x1d,
+ 0xf8, 0xad, 0x82, 0xa3, 0xc0, 0x02, 0xee, 0x6c,
+ 0x57, 0x11, 0x24, 0x08, 0x76, 0x05, 0x0f, 0x33,
+ 0x48, 0xaf, 0x26, 0x64, 0xaa, 0xc3, 0xa8, 0xb0,
+ 0x52, 0x81, 0x30, 0x4e, 0xbc, 0x7a, 0x79, 0x14,
+ 0xc6, 0xad, 0x50, 0xa4, 0xb4, 0xea, 0xc3, 0x83,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x38, },
+ {0x31, 0xc4, 0x9a, 0xe7, 0x5b, 0xce, 0x78, 0x07,
+ 0xcd, 0xff, 0x22, 0x05, 0x5d, 0x94, 0xee, 0x90,
+ 0x21, 0xfe, 0xdb, 0xb5, 0xab, 0x51, 0xc5, 0x75,
+ 0x26, 0xf0, 0x11, 0xaa, 0xd8, 0x17, 0x40, 0x0e,
+ 0x8b, 0xa9, 0xca, 0x13, 0xa4, 0x5f, 0x36, 0x0e,
+ 0x3d, 0x12, 0x1e, 0xaa, 0xeb, 0x39, 0xaf, 0x82,
+ 0xd6, 0x00, 0x1c, 0x81, 0x86, 0xf5, 0xf8, 0x66,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x39, },
+ {0xae, 0x99, 0xfe, 0xeb, 0xb5, 0xd2, 0x69, 0x45,
+ 0xb5, 0x48, 0x92, 0x09, 0x2a, 0x8a, 0xee, 0x02,
+ 0x91, 0x29, 0x30, 0xfa, 0x41, 0xcd, 0x11, 0x4e,
+ 0x40, 0x44, 0x73, 0x01, 0xfb, 0x7d, 0xa7, 0xf5,
+ 0xf1, 0x3a, 0x43, 0xb8, 0x17, 0x74, 0x37, 0x3c,
+ 0x87, 0x9c, 0xd3, 0x2d, 0x69, 0x34, 0xc0, 0x5f,
+ 0xa7, 0x58, 0xee, 0xb1, 0x4f, 0xcf, 0xab, 0x38,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x3a, },
+ {0xdf, 0x1b, 0x1d, 0x66, 0xa5, 0x51, 0xd0, 0xd3,
+ 0x1e, 0xff, 0x82, 0x25, 0x58, 0xb9, 0xd2, 0xcc,
+ 0x75, 0xc2, 0x18, 0x02, 0x79, 0xfe, 0x0d, 0x08,
+ 0xfd, 0x89, 0x6d, 0x04, 0x5c, 0x08, 0x0f, 0xc3,
+ 0x52, 0x2f, 0x41, 0xbb, 0xb3, 0xf5, 0x5a, 0x97,
+ 0xcf, 0xec, 0xf2, 0x1f, 0x88, 0x2c, 0xe8, 0xcb,
+ 0xb1, 0xe5, 0x0c, 0xa6, 0xe6, 0x7e, 0x56, 0xdc,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x3b, },
+ {0x70, 0x6a, 0x46, 0xdc, 0x76, 0xdc, 0xb7, 0x67,
+ 0x98, 0xe6, 0x0e, 0x6d, 0x89, 0x47, 0x47, 0x88,
+ 0xd1, 0x6d, 0xc1, 0x80, 0x32, 0xd2, 0x68, 0xfd,
+ 0x1a, 0x70, 0x4f, 0xa6, 0xe3, 0xd4, 0x89, 0x58,
+ 0x43, 0xda, 0x18, 0x8f, 0xd5, 0x8f, 0xb0, 0x56,
+ 0x79, 0x76, 0xd7, 0xb5, 0x03, 0x59, 0xd6, 0xb7,
+ 0x85, 0x30, 0xc8, 0xf6, 0x2d, 0x1b, 0x17, 0x46,
+ },
+ },
+ {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+ 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+ 0x5c, 0x5c, 0x2a, 0x3c, },
+ {0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f,
+ 0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03, 0xc1, 0xd3,
+ 0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6,
+ 0x11, 0x5c, 0x1d, 0x21, 0x42, 0xc8, 0x9c, 0x77,
+ 0x4a, 0x08, 0xdc, 0x04, 0xb3, 0xdd, 0x20, 0x19,
+ 0x32, 0xbc, 0x8a, 0x5e, 0xa5, 0xf8, 0xb8, 0x9b,
+ 0xbb, 0x2a, 0x7e, 0x66, 0x7a, 0xff, 0x81, 0xcd,
+ },
+ },
+};
+
+TEST(P224, ExternalToInternalAndBack) {
+ Point point;
+
+ EXPECT_TRUE(point.SetFromString(base::StringPiece(
+ reinterpret_cast<const char *>(kBasePointExternal),
+ sizeof(kBasePointExternal))));
+
+ const std::string external = point.ToString();
+
+ ASSERT_EQ(external.size(), 56u);
+ EXPECT_TRUE(memcmp(external.data(), kBasePointExternal,
+ sizeof(kBasePointExternal)) == 0);
+}
+
+TEST(P224, ScalarBaseMult) {
+ Point point;
+
+ for (size_t i = 0; i < arraysize(kNISTTestVectors); i++) {
+ p224::ScalarBaseMult(kNISTTestVectors[i].scalar, &point);
+ const std::string external = point.ToString();
+ ASSERT_EQ(external.size(), 56u);
+ EXPECT_TRUE(memcmp(external.data(), kNISTTestVectors[i].affine,
+ external.size()) == 0);
+ }
+}
+
+TEST(P224, Addition) {
+ Point a, b, minus_b, sum, a_again;
+
+ ASSERT_TRUE(a.SetFromString(base::StringPiece(
+ reinterpret_cast<const char *>(kNISTTestVectors[10].affine), 56)));
+ ASSERT_TRUE(b.SetFromString(base::StringPiece(
+ reinterpret_cast<const char *>(kNISTTestVectors[11].affine), 56)));
+
+ p224::Negate(b, &minus_b);
+ p224::Add(a, b, &sum);
+ EXPECT_TRUE(memcmp(&sum, &a, sizeof(sum)) != 0);
+ p224::Add(minus_b, sum, &a_again);
+ EXPECT_TRUE(a_again.ToString() == a.ToString());
+}
+
+TEST(P224, Infinity) {
+ char zeros[56];
+ memset(zeros, 0, sizeof(zeros));
+
+ // Test that x^0 = ∞.
+ Point a;
+ p224::ScalarBaseMult(reinterpret_cast<const uint8*>(zeros), &a);
+ EXPECT_TRUE(memcmp(zeros, a.ToString().data(), sizeof(zeros)) == 0);
+
+ // We shouldn't allow ∞ to be imported.
+ EXPECT_FALSE(a.SetFromString(std::string(zeros, sizeof(zeros))));
+}
+
+} // namespace crypto
diff --git a/crypto/random.cc b/crypto/random.cc
new file mode 100644
index 0000000000..a19bb1a11c
--- /dev/null
+++ b/crypto/random.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/random.h"
+
+#include "base/rand_util.h"
+
+namespace crypto {
+
+void RandBytes(void *bytes, size_t length) {
+ // It's OK to call base::RandBytes(), because it's already strongly random.
+ // But _other_ code should go through this function to ensure that code which
+ // needs secure randomness is easily discoverable.
+ base::RandBytes(bytes, length);
+}
+
+} // namespace crypto
+
diff --git a/crypto/random.h b/crypto/random.h
new file mode 100644
index 0000000000..002616bd30
--- /dev/null
+++ b/crypto/random.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_RANDOM_H_
+#define CRYPTO_RANDOM_H_
+
+#include <stddef.h>
+
+#include "crypto/crypto_export.h"
+
+namespace crypto {
+
+// Fills the given buffer with |length| random bytes of cryptographically
+// secure random numbers.
+// |length| must be positive.
+CRYPTO_EXPORT void RandBytes(void *bytes, size_t length);
+
+}
+
+#endif
diff --git a/crypto/random_unittest.cc b/crypto/random_unittest.cc
new file mode 100644
index 0000000000..846d9b66f3
--- /dev/null
+++ b/crypto/random_unittest.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/random.h"
+
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Basic functionality tests. Does NOT test the security of the random data.
+
+// Ensures we don't have all trivial data, i.e. that the data is indeed random.
+// Currently, that means the bytes cannot be all the same (e.g. all zeros).
+bool IsTrivial(const std::string& bytes) {
+ for (size_t i = 0; i < bytes.size(); i++) {
+ if (bytes[i] != bytes[0]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+TEST(RandBytes, RandBytes) {
+ std::string bytes(16, '\0');
+ crypto::RandBytes(WriteInto(&bytes, bytes.size()), bytes.size());
+ EXPECT_TRUE(!IsTrivial(bytes));
+}
diff --git a/crypto/rsa_private_key.cc b/crypto/rsa_private_key.cc
new file mode 100644
index 0000000000..812d9fa16e
--- /dev/null
+++ b/crypto/rsa_private_key.cc
@@ -0,0 +1,384 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/rsa_private_key.h"
+
+#include <algorithm>
+#include <list>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+
+// This file manually encodes and decodes RSA private keys using PrivateKeyInfo
+// from PKCS #8 and RSAPrivateKey from PKCS #1. These structures are:
+//
+// PrivateKeyInfo ::= SEQUENCE {
+// version Version,
+// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+// privateKey PrivateKey,
+// attributes [0] IMPLICIT Attributes OPTIONAL
+// }
+//
+// RSAPrivateKey ::= SEQUENCE {
+// version Version,
+// modulus INTEGER,
+// publicExponent INTEGER,
+// privateExponent INTEGER,
+// prime1 INTEGER,
+// prime2 INTEGER,
+// exponent1 INTEGER,
+// exponent2 INTEGER,
+// coefficient INTEGER
+// }
+
+namespace {
+// Helper for error handling during key import.
+#define READ_ASSERT(truth) \
+ if (!(truth)) { \
+ NOTREACHED(); \
+ return false; \
+ }
+} // namespace
+
+namespace crypto {
+
+const uint8 PrivateKeyInfoCodec::kRsaAlgorithmIdentifier[] = {
+ 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
+ 0x05, 0x00
+};
+
+PrivateKeyInfoCodec::PrivateKeyInfoCodec(bool big_endian)
+ : big_endian_(big_endian) {}
+
+PrivateKeyInfoCodec::~PrivateKeyInfoCodec() {}
+
+bool PrivateKeyInfoCodec::Export(std::vector<uint8>* output) {
+ std::list<uint8> content;
+
+ // Version (always zero)
+ uint8 version = 0;
+
+ PrependInteger(coefficient_, &content);
+ PrependInteger(exponent2_, &content);
+ PrependInteger(exponent1_, &content);
+ PrependInteger(prime2_, &content);
+ PrependInteger(prime1_, &content);
+ PrependInteger(private_exponent_, &content);
+ PrependInteger(public_exponent_, &content);
+ PrependInteger(modulus_, &content);
+ PrependInteger(&version, 1, &content);
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
+ PrependTypeHeaderAndLength(kOctetStringTag, content.size(), &content);
+
+ // RSA algorithm OID
+ for (size_t i = sizeof(kRsaAlgorithmIdentifier); i > 0; --i)
+ content.push_front(kRsaAlgorithmIdentifier[i - 1]);
+
+ PrependInteger(&version, 1, &content);
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
+
+ // Copy everying into the output.
+ output->reserve(content.size());
+ output->assign(content.begin(), content.end());
+
+ return true;
+}
+
+bool PrivateKeyInfoCodec::ExportPublicKeyInfo(std::vector<uint8>* output) {
+ // Create a sequence with the modulus (n) and public exponent (e).
+ std::vector<uint8> bit_string;
+ if (!ExportPublicKey(&bit_string))
+ return false;
+
+ // Add the sequence as the contents of a bit string.
+ std::list<uint8> content;
+ PrependBitString(&bit_string[0], static_cast<int>(bit_string.size()),
+ &content);
+
+ // Add the RSA algorithm OID.
+ for (size_t i = sizeof(kRsaAlgorithmIdentifier); i > 0; --i)
+ content.push_front(kRsaAlgorithmIdentifier[i - 1]);
+
+ // Finally, wrap everything in a sequence.
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
+
+ // Copy everything into the output.
+ output->reserve(content.size());
+ output->assign(content.begin(), content.end());
+
+ return true;
+}
+
+bool PrivateKeyInfoCodec::ExportPublicKey(std::vector<uint8>* output) {
+ // Create a sequence with the modulus (n) and public exponent (e).
+ std::list<uint8> content;
+ PrependInteger(&public_exponent_[0],
+ static_cast<int>(public_exponent_.size()),
+ &content);
+ PrependInteger(&modulus_[0], static_cast<int>(modulus_.size()), &content);
+ PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content);
+
+ // Copy everything into the output.
+ output->reserve(content.size());
+ output->assign(content.begin(), content.end());
+
+ return true;
+}
+
+bool PrivateKeyInfoCodec::Import(const std::vector<uint8>& input) {
+ if (input.empty()) {
+ return false;
+ }
+
+ // Parse the private key info up to the public key values, ignoring
+ // the subsequent private key values.
+ uint8* src = const_cast<uint8*>(&input.front());
+ uint8* end = src + input.size();
+ if (!ReadSequence(&src, end) ||
+ !ReadVersion(&src, end) ||
+ !ReadAlgorithmIdentifier(&src, end) ||
+ !ReadTypeHeaderAndLength(&src, end, kOctetStringTag, NULL) ||
+ !ReadSequence(&src, end) ||
+ !ReadVersion(&src, end) ||
+ !ReadInteger(&src, end, &modulus_))
+ return false;
+
+ int mod_size = modulus_.size();
+ READ_ASSERT(mod_size % 2 == 0);
+ int primes_size = mod_size / 2;
+
+ if (!ReadIntegerWithExpectedSize(&src, end, 4, &public_exponent_) ||
+ !ReadIntegerWithExpectedSize(&src, end, mod_size, &private_exponent_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &prime1_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &prime2_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &exponent1_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &exponent2_) ||
+ !ReadIntegerWithExpectedSize(&src, end, primes_size, &coefficient_))
+ return false;
+
+ READ_ASSERT(src == end);
+
+
+ return true;
+}
+
+void PrivateKeyInfoCodec::PrependInteger(const std::vector<uint8>& in,
+ std::list<uint8>* out) {
+ uint8* ptr = const_cast<uint8*>(&in.front());
+ PrependIntegerImpl(ptr, in.size(), out, big_endian_);
+}
+
+// Helper to prepend an ASN.1 integer.
+void PrivateKeyInfoCodec::PrependInteger(uint8* val,
+ int num_bytes,
+ std::list<uint8>* data) {
+ PrependIntegerImpl(val, num_bytes, data, big_endian_);
+}
+
+void PrivateKeyInfoCodec::PrependIntegerImpl(uint8* val,
+ int num_bytes,
+ std::list<uint8>* data,
+ bool big_endian) {
+ // Reverse input if little-endian.
+ std::vector<uint8> tmp;
+ if (!big_endian) {
+ tmp.assign(val, val + num_bytes);
+ std::reverse(tmp.begin(), tmp.end());
+ val = &tmp.front();
+ }
+
+ // ASN.1 integers are unpadded byte arrays, so skip any null padding bytes
+ // from the most-significant end of the integer.
+ int start = 0;
+ while (start < (num_bytes - 1) && val[start] == 0x00) {
+ start++;
+ num_bytes--;
+ }
+ PrependBytes(val, start, num_bytes, data);
+
+ // ASN.1 integers are signed. To encode a positive integer whose sign bit
+ // (the most significant bit) would otherwise be set and make the number
+ // negative, ASN.1 requires a leading null byte to force the integer to be
+ // positive.
+ uint8 front = data->front();
+ if ((front & 0x80) != 0) {
+ data->push_front(0x00);
+ num_bytes++;
+ }
+
+ PrependTypeHeaderAndLength(kIntegerTag, num_bytes, data);
+}
+
+bool PrivateKeyInfoCodec::ReadInteger(uint8** pos,
+ uint8* end,
+ std::vector<uint8>* out) {
+ return ReadIntegerImpl(pos, end, out, big_endian_);
+}
+
+bool PrivateKeyInfoCodec::ReadIntegerWithExpectedSize(uint8** pos,
+ uint8* end,
+ size_t expected_size,
+ std::vector<uint8>* out) {
+ std::vector<uint8> temp;
+ if (!ReadIntegerImpl(pos, end, &temp, true)) // Big-Endian
+ return false;
+
+ int pad = expected_size - temp.size();
+ int index = 0;
+ if (out->size() == expected_size + 1) {
+ READ_ASSERT(out->front() == 0x00);
+ pad++;
+ index++;
+ } else {
+ READ_ASSERT(out->size() <= expected_size);
+ }
+
+ out->insert(out->end(), pad, 0x00);
+ out->insert(out->end(), temp.begin(), temp.end());
+
+ // Reverse output if little-endian.
+ if (!big_endian_)
+ std::reverse(out->begin(), out->end());
+ return true;
+}
+
+bool PrivateKeyInfoCodec::ReadIntegerImpl(uint8** pos,
+ uint8* end,
+ std::vector<uint8>* out,
+ bool big_endian) {
+ uint32 length = 0;
+ if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length) || !length)
+ return false;
+
+ // The first byte can be zero to force positiveness. We can ignore this.
+ if (**pos == 0x00) {
+ ++(*pos);
+ --length;
+ }
+
+ if (length)
+ out->insert(out->end(), *pos, (*pos) + length);
+
+ (*pos) += length;
+
+ // Reverse output if little-endian.
+ if (!big_endian)
+ std::reverse(out->begin(), out->end());
+ return true;
+}
+
+void PrivateKeyInfoCodec::PrependBytes(uint8* val,
+ int start,
+ int num_bytes,
+ std::list<uint8>* data) {
+ while (num_bytes > 0) {
+ --num_bytes;
+ data->push_front(val[start + num_bytes]);
+ }
+}
+
+void PrivateKeyInfoCodec::PrependLength(size_t size, std::list<uint8>* data) {
+ // The high bit is used to indicate whether additional octets are needed to
+ // represent the length.
+ if (size < 0x80) {
+ data->push_front(static_cast<uint8>(size));
+ } else {
+ uint8 num_bytes = 0;
+ while (size > 0) {
+ data->push_front(static_cast<uint8>(size & 0xFF));
+ size >>= 8;
+ num_bytes++;
+ }
+ CHECK_LE(num_bytes, 4);
+ data->push_front(0x80 | num_bytes);
+ }
+}
+
+void PrivateKeyInfoCodec::PrependTypeHeaderAndLength(uint8 type,
+ uint32 length,
+ std::list<uint8>* output) {
+ PrependLength(length, output);
+ output->push_front(type);
+}
+
+void PrivateKeyInfoCodec::PrependBitString(uint8* val,
+ int num_bytes,
+ std::list<uint8>* output) {
+ // Start with the data.
+ PrependBytes(val, 0, num_bytes, output);
+ // Zero unused bits.
+ output->push_front(0);
+ // Add the length.
+ PrependLength(num_bytes + 1, output);
+ // Finally, add the bit string tag.
+ output->push_front((uint8) kBitStringTag);
+}
+
+bool PrivateKeyInfoCodec::ReadLength(uint8** pos, uint8* end, uint32* result) {
+ READ_ASSERT(*pos < end);
+ int length = 0;
+
+ // If the MSB is not set, the length is just the byte itself.
+ if (!(**pos & 0x80)) {
+ length = **pos;
+ (*pos)++;
+ } else {
+ // Otherwise, the lower 7 indicate the length of the length.
+ int length_of_length = **pos & 0x7F;
+ READ_ASSERT(length_of_length <= 4);
+ (*pos)++;
+ READ_ASSERT(*pos + length_of_length < end);
+
+ length = 0;
+ for (int i = 0; i < length_of_length; ++i) {
+ length <<= 8;
+ length |= **pos;
+ (*pos)++;
+ }
+ }
+
+ READ_ASSERT(*pos + length <= end);
+ if (result) *result = length;
+ return true;
+}
+
+bool PrivateKeyInfoCodec::ReadTypeHeaderAndLength(uint8** pos,
+ uint8* end,
+ uint8 expected_tag,
+ uint32* length) {
+ READ_ASSERT(*pos < end);
+ READ_ASSERT(**pos == expected_tag);
+ (*pos)++;
+
+ return ReadLength(pos, end, length);
+}
+
+bool PrivateKeyInfoCodec::ReadSequence(uint8** pos, uint8* end) {
+ return ReadTypeHeaderAndLength(pos, end, kSequenceTag, NULL);
+}
+
+bool PrivateKeyInfoCodec::ReadAlgorithmIdentifier(uint8** pos, uint8* end) {
+ READ_ASSERT(*pos + sizeof(kRsaAlgorithmIdentifier) < end);
+ READ_ASSERT(memcmp(*pos, kRsaAlgorithmIdentifier,
+ sizeof(kRsaAlgorithmIdentifier)) == 0);
+ (*pos) += sizeof(kRsaAlgorithmIdentifier);
+ return true;
+}
+
+bool PrivateKeyInfoCodec::ReadVersion(uint8** pos, uint8* end) {
+ uint32 length = 0;
+ if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length))
+ return false;
+
+ // The version should be zero.
+ for (uint32 i = 0; i < length; ++i) {
+ READ_ASSERT(**pos == 0x00);
+ (*pos)++;
+ }
+
+ return true;
+}
+
+} // namespace crypto
diff --git a/crypto/rsa_private_key.h b/crypto/rsa_private_key.h
new file mode 100644
index 0000000000..637be38836
--- /dev/null
+++ b/crypto/rsa_private_key.h
@@ -0,0 +1,231 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_RSA_PRIVATE_KEY_H_
+#define CRYPTO_RSA_PRIVATE_KEY_H_
+
+#include "build/build_config.h"
+
+#include <list>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "crypto/crypto_export.h"
+
+#if defined(USE_NSS_CERTS)
+#include "base/gtest_prod_util.h"
+#endif
+
+#if defined(USE_OPENSSL)
+// Forward declaration for openssl/*.h
+typedef struct evp_pkey_st EVP_PKEY;
+#else
+// Forward declaration.
+typedef struct PK11SlotInfoStr PK11SlotInfo;
+typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey;
+typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
+#endif
+
+
+namespace crypto {
+
+// Used internally by RSAPrivateKey for serializing and deserializing
+// PKCS #8 PrivateKeyInfo and PublicKeyInfo.
+class PrivateKeyInfoCodec {
+ public:
+
+ // ASN.1 encoding of the AlgorithmIdentifier from PKCS #8.
+ static const uint8 kRsaAlgorithmIdentifier[];
+
+ // ASN.1 tags for some types we use.
+ static const uint8 kBitStringTag = 0x03;
+ static const uint8 kIntegerTag = 0x02;
+ static const uint8 kNullTag = 0x05;
+ static const uint8 kOctetStringTag = 0x04;
+ static const uint8 kSequenceTag = 0x30;
+
+ // |big_endian| here specifies the byte-significance of the integer components
+ // that will be parsed & serialized (modulus(), etc...) during Import(),
+ // Export() and ExportPublicKeyInfo() -- not the ASN.1 DER encoding of the
+ // PrivateKeyInfo/PublicKeyInfo (which is always big-endian).
+ explicit PrivateKeyInfoCodec(bool big_endian);
+
+ ~PrivateKeyInfoCodec();
+
+ // Exports the contents of the integer components to the ASN.1 DER encoding
+ // of the PrivateKeyInfo structure to |output|.
+ bool Export(std::vector<uint8>* output);
+
+ // Exports the contents of the integer components to the ASN.1 DER encoding
+ // of the PublicKeyInfo structure to |output|.
+ bool ExportPublicKeyInfo(std::vector<uint8>* output);
+
+ // Exports the contents of the integer components to the ASN.1 DER encoding
+ // of the RSAPublicKey structure to |output|.
+ bool ExportPublicKey(std::vector<uint8>* output);
+
+ // Parses the ASN.1 DER encoding of the PrivateKeyInfo structure in |input|
+ // and populates the integer components with |big_endian_| byte-significance.
+ // IMPORTANT NOTE: This is currently *not* security-approved for importing
+ // keys from unstrusted sources.
+ bool Import(const std::vector<uint8>& input);
+
+ // Accessors to the contents of the integer components of the PrivateKeyInfo
+ // structure.
+ std::vector<uint8>* modulus() { return &modulus_; };
+ std::vector<uint8>* public_exponent() { return &public_exponent_; };
+ std::vector<uint8>* private_exponent() { return &private_exponent_; };
+ std::vector<uint8>* prime1() { return &prime1_; };
+ std::vector<uint8>* prime2() { return &prime2_; };
+ std::vector<uint8>* exponent1() { return &exponent1_; };
+ std::vector<uint8>* exponent2() { return &exponent2_; };
+ std::vector<uint8>* coefficient() { return &coefficient_; };
+
+ private:
+ // Utility wrappers for PrependIntegerImpl that use the class's |big_endian_|
+ // value.
+ void PrependInteger(const std::vector<uint8>& in, std::list<uint8>* out);
+ void PrependInteger(uint8* val, int num_bytes, std::list<uint8>* data);
+
+ // Prepends the integer stored in |val| - |val + num_bytes| with |big_endian|
+ // byte-significance into |data| as an ASN.1 integer.
+ void PrependIntegerImpl(uint8* val,
+ int num_bytes,
+ std::list<uint8>* data,
+ bool big_endian);
+
+ // Utility wrappers for ReadIntegerImpl that use the class's |big_endian_|
+ // value.
+ bool ReadInteger(uint8** pos, uint8* end, std::vector<uint8>* out);
+ bool ReadIntegerWithExpectedSize(uint8** pos,
+ uint8* end,
+ size_t expected_size,
+ std::vector<uint8>* out);
+
+ // Reads an ASN.1 integer from |pos|, and stores the result into |out| with
+ // |big_endian| byte-significance.
+ bool ReadIntegerImpl(uint8** pos,
+ uint8* end,
+ std::vector<uint8>* out,
+ bool big_endian);
+
+ // Prepends the integer stored in |val|, starting a index |start|, for
+ // |num_bytes| bytes onto |data|.
+ void PrependBytes(uint8* val,
+ int start,
+ int num_bytes,
+ std::list<uint8>* data);
+
+ // Helper to prepend an ASN.1 length field.
+ void PrependLength(size_t size, std::list<uint8>* data);
+
+ // Helper to prepend an ASN.1 type header.
+ void PrependTypeHeaderAndLength(uint8 type,
+ uint32 length,
+ std::list<uint8>* output);
+
+ // Helper to prepend an ASN.1 bit string
+ void PrependBitString(uint8* val, int num_bytes, std::list<uint8>* output);
+
+ // Read an ASN.1 length field. This also checks that the length does not
+ // extend beyond |end|.
+ bool ReadLength(uint8** pos, uint8* end, uint32* result);
+
+ // Read an ASN.1 type header and its length.
+ bool ReadTypeHeaderAndLength(uint8** pos,
+ uint8* end,
+ uint8 expected_tag,
+ uint32* length);
+
+ // Read an ASN.1 sequence declaration. This consumes the type header and
+ // length field, but not the contents of the sequence.
+ bool ReadSequence(uint8** pos, uint8* end);
+
+ // Read the RSA AlgorithmIdentifier.
+ bool ReadAlgorithmIdentifier(uint8** pos, uint8* end);
+
+ // Read one of the two version fields in PrivateKeyInfo.
+ bool ReadVersion(uint8** pos, uint8* end);
+
+ // The byte-significance of the stored components (modulus, etc..).
+ bool big_endian_;
+
+ // Component integers of the PrivateKeyInfo
+ std::vector<uint8> modulus_;
+ std::vector<uint8> public_exponent_;
+ std::vector<uint8> private_exponent_;
+ std::vector<uint8> prime1_;
+ std::vector<uint8> prime2_;
+ std::vector<uint8> exponent1_;
+ std::vector<uint8> exponent2_;
+ std::vector<uint8> coefficient_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrivateKeyInfoCodec);
+};
+
+// Encapsulates an RSA private key. Can be used to generate new keys, export
+// keys to other formats, or to extract a public key.
+// TODO(hclam): This class should be ref-counted so it can be reused easily.
+class CRYPTO_EXPORT RSAPrivateKey {
+ public:
+ ~RSAPrivateKey();
+
+ // Create a new random instance. Can return NULL if initialization fails.
+ static RSAPrivateKey* Create(uint16 num_bits);
+
+ // Create a new instance by importing an existing private key. The format is
+ // an ASN.1-encoded PrivateKeyInfo block from PKCS #8. This can return NULL if
+ // initialization fails.
+ static RSAPrivateKey* CreateFromPrivateKeyInfo(
+ const std::vector<uint8>& input);
+
+#if defined(USE_OPENSSL)
+ // Create a new instance from an existing EVP_PKEY, taking a
+ // reference to it. |key| must be an RSA key. Returns NULL on
+ // failure.
+ static RSAPrivateKey* CreateFromKey(EVP_PKEY* key);
+#else
+ // Create a new instance by referencing an existing private key
+ // structure. Does not import the key.
+ static RSAPrivateKey* CreateFromKey(SECKEYPrivateKey* key);
+#endif
+
+#if defined(USE_OPENSSL)
+ EVP_PKEY* key() { return key_; }
+#else
+ SECKEYPrivateKey* key() { return key_; }
+ SECKEYPublicKey* public_key() { return public_key_; }
+#endif
+
+ // Creates a copy of the object.
+ RSAPrivateKey* Copy() const;
+
+ // Exports the private key to a PKCS #1 PrivateKey block.
+ bool ExportPrivateKey(std::vector<uint8>* output) const;
+
+ // Exports the public key to an X509 SubjectPublicKeyInfo block.
+ bool ExportPublicKey(std::vector<uint8>* output) const;
+
+ private:
+#if defined(USE_NSS_CERTS)
+ FRIEND_TEST_ALL_PREFIXES(RSAPrivateKeyNSSTest, FindFromPublicKey);
+ FRIEND_TEST_ALL_PREFIXES(RSAPrivateKeyNSSTest, FailedFindFromPublicKey);
+#endif
+
+ // Constructor is private. Use one of the Create*() methods above instead.
+ RSAPrivateKey();
+
+#if defined(USE_OPENSSL)
+ EVP_PKEY* key_;
+#else
+ SECKEYPrivateKey* key_;
+ SECKEYPublicKey* public_key_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(RSAPrivateKey);
+};
+
+} // namespace crypto
+
+#endif // CRYPTO_RSA_PRIVATE_KEY_H_
diff --git a/crypto/rsa_private_key_nss.cc b/crypto/rsa_private_key_nss.cc
new file mode 100644
index 0000000000..374e6f9051
--- /dev/null
+++ b/crypto/rsa_private_key_nss.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/rsa_private_key.h"
+
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+
+#include <list>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "crypto/nss_key_util.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+
+// TODO(rafaelw): Consider using NSS's ASN.1 encoder.
+namespace {
+
+static bool ReadAttribute(SECKEYPrivateKey* key,
+ CK_ATTRIBUTE_TYPE type,
+ std::vector<uint8>* output) {
+ SECItem item;
+ SECStatus rv;
+ rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ output->assign(item.data, item.data + item.len);
+ SECITEM_FreeItem(&item, PR_FALSE);
+ return true;
+}
+
+} // namespace
+
+namespace crypto {
+
+RSAPrivateKey::~RSAPrivateKey() {
+ if (key_)
+ SECKEY_DestroyPrivateKey(key_);
+ if (public_key_)
+ SECKEY_DestroyPublicKey(public_key_);
+}
+
+// static
+RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) {
+ EnsureNSSInit();
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ ScopedSECKEYPublicKey public_key;
+ ScopedSECKEYPrivateKey private_key;
+ if (!GenerateRSAKeyPairNSS(slot.get(), num_bits, false /* not permanent */,
+ &public_key, &private_key)) {
+ return nullptr;
+ }
+
+ RSAPrivateKey* rsa_key = new RSAPrivateKey;
+ rsa_key->public_key_ = public_key.release();
+ rsa_key->key_ = private_key.release();
+ return rsa_key;
+}
+
+// static
+RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo(
+ const std::vector<uint8>& input) {
+ EnsureNSSInit();
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot) {
+ NOTREACHED();
+ return nullptr;
+ }
+ ScopedSECKEYPrivateKey key(ImportNSSKeyFromPrivateKeyInfo(
+ slot.get(), input, false /* not permanent */));
+ if (!key || SECKEY_GetPrivateKeyType(key.get()) != rsaKey)
+ return nullptr;
+ return RSAPrivateKey::CreateFromKey(key.get());
+}
+
+// static
+RSAPrivateKey* RSAPrivateKey::CreateFromKey(SECKEYPrivateKey* key) {
+ DCHECK(key);
+ if (SECKEY_GetPrivateKeyType(key) != rsaKey)
+ return NULL;
+ RSAPrivateKey* copy = new RSAPrivateKey();
+ copy->key_ = SECKEY_CopyPrivateKey(key);
+ copy->public_key_ = SECKEY_ConvertToPublicKey(key);
+ if (!copy->key_ || !copy->public_key_) {
+ NOTREACHED();
+ delete copy;
+ return NULL;
+ }
+ return copy;
+}
+
+RSAPrivateKey* RSAPrivateKey::Copy() const {
+ RSAPrivateKey* copy = new RSAPrivateKey();
+ copy->key_ = SECKEY_CopyPrivateKey(key_);
+ copy->public_key_ = SECKEY_CopyPublicKey(public_key_);
+ return copy;
+}
+
+bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) const {
+ PrivateKeyInfoCodec private_key_info(true);
+
+ // Manually read the component attributes of the private key and build up
+ // the PrivateKeyInfo.
+ if (!ReadAttribute(key_, CKA_MODULUS, private_key_info.modulus()) ||
+ !ReadAttribute(key_, CKA_PUBLIC_EXPONENT,
+ private_key_info.public_exponent()) ||
+ !ReadAttribute(key_, CKA_PRIVATE_EXPONENT,
+ private_key_info.private_exponent()) ||
+ !ReadAttribute(key_, CKA_PRIME_1, private_key_info.prime1()) ||
+ !ReadAttribute(key_, CKA_PRIME_2, private_key_info.prime2()) ||
+ !ReadAttribute(key_, CKA_EXPONENT_1, private_key_info.exponent1()) ||
+ !ReadAttribute(key_, CKA_EXPONENT_2, private_key_info.exponent2()) ||
+ !ReadAttribute(key_, CKA_COEFFICIENT, private_key_info.coefficient())) {
+ NOTREACHED();
+ return false;
+ }
+
+ return private_key_info.Export(output);
+}
+
+bool RSAPrivateKey::ExportPublicKey(std::vector<uint8>* output) const {
+ ScopedSECItem der_pubkey(SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_));
+ if (!der_pubkey.get()) {
+ NOTREACHED();
+ return false;
+ }
+
+ output->assign(der_pubkey->data, der_pubkey->data + der_pubkey->len);
+ return true;
+}
+
+RSAPrivateKey::RSAPrivateKey() : key_(NULL), public_key_(NULL) {
+ EnsureNSSInit();
+}
+
+} // namespace crypto
diff --git a/crypto/rsa_private_key_openssl.cc b/crypto/rsa_private_key_openssl.cc
new file mode 100644
index 0000000000..52a0a7a181
--- /dev/null
+++ b/crypto/rsa_private_key_openssl.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/rsa_private_key.h"
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rsa.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "crypto/openssl_util.h"
+#include "crypto/scoped_openssl_types.h"
+
+namespace crypto {
+
+namespace {
+
+using ScopedPKCS8_PRIV_KEY_INFO =
+ ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
+
+// Function pointer definition, for injecting the required key export function
+// into ExportKey, below. The supplied function should export EVP_PKEY into
+// the supplied BIO, returning 1 on success or 0 on failure.
+using ExportFunction = int (*)(BIO*, EVP_PKEY*);
+
+// Helper to export |key| into |output| via the specified ExportFunction.
+bool ExportKey(EVP_PKEY* key,
+ ExportFunction export_fn,
+ std::vector<uint8>* output) {
+ if (!key)
+ return false;
+
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ ScopedBIO bio(BIO_new(BIO_s_mem()));
+
+ int res = export_fn(bio.get(), key);
+ if (!res)
+ return false;
+
+ char* data = NULL;
+ long len = BIO_get_mem_data(bio.get(), &data);
+ if (!data || len < 0)
+ return false;
+
+ output->assign(data, data + len);
+ return true;
+}
+
+} // namespace
+
+// static
+RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) {
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ ScopedRSA rsa_key(RSA_new());
+ ScopedBIGNUM bn(BN_new());
+ if (!rsa_key.get() || !bn.get() || !BN_set_word(bn.get(), 65537L))
+ return NULL;
+
+ if (!RSA_generate_key_ex(rsa_key.get(), num_bits, bn.get(), NULL))
+ return NULL;
+
+ scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey);
+ result->key_ = EVP_PKEY_new();
+ if (!result->key_ || !EVP_PKEY_set1_RSA(result->key_, rsa_key.get()))
+ return NULL;
+
+ return result.release();
+}
+
+// static
+RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo(
+ const std::vector<uint8>& input) {
+ if (input.empty())
+ return NULL;
+
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ // Importing is a little more involved than exporting, as we must first
+ // PKCS#8 decode the input, and then import the EVP_PKEY from Private Key
+ // Info structure returned.
+ const uint8_t* ptr = &input[0];
+ ScopedPKCS8_PRIV_KEY_INFO p8inf(
+ d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr, input.size()));
+ if (!p8inf.get() || ptr != &input[0] + input.size())
+ return NULL;
+
+ scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey);
+ result->key_ = EVP_PKCS82PKEY(p8inf.get());
+ if (!result->key_ || EVP_PKEY_id(result->key_) != EVP_PKEY_RSA)
+ return NULL;
+
+ return result.release();
+}
+
+// static
+RSAPrivateKey* RSAPrivateKey::CreateFromKey(EVP_PKEY* key) {
+ DCHECK(key);
+ if (EVP_PKEY_type(key->type) != EVP_PKEY_RSA)
+ return NULL;
+ RSAPrivateKey* copy = new RSAPrivateKey();
+ copy->key_ = EVP_PKEY_up_ref(key);
+ return copy;
+}
+
+RSAPrivateKey::RSAPrivateKey()
+ : key_(NULL) {
+}
+
+RSAPrivateKey::~RSAPrivateKey() {
+ if (key_)
+ EVP_PKEY_free(key_);
+}
+
+RSAPrivateKey* RSAPrivateKey::Copy() const {
+ scoped_ptr<RSAPrivateKey> copy(new RSAPrivateKey());
+ ScopedRSA rsa(EVP_PKEY_get1_RSA(key_));
+ if (!rsa)
+ return NULL;
+ copy->key_ = EVP_PKEY_new();
+ if (!EVP_PKEY_set1_RSA(copy->key_, rsa.get()))
+ return NULL;
+ return copy.release();
+}
+
+bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) const {
+ return ExportKey(key_, i2d_PKCS8PrivateKeyInfo_bio, output);
+}
+
+bool RSAPrivateKey::ExportPublicKey(std::vector<uint8>* output) const {
+ return ExportKey(key_, i2d_PUBKEY_bio, output);
+}
+
+} // namespace crypto
diff --git a/crypto/rsa_private_key_unittest.cc b/crypto/rsa_private_key_unittest.cc
new file mode 100644
index 0000000000..9e7f6ff51f
--- /dev/null
+++ b/crypto/rsa_private_key_unittest.cc
@@ -0,0 +1,470 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/rsa_private_key.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const uint8 kTestPrivateKeyInfo[] = {
+ 0x30, 0x82, 0x02, 0x78, 0x02, 0x01, 0x00, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
+ 0x02, 0x62, 0x30, 0x82, 0x02, 0x5e, 0x02, 0x01,
+ 0x00, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b,
+ 0x20, 0xdc, 0x7c, 0x9b, 0x0c, 0xdc, 0x51, 0x61,
+ 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08,
+ 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64,
+ 0x85, 0x7b, 0x0c, 0x04, 0x13, 0x3f, 0x8d, 0xf4,
+ 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a,
+ 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f,
+ 0x9e, 0x36, 0x74, 0x30, 0xda, 0x8a, 0x31, 0x4f,
+ 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17,
+ 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5,
+ 0xb2, 0x65, 0x7a, 0x89, 0x4e, 0xb6, 0x47, 0xff,
+ 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85,
+ 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9,
+ 0x96, 0xd0, 0xd6, 0x14, 0x6f, 0x13, 0x8d, 0xc5,
+ 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18,
+ 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93,
+ 0xe7, 0x1f, 0x0f, 0xe6, 0x0f, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0x02, 0x81, 0x80, 0x03, 0x61, 0x89,
+ 0x37, 0xcb, 0xf2, 0x98, 0xa0, 0xce, 0xb4, 0xcb,
+ 0x16, 0x13, 0xf0, 0xe6, 0xaf, 0x5c, 0xc5, 0xa7,
+ 0x69, 0x71, 0xca, 0xba, 0x8d, 0xe0, 0x4d, 0xdd,
+ 0xed, 0xb8, 0x48, 0x8b, 0x16, 0x93, 0x36, 0x95,
+ 0xc2, 0x91, 0x40, 0x65, 0x17, 0xbd, 0x7f, 0xd6,
+ 0xad, 0x9e, 0x30, 0x28, 0x46, 0xe4, 0x3e, 0xcc,
+ 0x43, 0x78, 0xf9, 0xfe, 0x1f, 0x33, 0x23, 0x1e,
+ 0x31, 0x12, 0x9d, 0x3c, 0xa7, 0x08, 0x82, 0x7b,
+ 0x7d, 0x25, 0x4e, 0x5e, 0x19, 0xa8, 0x9b, 0xed,
+ 0x86, 0xb2, 0xcb, 0x3c, 0xfe, 0x4e, 0xa1, 0xfa,
+ 0x62, 0x87, 0x3a, 0x17, 0xf7, 0x60, 0xec, 0x38,
+ 0x29, 0xe8, 0x4f, 0x34, 0x9f, 0x76, 0x9d, 0xee,
+ 0xa3, 0xf6, 0x85, 0x6b, 0x84, 0x43, 0xc9, 0x1e,
+ 0x01, 0xff, 0xfd, 0xd0, 0x29, 0x4c, 0xfa, 0x8e,
+ 0x57, 0x0c, 0xc0, 0x71, 0xa5, 0xbb, 0x88, 0x46,
+ 0x29, 0x5c, 0xc0, 0x4f, 0x01, 0x02, 0x41, 0x00,
+ 0xf5, 0x83, 0xa4, 0x64, 0x4a, 0xf2, 0xdd, 0x8c,
+ 0x2c, 0xed, 0xa8, 0xd5, 0x60, 0x5a, 0xe4, 0xc7,
+ 0xcc, 0x61, 0xcd, 0x38, 0x42, 0x20, 0xd3, 0x82,
+ 0x18, 0xf2, 0x35, 0x00, 0x72, 0x2d, 0xf7, 0x89,
+ 0x80, 0x67, 0xb5, 0x93, 0x05, 0x5f, 0xdd, 0x42,
+ 0xba, 0x16, 0x1a, 0xea, 0x15, 0xc6, 0xf0, 0xb8,
+ 0x8c, 0xbc, 0xbf, 0x54, 0x9e, 0xf1, 0xc1, 0xb2,
+ 0xb3, 0x8b, 0xb6, 0x26, 0x02, 0x30, 0xc4, 0x81,
+ 0x02, 0x41, 0x00, 0xc0, 0x60, 0x62, 0x80, 0xe1,
+ 0x22, 0x78, 0xf6, 0x9d, 0x83, 0x18, 0xeb, 0x72,
+ 0x45, 0xd7, 0xc8, 0x01, 0x7f, 0xa9, 0xca, 0x8f,
+ 0x7d, 0xd6, 0xb8, 0x31, 0x2b, 0x84, 0x7f, 0x62,
+ 0xd9, 0xa9, 0x22, 0x17, 0x7d, 0x06, 0x35, 0x6c,
+ 0xf3, 0xc1, 0x94, 0x17, 0x85, 0x5a, 0xaf, 0x9c,
+ 0x5c, 0x09, 0x3c, 0xcf, 0x2f, 0x44, 0x9d, 0xb6,
+ 0x52, 0x68, 0x5f, 0xf9, 0x59, 0xc8, 0x84, 0x2b,
+ 0x39, 0x22, 0x8f, 0x02, 0x41, 0x00, 0xb2, 0x04,
+ 0xe2, 0x0e, 0x56, 0xca, 0x03, 0x1a, 0xc0, 0xf9,
+ 0x12, 0x92, 0xa5, 0x6b, 0x42, 0xb8, 0x1c, 0xda,
+ 0x4d, 0x93, 0x9d, 0x5f, 0x6f, 0xfd, 0xc5, 0x58,
+ 0xda, 0x55, 0x98, 0x74, 0xfc, 0x28, 0x17, 0x93,
+ 0x1b, 0x75, 0x9f, 0x50, 0x03, 0x7f, 0x7e, 0xae,
+ 0xc8, 0x95, 0x33, 0x75, 0x2c, 0xd6, 0xa4, 0x35,
+ 0xb8, 0x06, 0x03, 0xba, 0x08, 0x59, 0x2b, 0x17,
+ 0x02, 0xdc, 0x4c, 0x7a, 0x50, 0x01, 0x02, 0x41,
+ 0x00, 0x9d, 0xdb, 0x39, 0x59, 0x09, 0xe4, 0x30,
+ 0xa0, 0x24, 0xf5, 0xdb, 0x2f, 0xf0, 0x2f, 0xf1,
+ 0x75, 0x74, 0x0d, 0x5e, 0xb5, 0x11, 0x73, 0xb0,
+ 0x0a, 0xaa, 0x86, 0x4c, 0x0d, 0xff, 0x7e, 0x1d,
+ 0xb4, 0x14, 0xd4, 0x09, 0x91, 0x33, 0x5a, 0xfd,
+ 0xa0, 0x58, 0x80, 0x9b, 0xbe, 0x78, 0x2e, 0x69,
+ 0x82, 0x15, 0x7c, 0x72, 0xf0, 0x7b, 0x18, 0x39,
+ 0xff, 0x6e, 0xeb, 0xc6, 0x86, 0xf5, 0xb4, 0xc7,
+ 0x6f, 0x02, 0x41, 0x00, 0x8d, 0x1a, 0x37, 0x0f,
+ 0x76, 0xc4, 0x82, 0xfa, 0x5c, 0xc3, 0x79, 0x35,
+ 0x3e, 0x70, 0x8a, 0xbf, 0x27, 0x49, 0xb0, 0x99,
+ 0x63, 0xcb, 0x77, 0x5f, 0xa8, 0x82, 0x65, 0xf6,
+ 0x03, 0x52, 0x51, 0xf1, 0xae, 0x2e, 0x05, 0xb3,
+ 0xc6, 0xa4, 0x92, 0xd1, 0xce, 0x6c, 0x72, 0xfb,
+ 0x21, 0xb3, 0x02, 0x87, 0xe4, 0xfd, 0x61, 0xca,
+ 0x00, 0x42, 0x19, 0xf0, 0xda, 0x5a, 0x53, 0xe3,
+ 0xb1, 0xc5, 0x15, 0xf3
+};
+
+} // namespace
+
+// Generate random private keys with two different sizes. Reimport, then
+// export them again. We should get back the same exact bytes.
+TEST(RSAPrivateKeyUnitTest, InitRandomTest) {
+ scoped_ptr<crypto::RSAPrivateKey> keypair1(
+ crypto::RSAPrivateKey::Create(1024));
+ scoped_ptr<crypto::RSAPrivateKey> keypair2(
+ crypto::RSAPrivateKey::Create(2048));
+ ASSERT_TRUE(keypair1.get());
+ ASSERT_TRUE(keypair2.get());
+
+ std::vector<uint8> privkey1;
+ std::vector<uint8> privkey2;
+ std::vector<uint8> pubkey1;
+ std::vector<uint8> pubkey2;
+
+ ASSERT_TRUE(keypair1->ExportPrivateKey(&privkey1));
+ ASSERT_TRUE(keypair2->ExportPrivateKey(&privkey2));
+ ASSERT_TRUE(keypair1->ExportPublicKey(&pubkey1));
+ ASSERT_TRUE(keypair2->ExportPublicKey(&pubkey2));
+
+ scoped_ptr<crypto::RSAPrivateKey> keypair3(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(privkey1));
+ scoped_ptr<crypto::RSAPrivateKey> keypair4(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(privkey2));
+ ASSERT_TRUE(keypair3.get());
+ ASSERT_TRUE(keypair4.get());
+
+ std::vector<uint8> privkey3;
+ std::vector<uint8> privkey4;
+ ASSERT_TRUE(keypair3->ExportPrivateKey(&privkey3));
+ ASSERT_TRUE(keypair4->ExportPrivateKey(&privkey4));
+
+ ASSERT_EQ(privkey1.size(), privkey3.size());
+ ASSERT_EQ(privkey2.size(), privkey4.size());
+ ASSERT_TRUE(0 == memcmp(&privkey1.front(), &privkey3.front(),
+ privkey1.size()));
+ ASSERT_TRUE(0 == memcmp(&privkey2.front(), &privkey4.front(),
+ privkey2.size()));
+}
+
+// Test Copy() method.
+TEST(RSAPrivateKeyUnitTest, CopyTest) {
+ std::vector<uint8> input(
+ kTestPrivateKeyInfo, kTestPrivateKeyInfo + sizeof(kTestPrivateKeyInfo));
+
+ scoped_ptr<crypto::RSAPrivateKey> key(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input));
+
+ scoped_ptr<crypto::RSAPrivateKey> key_copy(key->Copy());
+ ASSERT_TRUE(key_copy.get());
+
+ std::vector<uint8> privkey_copy;
+ ASSERT_TRUE(key_copy->ExportPrivateKey(&privkey_copy));
+ ASSERT_EQ(input, privkey_copy);
+}
+
+// Test that CreateFromPrivateKeyInfo fails if there is extra data after the RSA
+// key.
+TEST(RSAPrivateKeyUnitTest, ExtraData) {
+ std::vector<uint8> input(
+ kTestPrivateKeyInfo, kTestPrivateKeyInfo + sizeof(kTestPrivateKeyInfo));
+ input.push_back(0);
+
+ scoped_ptr<crypto::RSAPrivateKey> key(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input));
+
+ // Import should fail.
+ EXPECT_FALSE(key);
+}
+
+TEST(RSAPrivateKeyUnitTest, NotRsaKey) {
+ // Defines a valid P-256 private key.
+ const uint8 kTestEcPrivateKeyInfo[] = {
+ 0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86,
+ 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D,
+ 0x03, 0x01, 0x07, 0x04, 0x6D, 0x30, 0x6B, 0x02, 0x01, 0x01, 0x04, 0x20,
+ 0x1F, 0xE3, 0x39, 0x50, 0xC5, 0xF4, 0x61, 0x12, 0x4A, 0xE9, 0x92, 0xC2,
+ 0xBD, 0xFD, 0xF1, 0xC7, 0x3B, 0x16, 0x15, 0xF5, 0x71, 0xBD, 0x56, 0x7E,
+ 0x60, 0xD1, 0x9A, 0xA1, 0xF4, 0x8C, 0xDF, 0x42, 0xA1, 0x44, 0x03, 0x42,
+ 0x00, 0x04, 0x7C, 0x11, 0x0C, 0x66, 0xDC, 0xFD, 0xA8, 0x07, 0xF6, 0xE6,
+ 0x9E, 0x45, 0xDD, 0xB3, 0xC7, 0x4F, 0x69, 0xA1, 0x48, 0x4D, 0x20, 0x3E,
+ 0x8D, 0xC5, 0xAD, 0xA8, 0xE9, 0xA9, 0xDD, 0x7C, 0xB3, 0xC7, 0x0D, 0xF4,
+ 0x48, 0x98, 0x6E, 0x51, 0xBD, 0xE5, 0xD1, 0x57, 0x6F, 0x99, 0x90, 0x1F,
+ 0x9C, 0x2C, 0x6A, 0x80, 0x6A, 0x47, 0xFD, 0x90, 0x76, 0x43, 0xA7, 0x2B,
+ 0x83, 0x55, 0x97, 0xEF, 0xC8, 0xC6
+ };
+
+ std::vector<uint8> input(
+ kTestEcPrivateKeyInfo,
+ kTestEcPrivateKeyInfo + sizeof(kTestEcPrivateKeyInfo));
+
+ scoped_ptr<crypto::RSAPrivateKey> key(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input));
+
+ // Import should fail as the given PKCS8 bytes were for an EC key not RSA key.
+ EXPECT_FALSE(key);
+}
+
+// Verify that generated public keys look good. This test data was generated
+// with the openssl command line tool.
+TEST(RSAPrivateKeyUnitTest, PublicKeyTest) {
+ const uint8 expected_public_key_info[] = {
+ 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81,
+ 0x89, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b,
+ 0x20, 0xdc, 0x7c, 0x9b, 0x0c, 0xdc, 0x51, 0x61,
+ 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08,
+ 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64,
+ 0x85, 0x7b, 0x0c, 0x04, 0x13, 0x3f, 0x8d, 0xf4,
+ 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a,
+ 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f,
+ 0x9e, 0x36, 0x74, 0x30, 0xda, 0x8a, 0x31, 0x4f,
+ 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17,
+ 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5,
+ 0xb2, 0x65, 0x7a, 0x89, 0x4e, 0xb6, 0x47, 0xff,
+ 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85,
+ 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9,
+ 0x96, 0xd0, 0xd6, 0x14, 0x6f, 0x13, 0x8d, 0xc5,
+ 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18,
+ 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93,
+ 0xe7, 0x1f, 0x0f, 0xe6, 0x0f, 0x02, 0x03, 0x01,
+ 0x00, 0x01
+ };
+
+ std::vector<uint8> input(
+ kTestPrivateKeyInfo, kTestPrivateKeyInfo + sizeof(kTestPrivateKeyInfo));
+
+ scoped_ptr<crypto::RSAPrivateKey> key(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input));
+ ASSERT_TRUE(key.get());
+
+ std::vector<uint8> output;
+ ASSERT_TRUE(key->ExportPublicKey(&output));
+
+ ASSERT_TRUE(
+ memcmp(expected_public_key_info, &output.front(), output.size()) == 0);
+}
+
+// These two test keys each contain an integer that has 0x00 for its most
+// significant byte. When encoded as ASN.1, this byte is dropped and there are
+// two interesting sub-cases. When the sign bit of the integer is set, an extra
+// null byte is added back to force the encoded value to be positive. When the
+// sign bit is not set, the encoded integer is just left shorter than usual.
+// See also: http://code.google.com/p/chromium/issues/detail?id=14877.
+//
+// Before we were handling this correctly, we would see one of two failures:
+// * RSAPrivateKey::CreateFromPrivateKeyInfo would return null because the
+// underlying windows API failed to import the key.
+// * The import would succeed, but incorrectly interpret the data. On export,
+// the key would contain different values.
+//
+// This test case verifies these two failures modes don't occur.
+TEST(RSAPrivateKeyUnitTest, ShortIntegers) {
+ const uint8 short_integer_with_high_bit[] = {
+ 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x00, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
+ 0x02, 0x61, 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01,
+ 0x00, 0x02, 0x81, 0x81, 0x00, 0x92, 0x59, 0x32,
+ 0x7d, 0x8e, 0xaf, 0x2e, 0xd5, 0xb2, 0x5c, 0x67,
+ 0xc8, 0x7d, 0x48, 0xb7, 0x84, 0x12, 0xd0, 0x76,
+ 0xda, 0xe1, 0xa3, 0x1e, 0x40, 0x01, 0x14, 0x5c,
+ 0xef, 0x26, 0x6e, 0x28, 0xa2, 0xf7, 0xa5, 0xb4,
+ 0x02, 0x37, 0xd0, 0x53, 0x10, 0xcb, 0x7c, 0x6a,
+ 0xf4, 0x53, 0x9f, 0xb8, 0xe0, 0x83, 0x93, 0xd1,
+ 0x19, 0xd8, 0x28, 0xd1, 0xd1, 0xd8, 0x87, 0x8f,
+ 0x92, 0xfd, 0x73, 0xc0, 0x4d, 0x3e, 0x07, 0x22,
+ 0x1f, 0xc1, 0x20, 0xb0, 0x70, 0xb2, 0x3b, 0xea,
+ 0xb1, 0xe5, 0x0a, 0xfd, 0x56, 0x49, 0x5e, 0x39,
+ 0x90, 0x91, 0xce, 0x04, 0x83, 0x29, 0xaa, 0xfd,
+ 0x12, 0xa4, 0x42, 0x26, 0x6c, 0x6e, 0x79, 0x70,
+ 0x77, 0x03, 0xb2, 0x07, 0x01, 0x3d, 0x85, 0x81,
+ 0x95, 0x9e, 0xda, 0x5a, 0xa3, 0xf4, 0x2d, 0x38,
+ 0x04, 0x58, 0xf5, 0x6b, 0xc9, 0xf1, 0xb5, 0x65,
+ 0xfe, 0x66, 0x0d, 0xa2, 0xd5, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0x02, 0x81, 0x80, 0x5e, 0x01, 0x5f,
+ 0xb6, 0x59, 0x1d, 0xdc, 0x36, 0xb6, 0x60, 0x36,
+ 0xe6, 0x08, 0xdb, 0xd9, 0xcd, 0xc3, 0x8c, 0x16,
+ 0x9c, 0x98, 0x8d, 0x7f, 0xd3, 0xdb, 0x1d, 0xaa,
+ 0x68, 0x8f, 0xc5, 0xf8, 0xe2, 0x5d, 0xb3, 0x19,
+ 0xc2, 0xc6, 0xf9, 0x51, 0x32, 0x1b, 0x93, 0x6a,
+ 0xdc, 0x50, 0x8e, 0xeb, 0x61, 0x84, 0x03, 0x42,
+ 0x30, 0x98, 0xb1, 0xf7, 0xbd, 0x14, 0x9a, 0x57,
+ 0x36, 0x33, 0x09, 0xd4, 0x3e, 0x90, 0xda, 0xef,
+ 0x09, 0x6e, 0xef, 0x49, 0xb6, 0x60, 0x68, 0x5e,
+ 0x54, 0x17, 0x25, 0x5b, 0x37, 0xe3, 0x35, 0x63,
+ 0x5b, 0x60, 0x3c, 0xbd, 0x50, 0xdf, 0x46, 0x43,
+ 0x08, 0xa4, 0x71, 0x21, 0xf1, 0x30, 0x71, 0xdc,
+ 0xda, 0xd7, 0x6f, 0xd2, 0x18, 0xbd, 0x39, 0xf1,
+ 0xe1, 0xbe, 0xa8, 0x8d, 0x62, 0xdf, 0xa2, 0x3e,
+ 0xb6, 0x15, 0x26, 0xb6, 0x57, 0xbd, 0x63, 0xdb,
+ 0xc1, 0x91, 0xec, 0xb8, 0x01, 0x02, 0x41, 0x00,
+ 0xc6, 0x1a, 0x06, 0x48, 0xf2, 0x12, 0x1c, 0x9f,
+ 0x74, 0x20, 0x5c, 0x85, 0xa2, 0xda, 0xe5, 0x62,
+ 0x96, 0x8d, 0x22, 0x7b, 0x78, 0x73, 0xea, 0xbb,
+ 0x9f, 0x59, 0x42, 0x13, 0x15, 0xc8, 0x11, 0x50,
+ 0x6c, 0x55, 0xf6, 0xdf, 0x8b, 0xfe, 0xc7, 0xdd,
+ 0xa8, 0xca, 0x54, 0x41, 0xe8, 0xce, 0xbe, 0x7d,
+ 0xbd, 0xe2, 0x13, 0x4b, 0x5b, 0x61, 0xeb, 0x69,
+ 0x6c, 0xb1, 0x9b, 0x28, 0x68, 0x5b, 0xd6, 0x01,
+ 0x02, 0x41, 0x00, 0xbd, 0x1e, 0xfe, 0x51, 0x99,
+ 0xb6, 0xe3, 0x84, 0xfe, 0xf1, 0x9e, 0xfd, 0x9c,
+ 0xe7, 0x86, 0x43, 0x68, 0x7f, 0x2f, 0x6a, 0x2a,
+ 0x4c, 0xae, 0xa6, 0x41, 0x1c, 0xf0, 0x10, 0x37,
+ 0x54, 0x23, 0xba, 0x05, 0x0d, 0x18, 0x27, 0x8d,
+ 0xb8, 0xe4, 0x8f, 0xf2, 0x25, 0x73, 0x8a, 0xd7,
+ 0x05, 0x98, 0x6b, 0x3d, 0x55, 0xb7, 0x6f, 0x7c,
+ 0xec, 0x77, 0x61, 0x54, 0x7b, 0xb6, 0x6b, 0x31,
+ 0xec, 0x94, 0xd5, 0x02, 0x41, 0x00, 0x90, 0xa2,
+ 0xa5, 0x9e, 0x12, 0xa7, 0x68, 0xa0, 0x7e, 0xdf,
+ 0xb5, 0xcd, 0x98, 0x26, 0xab, 0xbd, 0xbc, 0x5f,
+ 0xd5, 0x22, 0x42, 0xc2, 0x97, 0x4a, 0x5f, 0x40,
+ 0x82, 0xfe, 0x7e, 0x33, 0xb1, 0x78, 0x7f, 0x70,
+ 0x90, 0x2b, 0x8d, 0x01, 0xfb, 0x18, 0xfa, 0x48,
+ 0xa7, 0x15, 0xec, 0x0d, 0x2e, 0x85, 0x8d, 0xe2,
+ 0x86, 0xe5, 0xc9, 0x15, 0x88, 0x14, 0x53, 0xd8,
+ 0xa4, 0x88, 0xef, 0x10, 0xc6, 0x01, 0x02, 0x41,
+ 0x00, 0xba, 0xe4, 0xaf, 0x14, 0xfa, 0xdf, 0xf6,
+ 0xd5, 0xce, 0x8f, 0xfe, 0xbb, 0xc8, 0x5c, 0x30,
+ 0x9d, 0xda, 0xdd, 0x9d, 0x80, 0xc0, 0x0e, 0x89,
+ 0xa5, 0xb8, 0xc1, 0x1d, 0x28, 0x19, 0x55, 0x67,
+ 0xfd, 0x03, 0xd2, 0xdd, 0xe4, 0xf0, 0xb4, 0x20,
+ 0x03, 0x74, 0x9b, 0xb8, 0x24, 0x23, 0xbb, 0xde,
+ 0xd5, 0x53, 0x86, 0xaa, 0xc1, 0x5d, 0x65, 0xdd,
+ 0xcf, 0xec, 0x8a, 0x59, 0x4a, 0x73, 0xca, 0xc5,
+ 0x85, 0x02, 0x40, 0x00, 0xc4, 0x5e, 0x8d, 0xa4,
+ 0xea, 0xbb, 0x6a, 0x9b, 0xe6, 0x3a, 0x4d, 0xc1,
+ 0xdb, 0xe5, 0x52, 0x38, 0xf9, 0x59, 0x91, 0x2d,
+ 0x90, 0x82, 0xe3, 0x31, 0x1b, 0x48, 0xb7, 0x42,
+ 0xfa, 0x1d, 0x83, 0xd5, 0x3d, 0x02, 0xc2, 0x12,
+ 0x71, 0x10, 0x3a, 0xbd, 0x92, 0x8f, 0x9b, 0xa2,
+ 0x6b, 0x2d, 0x21, 0xa4, 0x65, 0xe9, 0xfa, 0x8c,
+ 0x30, 0x2a, 0x89, 0xce, 0xd0, 0xa7, 0x67, 0xd8,
+ 0x45, 0x84, 0xb0
+ };
+
+ const uint8 short_integer_without_high_bit[] = {
+ 0x30, 0x82, 0x02, 0x76, 0x02, 0x01, 0x00, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
+ 0x02, 0x60, 0x30, 0x82, 0x02, 0x5c, 0x02, 0x01,
+ 0x00, 0x02, 0x81, 0x81, 0x00, 0xc3, 0x9e, 0x8d,
+ 0xc4, 0x6d, 0x38, 0xe8, 0x0e, 0x9f, 0x84, 0x03,
+ 0x40, 0x8e, 0x81, 0x2e, 0x56, 0x67, 0x78, 0x11,
+ 0x85, 0x27, 0x81, 0x52, 0xf2, 0x1b, 0x3e, 0x5b,
+ 0xf8, 0xab, 0xfc, 0xaf, 0xca, 0x5c, 0x26, 0xd5,
+ 0xfa, 0xd4, 0x55, 0x50, 0x38, 0xb9, 0x9d, 0x89,
+ 0x92, 0x7e, 0x34, 0xcf, 0x37, 0x82, 0x48, 0x2d,
+ 0xaa, 0xc4, 0x6a, 0x0e, 0x93, 0xea, 0xad, 0x8a,
+ 0x33, 0xf0, 0x42, 0x23, 0xe0, 0x4c, 0x98, 0xbf,
+ 0x01, 0x00, 0x1b, 0xfe, 0x06, 0x15, 0xc6, 0xe3,
+ 0x80, 0x79, 0x6d, 0xfe, 0x48, 0xcd, 0x40, 0xbb,
+ 0xf9, 0x58, 0xe6, 0xbf, 0xd5, 0x4c, 0x29, 0x48,
+ 0x53, 0x78, 0x06, 0x03, 0x0d, 0x59, 0xf5, 0x20,
+ 0xe0, 0xe6, 0x8c, 0xb2, 0xf5, 0xd8, 0x61, 0x52,
+ 0x7e, 0x40, 0x83, 0xd7, 0x69, 0xae, 0xd7, 0x75,
+ 0x02, 0x2d, 0x49, 0xd5, 0x15, 0x5b, 0xf1, 0xd9,
+ 0x4d, 0x60, 0x7d, 0x62, 0xa5, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0x02, 0x7f, 0x6d, 0x45, 0x23, 0xeb,
+ 0x95, 0x17, 0x34, 0x88, 0xf6, 0x91, 0xc7, 0x3f,
+ 0x48, 0x5a, 0xe0, 0x87, 0x63, 0x44, 0xae, 0x84,
+ 0xb2, 0x8c, 0x8a, 0xc8, 0xb2, 0x6f, 0x22, 0xf0,
+ 0xc5, 0x21, 0x61, 0x10, 0xa8, 0x69, 0x09, 0x1e,
+ 0x13, 0x7d, 0x94, 0x52, 0x1b, 0x5c, 0xe4, 0x7b,
+ 0xf0, 0x03, 0x8f, 0xbc, 0x72, 0x09, 0xdf, 0x78,
+ 0x84, 0x3e, 0xb9, 0xe5, 0xe6, 0x31, 0x0a, 0x01,
+ 0xf9, 0x32, 0xf8, 0xd6, 0x57, 0xa3, 0x87, 0xe6,
+ 0xf5, 0x98, 0xbc, 0x8e, 0x41, 0xb9, 0x50, 0x17,
+ 0x7b, 0xd3, 0x97, 0x5a, 0x44, 0x3a, 0xee, 0xff,
+ 0x6b, 0xb3, 0x3a, 0x52, 0xe7, 0xa4, 0x96, 0x9a,
+ 0xf6, 0x83, 0xc8, 0x97, 0x1c, 0x63, 0xa1, 0xd6,
+ 0xb3, 0xa8, 0xb2, 0xc7, 0x73, 0x25, 0x0f, 0x58,
+ 0x36, 0xb9, 0x7a, 0x47, 0xa7, 0x4d, 0x30, 0xfe,
+ 0x4d, 0x74, 0x56, 0xe8, 0xfb, 0xd6, 0x50, 0xe5,
+ 0xe0, 0x28, 0x15, 0x02, 0x41, 0x00, 0xeb, 0x15,
+ 0x62, 0xb6, 0x37, 0x41, 0x7c, 0xc5, 0x00, 0x22,
+ 0x2c, 0x5a, 0x5e, 0xe4, 0xb2, 0x11, 0x87, 0x89,
+ 0xad, 0xf4, 0x57, 0x68, 0x90, 0xb7, 0x9f, 0xe2,
+ 0x79, 0x20, 0x6b, 0x98, 0x00, 0x0d, 0x3a, 0x3b,
+ 0xc1, 0xcd, 0x36, 0xf9, 0x27, 0xda, 0x40, 0x36,
+ 0x1d, 0xb8, 0x5c, 0x96, 0xeb, 0x04, 0x08, 0xe1,
+ 0x3f, 0xfa, 0x94, 0x8b, 0x0f, 0xa0, 0xff, 0xc1,
+ 0x51, 0xea, 0x90, 0xad, 0x15, 0xc7, 0x02, 0x41,
+ 0x00, 0xd5, 0x06, 0x45, 0xd7, 0x55, 0x63, 0x1a,
+ 0xf0, 0x89, 0x81, 0xae, 0x87, 0x23, 0xa2, 0x39,
+ 0xfe, 0x3d, 0x82, 0xc7, 0xcb, 0x15, 0xb9, 0xe3,
+ 0xe2, 0x5b, 0xc6, 0xd2, 0x55, 0xdd, 0xab, 0x55,
+ 0x29, 0x7c, 0xda, 0x0e, 0x1c, 0x09, 0xfc, 0x73,
+ 0x0d, 0x01, 0xed, 0x6d, 0x2f, 0x05, 0xd0, 0xd5,
+ 0x1d, 0xce, 0x18, 0x7f, 0xb0, 0xc8, 0x47, 0x77,
+ 0xd2, 0xa9, 0x9e, 0xfc, 0x39, 0x4b, 0x3d, 0x94,
+ 0x33, 0x02, 0x41, 0x00, 0x8f, 0x94, 0x09, 0x2d,
+ 0x17, 0x44, 0x75, 0x0a, 0xf1, 0x10, 0xee, 0x1b,
+ 0xe7, 0xd7, 0x2f, 0xf6, 0xca, 0xdc, 0x49, 0x15,
+ 0x72, 0x09, 0x58, 0x51, 0xfe, 0x61, 0xd8, 0xee,
+ 0xf7, 0x27, 0xe7, 0xe8, 0x2c, 0x47, 0xf1, 0x0f,
+ 0x00, 0x63, 0x5e, 0x76, 0xcb, 0x3f, 0x02, 0x19,
+ 0xe6, 0xda, 0xfa, 0x01, 0x05, 0xd7, 0x65, 0x37,
+ 0x0b, 0x60, 0x7f, 0x94, 0x2a, 0x80, 0x8d, 0x22,
+ 0x81, 0x68, 0x65, 0x63, 0x02, 0x41, 0x00, 0xc2,
+ 0xd4, 0x18, 0xde, 0x47, 0x9e, 0xfb, 0x8d, 0x91,
+ 0x05, 0xc5, 0x3c, 0x9d, 0xcf, 0x8a, 0x60, 0xc7,
+ 0x9b, 0x2b, 0xe5, 0xc6, 0xba, 0x1b, 0xfc, 0xf3,
+ 0xd9, 0x54, 0x97, 0xe9, 0xc4, 0x00, 0x80, 0x90,
+ 0x4a, 0xd2, 0x6a, 0xbc, 0x8b, 0x62, 0x22, 0x3c,
+ 0x68, 0x0c, 0xda, 0xdb, 0xe3, 0xd2, 0x76, 0x8e,
+ 0xff, 0x03, 0x12, 0x09, 0x2a, 0xac, 0x21, 0x44,
+ 0xb7, 0x3e, 0x91, 0x9c, 0x09, 0xf6, 0xd7, 0x02,
+ 0x41, 0x00, 0xc0, 0xa1, 0xbb, 0x70, 0xdc, 0xf8,
+ 0xeb, 0x17, 0x61, 0xd4, 0x8c, 0x7c, 0x3b, 0x82,
+ 0x91, 0x58, 0xff, 0xf9, 0x19, 0xac, 0x3a, 0x73,
+ 0xa7, 0x20, 0xe5, 0x22, 0x02, 0xc4, 0xf6, 0xb9,
+ 0xb9, 0x43, 0x53, 0x35, 0x88, 0xe1, 0x05, 0xb6,
+ 0x43, 0x9b, 0x39, 0xc8, 0x04, 0x4d, 0x2b, 0x01,
+ 0xf7, 0xe6, 0x1b, 0x8d, 0x7e, 0x89, 0xe3, 0x43,
+ 0xd4, 0xf3, 0xab, 0x28, 0xd4, 0x5a, 0x1f, 0x20,
+ 0xea, 0xbe
+ };
+
+ std::vector<uint8> input1;
+ std::vector<uint8> input2;
+
+ input1.resize(sizeof(short_integer_with_high_bit));
+ input2.resize(sizeof(short_integer_without_high_bit));
+
+ memcpy(&input1.front(), short_integer_with_high_bit,
+ sizeof(short_integer_with_high_bit));
+ memcpy(&input2.front(), short_integer_without_high_bit,
+ sizeof(short_integer_without_high_bit));
+
+ scoped_ptr<crypto::RSAPrivateKey> keypair1(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input1));
+ scoped_ptr<crypto::RSAPrivateKey> keypair2(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input2));
+ ASSERT_TRUE(keypair1.get());
+ ASSERT_TRUE(keypair2.get());
+
+ std::vector<uint8> output1;
+ std::vector<uint8> output2;
+ ASSERT_TRUE(keypair1->ExportPrivateKey(&output1));
+ ASSERT_TRUE(keypair2->ExportPrivateKey(&output2));
+
+ ASSERT_EQ(input1.size(), output1.size());
+ ASSERT_EQ(input2.size(), output2.size());
+ ASSERT_TRUE(0 == memcmp(&output1.front(), &input1.front(),
+ input1.size()));
+ ASSERT_TRUE(0 == memcmp(&output2.front(), &input2.front(),
+ input2.size()));
+}
+
+TEST(RSAPrivateKeyUnitTest, CreateFromKeyTest) {
+ scoped_ptr<crypto::RSAPrivateKey> key_pair(
+ crypto::RSAPrivateKey::Create(512));
+ ASSERT_TRUE(key_pair.get());
+
+ scoped_ptr<crypto::RSAPrivateKey> key_copy(
+ crypto::RSAPrivateKey::CreateFromKey(key_pair->key()));
+ ASSERT_TRUE(key_copy.get());
+
+ std::vector<uint8> privkey;
+ std::vector<uint8> pubkey;
+ ASSERT_TRUE(key_pair->ExportPrivateKey(&privkey));
+ ASSERT_TRUE(key_pair->ExportPublicKey(&pubkey));
+
+ std::vector<uint8> privkey_copy;
+ std::vector<uint8> pubkey_copy;
+ ASSERT_TRUE(key_copy->ExportPrivateKey(&privkey_copy));
+ ASSERT_TRUE(key_copy->ExportPublicKey(&pubkey_copy));
+
+ ASSERT_EQ(privkey, privkey_copy);
+ ASSERT_EQ(pubkey, pubkey_copy);
+}
+
diff --git a/crypto/scoped_test_nss_chromeos_user.cc b/crypto/scoped_test_nss_chromeos_user.cc
new file mode 100644
index 0000000000..aec25d8dff
--- /dev/null
+++ b/crypto/scoped_test_nss_chromeos_user.cc
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/scoped_test_nss_chromeos_user.h"
+
+#include "base/logging.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+
+namespace crypto {
+
+ScopedTestNSSChromeOSUser::ScopedTestNSSChromeOSUser(
+ const std::string& username_hash)
+ : username_hash_(username_hash), constructed_successfully_(false) {
+ if (!temp_dir_.CreateUniqueTempDir())
+ return;
+ // This opens a software DB in the given folder. In production code that is in
+ // the home folder, but for testing the temp folder is used.
+ constructed_successfully_ =
+ InitializeNSSForChromeOSUser(username_hash, temp_dir_.path());
+}
+
+ScopedTestNSSChromeOSUser::~ScopedTestNSSChromeOSUser() {
+ if (constructed_successfully_)
+ CloseChromeOSUserForTesting(username_hash_);
+}
+
+void ScopedTestNSSChromeOSUser::FinishInit() {
+ DCHECK(constructed_successfully_);
+ if (!ShouldInitializeTPMForChromeOSUser(username_hash_))
+ return;
+ WillInitializeTPMForChromeOSUser(username_hash_);
+ InitializePrivateSoftwareSlotForChromeOSUser(username_hash_);
+}
+
+} // namespace crypto
diff --git a/crypto/scoped_test_nss_chromeos_user.h b/crypto/scoped_test_nss_chromeos_user.h
new file mode 100644
index 0000000000..1638517704
--- /dev/null
+++ b/crypto/scoped_test_nss_chromeos_user.h
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_SCOPED_TEST_NSS_CHROMEOS_USER_H_
+#define CRYPTO_SCOPED_TEST_NSS_CHROMEOS_USER_H_
+
+#include <string>
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "crypto/crypto_export.h"
+
+namespace crypto {
+
+// Opens a persistent NSS software database in a temporary directory for the
+// user with |username_hash|. This database will be used for both the user's
+// public and private slot.
+class CRYPTO_EXPORT_PRIVATE ScopedTestNSSChromeOSUser {
+ public:
+ // Opens the software database and sets the public slot for the user. The
+ // private slot will not be initialized until FinishInit() is called.
+ explicit ScopedTestNSSChromeOSUser(const std::string& username_hash);
+ ~ScopedTestNSSChromeOSUser();
+
+ std::string username_hash() const { return username_hash_; }
+ bool constructed_successfully() const { return constructed_successfully_; }
+
+ // Completes initialization of user. Causes any waiting private slot callbacks
+ // to run, see GetPrivateSlotForChromeOSUser().
+ void FinishInit();
+
+ private:
+ const std::string username_hash_;
+ base::ScopedTempDir temp_dir_;
+ bool constructed_successfully_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTestNSSChromeOSUser);
+};
+
+} // namespace crypto
+
+#endif // CRYPTO_SCOPED_TEST_NSS_CHROMEOS_USER_H_
diff --git a/crypto/scoped_test_nss_db.cc b/crypto/scoped_test_nss_db.cc
new file mode 100644
index 0000000000..452c26d73e
--- /dev/null
+++ b/crypto/scoped_test_nss_db.cc
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/scoped_test_nss_db.h"
+
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+
+namespace crypto {
+
+ScopedTestNSSDB::ScopedTestNSSDB() {
+ EnsureNSSInit();
+ // NSS is allowed to do IO on the current thread since dispatching
+ // to a dedicated thread would still have the affect of blocking
+ // the current thread, due to NSS's internal locking requirements
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ if (!temp_dir_.CreateUniqueTempDir())
+ return;
+
+ const char kTestDescription[] = "Test DB";
+ slot_ = OpenSoftwareNSSDB(temp_dir_.path(), kTestDescription);
+}
+
+ScopedTestNSSDB::~ScopedTestNSSDB() {
+ // Don't close when NSS is < 3.15.1, because it would require an additional
+ // sleep for 1 second after closing the database, due to
+ // http://bugzil.la/875601.
+ if (!NSS_VersionCheck("3.15.1")) {
+ LOG(ERROR) << "NSS version is < 3.15.1, test DB will not be closed.";
+ temp_dir_.Take();
+ return;
+ }
+
+ // NSS is allowed to do IO on the current thread since dispatching
+ // to a dedicated thread would still have the affect of blocking
+ // the current thread, due to NSS's internal locking requirements
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ if (slot_) {
+ SECStatus status = SECMOD_CloseUserDB(slot_.get());
+ if (status != SECSuccess)
+ PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
+ }
+
+ if (!temp_dir_.Delete())
+ LOG(ERROR) << "Could not delete temporary directory.";
+}
+
+} // namespace crypto
diff --git a/crypto/scoped_test_nss_db.h b/crypto/scoped_test_nss_db.h
new file mode 100644
index 0000000000..88c2d55dbe
--- /dev/null
+++ b/crypto/scoped_test_nss_db.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_SCOPED_TEST_NSS_DB_H_
+#define CRYPTO_SCOPED_TEST_NSS_DB_H_
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "crypto/crypto_export.h"
+#include "crypto/scoped_nss_types.h"
+
+namespace crypto {
+
+// Opens a persistent NSS database in a temporary directory.
+// Prior NSS version 3.15.1, because of http://bugzil.la/875601 , the opened DB
+// will not be closed automatically.
+class CRYPTO_EXPORT_PRIVATE ScopedTestNSSDB {
+ public:
+ ScopedTestNSSDB();
+ ~ScopedTestNSSDB();
+
+ bool is_open() const { return slot_; }
+ PK11SlotInfo* slot() const { return slot_.get(); }
+
+ private:
+ base::ScopedTempDir temp_dir_;
+ ScopedPK11Slot slot_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTestNSSDB);
+};
+
+} // namespace crypto
+
+#endif // CRYPTO_SCOPED_TEST_NSS_DB_H_
diff --git a/crypto/scoped_test_system_nss_key_slot.cc b/crypto/scoped_test_system_nss_key_slot.cc
new file mode 100644
index 0000000000..53fbbffc1a
--- /dev/null
+++ b/crypto/scoped_test_system_nss_key_slot.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/scoped_test_system_nss_key_slot.h"
+
+#include "crypto/nss_util_internal.h"
+#include "crypto/scoped_test_nss_db.h"
+
+namespace crypto {
+
+ScopedTestSystemNSSKeySlot::ScopedTestSystemNSSKeySlot()
+ : test_db_(new ScopedTestNSSDB) {
+ if (!test_db_->is_open())
+ return;
+ SetSystemKeySlotForTesting(
+ ScopedPK11Slot(PK11_ReferenceSlot(test_db_->slot())));
+}
+
+ScopedTestSystemNSSKeySlot::~ScopedTestSystemNSSKeySlot() {
+ SetSystemKeySlotForTesting(ScopedPK11Slot());
+}
+
+bool ScopedTestSystemNSSKeySlot::ConstructedSuccessfully() const {
+ return test_db_->is_open();
+}
+
+PK11SlotInfo* ScopedTestSystemNSSKeySlot::slot() const {
+ return test_db_->slot();
+}
+
+} // namespace crypto
diff --git a/crypto/scoped_test_system_nss_key_slot.h b/crypto/scoped_test_system_nss_key_slot.h
new file mode 100644
index 0000000000..ac3b72c4ce
--- /dev/null
+++ b/crypto/scoped_test_system_nss_key_slot.h
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_SCOPED_TEST_SYSTEM_NSS_KEY_SLOT_H_
+#define CRYPTO_SCOPED_TEST_SYSTEM_NSS_KEY_SLOT_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "crypto/crypto_export.h"
+
+// Forward declaration, from <pk11pub.h>
+typedef struct PK11SlotInfoStr PK11SlotInfo;
+
+namespace crypto {
+
+class ScopedTestNSSDB;
+
+// Opens a persistent NSS software database in a temporary directory and sets
+// the test system slot to the opened database. This helper should be created in
+// tests to fake the system token that is usually provided by the Chaps module.
+// |slot| is exposed through |GetSystemNSSKeySlot| and |IsTPMTokenReady| will
+// return true.
+// |InitializeTPMTokenAndSystemSlot|, which triggers the TPM initialization,
+// does not have to be called if this helper is used.
+// At most one instance of this helper must be used at a time.
+class CRYPTO_EXPORT_PRIVATE ScopedTestSystemNSSKeySlot {
+ public:
+ explicit ScopedTestSystemNSSKeySlot();
+ ~ScopedTestSystemNSSKeySlot();
+
+ bool ConstructedSuccessfully() const;
+ PK11SlotInfo* slot() const;
+
+ private:
+ scoped_ptr<ScopedTestNSSDB> test_db_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTestSystemNSSKeySlot);
+};
+
+} // namespace crypto
+
+#endif // CRYPTO_SCOPED_TEST_SYSTEM_NSS_KEY_SLOT_H_
diff --git a/crypto/secure_hash_default.cc b/crypto/secure_hash_default.cc
new file mode 100644
index 0000000000..739b402b6c
--- /dev/null
+++ b/crypto/secure_hash_default.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/secure_hash.h"
+
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "crypto/third_party/nss/chromium-blapi.h"
+#include "crypto/third_party/nss/chromium-sha256.h"
+
+namespace crypto {
+
+namespace {
+
+const char kSHA256Descriptor[] = "NSS";
+
+class SecureHashSHA256NSS : public SecureHash {
+ public:
+ static const int kSecureHashVersion = 1;
+
+ SecureHashSHA256NSS() {
+ SHA256_Begin(&ctx_);
+ }
+
+ ~SecureHashSHA256NSS() override { memset(&ctx_, 0, sizeof(ctx_)); }
+
+ // SecureHash implementation:
+ void Update(const void* input, size_t len) override {
+ SHA256_Update(&ctx_, static_cast<const unsigned char*>(input), len);
+ }
+
+ void Finish(void* output, size_t len) override {
+ SHA256_End(&ctx_, static_cast<unsigned char*>(output), NULL,
+ static_cast<unsigned int>(len));
+ }
+
+ bool Serialize(base::Pickle* pickle) override;
+ bool Deserialize(base::PickleIterator* data_iterator) override;
+
+ private:
+ SHA256Context ctx_;
+};
+
+bool SecureHashSHA256NSS::Serialize(base::Pickle* pickle) {
+ if (!pickle)
+ return false;
+
+ if (!pickle->WriteInt(kSecureHashVersion) ||
+ !pickle->WriteString(kSHA256Descriptor) ||
+ !pickle->WriteBytes(&ctx_, sizeof(ctx_))) {
+ return false;
+ }
+
+ return true;
+}
+
+bool SecureHashSHA256NSS::Deserialize(base::PickleIterator* data_iterator) {
+ int version;
+ if (!data_iterator->ReadInt(&version))
+ return false;
+
+ if (version > kSecureHashVersion)
+ return false; // We don't know how to deal with this.
+
+ std::string type;
+ if (!data_iterator->ReadString(&type))
+ return false;
+
+ if (type != kSHA256Descriptor)
+ return false; // It's the wrong kind.
+
+ const char* data = NULL;
+ if (!data_iterator->ReadBytes(&data, sizeof(ctx_)))
+ return false;
+
+ memcpy(&ctx_, data, sizeof(ctx_));
+
+ return true;
+}
+
+} // namespace
+
+SecureHash* SecureHash::Create(Algorithm algorithm) {
+ switch (algorithm) {
+ case SHA256:
+ return new SecureHashSHA256NSS();
+ default:
+ NOTIMPLEMENTED();
+ return NULL;
+ }
+}
+
+} // namespace crypto
diff --git a/crypto/secure_hash_openssl.cc b/crypto/secure_hash_openssl.cc
new file mode 100644
index 0000000000..1033b8e25e
--- /dev/null
+++ b/crypto/secure_hash_openssl.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/secure_hash.h"
+
+#include <openssl/mem.h>
+#include <openssl/sha.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "crypto/openssl_util.h"
+
+namespace crypto {
+
+namespace {
+
+const char kSHA256Descriptor[] = "OpenSSL";
+
+class SecureHashSHA256OpenSSL : public SecureHash {
+ public:
+ static const int kSecureHashVersion = 1;
+
+ SecureHashSHA256OpenSSL() {
+ SHA256_Init(&ctx_);
+ }
+
+ ~SecureHashSHA256OpenSSL() override {
+ OPENSSL_cleanse(&ctx_, sizeof(ctx_));
+ }
+
+ void Update(const void* input, size_t len) override {
+ SHA256_Update(&ctx_, static_cast<const unsigned char*>(input), len);
+ }
+
+ void Finish(void* output, size_t len) override {
+ ScopedOpenSSLSafeSizeBuffer<SHA256_DIGEST_LENGTH> result(
+ static_cast<unsigned char*>(output), len);
+ SHA256_Final(result.safe_buffer(), &ctx_);
+ }
+
+ bool Serialize(base::Pickle* pickle) override;
+ bool Deserialize(base::PickleIterator* data_iterator) override;
+
+ private:
+ SHA256_CTX ctx_;
+};
+
+bool SecureHashSHA256OpenSSL::Serialize(base::Pickle* pickle) {
+ if (!pickle)
+ return false;
+
+ if (!pickle->WriteInt(kSecureHashVersion) ||
+ !pickle->WriteString(kSHA256Descriptor) ||
+ !pickle->WriteBytes(&ctx_, sizeof(ctx_))) {
+ return false;
+ }
+
+ return true;
+}
+
+bool SecureHashSHA256OpenSSL::Deserialize(base::PickleIterator* data_iterator) {
+ if (!data_iterator)
+ return false;
+
+ int version;
+ if (!data_iterator->ReadInt(&version))
+ return false;
+
+ if (version > kSecureHashVersion)
+ return false; // We don't know how to deal with this.
+
+ std::string type;
+ if (!data_iterator->ReadString(&type))
+ return false;
+
+ if (type != kSHA256Descriptor)
+ return false; // It's the wrong kind.
+
+ const char* data = NULL;
+ if (!data_iterator->ReadBytes(&data, sizeof(ctx_)))
+ return false;
+
+ memcpy(&ctx_, data, sizeof(ctx_));
+
+ return true;
+}
+
+} // namespace
+
+SecureHash* SecureHash::Create(Algorithm algorithm) {
+ switch (algorithm) {
+ case SHA256:
+ return new SecureHashSHA256OpenSSL();
+ default:
+ NOTIMPLEMENTED();
+ return NULL;
+ }
+}
+
+} // namespace crypto
diff --git a/crypto/secure_hash_unittest.cc b/crypto/secure_hash_unittest.cc
new file mode 100644
index 0000000000..facf476e0a
--- /dev/null
+++ b/crypto/secure_hash_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/secure_hash.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "crypto/sha2.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(SecureHashTest, TestUpdate) {
+ // Example B.3 from FIPS 180-2: long message.
+ std::string input3(500000, 'a'); // 'a' repeated half a million times
+ int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c,
+ 0x99, 0x14, 0xfb, 0x92,
+ 0x81, 0xa1, 0xc7, 0xe2,
+ 0x84, 0xd7, 0x3e, 0x67,
+ 0xf1, 0x80, 0x9a, 0x48,
+ 0xa4, 0x97, 0x20, 0x0e,
+ 0x04, 0x6d, 0x39, 0xcc,
+ 0xc7, 0x11, 0x2c, 0xd0 };
+
+ uint8 output3[crypto::kSHA256Length];
+
+ scoped_ptr<crypto::SecureHash> ctx(crypto::SecureHash::Create(
+ crypto::SecureHash::SHA256));
+ ctx->Update(input3.data(), input3.size());
+ ctx->Update(input3.data(), input3.size());
+
+ ctx->Finish(output3, sizeof(output3));
+ for (size_t i = 0; i < crypto::kSHA256Length; i++)
+ EXPECT_EQ(expected3[i], static_cast<int>(output3[i]));
+}
+
+// Save the crypto state mid-stream, and create another instance with the
+// saved state. Then feed the same data afterwards to both.
+// When done, both should have the same hash value.
+TEST(SecureHashTest, TestSerialization) {
+ std::string input1(10001, 'a'); // 'a' repeated 10001 times
+ std::string input2(10001, 'b'); // 'b' repeated 10001 times
+ std::string input3(10001, 'c'); // 'c' repeated 10001 times
+ std::string input4(10001, 'd'); // 'd' repeated 10001 times
+ std::string input5(10001, 'e'); // 'e' repeated 10001 times
+
+ uint8 output1[crypto::kSHA256Length];
+ uint8 output2[crypto::kSHA256Length];
+
+ scoped_ptr<crypto::SecureHash> ctx1(crypto::SecureHash::Create(
+ crypto::SecureHash::SHA256));
+ scoped_ptr<crypto::SecureHash> ctx2(crypto::SecureHash::Create(
+ crypto::SecureHash::SHA256));
+ base::Pickle pickle;
+ ctx1->Update(input1.data(), input1.size());
+ ctx1->Update(input2.data(), input2.size());
+ ctx1->Update(input3.data(), input3.size());
+
+ EXPECT_TRUE(ctx1->Serialize(&pickle));
+ ctx1->Update(input4.data(), input4.size());
+ ctx1->Update(input5.data(), input5.size());
+
+ ctx1->Finish(output1, sizeof(output1));
+
+ base::PickleIterator data_iterator(pickle);
+ EXPECT_TRUE(ctx2->Deserialize(&data_iterator));
+ ctx2->Update(input4.data(), input4.size());
+ ctx2->Update(input5.data(), input5.size());
+
+ ctx2->Finish(output2, sizeof(output2));
+
+ EXPECT_EQ(0, memcmp(output1, output2, crypto::kSHA256Length));
+}
diff --git a/crypto/secure_util.cc b/crypto/secure_util.cc
new file mode 100644
index 0000000000..3fe8aa961a
--- /dev/null
+++ b/crypto/secure_util.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/secure_util.h"
+
+namespace crypto {
+
+bool SecureMemEqual(const void* s1, const void* s2, size_t n) {
+ const unsigned char* s1_ptr = reinterpret_cast<const unsigned char*>(s1);
+ const unsigned char* s2_ptr = reinterpret_cast<const unsigned char*>(s2);
+ unsigned char tmp = 0;
+ for (size_t i = 0; i < n; ++i, ++s1_ptr, ++s2_ptr)
+ tmp |= *s1_ptr ^ *s2_ptr;
+ return (tmp == 0);
+}
+
+} // namespace crypto
+
diff --git a/crypto/secure_util.h b/crypto/secure_util.h
new file mode 100644
index 0000000000..cfe05ca155
--- /dev/null
+++ b/crypto/secure_util.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_SECURE_UTIL_H_
+#define CRYPTO_SECURE_UTIL_H_
+
+#include <stddef.h>
+
+#include "crypto/crypto_export.h"
+
+namespace crypto {
+
+// Performs a constant-time comparison of two strings, returning true if the
+// strings are equal.
+//
+// For cryptographic operations, comparison functions such as memcmp() may
+// expose side-channel information about input, allowing an attacker to
+// perform timing analysis to determine what the expected bits should be. In
+// order to avoid such attacks, the comparison must execute in constant time,
+// so as to not to reveal to the attacker where the difference(s) are.
+// For an example attack, see
+// http://groups.google.com/group/keyczar-discuss/browse_thread/thread/5571eca0948b2a13
+CRYPTO_EXPORT bool SecureMemEqual(const void* s1, const void* s2, size_t n);
+
+} // namespace crypto
+
+#endif // CRYPTO_SECURE_UTIL_H_
+
diff --git a/crypto/sha2.cc b/crypto/sha2.cc
new file mode 100644
index 0000000000..6f36237961
--- /dev/null
+++ b/crypto/sha2.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/sha2.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "crypto/secure_hash.h"
+
+namespace crypto {
+
+void SHA256HashString(const base::StringPiece& str, void* output, size_t len) {
+ scoped_ptr<SecureHash> ctx(SecureHash::Create(SecureHash::SHA256));
+ ctx->Update(str.data(), str.length());
+ ctx->Finish(output, len);
+}
+
+std::string SHA256HashString(const base::StringPiece& str) {
+ std::string output(kSHA256Length, 0);
+ SHA256HashString(str, string_as_array(&output), output.size());
+ return output;
+}
+
+} // namespace crypto
diff --git a/crypto/sha2.h b/crypto/sha2.h
new file mode 100644
index 0000000000..7e279d3e94
--- /dev/null
+++ b/crypto/sha2.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_SHA2_H_
+#define CRYPTO_SHA2_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "crypto/crypto_export.h"
+
+namespace crypto {
+
+// These functions perform SHA-256 operations.
+//
+// Functions for SHA-384 and SHA-512 can be added when the need arises.
+
+static const size_t kSHA256Length = 32; // Length in bytes of a SHA-256 hash.
+
+// Computes the SHA-256 hash of the input string 'str' and stores the first
+// 'len' bytes of the hash in the output buffer 'output'. If 'len' > 32,
+// only 32 bytes (the full hash) are stored in the 'output' buffer.
+CRYPTO_EXPORT void SHA256HashString(const base::StringPiece& str,
+ void* output, size_t len);
+
+// Convenience version of the above that returns the result in a 32-byte
+// string.
+CRYPTO_EXPORT std::string SHA256HashString(const base::StringPiece& str);
+
+} // namespace crypto
+
+#endif // CRYPTO_SHA2_H_
diff --git a/crypto/sha2_unittest.cc b/crypto/sha2_unittest.cc
new file mode 100644
index 0000000000..78da1360d4
--- /dev/null
+++ b/crypto/sha2_unittest.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/sha2.h"
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(Sha256Test, Test1) {
+ // Example B.1 from FIPS 180-2: one-block message.
+ std::string input1 = "abc";
+ int expected1[] = { 0xba, 0x78, 0x16, 0xbf,
+ 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde,
+ 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3,
+ 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61,
+ 0xf2, 0x00, 0x15, 0xad };
+
+ uint8 output1[crypto::kSHA256Length];
+ crypto::SHA256HashString(input1, output1, sizeof(output1));
+ for (size_t i = 0; i < crypto::kSHA256Length; i++)
+ EXPECT_EQ(expected1[i], static_cast<int>(output1[i]));
+
+ uint8 output_truncated1[4]; // 4 bytes == 32 bits
+ crypto::SHA256HashString(input1,
+ output_truncated1, sizeof(output_truncated1));
+ for (size_t i = 0; i < sizeof(output_truncated1); i++)
+ EXPECT_EQ(expected1[i], static_cast<int>(output_truncated1[i]));
+}
+
+TEST(Sha256Test, Test1_String) {
+ // Same as the above, but using the wrapper that returns a std::string.
+ // Example B.1 from FIPS 180-2: one-block message.
+ std::string input1 = "abc";
+ int expected1[] = { 0xba, 0x78, 0x16, 0xbf,
+ 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde,
+ 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3,
+ 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61,
+ 0xf2, 0x00, 0x15, 0xad };
+
+ std::string output1 = crypto::SHA256HashString(input1);
+ ASSERT_EQ(crypto::kSHA256Length, output1.size());
+ for (size_t i = 0; i < crypto::kSHA256Length; i++)
+ EXPECT_EQ(expected1[i], static_cast<uint8>(output1[i]));
+}
+
+TEST(Sha256Test, Test2) {
+ // Example B.2 from FIPS 180-2: multi-block message.
+ std::string input2 =
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ int expected2[] = { 0x24, 0x8d, 0x6a, 0x61,
+ 0xd2, 0x06, 0x38, 0xb8,
+ 0xe5, 0xc0, 0x26, 0x93,
+ 0x0c, 0x3e, 0x60, 0x39,
+ 0xa3, 0x3c, 0xe4, 0x59,
+ 0x64, 0xff, 0x21, 0x67,
+ 0xf6, 0xec, 0xed, 0xd4,
+ 0x19, 0xdb, 0x06, 0xc1 };
+
+ uint8 output2[crypto::kSHA256Length];
+ crypto::SHA256HashString(input2, output2, sizeof(output2));
+ for (size_t i = 0; i < crypto::kSHA256Length; i++)
+ EXPECT_EQ(expected2[i], static_cast<int>(output2[i]));
+
+ uint8 output_truncated2[6];
+ crypto::SHA256HashString(input2,
+ output_truncated2, sizeof(output_truncated2));
+ for (size_t i = 0; i < sizeof(output_truncated2); i++)
+ EXPECT_EQ(expected2[i], static_cast<int>(output_truncated2[i]));
+}
+
+TEST(Sha256Test, Test3) {
+ // Example B.3 from FIPS 180-2: long message.
+ std::string input3(1000000, 'a'); // 'a' repeated a million times
+ int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c,
+ 0x99, 0x14, 0xfb, 0x92,
+ 0x81, 0xa1, 0xc7, 0xe2,
+ 0x84, 0xd7, 0x3e, 0x67,
+ 0xf1, 0x80, 0x9a, 0x48,
+ 0xa4, 0x97, 0x20, 0x0e,
+ 0x04, 0x6d, 0x39, 0xcc,
+ 0xc7, 0x11, 0x2c, 0xd0 };
+
+ uint8 output3[crypto::kSHA256Length];
+ crypto::SHA256HashString(input3, output3, sizeof(output3));
+ for (size_t i = 0; i < crypto::kSHA256Length; i++)
+ EXPECT_EQ(expected3[i], static_cast<int>(output3[i]));
+
+ uint8 output_truncated3[12];
+ crypto::SHA256HashString(input3,
+ output_truncated3, sizeof(output_truncated3));
+ for (size_t i = 0; i < sizeof(output_truncated3); i++)
+ EXPECT_EQ(expected3[i], static_cast<int>(output_truncated3[i]));
+}
diff --git a/crypto/signature_creator_nss.cc b/crypto/signature_creator_nss.cc
new file mode 100644
index 0000000000..da03312881
--- /dev/null
+++ b/crypto/signature_creator_nss.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/signature_creator.h"
+
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "crypto/nss_util.h"
+#include "crypto/rsa_private_key.h"
+
+namespace crypto {
+
+namespace {
+
+SECOidTag ToNSSSigOid(SignatureCreator::HashAlgorithm hash_alg) {
+ switch (hash_alg) {
+ case SignatureCreator::SHA1:
+ return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+ case SignatureCreator::SHA256:
+ return SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+SECOidTag ToNSSHashOid(SignatureCreator::HashAlgorithm hash_alg) {
+ switch (hash_alg) {
+ case SignatureCreator::SHA1:
+ return SEC_OID_SHA1;
+ case SignatureCreator::SHA256:
+ return SEC_OID_SHA256;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+} // namespace
+
+SignatureCreator::~SignatureCreator() {
+ if (sign_context_) {
+ SGN_DestroyContext(sign_context_, PR_TRUE);
+ sign_context_ = NULL;
+ }
+}
+
+// static
+SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key,
+ HashAlgorithm hash_alg) {
+ scoped_ptr<SignatureCreator> result(new SignatureCreator);
+ result->sign_context_ = SGN_NewContext(ToNSSSigOid(hash_alg), key->key());
+ if (!result->sign_context_) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ SECStatus rv = SGN_Begin(result->sign_context_);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ return result.release();
+}
+
+// static
+bool SignatureCreator::Sign(RSAPrivateKey* key,
+ HashAlgorithm hash_alg,
+ const uint8* data,
+ int data_len,
+ std::vector<uint8>* signature) {
+ SECItem data_item;
+ data_item.type = siBuffer;
+ data_item.data = const_cast<unsigned char*>(data);
+ data_item.len = data_len;
+
+ SECItem signature_item;
+ SECStatus rv = SGN_Digest(key->key(), ToNSSHashOid(hash_alg), &signature_item,
+ &data_item);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+ signature->assign(signature_item.data,
+ signature_item.data + signature_item.len);
+ SECITEM_FreeItem(&signature_item, PR_FALSE);
+ return true;
+}
+
+bool SignatureCreator::Update(const uint8* data_part, int data_part_len) {
+ SECStatus rv = SGN_Update(sign_context_, data_part, data_part_len);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+bool SignatureCreator::Final(std::vector<uint8>* signature) {
+ SECItem signature_item;
+ SECStatus rv = SGN_End(sign_context_, &signature_item);
+ if (rv != SECSuccess) {
+ return false;
+ }
+ signature->assign(signature_item.data,
+ signature_item.data + signature_item.len);
+ SECITEM_FreeItem(&signature_item, PR_FALSE);
+ return true;
+}
+
+SignatureCreator::SignatureCreator() : sign_context_(NULL) {
+ EnsureNSSInit();
+}
+
+} // namespace crypto
diff --git a/crypto/signature_creator_openssl.cc b/crypto/signature_creator_openssl.cc
new file mode 100644
index 0000000000..0d90d50044
--- /dev/null
+++ b/crypto/signature_creator_openssl.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/signature_creator.h"
+
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "crypto/openssl_util.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/scoped_openssl_types.h"
+
+namespace crypto {
+
+namespace {
+
+const EVP_MD* ToOpenSSLDigest(SignatureCreator::HashAlgorithm hash_alg) {
+ switch (hash_alg) {
+ case SignatureCreator::SHA1:
+ return EVP_sha1();
+ case SignatureCreator::SHA256:
+ return EVP_sha256();
+ }
+ return NULL;
+}
+
+int ToOpenSSLDigestType(SignatureCreator::HashAlgorithm hash_alg) {
+ switch (hash_alg) {
+ case SignatureCreator::SHA1:
+ return NID_sha1;
+ case SignatureCreator::SHA256:
+ return NID_sha256;
+ }
+ return NID_undef;
+}
+
+} // namespace
+
+// static
+SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key,
+ HashAlgorithm hash_alg) {
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ scoped_ptr<SignatureCreator> result(new SignatureCreator);
+ const EVP_MD* const digest = ToOpenSSLDigest(hash_alg);
+ DCHECK(digest);
+ if (!digest) {
+ return NULL;
+ }
+ if (!EVP_DigestSignInit(result->sign_context_, NULL, digest, NULL,
+ key->key())) {
+ return NULL;
+ }
+ return result.release();
+}
+
+// static
+bool SignatureCreator::Sign(RSAPrivateKey* key,
+ HashAlgorithm hash_alg,
+ const uint8* data,
+ int data_len,
+ std::vector<uint8>* signature) {
+ ScopedRSA rsa_key(EVP_PKEY_get1_RSA(key->key()));
+ if (!rsa_key)
+ return false;
+ signature->resize(RSA_size(rsa_key.get()));
+
+ unsigned int len = 0;
+ if (!RSA_sign(ToOpenSSLDigestType(hash_alg), data, data_len,
+ vector_as_array(signature), &len, rsa_key.get())) {
+ signature->clear();
+ return false;
+ }
+ signature->resize(len);
+ return true;
+}
+
+SignatureCreator::SignatureCreator()
+ : sign_context_(EVP_MD_CTX_create()) {
+}
+
+SignatureCreator::~SignatureCreator() {
+ EVP_MD_CTX_destroy(sign_context_);
+}
+
+bool SignatureCreator::Update(const uint8* data_part, int data_part_len) {
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ return !!EVP_DigestSignUpdate(sign_context_, data_part, data_part_len);
+}
+
+bool SignatureCreator::Final(std::vector<uint8>* signature) {
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ // Determine the maximum length of the signature.
+ size_t len = 0;
+ if (!EVP_DigestSignFinal(sign_context_, NULL, &len)) {
+ signature->clear();
+ return false;
+ }
+ signature->resize(len);
+
+ // Sign it.
+ if (!EVP_DigestSignFinal(sign_context_, vector_as_array(signature), &len)) {
+ signature->clear();
+ return false;
+ }
+ signature->resize(len);
+ return true;
+}
+
+} // namespace crypto
diff --git a/crypto/signature_creator_unittest.cc b/crypto/signature_creator_unittest.cc
new file mode 100644
index 0000000000..694becdcb4
--- /dev/null
+++ b/crypto/signature_creator_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/sha1.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/sha2.h"
+#include "crypto/signature_creator.h"
+#include "crypto/signature_verifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// This is the algorithm ID for SHA-1 with RSA encryption.
+const uint8 kSHA1WithRSAAlgorithmID[] = {
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00
+};
+
+// This is the algorithm ID for SHA-1 with RSA encryption.
+const uint8 kSHA256WithRSAAlgorithmID[] = {
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0B, 0x05, 0x00
+};
+
+}
+
+TEST(SignatureCreatorTest, BasicTest) {
+ // Do a verify round trip.
+ scoped_ptr<crypto::RSAPrivateKey> key_original(
+ crypto::RSAPrivateKey::Create(1024));
+ ASSERT_TRUE(key_original.get());
+
+ std::vector<uint8> key_info;
+ key_original->ExportPrivateKey(&key_info);
+ scoped_ptr<crypto::RSAPrivateKey> key(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_info));
+ ASSERT_TRUE(key.get());
+
+ scoped_ptr<crypto::SignatureCreator> signer(
+ crypto::SignatureCreator::Create(key.get(),
+ crypto::SignatureCreator::SHA1));
+ ASSERT_TRUE(signer.get());
+
+ std::string data("Hello, World!");
+ ASSERT_TRUE(signer->Update(reinterpret_cast<const uint8*>(data.c_str()),
+ data.size()));
+
+ std::vector<uint8> signature;
+ ASSERT_TRUE(signer->Final(&signature));
+
+ std::vector<uint8> public_key_info;
+ ASSERT_TRUE(key_original->ExportPublicKey(&public_key_info));
+
+ crypto::SignatureVerifier verifier;
+ ASSERT_TRUE(verifier.VerifyInit(
+ kSHA1WithRSAAlgorithmID, sizeof(kSHA1WithRSAAlgorithmID),
+ &signature.front(), signature.size(),
+ &public_key_info.front(), public_key_info.size()));
+
+ verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
+ data.size());
+ ASSERT_TRUE(verifier.VerifyFinal());
+}
+
+TEST(SignatureCreatorTest, SignDigestTest) {
+ // Do a verify round trip.
+ scoped_ptr<crypto::RSAPrivateKey> key_original(
+ crypto::RSAPrivateKey::Create(1024));
+ ASSERT_TRUE(key_original.get());
+
+ std::vector<uint8> key_info;
+ key_original->ExportPrivateKey(&key_info);
+ scoped_ptr<crypto::RSAPrivateKey> key(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_info));
+ ASSERT_TRUE(key.get());
+
+ std::string data("Hello, World!");
+ std::string sha1 = base::SHA1HashString(data);
+ // Sign sha1 of the input data.
+ std::vector<uint8> signature;
+ ASSERT_TRUE(crypto::SignatureCreator::Sign(
+ key.get(),
+ crypto::SignatureCreator::SHA1,
+ reinterpret_cast<const uint8*>(sha1.c_str()),
+ sha1.size(),
+ &signature));
+
+ std::vector<uint8> public_key_info;
+ ASSERT_TRUE(key_original->ExportPublicKey(&public_key_info));
+
+ // Verify the input data.
+ crypto::SignatureVerifier verifier;
+ ASSERT_TRUE(verifier.VerifyInit(
+ kSHA1WithRSAAlgorithmID, sizeof(kSHA1WithRSAAlgorithmID),
+ &signature.front(), signature.size(),
+ &public_key_info.front(), public_key_info.size()));
+
+ verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
+ data.size());
+ ASSERT_TRUE(verifier.VerifyFinal());
+}
+
+TEST(SignatureCreatorTest, SignSHA256DigestTest) {
+ // Do a verify round trip.
+ scoped_ptr<crypto::RSAPrivateKey> key_original(
+ crypto::RSAPrivateKey::Create(1024));
+ ASSERT_TRUE(key_original.get());
+
+ std::vector<uint8> key_info;
+ key_original->ExportPrivateKey(&key_info);
+ scoped_ptr<crypto::RSAPrivateKey> key(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_info));
+ ASSERT_TRUE(key.get());
+
+ std::string data("Hello, World!");
+ std::string sha256 = crypto::SHA256HashString(data);
+ // Sign sha256 of the input data.
+ std::vector<uint8> signature;
+ ASSERT_TRUE(crypto::SignatureCreator::Sign(
+ key.get(),
+ crypto::SignatureCreator::HashAlgorithm::SHA256,
+ reinterpret_cast<const uint8*>(sha256.c_str()),
+ sha256.size(),
+ &signature));
+
+ std::vector<uint8> public_key_info;
+ ASSERT_TRUE(key_original->ExportPublicKey(&public_key_info));
+
+ // Verify the input data.
+ crypto::SignatureVerifier verifier;
+ ASSERT_TRUE(verifier.VerifyInit(
+ kSHA256WithRSAAlgorithmID, sizeof(kSHA256WithRSAAlgorithmID),
+ &signature.front(), signature.size(),
+ &public_key_info.front(), public_key_info.size()));
+
+ verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
+ data.size());
+ ASSERT_TRUE(verifier.VerifyFinal());
+}
diff --git a/crypto/signature_verifier_nss.cc b/crypto/signature_verifier_nss.cc
new file mode 100644
index 0000000000..5be620da6b
--- /dev/null
+++ b/crypto/signature_verifier_nss.cc
@@ -0,0 +1,226 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/signature_verifier.h"
+
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+#include <secerr.h>
+#include <sechash.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "crypto/nss_util.h"
+#include "crypto/third_party/nss/chromium-nss.h"
+
+namespace crypto {
+
+namespace {
+
+HASH_HashType ToNSSHashType(SignatureVerifier::HashAlgorithm hash_alg) {
+ switch (hash_alg) {
+ case SignatureVerifier::SHA1:
+ return HASH_AlgSHA1;
+ case SignatureVerifier::SHA256:
+ return HASH_AlgSHA256;
+ }
+ return HASH_AlgNULL;
+}
+
+SECStatus VerifyRSAPSS_End(SECKEYPublicKey* public_key,
+ HASHContext* hash_context,
+ HASH_HashType mask_hash_alg,
+ unsigned int salt_len,
+ const unsigned char* signature,
+ unsigned int signature_len) {
+ unsigned int hash_len = HASH_ResultLenContext(hash_context);
+ std::vector<unsigned char> hash(hash_len);
+ HASH_End(hash_context, &hash[0], &hash_len, hash.size());
+
+ unsigned int modulus_len = SECKEY_PublicKeyStrength(public_key);
+ if (signature_len != modulus_len) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure;
+ }
+ std::vector<unsigned char> enc(signature_len);
+ SECStatus rv = PK11_PubEncryptRaw(public_key, &enc[0],
+ const_cast<unsigned char*>(signature),
+ signature_len, NULL);
+ if (rv != SECSuccess) {
+ LOG(WARNING) << "PK11_PubEncryptRaw failed";
+ return rv;
+ }
+ return emsa_pss_verify(&hash[0], &enc[0], enc.size(),
+ HASH_GetType(hash_context), mask_hash_alg,
+ salt_len);
+}
+
+} // namespace
+
+SignatureVerifier::SignatureVerifier()
+ : vfy_context_(NULL),
+ hash_alg_(SHA1),
+ mask_hash_alg_(SHA1),
+ salt_len_(0),
+ public_key_(NULL),
+ hash_context_(NULL) {
+ EnsureNSSInit();
+}
+
+SignatureVerifier::~SignatureVerifier() {
+ Reset();
+}
+
+bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm,
+ int signature_algorithm_len,
+ const uint8* signature,
+ int signature_len,
+ const uint8* public_key_info,
+ int public_key_info_len) {
+ if (vfy_context_ || hash_context_)
+ return false;
+
+ signature_.assign(signature, signature + signature_len);
+
+ SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info,
+ public_key_info_len);
+ if (!public_key)
+ return false;
+
+ PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ SECKEY_DestroyPublicKey(public_key);
+ return false;
+ }
+
+ SECItem sig_alg_der;
+ sig_alg_der.type = siBuffer;
+ sig_alg_der.data = const_cast<uint8*>(signature_algorithm);
+ sig_alg_der.len = signature_algorithm_len;
+ SECAlgorithmID sig_alg_id;
+ SECStatus rv;
+ rv = SEC_QuickDERDecodeItem(arena, &sig_alg_id,
+ SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
+ &sig_alg_der);
+ if (rv != SECSuccess) {
+ SECKEY_DestroyPublicKey(public_key);
+ PORT_FreeArena(arena, PR_TRUE);
+ return false;
+ }
+
+ SECItem sig;
+ sig.type = siBuffer;
+ sig.data = const_cast<uint8*>(signature);
+ sig.len = signature_len;
+ SECOidTag hash_alg_tag;
+ vfy_context_ = VFY_CreateContextWithAlgorithmID(public_key, &sig,
+ &sig_alg_id, &hash_alg_tag,
+ NULL);
+ SECKEY_DestroyPublicKey(public_key); // Done with public_key.
+ PORT_FreeArena(arena, PR_TRUE); // Done with sig_alg_id.
+ if (!vfy_context_) {
+ // A corrupted RSA signature could be detected without the data, so
+ // VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE
+ // (-8182).
+ return false;
+ }
+
+ rv = VFY_Begin(vfy_context_);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool SignatureVerifier::VerifyInitRSAPSS(HashAlgorithm hash_alg,
+ HashAlgorithm mask_hash_alg,
+ int salt_len,
+ const uint8* signature,
+ int signature_len,
+ const uint8* public_key_info,
+ int public_key_info_len) {
+ if (vfy_context_ || hash_context_)
+ return false;
+
+ signature_.assign(signature, signature + signature_len);
+
+ SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info,
+ public_key_info_len);
+ if (!public_key)
+ return false;
+
+ public_key_ = public_key;
+ hash_alg_ = hash_alg;
+ mask_hash_alg_ = mask_hash_alg;
+ salt_len_ = salt_len;
+ hash_context_ = HASH_Create(ToNSSHashType(hash_alg_));
+ if (!hash_context_)
+ return false;
+ HASH_Begin(hash_context_);
+ return true;
+}
+
+void SignatureVerifier::VerifyUpdate(const uint8* data_part,
+ int data_part_len) {
+ if (vfy_context_) {
+ SECStatus rv = VFY_Update(vfy_context_, data_part, data_part_len);
+ DCHECK_EQ(SECSuccess, rv);
+ } else {
+ HASH_Update(hash_context_, data_part, data_part_len);
+ }
+}
+
+bool SignatureVerifier::VerifyFinal() {
+ SECStatus rv;
+ if (vfy_context_) {
+ rv = VFY_End(vfy_context_);
+ } else {
+ rv = VerifyRSAPSS_End(public_key_, hash_context_,
+ ToNSSHashType(mask_hash_alg_), salt_len_,
+ signature_.data(),
+ signature_.size());
+ }
+ Reset();
+
+ // If signature verification fails, the error code is
+ // SEC_ERROR_BAD_SIGNATURE (-8182).
+ return (rv == SECSuccess);
+}
+
+// static
+SECKEYPublicKey* SignatureVerifier::DecodePublicKeyInfo(
+ const uint8* public_key_info,
+ int public_key_info_len) {
+ CERTSubjectPublicKeyInfo* spki = NULL;
+ SECItem spki_der;
+ spki_der.type = siBuffer;
+ spki_der.data = const_cast<uint8*>(public_key_info);
+ spki_der.len = public_key_info_len;
+ spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der);
+ if (!spki)
+ return NULL;
+ SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki);
+ SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki.
+ return public_key;
+}
+
+void SignatureVerifier::Reset() {
+ if (vfy_context_) {
+ VFY_DestroyContext(vfy_context_, PR_TRUE);
+ vfy_context_ = NULL;
+ }
+ if (hash_context_) {
+ HASH_Destroy(hash_context_);
+ hash_context_ = NULL;
+ }
+ if (public_key_) {
+ SECKEY_DestroyPublicKey(public_key_);
+ public_key_ = NULL;
+ }
+ signature_.clear();
+}
+
+} // namespace crypto
diff --git a/crypto/signature_verifier_openssl.cc b/crypto/signature_verifier_openssl.cc
new file mode 100644
index 0000000000..a33d665ec1
--- /dev/null
+++ b/crypto/signature_verifier_openssl.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/signature_verifier.h"
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "crypto/openssl_util.h"
+#include "crypto/scoped_openssl_types.h"
+
+namespace crypto {
+
+namespace {
+
+const EVP_MD* ToOpenSSLDigest(SignatureVerifier::HashAlgorithm hash_alg) {
+ switch (hash_alg) {
+ case SignatureVerifier::SHA1:
+ return EVP_sha1();
+ case SignatureVerifier::SHA256:
+ return EVP_sha256();
+ }
+ return NULL;
+}
+
+} // namespace
+
+struct SignatureVerifier::VerifyContext {
+ ScopedEVP_MD_CTX ctx;
+};
+
+SignatureVerifier::SignatureVerifier()
+ : verify_context_(NULL) {
+}
+
+SignatureVerifier::~SignatureVerifier() {
+ Reset();
+}
+
+bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm,
+ int signature_algorithm_len,
+ const uint8* signature,
+ int signature_len,
+ const uint8* public_key_info,
+ int public_key_info_len) {
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ ScopedOpenSSL<X509_ALGOR, X509_ALGOR_free> algorithm(
+ d2i_X509_ALGOR(NULL, &signature_algorithm, signature_algorithm_len));
+ if (!algorithm.get())
+ return false;
+ int nid = OBJ_obj2nid(algorithm.get()->algorithm);
+ const EVP_MD* digest;
+ if (nid == NID_ecdsa_with_SHA1) {
+ digest = EVP_sha1();
+ } else if (nid == NID_ecdsa_with_SHA256) {
+ digest = EVP_sha256();
+ } else {
+ // This works for PKCS #1 v1.5 RSA signatures, but not for ECDSA
+ // signatures.
+ digest = EVP_get_digestbyobj(algorithm.get()->algorithm);
+ }
+ if (!digest)
+ return false;
+
+ return CommonInit(digest, signature, signature_len, public_key_info,
+ public_key_info_len, NULL);
+}
+
+bool SignatureVerifier::VerifyInitRSAPSS(HashAlgorithm hash_alg,
+ HashAlgorithm mask_hash_alg,
+ int salt_len,
+ const uint8* signature,
+ int signature_len,
+ const uint8* public_key_info,
+ int public_key_info_len) {
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ const EVP_MD* const digest = ToOpenSSLDigest(hash_alg);
+ DCHECK(digest);
+ if (!digest) {
+ return false;
+ }
+
+ EVP_PKEY_CTX* pkey_ctx;
+ if (!CommonInit(digest, signature, signature_len, public_key_info,
+ public_key_info_len, &pkey_ctx)) {
+ return false;
+ }
+
+ int rv = EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
+ if (rv != 1)
+ return false;
+ const EVP_MD* const mgf_digest = ToOpenSSLDigest(mask_hash_alg);
+ DCHECK(mgf_digest);
+ if (!mgf_digest) {
+ return false;
+ }
+ rv = EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf_digest);
+ if (rv != 1)
+ return false;
+ rv = EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len);
+ return rv == 1;
+}
+
+void SignatureVerifier::VerifyUpdate(const uint8* data_part,
+ int data_part_len) {
+ DCHECK(verify_context_);
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ int rv = EVP_DigestVerifyUpdate(verify_context_->ctx.get(),
+ data_part, data_part_len);
+ DCHECK_EQ(rv, 1);
+}
+
+bool SignatureVerifier::VerifyFinal() {
+ DCHECK(verify_context_);
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ int rv = EVP_DigestVerifyFinal(verify_context_->ctx.get(),
+ vector_as_array(&signature_),
+ signature_.size());
+ DCHECK_EQ(static_cast<int>(!!rv), rv);
+ Reset();
+ return rv == 1;
+}
+
+bool SignatureVerifier::CommonInit(const EVP_MD* digest,
+ const uint8* signature,
+ int signature_len,
+ const uint8* public_key_info,
+ int public_key_info_len,
+ EVP_PKEY_CTX** pkey_ctx) {
+ if (verify_context_)
+ return false;
+
+ verify_context_ = new VerifyContext;
+
+ signature_.assign(signature, signature + signature_len);
+
+ const uint8_t* ptr = public_key_info;
+ ScopedEVP_PKEY public_key(d2i_PUBKEY(nullptr, &ptr, public_key_info_len));
+ if (!public_key.get() || ptr != public_key_info + public_key_info_len)
+ return false;
+
+ verify_context_->ctx.reset(EVP_MD_CTX_create());
+ int rv = EVP_DigestVerifyInit(verify_context_->ctx.get(), pkey_ctx,
+ digest, nullptr, public_key.get());
+ return rv == 1;
+}
+
+void SignatureVerifier::Reset() {
+ delete verify_context_;
+ verify_context_ = NULL;
+ signature_.clear();
+}
+
+} // namespace crypto
diff --git a/crypto/signature_verifier_unittest.cc b/crypto/signature_verifier_unittest.cc
new file mode 100644
index 0000000000..a661ff7f8a
--- /dev/null
+++ b/crypto/signature_verifier_unittest.cc
@@ -0,0 +1,1167 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/signature_verifier.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(SignatureVerifierTest, BasicTest) {
+ // The input data in this test comes from real certificates.
+ //
+ // tbs_certificate ("to-be-signed certificate", the part of a certificate
+ // that is signed), signature_algorithm, and algorithm come from the
+ // certificate of bugs.webkit.org.
+ //
+ // public_key_info comes from the certificate of the issuer, Go Daddy Secure
+ // Certification Authority.
+ //
+ // The bytes in the array initializers are formatted to expose the DER
+ // encoding of the ASN.1 structures.
+
+ // The data that is signed is the following ASN.1 structure:
+ // TBSCertificate ::= SEQUENCE {
+ // ... -- omitted, not important
+ // }
+ const uint8 tbs_certificate[1017] = {
+ 0x30, 0x82, 0x03, 0xf5, // a SEQUENCE of length 1013 (0x3f5)
+ 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x43, 0xdd, 0x63, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x30, 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31,
+ 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63,
+ 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64,
+ 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64,
+ 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32,
+ 0x38, 0x37, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x33, 0x31, 0x38,
+ 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+ 0x33, 0x31, 0x38, 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x30, 0x79,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
+ 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x12,
+ 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x75, 0x70,
+ 0x65, 0x72, 0x74, 0x69, 0x6e, 0x6f, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x0c, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x46, 0x6f, 0x72,
+ 0x67, 0x65, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72,
+ 0x67, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xa7, 0x62, 0x79, 0x41, 0xda, 0x28,
+ 0xf2, 0xc0, 0x4f, 0xe0, 0x25, 0xaa, 0xa1, 0x2e, 0x3b, 0x30, 0x94, 0xb5,
+ 0xc9, 0x26, 0x3a, 0x1b, 0xe2, 0xd0, 0xcc, 0xa2, 0x95, 0xe2, 0x91, 0xc0,
+ 0xf0, 0x40, 0x9e, 0x27, 0x6e, 0xbd, 0x6e, 0xde, 0x7c, 0xb6, 0x30, 0x5c,
+ 0xb8, 0x9b, 0x01, 0x2f, 0x92, 0x04, 0xa1, 0xef, 0x4a, 0xb1, 0x6c, 0xb1,
+ 0x7e, 0x8e, 0xcd, 0xa6, 0xf4, 0x40, 0x73, 0x1f, 0x2c, 0x96, 0xad, 0xff,
+ 0x2a, 0x6d, 0x0e, 0xba, 0x52, 0x84, 0x83, 0xb0, 0x39, 0xee, 0xc9, 0x39,
+ 0xdc, 0x1e, 0x34, 0xd0, 0xd8, 0x5d, 0x7a, 0x09, 0xac, 0xa9, 0xee, 0xca,
+ 0x65, 0xf6, 0x85, 0x3a, 0x6b, 0xee, 0xe4, 0x5c, 0x5e, 0xf8, 0xda, 0xd1,
+ 0xce, 0x88, 0x47, 0xcd, 0x06, 0x21, 0xe0, 0xb9, 0x4b, 0xe4, 0x07, 0xcb,
+ 0x57, 0xdc, 0xca, 0x99, 0x54, 0xf7, 0x0e, 0xd5, 0x17, 0x95, 0x05, 0x2e,
+ 0xe9, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xce, 0x30,
+ 0x82, 0x01, 0xca, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02,
+ 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03,
+ 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16,
+ 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x57,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x50, 0x30, 0x4e, 0x30, 0x4c, 0xa0,
+ 0x4a, 0xa0, 0x48, 0x86, 0x46, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73,
+ 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x65, 0x78, 0x74, 0x65, 0x6e,
+ 0x64, 0x65, 0x64, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x33, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b,
+ 0x30, 0x49, 0x30, 0x47, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd,
+ 0x6d, 0x01, 0x07, 0x17, 0x02, 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x30, 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x73, 0x30, 0x71, 0x30, 0x23, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
+ 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4a, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64,
+ 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69,
+ 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65,
+ 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72, 0x74,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48,
+ 0xdf, 0x60, 0x32, 0xcc, 0x89, 0x01, 0xb6, 0xdc, 0x2f, 0xe3, 0x73, 0xb5,
+ 0x9c, 0x16, 0x58, 0x32, 0x68, 0xa9, 0xc3, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32,
+ 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee, 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76,
+ 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04,
+ 0x1c, 0x30, 0x1a, 0x82, 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69,
+ 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x82, 0x0a, 0x77, 0x65, 0x62, 0x6b, 0x69,
+ 0x74, 0x2e, 0x6f, 0x72, 0x67
+ };
+
+ // The signature algorithm is specified as the following ASN.1 structure:
+ // AlgorithmIdentifier ::= SEQUENCE {
+ // algorithm OBJECT IDENTIFIER,
+ // parameters ANY DEFINED BY algorithm OPTIONAL }
+ //
+ const uint8 signature_algorithm[15] = {
+ 0x30, 0x0d, // a SEQUENCE of length 13 (0xd)
+ 0x06, 0x09, // an OBJECT IDENTIFIER of length 9
+ // 1.2.840.113549.1.1.5 - sha1WithRSAEncryption
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, // a NULL of length 0
+ };
+
+ // RSA signature, a big integer in the big-endian byte order.
+ const uint8 signature[256] = {
+ 0x1e, 0x6a, 0xe7, 0xe0, 0x4f, 0xe7, 0x4d, 0xd0, 0x69, 0x7c, 0xf8, 0x8f,
+ 0x99, 0xb4, 0x18, 0x95, 0x36, 0x24, 0x0f, 0x0e, 0xa3, 0xea, 0x34, 0x37,
+ 0xf4, 0x7d, 0xd5, 0x92, 0x35, 0x53, 0x72, 0x76, 0x3f, 0x69, 0xf0, 0x82,
+ 0x56, 0xe3, 0x94, 0x7a, 0x1d, 0x1a, 0x81, 0xaf, 0x9f, 0xc7, 0x43, 0x01,
+ 0x64, 0xd3, 0x7c, 0x0d, 0xc8, 0x11, 0x4e, 0x4a, 0xe6, 0x1a, 0xc3, 0x01,
+ 0x74, 0xe8, 0x35, 0x87, 0x5c, 0x61, 0xaa, 0x8a, 0x46, 0x06, 0xbe, 0x98,
+ 0x95, 0x24, 0x9e, 0x01, 0xe3, 0xe6, 0xa0, 0x98, 0xee, 0x36, 0x44, 0x56,
+ 0x8d, 0x23, 0x9c, 0x65, 0xea, 0x55, 0x6a, 0xdf, 0x66, 0xee, 0x45, 0xe8,
+ 0xa0, 0xe9, 0x7d, 0x9a, 0xba, 0x94, 0xc5, 0xc8, 0xc4, 0x4b, 0x98, 0xff,
+ 0x9a, 0x01, 0x31, 0x6d, 0xf9, 0x2b, 0x58, 0xe7, 0xe7, 0x2a, 0xc5, 0x4d,
+ 0xbb, 0xbb, 0xcd, 0x0d, 0x70, 0xe1, 0xad, 0x03, 0xf5, 0xfe, 0xf4, 0x84,
+ 0x71, 0x08, 0xd2, 0xbc, 0x04, 0x7b, 0x26, 0x1c, 0xa8, 0x0f, 0x9c, 0xd8,
+ 0x12, 0x6a, 0x6f, 0x2b, 0x67, 0xa1, 0x03, 0x80, 0x9a, 0x11, 0x0b, 0xe9,
+ 0xe0, 0xb5, 0xb3, 0xb8, 0x19, 0x4e, 0x0c, 0xa4, 0xd9, 0x2b, 0x3b, 0xc2,
+ 0xca, 0x20, 0xd3, 0x0c, 0xa4, 0xff, 0x93, 0x13, 0x1f, 0xfc, 0xba, 0x94,
+ 0x93, 0x8c, 0x64, 0x15, 0x2e, 0x28, 0xa9, 0x55, 0x8c, 0x2c, 0x48, 0xd3,
+ 0xd3, 0xc1, 0x50, 0x69, 0x19, 0xe8, 0x34, 0xd3, 0xf1, 0x04, 0x9f, 0x0a,
+ 0x7a, 0x21, 0x87, 0xbf, 0xb9, 0x59, 0x37, 0x2e, 0xf4, 0x71, 0xa5, 0x3e,
+ 0xbe, 0xcd, 0x70, 0x83, 0x18, 0xf8, 0x8a, 0x72, 0x85, 0x45, 0x1f, 0x08,
+ 0x01, 0x6f, 0x37, 0xf5, 0x2b, 0x7b, 0xea, 0xb9, 0x8b, 0xa3, 0xcc, 0xfd,
+ 0x35, 0x52, 0xdd, 0x66, 0xde, 0x4f, 0x30, 0xc5, 0x73, 0x81, 0xb6, 0xe8,
+ 0x3c, 0xd8, 0x48, 0x8a
+ };
+
+ // The public key is specified as the following ASN.1 structure:
+ // SubjectPublicKeyInfo ::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING }
+ const uint8 public_key_info[294] = {
+ 0x30, 0x82, 0x01, 0x22, // a SEQUENCE of length 290 (0x122)
+ // algorithm
+ 0x30, 0x0d, // a SEQUENCE of length 13
+ 0x06, 0x09, // an OBJECT IDENTIFIER of length 9
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, // a NULL of length 0
+ // subjectPublicKey
+ 0x03, 0x82, 0x01, 0x0f, // a BIT STRING of length 271 (0x10f)
+ 0x00, // number of unused bits
+ 0x30, 0x82, 0x01, 0x0a, // a SEQUENCE of length 266 (0x10a)
+ // modulus
+ 0x02, 0x82, 0x01, 0x01, // an INTEGER of length 257 (0x101)
+ 0x00, 0xc4, 0x2d, 0xd5, 0x15, 0x8c, 0x9c, 0x26, 0x4c, 0xec,
+ 0x32, 0x35, 0xeb, 0x5f, 0xb8, 0x59, 0x01, 0x5a, 0xa6, 0x61,
+ 0x81, 0x59, 0x3b, 0x70, 0x63, 0xab, 0xe3, 0xdc, 0x3d, 0xc7,
+ 0x2a, 0xb8, 0xc9, 0x33, 0xd3, 0x79, 0xe4, 0x3a, 0xed, 0x3c,
+ 0x30, 0x23, 0x84, 0x8e, 0xb3, 0x30, 0x14, 0xb6, 0xb2, 0x87,
+ 0xc3, 0x3d, 0x95, 0x54, 0x04, 0x9e, 0xdf, 0x99, 0xdd, 0x0b,
+ 0x25, 0x1e, 0x21, 0xde, 0x65, 0x29, 0x7e, 0x35, 0xa8, 0xa9,
+ 0x54, 0xeb, 0xf6, 0xf7, 0x32, 0x39, 0xd4, 0x26, 0x55, 0x95,
+ 0xad, 0xef, 0xfb, 0xfe, 0x58, 0x86, 0xd7, 0x9e, 0xf4, 0x00,
+ 0x8d, 0x8c, 0x2a, 0x0c, 0xbd, 0x42, 0x04, 0xce, 0xa7, 0x3f,
+ 0x04, 0xf6, 0xee, 0x80, 0xf2, 0xaa, 0xef, 0x52, 0xa1, 0x69,
+ 0x66, 0xda, 0xbe, 0x1a, 0xad, 0x5d, 0xda, 0x2c, 0x66, 0xea,
+ 0x1a, 0x6b, 0xbb, 0xe5, 0x1a, 0x51, 0x4a, 0x00, 0x2f, 0x48,
+ 0xc7, 0x98, 0x75, 0xd8, 0xb9, 0x29, 0xc8, 0xee, 0xf8, 0x66,
+ 0x6d, 0x0a, 0x9c, 0xb3, 0xf3, 0xfc, 0x78, 0x7c, 0xa2, 0xf8,
+ 0xa3, 0xf2, 0xb5, 0xc3, 0xf3, 0xb9, 0x7a, 0x91, 0xc1, 0xa7,
+ 0xe6, 0x25, 0x2e, 0x9c, 0xa8, 0xed, 0x12, 0x65, 0x6e, 0x6a,
+ 0xf6, 0x12, 0x44, 0x53, 0x70, 0x30, 0x95, 0xc3, 0x9c, 0x2b,
+ 0x58, 0x2b, 0x3d, 0x08, 0x74, 0x4a, 0xf2, 0xbe, 0x51, 0xb0,
+ 0xbf, 0x87, 0xd0, 0x4c, 0x27, 0x58, 0x6b, 0xb5, 0x35, 0xc5,
+ 0x9d, 0xaf, 0x17, 0x31, 0xf8, 0x0b, 0x8f, 0xee, 0xad, 0x81,
+ 0x36, 0x05, 0x89, 0x08, 0x98, 0xcf, 0x3a, 0xaf, 0x25, 0x87,
+ 0xc0, 0x49, 0xea, 0xa7, 0xfd, 0x67, 0xf7, 0x45, 0x8e, 0x97,
+ 0xcc, 0x14, 0x39, 0xe2, 0x36, 0x85, 0xb5, 0x7e, 0x1a, 0x37,
+ 0xfd, 0x16, 0xf6, 0x71, 0x11, 0x9a, 0x74, 0x30, 0x16, 0xfe,
+ 0x13, 0x94, 0xa3, 0x3f, 0x84, 0x0d, 0x4f,
+ // public exponent
+ 0x02, 0x03, // an INTEGER of length 3
+ 0x01, 0x00, 0x01
+ };
+
+ // We use the signature verifier to perform four signature verification
+ // tests.
+ crypto::SignatureVerifier verifier;
+ bool ok;
+
+ // Test 1: feed all of the data to the verifier at once (a single
+ // VerifyUpdate call).
+ ok = verifier.VerifyInit(signature_algorithm,
+ sizeof(signature_algorithm),
+ signature, sizeof(signature),
+ public_key_info, sizeof(public_key_info));
+ EXPECT_TRUE(ok);
+ verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate));
+ ok = verifier.VerifyFinal();
+ EXPECT_TRUE(ok);
+
+ // Test 2: feed the data to the verifier in three parts (three VerifyUpdate
+ // calls).
+ ok = verifier.VerifyInit(signature_algorithm,
+ sizeof(signature_algorithm),
+ signature, sizeof(signature),
+ public_key_info, sizeof(public_key_info));
+ EXPECT_TRUE(ok);
+ verifier.VerifyUpdate(tbs_certificate, 256);
+ verifier.VerifyUpdate(tbs_certificate + 256, 256);
+ verifier.VerifyUpdate(tbs_certificate + 512, sizeof(tbs_certificate) - 512);
+ ok = verifier.VerifyFinal();
+ EXPECT_TRUE(ok);
+
+ // Test 3: verify the signature with incorrect data.
+ uint8 bad_tbs_certificate[sizeof(tbs_certificate)];
+ memcpy(bad_tbs_certificate, tbs_certificate, sizeof(tbs_certificate));
+ bad_tbs_certificate[10] += 1; // Corrupt one byte of the data.
+ ok = verifier.VerifyInit(signature_algorithm,
+ sizeof(signature_algorithm),
+ signature, sizeof(signature),
+ public_key_info, sizeof(public_key_info));
+ EXPECT_TRUE(ok);
+ verifier.VerifyUpdate(bad_tbs_certificate, sizeof(bad_tbs_certificate));
+ ok = verifier.VerifyFinal();
+ EXPECT_FALSE(ok);
+
+ // Test 4: verify a bad signature.
+ uint8 bad_signature[sizeof(signature)];
+ memcpy(bad_signature, signature, sizeof(signature));
+ bad_signature[10] += 1; // Corrupt one byte of the signature.
+ ok = verifier.VerifyInit(signature_algorithm,
+ sizeof(signature_algorithm),
+ bad_signature, sizeof(bad_signature),
+ public_key_info, sizeof(public_key_info));
+
+ // A crypto library (e.g., NSS) may detect that the signature is corrupted
+ // and cause VerifyInit to return false, so it is fine for 'ok' to be false.
+ if (ok) {
+ verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate));
+ ok = verifier.VerifyFinal();
+ EXPECT_FALSE(ok);
+ }
+
+ // Test 5: import an invalid key.
+ uint8_t bad_public_key_info[sizeof(public_key_info)];
+ memcpy(bad_public_key_info, public_key_info, sizeof(public_key_info));
+ bad_public_key_info[0] += 1; // Corrupt part of the SPKI syntax.
+ ok = verifier.VerifyInit(signature_algorithm,
+ sizeof(signature_algorithm),
+ signature, sizeof(signature),
+ bad_public_key_info, sizeof(bad_public_key_info));
+ EXPECT_FALSE(ok);
+
+ // Test 6: import a key with extra data.
+ uint8_t long_public_key_info[sizeof(public_key_info) + 5];
+ memset(long_public_key_info, 0, sizeof(long_public_key_info));
+ memcpy(long_public_key_info, public_key_info, sizeof(public_key_info));
+ ok = verifier.VerifyInit(signature_algorithm,
+ sizeof(signature_algorithm),
+ signature, sizeof(signature),
+ long_public_key_info, sizeof(long_public_key_info));
+ EXPECT_FALSE(ok);
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// RSA-PSS signature verification known answer test
+//
+//////////////////////////////////////////////////////////////////////
+
+// The following RSA-PSS signature test vectors come from the pss-vect.txt
+// file downloaded from
+// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip.
+//
+// For each key, 6 random messages of length between 1 and 256 octets have
+// been RSASSA-PSS signed.
+//
+// Hash function: SHA-1
+// Mask generation function: MGF1 with SHA-1
+// Salt length: 20 octets
+
+// Example 1: A 1024-bit RSA Key Pair"
+
+// RSA modulus n:
+static const char rsa_modulus_n_1[] =
+ "a5 6e 4a 0e 70 10 17 58 9a 51 87 dc 7e a8 41 d1 "
+ "56 f2 ec 0e 36 ad 52 a4 4d fe b1 e6 1f 7a d9 91 "
+ "d8 c5 10 56 ff ed b1 62 b4 c0 f2 83 a1 2a 88 a3 "
+ "94 df f5 26 ab 72 91 cb b3 07 ce ab fc e0 b1 df "
+ "d5 cd 95 08 09 6d 5b 2b 8b 6d f5 d6 71 ef 63 77 "
+ "c0 92 1c b2 3c 27 0a 70 e2 59 8e 6f f8 9d 19 f1 "
+ "05 ac c2 d3 f0 cb 35 f2 92 80 e1 38 6b 6f 64 c4 "
+ "ef 22 e1 e1 f2 0d 0c e8 cf fb 22 49 bd 9a 21 37 ";
+// RSA public exponent e: "
+static const char rsa_public_exponent_e_1[] =
+ "01 00 01 ";
+
+// RSASSA-PSS Signature Example 1.1
+// Message to be signed:
+static const char message_1_1[] =
+ "cd c8 7d a2 23 d7 86 df 3b 45 e0 bb bc 72 13 26 "
+ "d1 ee 2a f8 06 cc 31 54 75 cc 6f 0d 9c 66 e1 b6 "
+ "23 71 d4 5c e2 39 2e 1a c9 28 44 c3 10 10 2f 15 "
+ "6a 0d 8d 52 c1 f4 c4 0b a3 aa 65 09 57 86 cb 76 "
+ "97 57 a6 56 3b a9 58 fe d0 bc c9 84 e8 b5 17 a3 "
+ "d5 f5 15 b2 3b 8a 41 e7 4a a8 67 69 3f 90 df b0 "
+ "61 a6 e8 6d fa ae e6 44 72 c0 0e 5f 20 94 57 29 "
+ "cb eb e7 7f 06 ce 78 e0 8f 40 98 fb a4 1f 9d 61 "
+ "93 c0 31 7e 8b 60 d4 b6 08 4a cb 42 d2 9e 38 08 "
+ "a3 bc 37 2d 85 e3 31 17 0f cb f7 cc 72 d0 b7 1c "
+ "29 66 48 b3 a4 d1 0f 41 62 95 d0 80 7a a6 25 ca "
+ "b2 74 4f d9 ea 8f d2 23 c4 25 37 02 98 28 bd 16 "
+ "be 02 54 6f 13 0f d2 e3 3b 93 6d 26 76 e0 8a ed "
+ "1b 73 31 8b 75 0a 01 67 d0 ";
+// Salt:
+static const char salt_1_1[] =
+ "de e9 59 c7 e0 64 11 36 14 20 ff 80 18 5e d5 7f "
+ "3e 67 76 af ";
+// Signature:
+static const char signature_1_1[] =
+ "90 74 30 8f b5 98 e9 70 1b 22 94 38 8e 52 f9 71 "
+ "fa ac 2b 60 a5 14 5a f1 85 df 52 87 b5 ed 28 87 "
+ "e5 7c e7 fd 44 dc 86 34 e4 07 c8 e0 e4 36 0b c2 "
+ "26 f3 ec 22 7f 9d 9e 54 63 8e 8d 31 f5 05 12 15 "
+ "df 6e bb 9c 2f 95 79 aa 77 59 8a 38 f9 14 b5 b9 "
+ "c1 bd 83 c4 e2 f9 f3 82 a0 d0 aa 35 42 ff ee 65 "
+ "98 4a 60 1b c6 9e b2 8d eb 27 dc a1 2c 82 c2 d4 "
+ "c3 f6 6c d5 00 f1 ff 2b 99 4d 8a 4e 30 cb b3 3c ";
+
+// RSASSA-PSS Signature Example 1.2
+// Message to be signed:
+static const char message_1_2[] =
+ "85 13 84 cd fe 81 9c 22 ed 6c 4c cb 30 da eb 5c "
+ "f0 59 bc 8e 11 66 b7 e3 53 0c 4c 23 3e 2b 5f 8f "
+ "71 a1 cc a5 82 d4 3e cc 72 b1 bc a1 6d fc 70 13 "
+ "22 6b 9e ";
+// Salt:
+static const char salt_1_2[] =
+ "ef 28 69 fa 40 c3 46 cb 18 3d ab 3d 7b ff c9 8f "
+ "d5 6d f4 2d ";
+// Signature:
+static const char signature_1_2[] =
+ "3e f7 f4 6e 83 1b f9 2b 32 27 41 42 a5 85 ff ce "
+ "fb dc a7 b3 2a e9 0d 10 fb 0f 0c 72 99 84 f0 4e "
+ "f2 9a 9d f0 78 07 75 ce 43 73 9b 97 83 83 90 db "
+ "0a 55 05 e6 3d e9 27 02 8d 9d 29 b2 19 ca 2c 45 "
+ "17 83 25 58 a5 5d 69 4a 6d 25 b9 da b6 60 03 c4 "
+ "cc cd 90 78 02 19 3b e5 17 0d 26 14 7d 37 b9 35 "
+ "90 24 1b e5 1c 25 05 5f 47 ef 62 75 2c fb e2 14 "
+ "18 fa fe 98 c2 2c 4d 4d 47 72 4f db 56 69 e8 43 ";
+
+// RSASSA-PSS Signature Example 1.3
+// Message to be signed:
+static const char message_1_3[] =
+ "a4 b1 59 94 17 61 c4 0c 6a 82 f2 b8 0d 1b 94 f5 "
+ "aa 26 54 fd 17 e1 2d 58 88 64 67 9b 54 cd 04 ef "
+ "8b d0 30 12 be 8d c3 7f 4b 83 af 79 63 fa ff 0d "
+ "fa 22 54 77 43 7c 48 01 7f f2 be 81 91 cf 39 55 "
+ "fc 07 35 6e ab 3f 32 2f 7f 62 0e 21 d2 54 e5 db "
+ "43 24 27 9f e0 67 e0 91 0e 2e 81 ca 2c ab 31 c7 "
+ "45 e6 7a 54 05 8e b5 0d 99 3c db 9e d0 b4 d0 29 "
+ "c0 6d 21 a9 4c a6 61 c3 ce 27 fa e1 d6 cb 20 f4 "
+ "56 4d 66 ce 47 67 58 3d 0e 5f 06 02 15 b5 90 17 "
+ "be 85 ea 84 89 39 12 7b d8 c9 c4 d4 7b 51 05 6c "
+ "03 1c f3 36 f1 7c 99 80 f3 b8 f5 b9 b6 87 8e 8b "
+ "79 7a a4 3b 88 26 84 33 3e 17 89 3f e9 ca a6 aa "
+ "29 9f 7e d1 a1 8e e2 c5 48 64 b7 b2 b9 9b 72 61 "
+ "8f b0 25 74 d1 39 ef 50 f0 19 c9 ee f4 16 97 13 "
+ "38 e7 d4 70 ";
+// Salt:
+static const char salt_1_3[] =
+ "71 0b 9c 47 47 d8 00 d4 de 87 f1 2a fd ce 6d f1 "
+ "81 07 cc 77 ";
+// Signature:
+static const char signature_1_3[] =
+ "66 60 26 fb a7 1b d3 e7 cf 13 15 7c c2 c5 1a 8e "
+ "4a a6 84 af 97 78 f9 18 49 f3 43 35 d1 41 c0 01 "
+ "54 c4 19 76 21 f9 62 4a 67 5b 5a bc 22 ee 7d 5b "
+ "aa ff aa e1 c9 ba ca 2c c3 73 b3 f3 3e 78 e6 14 "
+ "3c 39 5a 91 aa 7f ac a6 64 eb 73 3a fd 14 d8 82 "
+ "72 59 d9 9a 75 50 fa ca 50 1e f2 b0 4e 33 c2 3a "
+ "a5 1f 4b 9e 82 82 ef db 72 8c c0 ab 09 40 5a 91 "
+ "60 7c 63 69 96 1b c8 27 0d 2d 4f 39 fc e6 12 b1 ";
+
+// RSASSA-PSS Signature Example 1.4
+// Message to be signed:
+static const char message_1_4[] =
+ "bc 65 67 47 fa 9e af b3 f0 ";
+// Salt:
+static const char salt_1_4[] =
+ "05 6f 00 98 5d e1 4d 8e f5 ce a9 e8 2f 8c 27 be "
+ "f7 20 33 5e ";
+// Signature:
+static const char signature_1_4[] =
+ "46 09 79 3b 23 e9 d0 93 62 dc 21 bb 47 da 0b 4f "
+ "3a 76 22 64 9a 47 d4 64 01 9b 9a ea fe 53 35 9c "
+ "17 8c 91 cd 58 ba 6b cb 78 be 03 46 a7 bc 63 7f "
+ "4b 87 3d 4b ab 38 ee 66 1f 19 96 34 c5 47 a1 ad "
+ "84 42 e0 3d a0 15 b1 36 e5 43 f7 ab 07 c0 c1 3e "
+ "42 25 b8 de 8c ce 25 d4 f6 eb 84 00 f8 1f 7e 18 "
+ "33 b7 ee 6e 33 4d 37 09 64 ca 79 fd b8 72 b4 d7 "
+ "52 23 b5 ee b0 81 01 59 1f b5 32 d1 55 a6 de 87 ";
+
+// RSASSA-PSS Signature Example 1.5
+// Message to be signed:
+static const char message_1_5[] =
+ "b4 55 81 54 7e 54 27 77 0c 76 8e 8b 82 b7 55 64 "
+ "e0 ea 4e 9c 32 59 4d 6b ff 70 65 44 de 0a 87 76 "
+ "c7 a8 0b 45 76 55 0e ee 1b 2a ca bc 7e 8b 7d 3e "
+ "f7 bb 5b 03 e4 62 c1 10 47 ea dd 00 62 9a e5 75 "
+ "48 0a c1 47 0f e0 46 f1 3a 2b f5 af 17 92 1d c4 "
+ "b0 aa 8b 02 be e6 33 49 11 65 1d 7f 85 25 d1 0f "
+ "32 b5 1d 33 be 52 0d 3d df 5a 70 99 55 a3 df e7 "
+ "82 83 b9 e0 ab 54 04 6d 15 0c 17 7f 03 7f dc cc "
+ "5b e4 ea 5f 68 b5 e5 a3 8c 9d 7e dc cc c4 97 5f "
+ "45 5a 69 09 b4 ";
+// Salt:
+static const char salt_1_5[] =
+ "80 e7 0f f8 6a 08 de 3e c6 09 72 b3 9b 4f bf dc "
+ "ea 67 ae 8e ";
+// Signature:
+static const char signature_1_5[] =
+ "1d 2a ad 22 1c a4 d3 1d df 13 50 92 39 01 93 98 "
+ "e3 d1 4b 32 dc 34 dc 5a f4 ae ae a3 c0 95 af 73 "
+ "47 9c f0 a4 5e 56 29 63 5a 53 a0 18 37 76 15 b1 "
+ "6c b9 b1 3b 3e 09 d6 71 eb 71 e3 87 b8 54 5c 59 "
+ "60 da 5a 64 77 6e 76 8e 82 b2 c9 35 83 bf 10 4c "
+ "3f db 23 51 2b 7b 4e 89 f6 33 dd 00 63 a5 30 db "
+ "45 24 b0 1c 3f 38 4c 09 31 0e 31 5a 79 dc d3 d6 "
+ "84 02 2a 7f 31 c8 65 a6 64 e3 16 97 8b 75 9f ad ";
+
+// RSASSA-PSS Signature Example 1.6
+// Message to be signed:
+static const char message_1_6[] =
+ "10 aa e9 a0 ab 0b 59 5d 08 41 20 7b 70 0d 48 d7 "
+ "5f ae dd e3 b7 75 cd 6b 4c c8 8a e0 6e 46 94 ec "
+ "74 ba 18 f8 52 0d 4f 5e a6 9c bb e7 cc 2b eb a4 "
+ "3e fd c1 02 15 ac 4e b3 2d c3 02 a1 f5 3d c6 c4 "
+ "35 22 67 e7 93 6c fe bf 7c 8d 67 03 57 84 a3 90 "
+ "9f a8 59 c7 b7 b5 9b 8e 39 c5 c2 34 9f 18 86 b7 "
+ "05 a3 02 67 d4 02 f7 48 6a b4 f5 8c ad 5d 69 ad "
+ "b1 7a b8 cd 0c e1 ca f5 02 5a f4 ae 24 b1 fb 87 "
+ "94 c6 07 0c c0 9a 51 e2 f9 91 13 11 e3 87 7d 00 "
+ "44 c7 1c 57 a9 93 39 50 08 80 6b 72 3a c3 83 73 "
+ "d3 95 48 18 18 52 8c 1e 70 53 73 92 82 05 35 29 "
+ "51 0e 93 5c d0 fa 77 b8 fa 53 cc 2d 47 4b d4 fb "
+ "3c c5 c6 72 d6 ff dc 90 a0 0f 98 48 71 2c 4b cf "
+ "e4 6c 60 57 36 59 b1 1e 64 57 e8 61 f0 f6 04 b6 "
+ "13 8d 14 4f 8c e4 e2 da 73 ";
+// Salt:
+static const char salt_1_6[] =
+ "a8 ab 69 dd 80 1f 00 74 c2 a1 fc 60 64 98 36 c6 "
+ "16 d9 96 81 ";
+// Signature:
+static const char signature_1_6[] =
+ "2a 34 f6 12 5e 1f 6b 0b f9 71 e8 4f bd 41 c6 32 "
+ "be 8f 2c 2a ce 7d e8 b6 92 6e 31 ff 93 e9 af 98 "
+ "7f bc 06 e5 1e 9b e1 4f 51 98 f9 1f 3f 95 3b d6 "
+ "7d a6 0a 9d f5 97 64 c3 dc 0f e0 8e 1c be f0 b7 "
+ "5f 86 8d 10 ad 3f ba 74 9f ef 59 fb 6d ac 46 a0 "
+ "d6 e5 04 36 93 31 58 6f 58 e4 62 8f 39 aa 27 89 "
+ "82 54 3b c0 ee b5 37 dc 61 95 80 19 b3 94 fb 27 "
+ "3f 21 58 58 a0 a0 1a c4 d6 50 b9 55 c6 7f 4c 58 ";
+
+// Example 9: A 1536-bit RSA Key Pair
+
+// RSA modulus n:
+static const char rsa_modulus_n_9[] =
+ "e6 bd 69 2a c9 66 45 79 04 03 fd d0 f5 be b8 b9 "
+ "bf 92 ed 10 00 7f c3 65 04 64 19 dd 06 c0 5c 5b "
+ "5b 2f 48 ec f9 89 e4 ce 26 91 09 97 9c bb 40 b4 "
+ "a0 ad 24 d2 24 83 d1 ee 31 5a d4 cc b1 53 42 68 "
+ "35 26 91 c5 24 f6 dd 8e 6c 29 d2 24 cf 24 69 73 "
+ "ae c8 6c 5b f6 b1 40 1a 85 0d 1b 9a d1 bb 8c bc "
+ "ec 47 b0 6f 0f 8c 7f 45 d3 fc 8f 31 92 99 c5 43 "
+ "3d db c2 b3 05 3b 47 de d2 ec d4 a4 ca ef d6 14 "
+ "83 3d c8 bb 62 2f 31 7e d0 76 b8 05 7f e8 de 3f "
+ "84 48 0a d5 e8 3e 4a 61 90 4a 4f 24 8f b3 97 02 "
+ "73 57 e1 d3 0e 46 31 39 81 5c 6f d4 fd 5a c5 b8 "
+ "17 2a 45 23 0e cb 63 18 a0 4f 14 55 d8 4e 5a 8b ";
+// RSA public exponent e:
+static const char rsa_public_exponent_e_9[] =
+ "01 00 01 ";
+
+// RSASSA-PSS Signature Example 9.1
+// Message to be signed:
+static const char message_9_1[] =
+ "a8 8e 26 58 55 e9 d7 ca 36 c6 87 95 f0 b3 1b 59 "
+ "1c d6 58 7c 71 d0 60 a0 b3 f7 f3 ea ef 43 79 59 "
+ "22 02 8b c2 b6 ad 46 7c fc 2d 7f 65 9c 53 85 aa "
+ "70 ba 36 72 cd de 4c fe 49 70 cc 79 04 60 1b 27 "
+ "88 72 bf 51 32 1c 4a 97 2f 3c 95 57 0f 34 45 d4 "
+ "f5 79 80 e0 f2 0d f5 48 46 e6 a5 2c 66 8f 12 88 "
+ "c0 3f 95 00 6e a3 2f 56 2d 40 d5 2a f9 fe b3 2f "
+ "0f a0 6d b6 5b 58 8a 23 7b 34 e5 92 d5 5c f9 79 "
+ "f9 03 a6 42 ef 64 d2 ed 54 2a a8 c7 7d c1 dd 76 "
+ "2f 45 a5 93 03 ed 75 e5 41 ca 27 1e 2b 60 ca 70 "
+ "9e 44 fa 06 61 13 1e 8d 5d 41 63 fd 8d 39 85 66 "
+ "ce 26 de 87 30 e7 2f 9c ca 73 76 41 c2 44 15 94 "
+ "20 63 70 28 df 0a 18 07 9d 62 08 ea 8b 47 11 a2 "
+ "c7 50 f5 ";
+// Salt:
+static const char salt_9_1[] =
+ "c0 a4 25 31 3d f8 d7 56 4b d2 43 4d 31 15 23 d5 "
+ "25 7e ed 80 ";
+// Signature:
+static const char signature_9_1[] =
+ "58 61 07 22 6c 3c e0 13 a7 c8 f0 4d 1a 6a 29 59 "
+ "bb 4b 8e 20 5b a4 3a 27 b5 0f 12 41 11 bc 35 ef "
+ "58 9b 03 9f 59 32 18 7c b6 96 d7 d9 a3 2c 0c 38 "
+ "30 0a 5c dd a4 83 4b 62 d2 eb 24 0a f3 3f 79 d1 "
+ "3d fb f0 95 bf 59 9e 0d 96 86 94 8c 19 64 74 7b "
+ "67 e8 9c 9a ba 5c d8 50 16 23 6f 56 6c c5 80 2c "
+ "b1 3e ad 51 bc 7c a6 be f3 b9 4d cb db b1 d5 70 "
+ "46 97 71 df 0e 00 b1 a8 a0 67 77 47 2d 23 16 27 "
+ "9e da e8 64 74 66 8d 4e 1e ff f9 5f 1d e6 1c 60 "
+ "20 da 32 ae 92 bb f1 65 20 fe f3 cf 4d 88 f6 11 "
+ "21 f2 4b bd 9f e9 1b 59 ca f1 23 5b 2a 93 ff 81 "
+ "fc 40 3a dd f4 eb de a8 49 34 a9 cd af 8e 1a 9e ";
+
+// RSASSA-PSS Signature Example 9.2
+// Message to be signed:
+static const char message_9_2[] =
+ "c8 c9 c6 af 04 ac da 41 4d 22 7e f2 3e 08 20 c3 "
+ "73 2c 50 0d c8 72 75 e9 5b 0d 09 54 13 99 3c 26 "
+ "58 bc 1d 98 85 81 ba 87 9c 2d 20 1f 14 cb 88 ce "
+ "d1 53 a0 19 69 a7 bf 0a 7b e7 9c 84 c1 48 6b c1 "
+ "2b 3f a6 c5 98 71 b6 82 7c 8c e2 53 ca 5f ef a8 "
+ "a8 c6 90 bf 32 6e 8e 37 cd b9 6d 90 a8 2e ba b6 "
+ "9f 86 35 0e 18 22 e8 bd 53 6a 2e ";
+// Salt:
+static const char salt_9_2[] =
+ "b3 07 c4 3b 48 50 a8 da c2 f1 5f 32 e3 78 39 ef "
+ "8c 5c 0e 91 ";
+// Signature:
+static const char signature_9_2[] =
+ "80 b6 d6 43 25 52 09 f0 a4 56 76 38 97 ac 9e d2 "
+ "59 d4 59 b4 9c 28 87 e5 88 2e cb 44 34 cf d6 6d "
+ "d7 e1 69 93 75 38 1e 51 cd 7f 55 4f 2c 27 17 04 "
+ "b3 99 d4 2b 4b e2 54 0a 0e ca 61 95 1f 55 26 7f "
+ "7c 28 78 c1 22 84 2d ad b2 8b 01 bd 5f 8c 02 5f "
+ "7e 22 84 18 a6 73 c0 3d 6b c0 c7 36 d0 a2 95 46 "
+ "bd 67 f7 86 d9 d6 92 cc ea 77 8d 71 d9 8c 20 63 "
+ "b7 a7 10 92 18 7a 4d 35 af 10 81 11 d8 3e 83 ea "
+ "e4 6c 46 aa 34 27 7e 06 04 45 89 90 37 88 f1 d5 "
+ "e7 ce e2 5f b4 85 e9 29 49 11 88 14 d6 f2 c3 ee "
+ "36 14 89 01 6f 32 7f b5 bc 51 7e b5 04 70 bf fa "
+ "1a fa 5f 4c e9 aa 0c e5 b8 ee 19 bf 55 01 b9 58 ";
+
+// RSASSA-PSS Signature Example 9.3
+// Message to be signed:
+static const char message_9_3[] =
+ "0a fa d4 2c cd 4f c6 06 54 a5 50 02 d2 28 f5 2a "
+ "4a 5f e0 3b 8b bb 08 ca 82 da ca 55 8b 44 db e1 "
+ "26 6e 50 c0 e7 45 a3 6d 9d 29 04 e3 40 8a bc d1 "
+ "fd 56 99 94 06 3f 4a 75 cc 72 f2 fe e2 a0 cd 89 "
+ "3a 43 af 1c 5b 8b 48 7d f0 a7 16 10 02 4e 4f 6d "
+ "df 9f 28 ad 08 13 c1 aa b9 1b cb 3c 90 64 d5 ff "
+ "74 2d ef fe a6 57 09 41 39 36 9e 5e a6 f4 a9 63 "
+ "19 a5 cc 82 24 14 5b 54 50 62 75 8f ef d1 fe 34 "
+ "09 ae 16 92 59 c6 cd fd 6b 5f 29 58 e3 14 fa ec "
+ "be 69 d2 ca ce 58 ee 55 17 9a b9 b3 e6 d1 ec c1 "
+ "4a 55 7c 5f eb e9 88 59 52 64 fc 5d a1 c5 71 46 "
+ "2e ca 79 8a 18 a1 a4 94 0c da b4 a3 e9 20 09 cc "
+ "d4 2e 1e 94 7b 13 14 e3 22 38 a2 de ce 7d 23 a8 "
+ "9b 5b 30 c7 51 fd 0a 4a 43 0d 2c 54 85 94 ";
+// Salt:
+static const char salt_9_3[] =
+ "9a 2b 00 7e 80 97 8b bb 19 2c 35 4e b7 da 9a ed "
+ "fc 74 db f5 ";
+// Signature:
+static const char signature_9_3[] =
+ "48 44 08 f3 89 8c d5 f5 34 83 f8 08 19 ef bf 27 "
+ "08 c3 4d 27 a8 b2 a6 fa e8 b3 22 f9 24 02 37 f9 "
+ "81 81 7a ca 18 46 f1 08 4d aa 6d 7c 07 95 f6 e5 "
+ "bf 1a f5 9c 38 e1 85 84 37 ce 1f 7e c4 19 b9 8c "
+ "87 36 ad f6 dd 9a 00 b1 80 6d 2b d3 ad 0a 73 77 "
+ "5e 05 f5 2d fe f3 a5 9a b4 b0 81 43 f0 df 05 cd "
+ "1a d9 d0 4b ec ec a6 da a4 a2 12 98 03 e2 00 cb "
+ "c7 77 87 ca f4 c1 d0 66 3a 6c 59 87 b6 05 95 20 "
+ "19 78 2c af 2e c1 42 6d 68 fb 94 ed 1d 4b e8 16 "
+ "a7 ed 08 1b 77 e6 ab 33 0b 3f fc 07 38 20 fe cd "
+ "e3 72 7f cb e2 95 ee 61 a0 50 a3 43 65 86 37 c3 "
+ "fd 65 9c fb 63 73 6d e3 2d 9f 90 d3 c2 f6 3e ca ";
+
+// RSASSA-PSS Signature Example 9.4
+// Message to be signed:
+static const char message_9_4[] =
+ "1d fd 43 b4 6c 93 db 82 62 9b da e2 bd 0a 12 b8 "
+ "82 ea 04 c3 b4 65 f5 cf 93 02 3f 01 05 96 26 db "
+ "be 99 f2 6b b1 be 94 9d dd d1 6d c7 f3 de bb 19 "
+ "a1 94 62 7f 0b 22 44 34 df 7d 87 00 e9 e9 8b 06 "
+ "e3 60 c1 2f db e3 d1 9f 51 c9 68 4e b9 08 9e cb "
+ "b0 a2 f0 45 03 99 d3 f5 9e ac 72 94 08 5d 04 4f "
+ "53 93 c6 ce 73 74 23 d8 b8 6c 41 53 70 d3 89 e3 "
+ "0b 9f 0a 3c 02 d2 5d 00 82 e8 ad 6f 3f 1e f2 4a "
+ "45 c3 cf 82 b3 83 36 70 63 a4 d4 61 3e 42 64 f0 "
+ "1b 2d ac 2e 5a a4 20 43 f8 fb 5f 69 fa 87 1d 14 "
+ "fb 27 3e 76 7a 53 1c 40 f0 2f 34 3b c2 fb 45 a0 "
+ "c7 e0 f6 be 25 61 92 3a 77 21 1d 66 a6 e2 db b4 "
+ "3c 36 63 50 be ae 22 da 3a c2 c1 f5 07 70 96 fc "
+ "b5 c4 bf 25 5f 75 74 35 1a e0 b1 e1 f0 36 32 81 "
+ "7c 08 56 d4 a8 ba 97 af bd c8 b8 58 55 40 2b c5 "
+ "69 26 fc ec 20 9f 9e a8 ";
+// Salt:
+static const char salt_9_4[] =
+ "70 f3 82 bd df 4d 5d 2d d8 8b 3b c7 b7 30 8b e6 "
+ "32 b8 40 45 ";
+// Signature:
+static const char signature_9_4[] =
+ "84 eb eb 48 1b e5 98 45 b4 64 68 ba fb 47 1c 01 "
+ "12 e0 2b 23 5d 84 b5 d9 11 cb d1 92 6e e5 07 4a "
+ "e0 42 44 95 cb 20 e8 23 08 b8 eb b6 5f 41 9a 03 "
+ "fb 40 e7 2b 78 98 1d 88 aa d1 43 05 36 85 17 2c "
+ "97 b2 9c 8b 7b f0 ae 73 b5 b2 26 3c 40 3d a0 ed "
+ "2f 80 ff 74 50 af 78 28 eb 8b 86 f0 02 8b d2 a8 "
+ "b1 76 a4 d2 28 cc ce a1 83 94 f2 38 b0 9f f7 58 "
+ "cc 00 bc 04 30 11 52 35 57 42 f2 82 b5 4e 66 3a "
+ "91 9e 70 9d 8d a2 4a de 55 00 a7 b9 aa 50 22 6e "
+ "0c a5 29 23 e6 c2 d8 60 ec 50 ff 48 0f a5 74 77 "
+ "e8 2b 05 65 f4 37 9f 79 c7 72 d5 c2 da 80 af 9f "
+ "bf 32 5e ce 6f c2 0b 00 96 16 14 be e8 9a 18 3e ";
+
+// RSASSA-PSS Signature Example 9.5
+// Message to be signed:
+static const char message_9_5[] =
+ "1b dc 6e 7c 98 fb 8c f5 4e 9b 09 7b 66 a8 31 e9 "
+ "cf e5 2d 9d 48 88 44 8e e4 b0 97 80 93 ba 1d 7d "
+ "73 ae 78 b3 a6 2b a4 ad 95 cd 28 9c cb 9e 00 52 "
+ "26 bb 3d 17 8b cc aa 82 1f b0 44 a4 e2 1e e9 76 "
+ "96 c1 4d 06 78 c9 4c 2d ae 93 b0 ad 73 92 22 18 "
+ "55 3d aa 7e 44 eb e5 77 25 a7 a4 5c c7 2b 9b 21 "
+ "38 a6 b1 7c 8d b4 11 ce 82 79 ee 12 41 af f0 a8 "
+ "be c6 f7 7f 87 ed b0 c6 9c b2 72 36 e3 43 5a 80 "
+ "0b 19 2e 4f 11 e5 19 e3 fe 30 fc 30 ea cc ca 4f "
+ "bb 41 76 90 29 bf 70 8e 81 7a 9e 68 38 05 be 67 "
+ "fa 10 09 84 68 3b 74 83 8e 3b cf fa 79 36 6e ed "
+ "1d 48 1c 76 72 91 18 83 8f 31 ba 8a 04 8a 93 c1 "
+ "be 44 24 59 8e 8d f6 32 8b 7a 77 88 0a 3f 9c 7e "
+ "2e 8d fc a8 eb 5a 26 fb 86 bd c5 56 d4 2b be 01 "
+ "d9 fa 6e d8 06 46 49 1c 93 41 ";
+// Salt:
+static const char salt_9_5[] =
+ "d6 89 25 7a 86 ef fa 68 21 2c 5e 0c 61 9e ca 29 "
+ "5f b9 1b 67 ";
+// Signature:
+static const char signature_9_5[] =
+ "82 10 2d f8 cb 91 e7 17 99 19 a0 4d 26 d3 35 d6 "
+ "4f bc 2f 87 2c 44 83 39 43 24 1d e8 45 48 10 27 "
+ "4c df 3d b5 f4 2d 42 3d b1 52 af 71 35 f7 01 42 "
+ "0e 39 b4 94 a6 7c bf d1 9f 91 19 da 23 3a 23 da "
+ "5c 64 39 b5 ba 0d 2b c3 73 ee e3 50 70 01 37 8d "
+ "4a 40 73 85 6b 7f e2 ab a0 b5 ee 93 b2 7f 4a fe "
+ "c7 d4 d1 20 92 1c 83 f6 06 76 5b 02 c1 9e 4d 6a "
+ "1a 3b 95 fa 4c 42 29 51 be 4f 52 13 10 77 ef 17 "
+ "17 97 29 cd df bd b5 69 50 db ac ee fe 78 cb 16 "
+ "64 0a 09 9e a5 6d 24 38 9e ef 10 f8 fe cb 31 ba "
+ "3e a3 b2 27 c0 a8 66 98 bb 89 e3 e9 36 39 05 bf "
+ "22 77 7b 2a 3a a5 21 b6 5b 4c ef 76 d8 3b de 4c ";
+
+// RSASSA-PSS Signature Example 9.6
+// Message to be signed:
+static const char message_9_6[] =
+ "88 c7 a9 f1 36 04 01 d9 0e 53 b1 01 b6 1c 53 25 "
+ "c3 c7 5d b1 b4 11 fb eb 8e 83 0b 75 e9 6b 56 67 "
+ "0a d2 45 40 4e 16 79 35 44 ee 35 4b c6 13 a9 0c "
+ "c9 84 87 15 a7 3d b5 89 3e 7f 6d 27 98 15 c0 c1 "
+ "de 83 ef 8e 29 56 e3 a5 6e d2 6a 88 8d 7a 9c dc "
+ "d0 42 f4 b1 6b 7f a5 1e f1 a0 57 36 62 d1 6a 30 "
+ "2d 0e c5 b2 85 d2 e0 3a d9 65 29 c8 7b 3d 37 4d "
+ "b3 72 d9 5b 24 43 d0 61 b6 b1 a3 50 ba 87 80 7e "
+ "d0 83 af d1 eb 05 c3 f5 2f 4e ba 5e d2 22 77 14 "
+ "fd b5 0b 9d 9d 9d d6 81 4f 62 f6 27 2f cd 5c db "
+ "ce 7a 9e f7 97 ";
+// Salt:
+static const char salt_9_6[] =
+ "c2 5f 13 bf 67 d0 81 67 1a 04 81 a1 f1 82 0d 61 "
+ "3b ba 22 76 ";
+// Signature:
+static const char signature_9_6[] =
+ "a7 fd b0 d2 59 16 5c a2 c8 8d 00 bb f1 02 8a 86 "
+ "7d 33 76 99 d0 61 19 3b 17 a9 64 8e 14 cc bb aa "
+ "de ac aa cd ec 81 5e 75 71 29 4e bb 8a 11 7a f2 "
+ "05 fa 07 8b 47 b0 71 2c 19 9e 3a d0 51 35 c5 04 "
+ "c2 4b 81 70 51 15 74 08 02 48 79 92 ff d5 11 d4 "
+ "af c6 b8 54 49 1e b3 f0 dd 52 31 39 54 2f f1 5c "
+ "31 01 ee 85 54 35 17 c6 a3 c7 94 17 c6 7e 2d d9 "
+ "aa 74 1e 9a 29 b0 6d cb 59 3c 23 36 b3 67 0a e3 "
+ "af ba c7 c3 e7 6e 21 54 73 e8 66 e3 38 ca 24 4d "
+ "e0 0b 62 62 4d 6b 94 26 82 2c ea e9 f8 cc 46 08 "
+ "95 f4 12 50 07 3f d4 5c 5a 1e 7b 42 5c 20 4a 42 "
+ "3a 69 91 59 f6 90 3e 71 0b 37 a7 bb 2b c8 04 9f ";
+
+// Example 10: A 2048-bit RSA Key Pair
+
+// RSA modulus n:
+static const char rsa_modulus_n_10[] =
+ "a5 dd 86 7a c4 cb 02 f9 0b 94 57 d4 8c 14 a7 70 "
+ "ef 99 1c 56 c3 9c 0e c6 5f d1 1a fa 89 37 ce a5 "
+ "7b 9b e7 ac 73 b4 5c 00 17 61 5b 82 d6 22 e3 18 "
+ "75 3b 60 27 c0 fd 15 7b e1 2f 80 90 fe e2 a7 ad "
+ "cd 0e ef 75 9f 88 ba 49 97 c7 a4 2d 58 c9 aa 12 "
+ "cb 99 ae 00 1f e5 21 c1 3b b5 43 14 45 a8 d5 ae "
+ "4f 5e 4c 7e 94 8a c2 27 d3 60 40 71 f2 0e 57 7e "
+ "90 5f be b1 5d fa f0 6d 1d e5 ae 62 53 d6 3a 6a "
+ "21 20 b3 1a 5d a5 da bc 95 50 60 0e 20 f2 7d 37 "
+ "39 e2 62 79 25 fe a3 cc 50 9f 21 df f0 4e 6e ea "
+ "45 49 c5 40 d6 80 9f f9 30 7e ed e9 1f ff 58 73 "
+ "3d 83 85 a2 37 d6 d3 70 5a 33 e3 91 90 09 92 07 "
+ "0d f7 ad f1 35 7c f7 e3 70 0c e3 66 7d e8 3f 17 "
+ "b8 df 17 78 db 38 1d ce 09 cb 4a d0 58 a5 11 00 "
+ "1a 73 81 98 ee 27 cf 55 a1 3b 75 45 39 90 65 82 "
+ "ec 8b 17 4b d5 8d 5d 1f 3d 76 7c 61 37 21 ae 05 ";
+// RSA public exponent e:
+static const char rsa_public_exponent_e_10[] =
+ "01 00 01 ";
+
+// RSASSA-PSS Signature Example 10.1
+// Message to be signed:
+static const char message_10_1[] =
+ "88 31 77 e5 12 6b 9b e2 d9 a9 68 03 27 d5 37 0c "
+ "6f 26 86 1f 58 20 c4 3d a6 7a 3a d6 09 ";
+// Salt:
+static const char salt_10_1[] =
+ "04 e2 15 ee 6f f9 34 b9 da 70 d7 73 0c 87 34 ab "
+ "fc ec de 89 ";
+// Signature:
+static const char signature_10_1[] =
+ "82 c2 b1 60 09 3b 8a a3 c0 f7 52 2b 19 f8 73 54 "
+ "06 6c 77 84 7a bf 2a 9f ce 54 2d 0e 84 e9 20 c5 "
+ "af b4 9f fd fd ac e1 65 60 ee 94 a1 36 96 01 14 "
+ "8e ba d7 a0 e1 51 cf 16 33 17 91 a5 72 7d 05 f2 "
+ "1e 74 e7 eb 81 14 40 20 69 35 d7 44 76 5a 15 e7 "
+ "9f 01 5c b6 6c 53 2c 87 a6 a0 59 61 c8 bf ad 74 "
+ "1a 9a 66 57 02 28 94 39 3e 72 23 73 97 96 c0 2a "
+ "77 45 5d 0f 55 5b 0e c0 1d df 25 9b 62 07 fd 0f "
+ "d5 76 14 ce f1 a5 57 3b aa ff 4e c0 00 69 95 16 "
+ "59 b8 5f 24 30 0a 25 16 0c a8 52 2d c6 e6 72 7e "
+ "57 d0 19 d7 e6 36 29 b8 fe 5e 89 e2 5c c1 5b eb "
+ "3a 64 75 77 55 92 99 28 0b 9b 28 f7 9b 04 09 00 "
+ "0b e2 5b bd 96 40 8b a3 b4 3c c4 86 18 4d d1 c8 "
+ "e6 25 53 fa 1a f4 04 0f 60 66 3d e7 f5 e4 9c 04 "
+ "38 8e 25 7f 1c e8 9c 95 da b4 8a 31 5d 9b 66 b1 "
+ "b7 62 82 33 87 6f f2 38 52 30 d0 70 d0 7e 16 66 ";
+
+// RSASSA-PSS Signature Example 10.2
+// Message to be signed:
+static const char message_10_2[] =
+ "dd 67 0a 01 46 58 68 ad c9 3f 26 13 19 57 a5 0c "
+ "52 fb 77 7c db aa 30 89 2c 9e 12 36 11 64 ec 13 "
+ "97 9d 43 04 81 18 e4 44 5d b8 7b ee 58 dd 98 7b "
+ "34 25 d0 20 71 d8 db ae 80 70 8b 03 9d bb 64 db "
+ "d1 de 56 57 d9 fe d0 c1 18 a5 41 43 74 2e 0f f3 "
+ "c8 7f 74 e4 58 57 64 7a f3 f7 9e b0 a1 4c 9d 75 "
+ "ea 9a 1a 04 b7 cf 47 8a 89 7a 70 8f d9 88 f4 8e "
+ "80 1e db 0b 70 39 df 8c 23 bb 3c 56 f4 e8 21 ac ";
+// Salt:
+static const char salt_10_2[] =
+ "8b 2b dd 4b 40 fa f5 45 c7 78 dd f9 bc 1a 49 cb "
+ "57 f9 b7 1b ";
+// Signature:
+static const char signature_10_2[] =
+ "14 ae 35 d9 dd 06 ba 92 f7 f3 b8 97 97 8a ed 7c "
+ "d4 bf 5f f0 b5 85 a4 0b d4 6c e1 b4 2c d2 70 30 "
+ "53 bb 90 44 d6 4e 81 3d 8f 96 db 2d d7 00 7d 10 "
+ "11 8f 6f 8f 84 96 09 7a d7 5e 1f f6 92 34 1b 28 "
+ "92 ad 55 a6 33 a1 c5 5e 7f 0a 0a d5 9a 0e 20 3a "
+ "5b 82 78 ae c5 4d d8 62 2e 28 31 d8 71 74 f8 ca "
+ "ff 43 ee 6c 46 44 53 45 d8 4a 59 65 9b fb 92 ec "
+ "d4 c8 18 66 86 95 f3 47 06 f6 68 28 a8 99 59 63 "
+ "7f 2b f3 e3 25 1c 24 bd ba 4d 4b 76 49 da 00 22 "
+ "21 8b 11 9c 84 e7 9a 65 27 ec 5b 8a 5f 86 1c 15 "
+ "99 52 e2 3e c0 5e 1e 71 73 46 fa ef e8 b1 68 68 "
+ "25 bd 2b 26 2f b2 53 10 66 c0 de 09 ac de 2e 42 "
+ "31 69 07 28 b5 d8 5e 11 5a 2f 6b 92 b7 9c 25 ab "
+ "c9 bd 93 99 ff 8b cf 82 5a 52 ea 1f 56 ea 76 dd "
+ "26 f4 3b aa fa 18 bf a9 2a 50 4c bd 35 69 9e 26 "
+ "d1 dc c5 a2 88 73 85 f3 c6 32 32 f0 6f 32 44 c3 ";
+
+// RSASSA-PSS Signature Example 10.3
+// Message to be signed:
+static const char message_10_3[] =
+ "48 b2 b6 a5 7a 63 c8 4c ea 85 9d 65 c6 68 28 4b "
+ "08 d9 6b dc aa be 25 2d b0 e4 a9 6c b1 ba c6 01 "
+ "93 41 db 6f be fb 8d 10 6b 0e 90 ed a6 bc c6 c6 "
+ "26 2f 37 e7 ea 9c 7e 5d 22 6b d7 df 85 ec 5e 71 "
+ "ef ff 2f 54 c5 db 57 7f f7 29 ff 91 b8 42 49 1d "
+ "e2 74 1d 0c 63 16 07 df 58 6b 90 5b 23 b9 1a f1 "
+ "3d a1 23 04 bf 83 ec a8 a7 3e 87 1f f9 db ";
+// Salt:
+static const char salt_10_3[] =
+ "4e 96 fc 1b 39 8f 92 b4 46 71 01 0c 0d c3 ef d6 "
+ "e2 0c 2d 73 ";
+// Signature:
+static const char signature_10_3[] =
+ "6e 3e 4d 7b 6b 15 d2 fb 46 01 3b 89 00 aa 5b bb "
+ "39 39 cf 2c 09 57 17 98 70 42 02 6e e6 2c 74 c5 "
+ "4c ff d5 d7 d5 7e fb bf 95 0a 0f 5c 57 4f a0 9d "
+ "3f c1 c9 f5 13 b0 5b 4f f5 0d d8 df 7e df a2 01 "
+ "02 85 4c 35 e5 92 18 01 19 a7 0c e5 b0 85 18 2a "
+ "a0 2d 9e a2 aa 90 d1 df 03 f2 da ae 88 5b a2 f5 "
+ "d0 5a fd ac 97 47 6f 06 b9 3b 5b c9 4a 1a 80 aa "
+ "91 16 c4 d6 15 f3 33 b0 98 89 2b 25 ff ac e2 66 "
+ "f5 db 5a 5a 3b cc 10 a8 24 ed 55 aa d3 5b 72 78 "
+ "34 fb 8c 07 da 28 fc f4 16 a5 d9 b2 22 4f 1f 8b "
+ "44 2b 36 f9 1e 45 6f de a2 d7 cf e3 36 72 68 de "
+ "03 07 a4 c7 4e 92 41 59 ed 33 39 3d 5e 06 55 53 "
+ "1c 77 32 7b 89 82 1b de df 88 01 61 c7 8c d4 19 "
+ "6b 54 19 f7 ac c3 f1 3e 5e bf 16 1b 6e 7c 67 24 "
+ "71 6c a3 3b 85 c2 e2 56 40 19 2a c2 85 96 51 d5 "
+ "0b de 7e b9 76 e5 1c ec 82 8b 98 b6 56 3b 86 bb ";
+
+// RSASSA-PSS Signature Example 10.4
+// Message to be signed:
+static const char message_10_4[] =
+ "0b 87 77 c7 f8 39 ba f0 a6 4b bb db c5 ce 79 75 "
+ "5c 57 a2 05 b8 45 c1 74 e2 d2 e9 05 46 a0 89 c4 "
+ "e6 ec 8a df fa 23 a7 ea 97 ba e6 b6 5d 78 2b 82 "
+ "db 5d 2b 5a 56 d2 2a 29 a0 5e 7c 44 33 e2 b8 2a "
+ "62 1a bb a9 0a dd 05 ce 39 3f c4 8a 84 05 42 45 "
+ "1a ";
+// Salt:
+static const char salt_10_4[] =
+ "c7 cd 69 8d 84 b6 51 28 d8 83 5e 3a 8b 1e b0 e0 "
+ "1c b5 41 ec ";
+// Signature:
+static const char signature_10_4[] =
+ "34 04 7f f9 6c 4d c0 dc 90 b2 d4 ff 59 a1 a3 61 "
+ "a4 75 4b 25 5d 2e e0 af 7d 8b f8 7c 9b c9 e7 dd "
+ "ee de 33 93 4c 63 ca 1c 0e 3d 26 2c b1 45 ef 93 "
+ "2a 1f 2c 0a 99 7a a6 a3 4f 8e ae e7 47 7d 82 cc "
+ "f0 90 95 a6 b8 ac ad 38 d4 ee c9 fb 7e ab 7a d0 "
+ "2d a1 d1 1d 8e 54 c1 82 5e 55 bf 58 c2 a2 32 34 "
+ "b9 02 be 12 4f 9e 90 38 a8 f6 8f a4 5d ab 72 f6 "
+ "6e 09 45 bf 1d 8b ac c9 04 4c 6f 07 09 8c 9f ce "
+ "c5 8a 3a ab 10 0c 80 51 78 15 5f 03 0a 12 4c 45 "
+ "0e 5a cb da 47 d0 e4 f1 0b 80 a2 3f 80 3e 77 4d "
+ "02 3b 00 15 c2 0b 9f 9b be 7c 91 29 63 38 d5 ec "
+ "b4 71 ca fb 03 20 07 b6 7a 60 be 5f 69 50 4a 9f "
+ "01 ab b3 cb 46 7b 26 0e 2b ce 86 0b e8 d9 5b f9 "
+ "2c 0c 8e 14 96 ed 1e 52 85 93 a4 ab b6 df 46 2d "
+ "de 8a 09 68 df fe 46 83 11 68 57 a2 32 f5 eb f6 "
+ "c8 5b e2 38 74 5a d0 f3 8f 76 7a 5f db f4 86 fb ";
+
+// RSASSA-PSS Signature Example 10.5
+// Message to be signed:
+static const char message_10_5[] =
+ "f1 03 6e 00 8e 71 e9 64 da dc 92 19 ed 30 e1 7f "
+ "06 b4 b6 8a 95 5c 16 b3 12 b1 ed df 02 8b 74 97 "
+ "6b ed 6b 3f 6a 63 d4 e7 78 59 24 3c 9c cc dc 98 "
+ "01 65 23 ab b0 24 83 b3 55 91 c3 3a ad 81 21 3b "
+ "b7 c7 bb 1a 47 0a ab c1 0d 44 25 6c 4d 45 59 d9 "
+ "16 ";
+// Salt:
+static const char salt_10_5[] =
+ "ef a8 bf f9 62 12 b2 f4 a3 f3 71 a1 0d 57 41 52 "
+ "65 5f 5d fb ";
+// Signature:
+static const char signature_10_5[] =
+ "7e 09 35 ea 18 f4 d6 c1 d1 7c e8 2e b2 b3 83 6c "
+ "55 b3 84 58 9c e1 9d fe 74 33 63 ac 99 48 d1 f3 "
+ "46 b7 bf dd fe 92 ef d7 8a db 21 fa ef c8 9a de "
+ "42 b1 0f 37 40 03 fe 12 2e 67 42 9a 1c b8 cb d1 "
+ "f8 d9 01 45 64 c4 4d 12 01 16 f4 99 0f 1a 6e 38 "
+ "77 4c 19 4b d1 b8 21 32 86 b0 77 b0 49 9d 2e 7b "
+ "3f 43 4a b1 22 89 c5 56 68 4d ee d7 81 31 93 4b "
+ "b3 dd 65 37 23 6f 7c 6f 3d cb 09 d4 76 be 07 72 "
+ "1e 37 e1 ce ed 9b 2f 7b 40 68 87 bd 53 15 73 05 "
+ "e1 c8 b4 f8 4d 73 3b c1 e1 86 fe 06 cc 59 b6 ed "
+ "b8 f4 bd 7f fe fd f4 f7 ba 9c fb 9d 57 06 89 b5 "
+ "a1 a4 10 9a 74 6a 69 08 93 db 37 99 25 5a 0c b9 "
+ "21 5d 2d 1c d4 90 59 0e 95 2e 8c 87 86 aa 00 11 "
+ "26 52 52 47 0c 04 1d fb c3 ee c7 c3 cb f7 1c 24 "
+ "86 9d 11 5c 0c b4 a9 56 f5 6d 53 0b 80 ab 58 9a "
+ "cf ef c6 90 75 1d df 36 e8 d3 83 f8 3c ed d2 cc ";
+
+// RSASSA-PSS Signature Example 10.6
+// Message to be signed:
+static const char message_10_6[] =
+ "25 f1 08 95 a8 77 16 c1 37 45 0b b9 51 9d fa a1 "
+ "f2 07 fa a9 42 ea 88 ab f7 1e 9c 17 98 00 85 b5 "
+ "55 ae ba b7 62 64 ae 2a 3a b9 3c 2d 12 98 11 91 "
+ "dd ac 6f b5 94 9e b3 6a ee 3c 5d a9 40 f0 07 52 "
+ "c9 16 d9 46 08 fa 7d 97 ba 6a 29 15 b6 88 f2 03 "
+ "23 d4 e9 d9 68 01 d8 9a 72 ab 58 92 dc 21 17 c0 "
+ "74 34 fc f9 72 e0 58 cf 8c 41 ca 4b 4f f5 54 f7 "
+ "d5 06 8a d3 15 5f ce d0 f3 12 5b c0 4f 91 93 37 "
+ "8a 8f 5c 4c 3b 8c b4 dd 6d 1c c6 9d 30 ec ca 6e "
+ "aa 51 e3 6a 05 73 0e 9e 34 2e 85 5b af 09 9d ef "
+ "b8 af d7 ";
+// Salt:
+static const char salt_10_6[] =
+ "ad 8b 15 23 70 36 46 22 4b 66 0b 55 08 85 91 7c "
+ "a2 d1 df 28 ";
+// Signature:
+static const char signature_10_6[] =
+ "6d 3b 5b 87 f6 7e a6 57 af 21 f7 54 41 97 7d 21 "
+ "80 f9 1b 2c 5f 69 2d e8 29 55 69 6a 68 67 30 d9 "
+ "b9 77 8d 97 07 58 cc b2 60 71 c2 20 9f fb d6 12 "
+ "5b e2 e9 6e a8 1b 67 cb 9b 93 08 23 9f da 17 f7 "
+ "b2 b6 4e cd a0 96 b6 b9 35 64 0a 5a 1c b4 2a 91 "
+ "55 b1 c9 ef 7a 63 3a 02 c5 9f 0d 6e e5 9b 85 2c "
+ "43 b3 50 29 e7 3c 94 0f f0 41 0e 8f 11 4e ed 46 "
+ "bb d0 fa e1 65 e4 2b e2 52 8a 40 1c 3b 28 fd 81 "
+ "8e f3 23 2d ca 9f 4d 2a 0f 51 66 ec 59 c4 23 96 "
+ "d6 c1 1d bc 12 15 a5 6f a1 71 69 db 95 75 34 3e "
+ "f3 4f 9d e3 2a 49 cd c3 17 49 22 f2 29 c2 3e 18 "
+ "e4 5d f9 35 31 19 ec 43 19 ce dc e7 a1 7c 64 08 "
+ "8c 1f 6f 52 be 29 63 41 00 b3 91 9d 38 f3 d1 ed "
+ "94 e6 89 1e 66 a7 3b 8f b8 49 f5 87 4d f5 94 59 "
+ "e2 98 c7 bb ce 2e ee 78 2a 19 5a a6 6f e2 d0 73 "
+ "2b 25 e5 95 f5 7d 3e 06 1b 1f c3 e4 06 3b f9 8f ";
+
+struct SignatureExample {
+ const char* message;
+ const char* salt;
+ const char* signature;
+};
+
+struct PSSTestVector {
+ const char* modulus_n;
+ const char* public_exponent_e;
+ SignatureExample example[6];
+};
+
+static const PSSTestVector pss_test[] = {
+ {
+ rsa_modulus_n_1,
+ rsa_public_exponent_e_1,
+ {
+ { message_1_1, salt_1_1, signature_1_1 },
+ { message_1_2, salt_1_2, signature_1_2 },
+ { message_1_3, salt_1_3, signature_1_3 },
+ { message_1_4, salt_1_4, signature_1_4 },
+ { message_1_5, salt_1_5, signature_1_5 },
+ { message_1_6, salt_1_6, signature_1_6 },
+ }
+ },
+ {
+ rsa_modulus_n_9,
+ rsa_public_exponent_e_9,
+ {
+ { message_9_1, salt_9_1, signature_9_1 },
+ { message_9_2, salt_9_2, signature_9_2 },
+ { message_9_3, salt_9_3, signature_9_3 },
+ { message_9_4, salt_9_4, signature_9_4 },
+ { message_9_5, salt_9_5, signature_9_5 },
+ { message_9_6, salt_9_6, signature_9_6 },
+ }
+ },
+ {
+ rsa_modulus_n_10,
+ rsa_public_exponent_e_10,
+ {
+ { message_10_1, salt_10_1, signature_10_1 },
+ { message_10_2, salt_10_2, signature_10_2 },
+ { message_10_3, salt_10_3, signature_10_3 },
+ { message_10_4, salt_10_4, signature_10_4 },
+ { message_10_5, salt_10_5, signature_10_5 },
+ { message_10_6, salt_10_6, signature_10_6 },
+ }
+ },
+};
+
+static uint8 HexDigitValue(char digit) {
+ if ('0' <= digit && digit <= '9')
+ return digit - '0';
+ if ('a' <= digit && digit <= 'f')
+ return digit - 'a' + 10;
+ return digit - 'A' + 10;
+}
+
+static bool DecodeTestInput(const char* in, std::vector<uint8>* out) {
+ out->clear();
+ while (in[0] != '\0') {
+ if (!isxdigit(in[0]) || !isxdigit(in[1]) || in[2] != ' ')
+ return false;
+ uint8 octet = HexDigitValue(in[0]) * 16 + HexDigitValue(in[1]);
+ out->push_back(octet);
+ in += 3;
+ }
+ return true;
+}
+
+// PrependASN1Length prepends an ASN.1 serialized length to the beginning of
+// |out|.
+static void PrependASN1Length(std::vector<uint8>* out, size_t len) {
+ if (len < 128) {
+ out->insert(out->begin(), static_cast<uint8>(len));
+ } else if (len < 256) {
+ out->insert(out->begin(), static_cast<uint8>(len));
+ out->insert(out->begin(), 0x81);
+ } else if (len < 0x10000) {
+ out->insert(out->begin(), static_cast<uint8>(len));
+ out->insert(out->begin(), static_cast<uint8>(len >> 8));
+ out->insert(out->begin(), 0x82);
+ } else {
+ CHECK(false) << "ASN.1 length not handled: " << len;
+ }
+}
+
+static bool EncodeRSAPublicKey(const std::vector<uint8>& modulus_n,
+ const std::vector<uint8>& public_exponent_e,
+ std::vector<uint8>* public_key_info) {
+ // The public key is specified as the following ASN.1 structure:
+ // SubjectPublicKeyInfo ::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING }
+ //
+ // The signature algorithm is specified as the following ASN.1 structure:
+ // AlgorithmIdentifier ::= SEQUENCE {
+ // algorithm OBJECT IDENTIFIER,
+ // parameters ANY DEFINED BY algorithm OPTIONAL }
+ //
+ // An RSA public key is specified as the following ASN.1 structure:
+ // RSAPublicKey ::= SEQUENCE {
+ // modulus INTEGER, -- n
+ // publicExponent INTEGER -- e
+ // }
+ static const uint8 kIntegerTag = 0x02;
+ static const uint8 kBitStringTag = 0x03;
+ static const uint8 kSequenceTag = 0x30;
+ public_key_info->clear();
+
+ // Encode the public exponent e as an INTEGER.
+ public_key_info->insert(public_key_info->begin(),
+ public_exponent_e.begin(),
+ public_exponent_e.end());
+ PrependASN1Length(public_key_info, public_exponent_e.size());
+ public_key_info->insert(public_key_info->begin(), kIntegerTag);
+
+ // Encode the modulus n as an INTEGER.
+ public_key_info->insert(public_key_info->begin(),
+ modulus_n.begin(), modulus_n.end());
+ size_t modulus_size = modulus_n.size();
+ if (modulus_n[0] & 0x80) {
+ public_key_info->insert(public_key_info->begin(), 0x00);
+ modulus_size++;
+ }
+ PrependASN1Length(public_key_info, modulus_size);
+ public_key_info->insert(public_key_info->begin(), kIntegerTag);
+
+ // Encode the RSAPublicKey SEQUENCE.
+ PrependASN1Length(public_key_info, public_key_info->size());
+ public_key_info->insert(public_key_info->begin(), kSequenceTag);
+
+ // Encode the BIT STRING.
+ // Number of unused bits.
+ public_key_info->insert(public_key_info->begin(), 0x00);
+ PrependASN1Length(public_key_info, public_key_info->size());
+ public_key_info->insert(public_key_info->begin(), kBitStringTag);
+
+ // Encode the AlgorithmIdentifier.
+ static const uint8 algorithm[] = {
+ 0x30, 0x0d, // a SEQUENCE of length 13
+ 0x06, 0x09, // an OBJECT IDENTIFIER of length 9
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00,
+ };
+ public_key_info->insert(public_key_info->begin(),
+ algorithm, algorithm + sizeof(algorithm));
+
+ // Encode the outermost SEQUENCE.
+ PrependASN1Length(public_key_info, public_key_info->size());
+ public_key_info->insert(public_key_info->begin(), kSequenceTag);
+
+ return true;
+}
+
+TEST(SignatureVerifierTest, VerifyRSAPSS) {
+ for (unsigned int i = 0; i < arraysize(pss_test); i++) {
+ SCOPED_TRACE(i);
+ std::vector<uint8> modulus_n;
+ std::vector<uint8> public_exponent_e;
+ ASSERT_TRUE(DecodeTestInput(pss_test[i].modulus_n, &modulus_n));
+ ASSERT_TRUE(DecodeTestInput(pss_test[i].public_exponent_e,
+ &public_exponent_e));
+ std::vector<uint8> public_key_info;
+ ASSERT_TRUE(EncodeRSAPublicKey(modulus_n, public_exponent_e,
+ &public_key_info));
+
+ for (unsigned int j = 0; j < arraysize(pss_test[i].example); j++) {
+ SCOPED_TRACE(j);
+ std::vector<uint8> message;
+ std::vector<uint8> salt;
+ std::vector<uint8> signature;
+ ASSERT_TRUE(DecodeTestInput(pss_test[i].example[j].message, &message));
+ ASSERT_TRUE(DecodeTestInput(pss_test[i].example[j].salt, &salt));
+ ASSERT_TRUE(DecodeTestInput(pss_test[i].example[j].signature,
+ &signature));
+
+ crypto::SignatureVerifier verifier;
+ bool ok;
+
+ // Positive test.
+ ok = verifier.VerifyInitRSAPSS(crypto::SignatureVerifier::SHA1,
+ crypto::SignatureVerifier::SHA1,
+ salt.size(),
+ &signature[0], signature.size(),
+ &public_key_info[0],
+ public_key_info.size());
+ ASSERT_TRUE(ok);
+ verifier.VerifyUpdate(&message[0], message.size());
+ ok = verifier.VerifyFinal();
+ EXPECT_TRUE(ok);
+
+ // Modify the first byte of the message.
+ ok = verifier.VerifyInitRSAPSS(crypto::SignatureVerifier::SHA1,
+ crypto::SignatureVerifier::SHA1,
+ salt.size(),
+ &signature[0], signature.size(),
+ &public_key_info[0],
+ public_key_info.size());
+ ASSERT_TRUE(ok);
+ message[0] += 1;
+ verifier.VerifyUpdate(&message[0], message.size());
+ message[0] -= 1;
+ ok = verifier.VerifyFinal();
+ EXPECT_FALSE(ok);
+
+ // Truncate the message.
+ ASSERT_FALSE(message.empty());
+ ok = verifier.VerifyInitRSAPSS(crypto::SignatureVerifier::SHA1,
+ crypto::SignatureVerifier::SHA1,
+ salt.size(),
+ &signature[0], signature.size(),
+ &public_key_info[0],
+ public_key_info.size());
+ ASSERT_TRUE(ok);
+ verifier.VerifyUpdate(&message[0], message.size() - 1);
+ ok = verifier.VerifyFinal();
+ EXPECT_FALSE(ok);
+
+ // Corrupt the signature.
+ signature[0] += 1;
+ ok = verifier.VerifyInitRSAPSS(crypto::SignatureVerifier::SHA1,
+ crypto::SignatureVerifier::SHA1,
+ salt.size(),
+ &signature[0], signature.size(),
+ &public_key_info[0],
+ public_key_info.size());
+ signature[0] -= 1;
+ ASSERT_TRUE(ok);
+ verifier.VerifyUpdate(&message[0], message.size());
+ ok = verifier.VerifyFinal();
+ EXPECT_FALSE(ok);
+ }
+ }
+}
diff --git a/crypto/symmetric_key_nss.cc b/crypto/symmetric_key_nss.cc
new file mode 100644
index 0000000000..95ca9bd073
--- /dev/null
+++ b/crypto/symmetric_key_nss.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/symmetric_key.h"
+
+#include <nss.h>
+#include <pk11pub.h>
+
+#include "base/logging.h"
+#include "crypto/nss_util.h"
+
+namespace crypto {
+
+SymmetricKey::~SymmetricKey() {}
+
+// static
+SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
+ size_t key_size_in_bits) {
+ DCHECK_EQ(AES, algorithm);
+
+ EnsureNSSInit();
+
+ // Whitelist supported key sizes to avoid accidentaly relying on
+ // algorithms available in NSS but not BoringSSL and vice
+ // versa. Note that BoringSSL does not support AES-192.
+ if (key_size_in_bits != 128 && key_size_in_bits != 256)
+ return NULL;
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot.get())
+ return NULL;
+
+ PK11SymKey* sym_key = PK11_KeyGen(slot.get(), CKM_AES_KEY_GEN, NULL,
+ key_size_in_bits / 8, NULL);
+ if (!sym_key)
+ return NULL;
+
+ return new SymmetricKey(sym_key);
+}
+
+// static
+SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm,
+ const std::string& password,
+ const std::string& salt,
+ size_t iterations,
+ size_t key_size_in_bits) {
+ EnsureNSSInit();
+ if (salt.empty() || iterations == 0 || key_size_in_bits == 0)
+ return NULL;
+
+ if (algorithm == AES) {
+ // Whitelist supported key sizes to avoid accidentaly relying on
+ // algorithms available in NSS but not BoringSSL and vice
+ // versa. Note that BoringSSL does not support AES-192.
+ if (key_size_in_bits != 128 && key_size_in_bits != 256)
+ return NULL;
+ }
+
+ SECItem password_item;
+ password_item.type = siBuffer;
+ password_item.data = reinterpret_cast<unsigned char*>(
+ const_cast<char *>(password.data()));
+ password_item.len = password.size();
+
+ SECItem salt_item;
+ salt_item.type = siBuffer;
+ salt_item.data = reinterpret_cast<unsigned char*>(
+ const_cast<char *>(salt.data()));
+ salt_item.len = salt.size();
+
+ SECOidTag cipher_algorithm =
+ algorithm == AES ? SEC_OID_AES_256_CBC : SEC_OID_HMAC_SHA1;
+ ScopedSECAlgorithmID alg_id(PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2,
+ cipher_algorithm,
+ SEC_OID_HMAC_SHA1,
+ key_size_in_bits / 8,
+ iterations,
+ &salt_item));
+ if (!alg_id.get())
+ return NULL;
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot.get())
+ return NULL;
+
+ PK11SymKey* sym_key = PK11_PBEKeyGen(slot.get(), alg_id.get(), &password_item,
+ PR_FALSE, NULL);
+ if (!sym_key)
+ return NULL;
+
+ return new SymmetricKey(sym_key);
+}
+
+// static
+SymmetricKey* SymmetricKey::Import(Algorithm algorithm,
+ const std::string& raw_key) {
+ EnsureNSSInit();
+
+ if (algorithm == AES) {
+ // Whitelist supported key sizes to avoid accidentaly relying on
+ // algorithms available in NSS but not BoringSSL and vice
+ // versa. Note that BoringSSL does not support AES-192.
+ if (raw_key.size() != 128/8 && raw_key.size() != 256/8)
+ return NULL;
+ }
+
+ CK_MECHANISM_TYPE cipher =
+ algorithm == AES ? CKM_AES_CBC : CKM_SHA_1_HMAC;
+
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = reinterpret_cast<unsigned char*>(
+ const_cast<char *>(raw_key.data()));
+ key_item.len = raw_key.size();
+
+ ScopedPK11Slot slot(PK11_GetInternalSlot());
+ if (!slot.get())
+ return NULL;
+
+ // The exact value of the |origin| argument doesn't matter to NSS as long as
+ // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a
+ // placeholder.
+ PK11SymKey* sym_key = PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap,
+ CKA_ENCRYPT, &key_item, NULL);
+ if (!sym_key)
+ return NULL;
+
+ return new SymmetricKey(sym_key);
+}
+
+bool SymmetricKey::GetRawKey(std::string* raw_key) {
+ SECStatus rv = PK11_ExtractKeyValue(key_.get());
+ if (SECSuccess != rv)
+ return false;
+
+ SECItem* key_item = PK11_GetKeyData(key_.get());
+ if (!key_item)
+ return false;
+
+ raw_key->assign(reinterpret_cast<char*>(key_item->data), key_item->len);
+ return true;
+}
+
+SymmetricKey::SymmetricKey(PK11SymKey* key) : key_(key) {
+ DCHECK(key);
+}
+
+} // namespace crypto
diff --git a/crypto/symmetric_key_openssl.cc b/crypto/symmetric_key_openssl.cc
new file mode 100644
index 0000000000..e268a1d0f9
--- /dev/null
+++ b/crypto/symmetric_key_openssl.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/symmetric_key.h"
+
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "crypto/openssl_util.h"
+
+namespace crypto {
+
+SymmetricKey::~SymmetricKey() {
+ std::fill(key_.begin(), key_.end(), '\0'); // Zero out the confidential key.
+}
+
+// static
+SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
+ size_t key_size_in_bits) {
+ DCHECK_EQ(AES, algorithm);
+
+ // Whitelist supported key sizes to avoid accidentaly relying on
+ // algorithms available in NSS but not BoringSSL and vice
+ // versa. Note that BoringSSL does not support AES-192.
+ if (key_size_in_bits != 128 && key_size_in_bits != 256)
+ return NULL;
+
+ size_t key_size_in_bytes = key_size_in_bits / 8;
+ DCHECK_EQ(key_size_in_bits, key_size_in_bytes * 8);
+
+ if (key_size_in_bytes == 0)
+ return NULL;
+
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ scoped_ptr<SymmetricKey> key(new SymmetricKey);
+ uint8* key_data =
+ reinterpret_cast<uint8*>(WriteInto(&key->key_, key_size_in_bytes + 1));
+
+ int rv = RAND_bytes(key_data, static_cast<int>(key_size_in_bytes));
+ return rv == 1 ? key.release() : NULL;
+}
+
+// static
+SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm,
+ const std::string& password,
+ const std::string& salt,
+ size_t iterations,
+ size_t key_size_in_bits) {
+ DCHECK(algorithm == AES || algorithm == HMAC_SHA1);
+
+ if (algorithm == AES) {
+ // Whitelist supported key sizes to avoid accidentaly relying on
+ // algorithms available in NSS but not BoringSSL and vice
+ // versa. Note that BoringSSL does not support AES-192.
+ if (key_size_in_bits != 128 && key_size_in_bits != 256)
+ return NULL;
+ }
+
+ size_t key_size_in_bytes = key_size_in_bits / 8;
+ DCHECK_EQ(key_size_in_bits, key_size_in_bytes * 8);
+
+ if (key_size_in_bytes == 0)
+ return NULL;
+
+ OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ scoped_ptr<SymmetricKey> key(new SymmetricKey);
+ uint8* key_data =
+ reinterpret_cast<uint8*>(WriteInto(&key->key_, key_size_in_bytes + 1));
+ int rv = PKCS5_PBKDF2_HMAC_SHA1(password.data(), password.length(),
+ reinterpret_cast<const uint8*>(salt.data()),
+ salt.length(), iterations,
+ static_cast<int>(key_size_in_bytes),
+ key_data);
+ return rv == 1 ? key.release() : NULL;
+}
+
+// static
+SymmetricKey* SymmetricKey::Import(Algorithm algorithm,
+ const std::string& raw_key) {
+ if (algorithm == AES) {
+ // Whitelist supported key sizes to avoid accidentaly relying on
+ // algorithms available in NSS but not BoringSSL and vice
+ // versa. Note that BoringSSL does not support AES-192.
+ if (raw_key.size() != 128/8 && raw_key.size() != 256/8)
+ return NULL;
+ }
+
+ scoped_ptr<SymmetricKey> key(new SymmetricKey);
+ key->key_ = raw_key;
+ return key.release();
+}
+
+bool SymmetricKey::GetRawKey(std::string* raw_key) {
+ *raw_key = key_;
+ return true;
+}
+
+} // namespace crypto
diff --git a/crypto/symmetric_key_unittest.cc b/crypto/symmetric_key_unittest.cc
new file mode 100644
index 0000000000..28e44d27da
--- /dev/null
+++ b/crypto/symmetric_key_unittest.cc
@@ -0,0 +1,225 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/symmetric_key.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(SymmetricKeyTest, GenerateRandomKey) {
+ scoped_ptr<crypto::SymmetricKey> key(
+ crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256));
+ ASSERT_TRUE(NULL != key.get());
+ std::string raw_key;
+ EXPECT_TRUE(key->GetRawKey(&raw_key));
+ EXPECT_EQ(32U, raw_key.size());
+
+ // Do it again and check that the keys are different.
+ // (Note: this has a one-in-10^77 chance of failure!)
+ scoped_ptr<crypto::SymmetricKey> key2(
+ crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256));
+ ASSERT_TRUE(NULL != key2.get());
+ std::string raw_key2;
+ EXPECT_TRUE(key2->GetRawKey(&raw_key2));
+ EXPECT_EQ(32U, raw_key2.size());
+ EXPECT_NE(raw_key, raw_key2);
+}
+
+TEST(SymmetricKeyTest, ImportGeneratedKey) {
+ scoped_ptr<crypto::SymmetricKey> key1(
+ crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256));
+ ASSERT_TRUE(NULL != key1.get());
+ std::string raw_key1;
+ EXPECT_TRUE(key1->GetRawKey(&raw_key1));
+
+ scoped_ptr<crypto::SymmetricKey> key2(
+ crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key1));
+ ASSERT_TRUE(NULL != key2.get());
+
+ std::string raw_key2;
+ EXPECT_TRUE(key2->GetRawKey(&raw_key2));
+
+ EXPECT_EQ(raw_key1, raw_key2);
+}
+
+TEST(SymmetricKeyTest, ImportDerivedKey) {
+ scoped_ptr<crypto::SymmetricKey> key1(
+ crypto::SymmetricKey::DeriveKeyFromPassword(
+ crypto::SymmetricKey::HMAC_SHA1, "password", "somesalt", 1024, 160));
+ ASSERT_TRUE(NULL != key1.get());
+ std::string raw_key1;
+ EXPECT_TRUE(key1->GetRawKey(&raw_key1));
+
+ scoped_ptr<crypto::SymmetricKey> key2(
+ crypto::SymmetricKey::Import(crypto::SymmetricKey::HMAC_SHA1, raw_key1));
+ ASSERT_TRUE(NULL != key2.get());
+
+ std::string raw_key2;
+ EXPECT_TRUE(key2->GetRawKey(&raw_key2));
+
+ EXPECT_EQ(raw_key1, raw_key2);
+}
+
+struct PBKDF2TestVector {
+ crypto::SymmetricKey::Algorithm algorithm;
+ const char* password;
+ const char* salt;
+ unsigned int rounds;
+ unsigned int key_size_in_bits;
+ const char* expected; // ASCII encoded hex bytes
+};
+
+class SymmetricKeyDeriveKeyFromPasswordTest
+ : public testing::TestWithParam<PBKDF2TestVector> {
+};
+
+TEST_P(SymmetricKeyDeriveKeyFromPasswordTest, DeriveKeyFromPassword) {
+ PBKDF2TestVector test_data(GetParam());
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // The OS X crypto libraries have minimum salt and iteration requirements
+ // so some of the tests below will cause them to barf. Skip these.
+ if (strlen(test_data.salt) < 8 || test_data.rounds < 1000) {
+ VLOG(1) << "Skipped test vector for " << test_data.expected;
+ return;
+ }
+#endif // OS_MACOSX
+
+ scoped_ptr<crypto::SymmetricKey> key(
+ crypto::SymmetricKey::DeriveKeyFromPassword(
+ test_data.algorithm,
+ test_data.password, test_data.salt,
+ test_data.rounds, test_data.key_size_in_bits));
+ ASSERT_TRUE(NULL != key.get());
+
+ std::string raw_key;
+ key->GetRawKey(&raw_key);
+ EXPECT_EQ(test_data.key_size_in_bits / 8, raw_key.size());
+ EXPECT_EQ(test_data.expected,
+ base::StringToLowerASCII(base::HexEncode(raw_key.data(),
+ raw_key.size())));
+}
+
+static const PBKDF2TestVector kTestVectors[] = {
+ // These tests come from
+ // http://www.ietf.org/id/draft-josefsson-pbkdf2-test-vectors-00.txt
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "password",
+ "salt",
+ 1,
+ 160,
+ "0c60c80f961f0e71f3a9b524af6012062fe037a6",
+ },
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "password",
+ "salt",
+ 2,
+ 160,
+ "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957",
+ },
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "password",
+ "salt",
+ 4096,
+ 160,
+ "4b007901b765489abead49d926f721d065a429c1",
+ },
+ // This test takes over 30s to run on the trybots.
+#if 0
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "password",
+ "salt",
+ 16777216,
+ 160,
+ "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984",
+ },
+#endif
+
+ // These tests come from RFC 3962, via BSD source code at
+ // http://www.openbsd.org/cgi-bin/cvsweb/src/sbin/bioctl/pbkdf2.c?rev=HEAD&content-type=text/plain
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "password",
+ "ATHENA.MIT.EDUraeburn",
+ 1,
+ 160,
+ "cdedb5281bb2f801565a1122b25635150ad1f7a0",
+ },
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "password",
+ "ATHENA.MIT.EDUraeburn",
+ 2,
+ 160,
+ "01dbee7f4a9e243e988b62c73cda935da05378b9",
+ },
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "password",
+ "ATHENA.MIT.EDUraeburn",
+ 1200,
+ 160,
+ "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddb",
+ },
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "password",
+ "\022" "4VxxV4\022", /* 0x1234567878563412 */
+ 5,
+ 160,
+ "d1daa78615f287e6a1c8b120d7062a493f98d203",
+ },
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+ "pass phrase equals block size",
+ 1200,
+ 160,
+ "139c30c0966bc32ba55fdbf212530ac9c5ec59f1",
+ },
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+ "pass phrase exceeds block size",
+ 1200,
+ 160,
+ "9ccad6d468770cd51b10e6a68721be611a8b4d28",
+ },
+ {
+ crypto::SymmetricKey::HMAC_SHA1,
+ "\360\235\204\236", /* g-clef (0xf09d849e) */
+ "EXAMPLE.COMpianist",
+ 50,
+ 160,
+ "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0",
+ },
+
+ // Regression tests for AES keys, derived from the Linux NSS implementation.
+ {
+ crypto::SymmetricKey::AES,
+ "A test password",
+ "saltsalt",
+ 1,
+ 256,
+ "44899a7777f0e6e8b752f875f02044b8ac593de146de896f2e8a816e315a36de",
+ },
+ {
+ crypto::SymmetricKey::AES,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+ "pass phrase exceeds block size",
+ 20,
+ 256,
+ "e0739745dc28b8721ba402e05214d2ac1eab54cf72bee1fba388297a09eb493c",
+ },
+};
+
+INSTANTIATE_TEST_CASE_P(, SymmetricKeyDeriveKeyFromPasswordTest,
+ testing::ValuesIn(kTestVectors));
diff --git a/crypto/symmetric_key_win.cc b/crypto/symmetric_key_win.cc
new file mode 100644
index 0000000000..b3d65f6613
--- /dev/null
+++ b/crypto/symmetric_key_win.cc
@@ -0,0 +1,536 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/symmetric_key.h"
+
+#include <vector>
+
+// TODO(wtc): replace scoped_array by std::vector.
+#include "base/memory/scoped_ptr.h"
+#include "base/sys_byteorder.h"
+
+namespace crypto {
+
+namespace {
+
+// The following is a non-public Microsoft header documented in MSDN under
+// CryptImportKey / CryptExportKey. Following the header is the byte array of
+// the actual plaintext key.
+struct PlaintextBlobHeader {
+ BLOBHEADER hdr;
+ DWORD cbKeySize;
+};
+
+// CryptoAPI makes use of three distinct ALG_IDs for AES, rather than just
+// CALG_AES (which exists, but depending on the functions you are calling, may
+// result in function failure, whereas the subtype would succeed).
+ALG_ID GetAESAlgIDForKeySize(size_t key_size_in_bits) {
+ // Only AES-128/-192/-256 is supported in CryptoAPI.
+ switch (key_size_in_bits) {
+ case 128:
+ return CALG_AES_128;
+ case 192:
+ return CALG_AES_192;
+ case 256:
+ return CALG_AES_256;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+};
+
+// Imports a raw/plaintext key of |key_size| stored in |*key_data| into a new
+// key created for the specified |provider|. |alg| contains the algorithm of
+// the key being imported.
+// If |key_data| is intended to be used as an HMAC key, then |alg| should be
+// CALG_HMAC.
+// If successful, returns true and stores the imported key in |*key|.
+// TODO(wtc): use this function in hmac_win.cc.
+bool ImportRawKey(HCRYPTPROV provider,
+ ALG_ID alg,
+ const void* key_data, size_t key_size,
+ ScopedHCRYPTKEY* key) {
+ DCHECK_GT(key_size, 0);
+
+ DWORD actual_size =
+ static_cast<DWORD>(sizeof(PlaintextBlobHeader) + key_size);
+ std::vector<BYTE> tmp_data(actual_size);
+ BYTE* actual_key = &tmp_data[0];
+ memcpy(actual_key + sizeof(PlaintextBlobHeader), key_data, key_size);
+ PlaintextBlobHeader* key_header =
+ reinterpret_cast<PlaintextBlobHeader*>(actual_key);
+ memset(key_header, 0, sizeof(PlaintextBlobHeader));
+
+ key_header->hdr.bType = PLAINTEXTKEYBLOB;
+ key_header->hdr.bVersion = CUR_BLOB_VERSION;
+ key_header->hdr.aiKeyAlg = alg;
+
+ key_header->cbKeySize = static_cast<DWORD>(key_size);
+
+ HCRYPTKEY unsafe_key = NULL;
+ DWORD flags = CRYPT_EXPORTABLE;
+ if (alg == CALG_HMAC) {
+ // Though it may appear odd that IPSEC and RC2 are being used, this is
+ // done in accordance with Microsoft's FIPS 140-2 Security Policy for the
+ // RSA Enhanced Provider, as the approved means of using arbitrary HMAC
+ // key material.
+ key_header->hdr.aiKeyAlg = CALG_RC2;
+ flags |= CRYPT_IPSEC_HMAC_KEY;
+ }
+
+ BOOL ok =
+ CryptImportKey(provider, actual_key, actual_size, 0, flags, &unsafe_key);
+
+ // Clean up the temporary copy of key, regardless of whether it was imported
+ // sucessfully or not.
+ SecureZeroMemory(actual_key, actual_size);
+
+ if (!ok)
+ return false;
+
+ key->reset(unsafe_key);
+ return true;
+}
+
+// Attempts to generate a random AES key of |key_size_in_bits|. Returns true
+// if generation is successful, storing the generated key in |*key| and the
+// key provider (CSP) in |*provider|.
+bool GenerateAESKey(size_t key_size_in_bits,
+ ScopedHCRYPTPROV* provider,
+ ScopedHCRYPTKEY* key) {
+ DCHECK(provider);
+ DCHECK(key);
+
+ ALG_ID alg = GetAESAlgIDForKeySize(key_size_in_bits);
+ if (alg == 0)
+ return false;
+
+ ScopedHCRYPTPROV safe_provider;
+ // Note: The only time NULL is safe to be passed as pszContainer is when
+ // dwFlags contains CRYPT_VERIFYCONTEXT, as all keys generated and/or used
+ // will be treated as ephemeral keys and not persisted.
+ BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL,
+ PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
+ if (!ok)
+ return false;
+
+ ScopedHCRYPTKEY safe_key;
+ // In the FIPS 140-2 Security Policy for CAPI on XP/Vista+, Microsoft notes
+ // that CryptGenKey makes use of the same functionality exposed via
+ // CryptGenRandom. The reason this is being used, as opposed to
+ // CryptGenRandom and CryptImportKey is for compliance with the security
+ // policy
+ ok = CryptGenKey(safe_provider.get(), alg, CRYPT_EXPORTABLE,
+ safe_key.receive());
+ if (!ok)
+ return false;
+
+ key->swap(safe_key);
+ provider->swap(safe_provider);
+
+ return true;
+}
+
+// Returns true if the HMAC key size meets the requirement of FIPS 198
+// Section 3. |alg| is the hash function used in the HMAC.
+bool CheckHMACKeySize(size_t key_size_in_bits, ALG_ID alg) {
+ DWORD hash_size = 0;
+ switch (alg) {
+ case CALG_SHA1:
+ hash_size = 20;
+ break;
+ case CALG_SHA_256:
+ hash_size = 32;
+ break;
+ case CALG_SHA_384:
+ hash_size = 48;
+ break;
+ case CALG_SHA_512:
+ hash_size = 64;
+ break;
+ }
+ if (hash_size == 0)
+ return false;
+
+ // An HMAC key must be >= L/2, where L is the output size of the hash
+ // function being used.
+ return (key_size_in_bits >= (hash_size / 2 * 8) &&
+ (key_size_in_bits % 8) == 0);
+}
+
+// Attempts to generate a random, |key_size_in_bits|-long HMAC key, for use
+// with the hash function |alg|.
+// |key_size_in_bits| must be >= 1/2 the hash size of |alg| for security.
+// Returns true if generation is successful, storing the generated key in
+// |*key| and the key provider (CSP) in |*provider|.
+bool GenerateHMACKey(size_t key_size_in_bits,
+ ALG_ID alg,
+ ScopedHCRYPTPROV* provider,
+ ScopedHCRYPTKEY* key,
+ scoped_ptr<BYTE[]>* raw_key) {
+ DCHECK(provider);
+ DCHECK(key);
+ DCHECK(raw_key);
+
+ if (!CheckHMACKeySize(key_size_in_bits, alg))
+ return false;
+
+ ScopedHCRYPTPROV safe_provider;
+ // See comment in GenerateAESKey as to why NULL is acceptable for the
+ // container name.
+ BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL,
+ PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+ if (!ok)
+ return false;
+
+ DWORD key_size_in_bytes = static_cast<DWORD>(key_size_in_bits / 8);
+ scoped_ptr<BYTE[]> random(new BYTE[key_size_in_bytes]);
+ ok = CryptGenRandom(safe_provider, key_size_in_bytes, random.get());
+ if (!ok)
+ return false;
+
+ ScopedHCRYPTKEY safe_key;
+ bool rv = ImportRawKey(safe_provider, CALG_HMAC, random.get(),
+ key_size_in_bytes, &safe_key);
+ if (rv) {
+ key->swap(safe_key);
+ provider->swap(safe_provider);
+ raw_key->swap(random);
+ }
+
+ SecureZeroMemory(random.get(), key_size_in_bytes);
+ return rv;
+}
+
+// Attempts to create an HMAC hash instance using the specified |provider|
+// and |key|. The inner hash function will be |hash_alg|. If successful,
+// returns true and stores the hash in |*hash|.
+// TODO(wtc): use this function in hmac_win.cc.
+bool CreateHMACHash(HCRYPTPROV provider,
+ HCRYPTKEY key,
+ ALG_ID hash_alg,
+ ScopedHCRYPTHASH* hash) {
+ ScopedHCRYPTHASH safe_hash;
+ BOOL ok = CryptCreateHash(provider, CALG_HMAC, key, 0, safe_hash.receive());
+ if (!ok)
+ return false;
+
+ HMAC_INFO hmac_info;
+ memset(&hmac_info, 0, sizeof(hmac_info));
+ hmac_info.HashAlgid = hash_alg;
+
+ ok = CryptSetHashParam(safe_hash, HP_HMAC_INFO,
+ reinterpret_cast<const BYTE*>(&hmac_info), 0);
+ if (!ok)
+ return false;
+
+ hash->swap(safe_hash);
+ return true;
+}
+
+// Computes a block of the derived key using the PBKDF2 function F for the
+// specified |block_index| using the PRF |hash|, writing the output to
+// |output_buf|.
+// |output_buf| must have enough space to accomodate the output of the PRF
+// specified by |hash|.
+// Returns true if the block was successfully computed.
+bool ComputePBKDF2Block(HCRYPTHASH hash,
+ DWORD hash_size,
+ const std::string& salt,
+ size_t iterations,
+ uint32 block_index,
+ BYTE* output_buf) {
+ // From RFC 2898:
+ // 3. <snip> The function F is defined as the exclusive-or sum of the first
+ // c iterates of the underlying pseudorandom function PRF applied to the
+ // password P and the concatenation of the salt S and the block index i:
+ // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
+ // where
+ // U_1 = PRF(P, S || INT (i))
+ // U_2 = PRF(P, U_1)
+ // ...
+ // U_c = PRF(P, U_{c-1})
+ ScopedHCRYPTHASH safe_hash;
+ BOOL ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive());
+ if (!ok)
+ return false;
+
+ // Iteration U_1: Compute PRF for S.
+ ok = CryptHashData(safe_hash, reinterpret_cast<const BYTE*>(salt.data()),
+ static_cast<DWORD>(salt.size()), 0);
+ if (!ok)
+ return false;
+
+ // Iteration U_1: and append (big-endian) INT (i).
+ uint32 big_endian_block_index = base::HostToNet32(block_index);
+ ok = CryptHashData(safe_hash,
+ reinterpret_cast<BYTE*>(&big_endian_block_index),
+ sizeof(big_endian_block_index), 0);
+
+ std::vector<BYTE> hash_value(hash_size);
+
+ DWORD size = hash_size;
+ ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0);
+ if (!ok || size != hash_size)
+ return false;
+
+ memcpy(output_buf, &hash_value[0], hash_size);
+
+ // Iteration 2 - c: Compute U_{iteration} by applying the PRF to
+ // U_{iteration - 1}, then xor the resultant hash with |output|, which
+ // contains U_1 ^ U_2 ^ ... ^ U_{iteration - 1}.
+ for (size_t iteration = 2; iteration <= iterations; ++iteration) {
+ safe_hash.reset();
+ ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive());
+ if (!ok)
+ return false;
+
+ ok = CryptHashData(safe_hash, &hash_value[0], hash_size, 0);
+ if (!ok)
+ return false;
+
+ size = hash_size;
+ ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0);
+ if (!ok || size != hash_size)
+ return false;
+
+ for (int i = 0; i < hash_size; ++i)
+ output_buf[i] ^= hash_value[i];
+ }
+
+ return true;
+}
+
+} // namespace
+
+SymmetricKey::~SymmetricKey() {
+ // TODO(wtc): create a "secure" string type that zeroes itself in the
+ // destructor.
+ if (!raw_key_.empty())
+ SecureZeroMemory(const_cast<char *>(raw_key_.data()), raw_key_.size());
+}
+
+// static
+SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
+ size_t key_size_in_bits) {
+ DCHECK_GE(key_size_in_bits, 8);
+
+ ScopedHCRYPTPROV provider;
+ ScopedHCRYPTKEY key;
+
+ bool ok = false;
+ scoped_ptr<BYTE[]> raw_key;
+
+ switch (algorithm) {
+ case AES:
+ ok = GenerateAESKey(key_size_in_bits, &provider, &key);
+ break;
+ case HMAC_SHA1:
+ ok = GenerateHMACKey(key_size_in_bits, CALG_SHA1, &provider,
+ &key, &raw_key);
+ break;
+ }
+
+ if (!ok) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ size_t key_size_in_bytes = key_size_in_bits / 8;
+ if (raw_key == NULL)
+ key_size_in_bytes = 0;
+
+ SymmetricKey* result = new SymmetricKey(provider.release(),
+ key.release(),
+ raw_key.get(),
+ key_size_in_bytes);
+ if (raw_key != NULL)
+ SecureZeroMemory(raw_key.get(), key_size_in_bytes);
+
+ return result;
+}
+
+// static
+SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm,
+ const std::string& password,
+ const std::string& salt,
+ size_t iterations,
+ size_t key_size_in_bits) {
+ // CryptoAPI lacks routines to perform PBKDF2 derivation as specified
+ // in RFC 2898, so it must be manually implemented. Only HMAC-SHA1 is
+ // supported as the PRF.
+
+ // While not used until the end, sanity-check the input before proceeding
+ // with the expensive computation.
+ DWORD provider_type = 0;
+ ALG_ID alg = 0;
+ switch (algorithm) {
+ case AES:
+ provider_type = PROV_RSA_AES;
+ alg = GetAESAlgIDForKeySize(key_size_in_bits);
+ break;
+ case HMAC_SHA1:
+ provider_type = PROV_RSA_FULL;
+ alg = CALG_HMAC;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ if (provider_type == 0 || alg == 0)
+ return NULL;
+
+ ScopedHCRYPTPROV provider;
+ BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type,
+ CRYPT_VERIFYCONTEXT);
+ if (!ok)
+ return NULL;
+
+ // Convert the user password into a key suitable to be fed into the PRF
+ // function.
+ ScopedHCRYPTKEY password_as_key;
+ BYTE* password_as_bytes =
+ const_cast<BYTE*>(reinterpret_cast<const BYTE*>(password.data()));
+ if (!ImportRawKey(provider, CALG_HMAC, password_as_bytes,
+ password.size(), &password_as_key))
+ return NULL;
+
+ // Configure the PRF function. Only HMAC variants are supported, with the
+ // only hash function supported being SHA1.
+ // TODO(rsleevi): Support SHA-256 on XP SP3+.
+ ScopedHCRYPTHASH prf;
+ if (!CreateHMACHash(provider, password_as_key, CALG_SHA1, &prf))
+ return NULL;
+
+ DWORD hLen = 0;
+ DWORD param_size = sizeof(hLen);
+ ok = CryptGetHashParam(prf, HP_HASHSIZE,
+ reinterpret_cast<BYTE*>(&hLen), &param_size, 0);
+ if (!ok || hLen == 0)
+ return NULL;
+
+ // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop.
+ size_t dkLen = key_size_in_bits / 8;
+ DCHECK_GT(dkLen, 0);
+
+ if ((dkLen / hLen) > 0xFFFFFFFF) {
+ DLOG(ERROR) << "Derived key too long.";
+ return NULL;
+ }
+
+ // 2. Let l be the number of hLen-octet blocks in the derived key,
+ // rounding up, and let r be the number of octets in the last
+ // block:
+ size_t L = (dkLen + hLen - 1) / hLen;
+ DCHECK_GT(L, 0);
+
+ size_t total_generated_size = L * hLen;
+ std::vector<BYTE> generated_key(total_generated_size);
+ BYTE* block_offset = &generated_key[0];
+
+ // 3. For each block of the derived key apply the function F defined below
+ // to the password P, the salt S, the iteration count c, and the block
+ // index to compute the block:
+ // T_1 = F (P, S, c, 1)
+ // T_2 = F (P, S, c, 2)
+ // ...
+ // T_l = F (P, S, c, l)
+ // <snip>
+ // 4. Concatenate the blocks and extract the first dkLen octets to produce
+ // a derived key DK:
+ // DK = T_1 || T_2 || ... || T_l<0..r-1>
+ for (uint32 block_index = 1; block_index <= L; ++block_index) {
+ if (!ComputePBKDF2Block(prf, hLen, salt, iterations, block_index,
+ block_offset))
+ return NULL;
+ block_offset += hLen;
+ }
+
+ // Convert the derived key bytes into a key handle for the desired algorithm.
+ ScopedHCRYPTKEY key;
+ if (!ImportRawKey(provider, alg, &generated_key[0], dkLen, &key))
+ return NULL;
+
+ SymmetricKey* result = new SymmetricKey(provider.release(), key.release(),
+ &generated_key[0], dkLen);
+
+ SecureZeroMemory(&generated_key[0], total_generated_size);
+
+ return result;
+}
+
+// static
+SymmetricKey* SymmetricKey::Import(Algorithm algorithm,
+ const std::string& raw_key) {
+ DWORD provider_type = 0;
+ ALG_ID alg = 0;
+ switch (algorithm) {
+ case AES:
+ provider_type = PROV_RSA_AES;
+ alg = GetAESAlgIDForKeySize(raw_key.size() * 8);
+ break;
+ case HMAC_SHA1:
+ provider_type = PROV_RSA_FULL;
+ alg = CALG_HMAC;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ if (provider_type == 0 || alg == 0)
+ return NULL;
+
+ ScopedHCRYPTPROV provider;
+ BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type,
+ CRYPT_VERIFYCONTEXT);
+ if (!ok)
+ return NULL;
+
+ ScopedHCRYPTKEY key;
+ if (!ImportRawKey(provider, alg, raw_key.data(), raw_key.size(), &key))
+ return NULL;
+
+ return new SymmetricKey(provider.release(), key.release(),
+ raw_key.data(), raw_key.size());
+}
+
+bool SymmetricKey::GetRawKey(std::string* raw_key) {
+ // Short circuit for when the key was supplied to the constructor.
+ if (!raw_key_.empty()) {
+ *raw_key = raw_key_;
+ return true;
+ }
+
+ DWORD size = 0;
+ BOOL ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, NULL, &size);
+ if (!ok)
+ return false;
+
+ std::vector<BYTE> result(size);
+
+ ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, &result[0], &size);
+ if (!ok)
+ return false;
+
+ PlaintextBlobHeader* header =
+ reinterpret_cast<PlaintextBlobHeader*>(&result[0]);
+ raw_key->assign(reinterpret_cast<char*>(&result[sizeof(*header)]),
+ header->cbKeySize);
+
+ SecureZeroMemory(&result[0], size);
+
+ return true;
+}
+
+SymmetricKey::SymmetricKey(HCRYPTPROV provider,
+ HCRYPTKEY key,
+ const void* key_data, size_t key_size_in_bytes)
+ : provider_(provider), key_(key) {
+ if (key_data) {
+ raw_key_.assign(reinterpret_cast<const char*>(key_data),
+ key_size_in_bytes);
+ }
+}
+
+} // namespace crypto
diff --git a/crypto/third_party/nss/pk11akey.cc b/crypto/third_party/nss/pk11akey.cc
new file mode 100644
index 0000000000..4db582fc6c
--- /dev/null
+++ b/crypto/third_party/nss/pk11akey.cc
@@ -0,0 +1,98 @@
+ /* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dr Stephen Henson <stephen.henson@gemplus.com>
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, and
+ * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "crypto/third_party/nss/chromium-nss.h"
+
+#include <pk11pub.h>
+
+#include "base/logging.h"
+
+// Based on PK11_ImportEncryptedPrivateKeyInfo function in
+// mozilla/security/nss/lib/pk11wrap/pk11akey.c.
+SECStatus ImportEncryptedECPrivateKeyInfoAndReturnKey(
+ PK11SlotInfo* slot,
+ SECKEYEncryptedPrivateKeyInfo* epki,
+ SECItem* password,
+ SECItem* nickname,
+ SECItem* public_value,
+ PRBool permanent,
+ PRBool sensitive,
+ SECKEYPrivateKey** private_key,
+ void* wincx) {
+ SECItem* crypto_param = NULL;
+
+ CK_ATTRIBUTE_TYPE usage = CKA_SIGN;
+
+ PK11SymKey* key = PK11_PBEKeyGen(slot,
+ &epki->algorithm,
+ password,
+ PR_FALSE, // faulty3DES
+ wincx);
+ if (key == NULL) {
+ DLOG(ERROR) << "PK11_PBEKeyGen: " << PORT_GetError();
+ return SECFailure;
+ }
+
+ CK_MECHANISM_TYPE crypto_mech_type = PK11_GetPBECryptoMechanism(
+ &epki->algorithm, &crypto_param, password);
+ if (crypto_mech_type == CKM_INVALID_MECHANISM) {
+ DLOG(ERROR) << "PK11_GetPBECryptoMechanism: " << PORT_GetError();
+ PK11_FreeSymKey(key);
+ return SECFailure;
+ }
+
+ crypto_mech_type = PK11_GetPadMechanism(crypto_mech_type);
+
+ *private_key = PK11_UnwrapPrivKey(slot, key, crypto_mech_type, crypto_param,
+ &epki->encryptedData, nickname,
+ public_value, permanent, sensitive, CKK_EC,
+ &usage, 1, wincx);
+
+ if (crypto_param != NULL)
+ SECITEM_ZfreeItem(crypto_param, PR_TRUE);
+
+ PK11_FreeSymKey(key);
+
+ if (!*private_key) {
+ DLOG(ERROR) << "PK11_UnwrapPrivKey: " << PORT_GetError();
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
diff --git a/crypto/third_party/nss/secsign.cc b/crypto/third_party/nss/secsign.cc
new file mode 100644
index 0000000000..a788defc70
--- /dev/null
+++ b/crypto/third_party/nss/secsign.cc
@@ -0,0 +1,132 @@
+/*
+ * Signature stuff.
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "crypto/third_party/nss/chromium-nss.h"
+
+#include <vector>
+
+#include <cryptohi.h>
+#include <pk11pub.h>
+#include <secerr.h>
+#include <sechash.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+
+SECStatus DerSignData(PLArenaPool *arena,
+ SECItem *result,
+ SECItem *input,
+ SECKEYPrivateKey *key,
+ SECOidTag algo_id) {
+ if (key->keyType != ecKey) {
+ return SEC_DerSignData(arena, result, input->data, input->len, key,
+ algo_id);
+ }
+
+ // NSS has a private function sec_DecodeSigAlg it uses to figure out the
+ // correct hash from the algorithm id.
+ HASH_HashType hash_type;
+ switch (algo_id) {
+ case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
+ hash_type = HASH_AlgSHA1;
+ break;
+#ifdef SHA224_LENGTH
+ case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
+ hash_type = HASH_AlgSHA224;
+ break;
+#endif
+ case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
+ hash_type = HASH_AlgSHA256;
+ break;
+ case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
+ hash_type = HASH_AlgSHA384;
+ break;
+ case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
+ hash_type = HASH_AlgSHA512;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+
+ // Hash the input.
+ std::vector<uint8> hash_data(HASH_ResultLen(hash_type));
+ SECStatus rv = HASH_HashBuf(
+ hash_type, &hash_data[0], input->data, input->len);
+ if (rv != SECSuccess)
+ return rv;
+ SECItem hash = {siBuffer, &hash_data[0],
+ static_cast<unsigned int>(hash_data.size())};
+
+ // Compute signature of hash.
+ int signature_len = PK11_SignatureLen(key);
+ std::vector<uint8> signature_data(signature_len);
+ SECItem sig = {siBuffer, &signature_data[0],
+ static_cast<unsigned int>(signature_len)};
+ rv = PK11_Sign(key, &sig, &hash);
+ if (rv != SECSuccess)
+ return rv;
+
+ CERTSignedData sd;
+ PORT_Memset(&sd, 0, sizeof(sd));
+ // Fill in tbsCertificate.
+ sd.data.data = (unsigned char*) input->data;
+ sd.data.len = input->len;
+
+ // Fill in signatureAlgorithm.
+ rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algo_id, 0);
+ if (rv != SECSuccess)
+ return rv;
+
+ // Fill in signatureValue.
+ rv = DSAU_EncodeDerSigWithLen(&sd.signature, &sig, sig.len);
+ if (rv != SECSuccess)
+ return rv;
+ sd.signature.len <<= 3; // Convert to bit string.
+
+ // DER encode the signed data object.
+ void* encode_result = SEC_ASN1EncodeItem(
+ arena, result, &sd, SEC_ASN1_GET(CERT_SignedDataTemplate));
+
+ PORT_Free(sd.signature.data);
+
+ return encode_result ? SECSuccess : SECFailure;
+}
diff --git a/crypto/third_party/nss/sha512.cc b/crypto/third_party/nss/sha512.cc
new file mode 100644
index 0000000000..5ef4e5062d
--- /dev/null
+++ b/crypto/third_party/nss/sha512.cc
@@ -0,0 +1,1391 @@
+/*
+ * sha512.c - implementation of SHA256, SHA384 and SHA512
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* $Id: sha512.c,v 1.9 2006/10/13 16:54:04 wtchang%redhat.com Exp $ */
+
+// Prevent manual unrolling in the sha256 code, which reduces the binary code
+// size from ~10k to ~1k. The performance should be reasonable for our use.
+#define NOUNROLL256 1
+
+#include "crypto/third_party/nss/chromium-prtypes.h" /* for PRUintXX */
+#if defined(_X86_) || defined(SHA_NO_LONG_LONG)
+#define NOUNROLL512 1
+#undef HAVE_LONG_LONG
+#endif
+#include "crypto/third_party/nss/chromium-blapi.h"
+#include "crypto/third_party/nss/chromium-sha256.h" /* for struct SHA256ContextStr */
+
+#include <stdlib.h>
+#include <string.h>
+#define PORT_New(type) static_cast<type*>(malloc(sizeof(type)))
+#define PORT_ZFree(ptr, len) do { memset(ptr, 0, len); free(ptr); } while (0)
+#define PORT_Strlen(s) static_cast<unsigned int>(strlen(s))
+#define PORT_Memcpy memcpy
+
+/* ============= Common constants and defines ======================= */
+
+#define W ctx->u.w
+#define B ctx->u.b
+#define H ctx->h
+
+#define SHR(x,n) (x >> n)
+#define SHL(x,n) (x << n)
+#define Ch(x,y,z) ((x & y) ^ (~x & z))
+#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
+
+/* Padding used with all flavors of SHA */
+static const PRUint8 pad[240] = {
+0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ /* compiler will fill the rest in with zeros */
+};
+
+/* ============= SHA256 implemenmtation ================================== */
+
+/* SHA-256 constants, K256. */
+static const PRUint32 K256[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/* SHA-256 initial hash values */
+static const PRUint32 H256[8] = {
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
+};
+
+#if defined(_MSC_VER) && defined(_X86_)
+#ifndef FORCEINLINE
+#if (_MSC_VER >= 1200)
+#define FORCEINLINE __forceinline
+#else
+#define FORCEINLINE __inline
+#endif
+#endif
+#define FASTCALL __fastcall
+
+static FORCEINLINE PRUint32 FASTCALL
+swap4b(PRUint32 dwd)
+{
+ __asm {
+ mov eax,dwd
+ bswap eax
+ }
+}
+
+#define SHA_HTONL(x) swap4b(x)
+#define BYTESWAP4(x) x = SHA_HTONL(x)
+
+#elif defined(LINUX) && defined(_X86_)
+#undef __OPTIMIZE__
+#define __OPTIMIZE__ 1
+#undef __pentium__
+#define __pentium__ 1
+#include <byteswap.h>
+#define SHA_HTONL(x) bswap_32(x)
+#define BYTESWAP4(x) x = SHA_HTONL(x)
+
+#else /* neither windows nor Linux PC */
+#define SWAP4MASK 0x00FF00FF
+#define SHA_HTONL(x) (t1 = (x), t1 = (t1 << 16) | (t1 >> 16), \
+ ((t1 & SWAP4MASK) << 8) | ((t1 >> 8) & SWAP4MASK))
+#define BYTESWAP4(x) x = SHA_HTONL(x)
+#endif
+
+#if defined(_MSC_VER) && defined(_X86_)
+#pragma intrinsic (_lrotr, _lrotl)
+#define ROTR32(x,n) _lrotr(x,n)
+#define ROTL32(x,n) _lrotl(x,n)
+#else
+#define ROTR32(x,n) ((x >> n) | (x << ((8 * sizeof x) - n)))
+#define ROTL32(x,n) ((x << n) | (x >> ((8 * sizeof x) - n)))
+#endif
+
+/* Capitol Sigma and lower case sigma functions */
+#define S0(x) (ROTR32(x, 2) ^ ROTR32(x,13) ^ ROTR32(x,22))
+#define S1(x) (ROTR32(x, 6) ^ ROTR32(x,11) ^ ROTR32(x,25))
+#define s0(x) (t1 = x, ROTR32(t1, 7) ^ ROTR32(t1,18) ^ SHR(t1, 3))
+#define s1(x) (t2 = x, ROTR32(t2,17) ^ ROTR32(t2,19) ^ SHR(t2,10))
+
+SHA256Context *
+SHA256_NewContext(void)
+{
+ SHA256Context *ctx = PORT_New(SHA256Context);
+ return ctx;
+}
+
+void
+SHA256_DestroyContext(SHA256Context *ctx, PRBool freeit)
+{
+ if (freeit) {
+ PORT_ZFree(ctx, sizeof *ctx);
+ }
+}
+
+void
+SHA256_Begin(SHA256Context *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ memcpy(H, H256, sizeof H256);
+}
+
+static void
+SHA256_Compress(SHA256Context *ctx)
+{
+ {
+ register PRUint32 t1, t2;
+
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP4(W[0]);
+ BYTESWAP4(W[1]);
+ BYTESWAP4(W[2]);
+ BYTESWAP4(W[3]);
+ BYTESWAP4(W[4]);
+ BYTESWAP4(W[5]);
+ BYTESWAP4(W[6]);
+ BYTESWAP4(W[7]);
+ BYTESWAP4(W[8]);
+ BYTESWAP4(W[9]);
+ BYTESWAP4(W[10]);
+ BYTESWAP4(W[11]);
+ BYTESWAP4(W[12]);
+ BYTESWAP4(W[13]);
+ BYTESWAP4(W[14]);
+ BYTESWAP4(W[15]);
+#endif
+
+#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16])
+
+ /* prepare the "message schedule" */
+#ifdef NOUNROLL256
+ {
+ int t;
+ for (t = 16; t < 64; ++t) {
+ INITW(t);
+ }
+ }
+#else
+ INITW(16);
+ INITW(17);
+ INITW(18);
+ INITW(19);
+
+ INITW(20);
+ INITW(21);
+ INITW(22);
+ INITW(23);
+ INITW(24);
+ INITW(25);
+ INITW(26);
+ INITW(27);
+ INITW(28);
+ INITW(29);
+
+ INITW(30);
+ INITW(31);
+ INITW(32);
+ INITW(33);
+ INITW(34);
+ INITW(35);
+ INITW(36);
+ INITW(37);
+ INITW(38);
+ INITW(39);
+
+ INITW(40);
+ INITW(41);
+ INITW(42);
+ INITW(43);
+ INITW(44);
+ INITW(45);
+ INITW(46);
+ INITW(47);
+ INITW(48);
+ INITW(49);
+
+ INITW(50);
+ INITW(51);
+ INITW(52);
+ INITW(53);
+ INITW(54);
+ INITW(55);
+ INITW(56);
+ INITW(57);
+ INITW(58);
+ INITW(59);
+
+ INITW(60);
+ INITW(61);
+ INITW(62);
+ INITW(63);
+
+#endif
+#undef INITW
+ }
+ {
+ PRUint32 a, b, c, d, e, f, g, h;
+
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+#define ROUND(n,a,b,c,d,e,f,g,h) \
+ h += S1(e) + Ch(e,f,g) + K256[n] + W[n]; \
+ d += h; \
+ h += S0(a) + Maj(a,b,c);
+
+#ifdef NOUNROLL256
+ {
+ int t;
+ for (t = 0; t < 64; t+= 8) {
+ ROUND(t+0,a,b,c,d,e,f,g,h)
+ ROUND(t+1,h,a,b,c,d,e,f,g)
+ ROUND(t+2,g,h,a,b,c,d,e,f)
+ ROUND(t+3,f,g,h,a,b,c,d,e)
+ ROUND(t+4,e,f,g,h,a,b,c,d)
+ ROUND(t+5,d,e,f,g,h,a,b,c)
+ ROUND(t+6,c,d,e,f,g,h,a,b)
+ ROUND(t+7,b,c,d,e,f,g,h,a)
+ }
+ }
+#else
+ ROUND( 0,a,b,c,d,e,f,g,h)
+ ROUND( 1,h,a,b,c,d,e,f,g)
+ ROUND( 2,g,h,a,b,c,d,e,f)
+ ROUND( 3,f,g,h,a,b,c,d,e)
+ ROUND( 4,e,f,g,h,a,b,c,d)
+ ROUND( 5,d,e,f,g,h,a,b,c)
+ ROUND( 6,c,d,e,f,g,h,a,b)
+ ROUND( 7,b,c,d,e,f,g,h,a)
+
+ ROUND( 8,a,b,c,d,e,f,g,h)
+ ROUND( 9,h,a,b,c,d,e,f,g)
+ ROUND(10,g,h,a,b,c,d,e,f)
+ ROUND(11,f,g,h,a,b,c,d,e)
+ ROUND(12,e,f,g,h,a,b,c,d)
+ ROUND(13,d,e,f,g,h,a,b,c)
+ ROUND(14,c,d,e,f,g,h,a,b)
+ ROUND(15,b,c,d,e,f,g,h,a)
+
+ ROUND(16,a,b,c,d,e,f,g,h)
+ ROUND(17,h,a,b,c,d,e,f,g)
+ ROUND(18,g,h,a,b,c,d,e,f)
+ ROUND(19,f,g,h,a,b,c,d,e)
+ ROUND(20,e,f,g,h,a,b,c,d)
+ ROUND(21,d,e,f,g,h,a,b,c)
+ ROUND(22,c,d,e,f,g,h,a,b)
+ ROUND(23,b,c,d,e,f,g,h,a)
+
+ ROUND(24,a,b,c,d,e,f,g,h)
+ ROUND(25,h,a,b,c,d,e,f,g)
+ ROUND(26,g,h,a,b,c,d,e,f)
+ ROUND(27,f,g,h,a,b,c,d,e)
+ ROUND(28,e,f,g,h,a,b,c,d)
+ ROUND(29,d,e,f,g,h,a,b,c)
+ ROUND(30,c,d,e,f,g,h,a,b)
+ ROUND(31,b,c,d,e,f,g,h,a)
+
+ ROUND(32,a,b,c,d,e,f,g,h)
+ ROUND(33,h,a,b,c,d,e,f,g)
+ ROUND(34,g,h,a,b,c,d,e,f)
+ ROUND(35,f,g,h,a,b,c,d,e)
+ ROUND(36,e,f,g,h,a,b,c,d)
+ ROUND(37,d,e,f,g,h,a,b,c)
+ ROUND(38,c,d,e,f,g,h,a,b)
+ ROUND(39,b,c,d,e,f,g,h,a)
+
+ ROUND(40,a,b,c,d,e,f,g,h)
+ ROUND(41,h,a,b,c,d,e,f,g)
+ ROUND(42,g,h,a,b,c,d,e,f)
+ ROUND(43,f,g,h,a,b,c,d,e)
+ ROUND(44,e,f,g,h,a,b,c,d)
+ ROUND(45,d,e,f,g,h,a,b,c)
+ ROUND(46,c,d,e,f,g,h,a,b)
+ ROUND(47,b,c,d,e,f,g,h,a)
+
+ ROUND(48,a,b,c,d,e,f,g,h)
+ ROUND(49,h,a,b,c,d,e,f,g)
+ ROUND(50,g,h,a,b,c,d,e,f)
+ ROUND(51,f,g,h,a,b,c,d,e)
+ ROUND(52,e,f,g,h,a,b,c,d)
+ ROUND(53,d,e,f,g,h,a,b,c)
+ ROUND(54,c,d,e,f,g,h,a,b)
+ ROUND(55,b,c,d,e,f,g,h,a)
+
+ ROUND(56,a,b,c,d,e,f,g,h)
+ ROUND(57,h,a,b,c,d,e,f,g)
+ ROUND(58,g,h,a,b,c,d,e,f)
+ ROUND(59,f,g,h,a,b,c,d,e)
+ ROUND(60,e,f,g,h,a,b,c,d)
+ ROUND(61,d,e,f,g,h,a,b,c)
+ ROUND(62,c,d,e,f,g,h,a,b)
+ ROUND(63,b,c,d,e,f,g,h,a)
+#endif
+
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+ }
+#undef ROUND
+}
+
+#undef s0
+#undef s1
+#undef S0
+#undef S1
+
+void
+SHA256_Update(SHA256Context *ctx, const unsigned char *input,
+ unsigned int inputLen)
+{
+ unsigned int inBuf = ctx->sizeLo & 0x3f;
+ if (!inputLen)
+ return;
+
+ /* Add inputLen into the count of bytes processed, before processing */
+ if ((ctx->sizeLo += inputLen) < inputLen)
+ ctx->sizeHi++;
+
+ /* if data already in buffer, attemp to fill rest of buffer */
+ if (inBuf) {
+ unsigned int todo = SHA256_BLOCK_LENGTH - inBuf;
+ if (inputLen < todo)
+ todo = inputLen;
+ memcpy(B + inBuf, input, todo);
+ input += todo;
+ inputLen -= todo;
+ if (inBuf + todo == SHA256_BLOCK_LENGTH)
+ SHA256_Compress(ctx);
+ }
+
+ /* if enough data to fill one or more whole buffers, process them. */
+ while (inputLen >= SHA256_BLOCK_LENGTH) {
+ memcpy(B, input, SHA256_BLOCK_LENGTH);
+ input += SHA256_BLOCK_LENGTH;
+ inputLen -= SHA256_BLOCK_LENGTH;
+ SHA256_Compress(ctx);
+ }
+ /* if data left over, fill it into buffer */
+ if (inputLen)
+ memcpy(B, input, inputLen);
+}
+
+void
+SHA256_End(SHA256Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+ unsigned int inBuf = ctx->sizeLo & 0x3f;
+ unsigned int padLen = (inBuf < 56) ? (56 - inBuf) : (56 + 64 - inBuf);
+ PRUint32 hi, lo;
+#ifdef SWAP4MASK
+ PRUint32 t1;
+#endif
+
+ hi = (ctx->sizeHi << 3) | (ctx->sizeLo >> 29);
+ lo = (ctx->sizeLo << 3);
+
+ SHA256_Update(ctx, pad, padLen);
+
+#if defined(IS_LITTLE_ENDIAN)
+ W[14] = SHA_HTONL(hi);
+ W[15] = SHA_HTONL(lo);
+#else
+ W[14] = hi;
+ W[15] = lo;
+#endif
+ SHA256_Compress(ctx);
+
+ /* now output the answer */
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP4(H[0]);
+ BYTESWAP4(H[1]);
+ BYTESWAP4(H[2]);
+ BYTESWAP4(H[3]);
+ BYTESWAP4(H[4]);
+ BYTESWAP4(H[5]);
+ BYTESWAP4(H[6]);
+ BYTESWAP4(H[7]);
+#endif
+ padLen = PR_MIN(SHA256_LENGTH, maxDigestLen);
+ memcpy(digest, H, padLen);
+ if (digestLen)
+ *digestLen = padLen;
+}
+
+/* Comment out unused code, mostly the SHA384 and SHA512 implementations. */
+#if 0
+SECStatus
+SHA256_HashBuf(unsigned char *dest, const unsigned char *src,
+ unsigned int src_length)
+{
+ SHA256Context ctx;
+ unsigned int outLen;
+
+ SHA256_Begin(&ctx);
+ SHA256_Update(&ctx, src, src_length);
+ SHA256_End(&ctx, dest, &outLen, SHA256_LENGTH);
+
+ return SECSuccess;
+}
+
+
+SECStatus
+SHA256_Hash(unsigned char *dest, const char *src)
+{
+ return SHA256_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src));
+}
+
+
+void SHA256_TraceState(SHA256Context *ctx) { }
+
+unsigned int
+SHA256_FlattenSize(SHA256Context *ctx)
+{
+ return sizeof *ctx;
+}
+
+SECStatus
+SHA256_Flatten(SHA256Context *ctx,unsigned char *space)
+{
+ PORT_Memcpy(space, ctx, sizeof *ctx);
+ return SECSuccess;
+}
+
+SHA256Context *
+SHA256_Resurrect(unsigned char *space, void *arg)
+{
+ SHA256Context *ctx = SHA256_NewContext();
+ if (ctx)
+ PORT_Memcpy(ctx, space, sizeof *ctx);
+ return ctx;
+}
+
+void SHA256_Clone(SHA256Context *dest, SHA256Context *src)
+{
+ memcpy(dest, src, sizeof *dest);
+}
+
+
+/* ======= SHA512 and SHA384 common constants and defines ================= */
+
+/* common #defines for SHA512 and SHA384 */
+#if defined(HAVE_LONG_LONG)
+#define ROTR64(x,n) ((x >> n) | (x << (64 - n)))
+#define ROTL64(x,n) ((x << n) | (x >> (64 - n)))
+
+#define S0(x) (ROTR64(x,28) ^ ROTR64(x,34) ^ ROTR64(x,39))
+#define S1(x) (ROTR64(x,14) ^ ROTR64(x,18) ^ ROTR64(x,41))
+#define s0(x) (t1 = x, ROTR64(t1, 1) ^ ROTR64(t1, 8) ^ SHR(t1,7))
+#define s1(x) (t2 = x, ROTR64(t2,19) ^ ROTR64(t2,61) ^ SHR(t2,6))
+
+#if PR_BYTES_PER_LONG == 8
+#define ULLC(hi,lo) 0x ## hi ## lo ## UL
+#elif defined(_MSC_VER)
+#define ULLC(hi,lo) 0x ## hi ## lo ## ui64
+#else
+#define ULLC(hi,lo) 0x ## hi ## lo ## ULL
+#endif
+
+#define SHA_MASK16 ULLC(0000FFFF,0000FFFF)
+#define SHA_MASK8 ULLC(00FF00FF,00FF00FF)
+#define SHA_HTONLL(x) (t1 = x, \
+ t1 = ((t1 & SHA_MASK8 ) << 8) | ((t1 >> 8) & SHA_MASK8 ), \
+ t1 = ((t1 & SHA_MASK16) << 16) | ((t1 >> 16) & SHA_MASK16), \
+ (t1 >> 32) | (t1 << 32))
+#define BYTESWAP8(x) x = SHA_HTONLL(x)
+
+#else /* no long long */
+
+#if defined(IS_LITTLE_ENDIAN)
+#define ULLC(hi,lo) { 0x ## lo ## U, 0x ## hi ## U }
+#else
+#define ULLC(hi,lo) { 0x ## hi ## U, 0x ## lo ## U }
+#endif
+
+#define SHA_HTONLL(x) ( BYTESWAP4(x.lo), BYTESWAP4(x.hi), \
+ x.hi ^= x.lo ^= x.hi ^= x.lo, x)
+#define BYTESWAP8(x) do { PRUint32 tmp; BYTESWAP4(x.lo); BYTESWAP4(x.hi); \
+ tmp = x.lo; x.lo = x.hi; x.hi = tmp; } while (0)
+#endif
+
+/* SHA-384 and SHA-512 constants, K512. */
+static const PRUint64 K512[80] = {
+#if PR_BYTES_PER_LONG == 8
+ 0x428a2f98d728ae22UL , 0x7137449123ef65cdUL ,
+ 0xb5c0fbcfec4d3b2fUL , 0xe9b5dba58189dbbcUL ,
+ 0x3956c25bf348b538UL , 0x59f111f1b605d019UL ,
+ 0x923f82a4af194f9bUL , 0xab1c5ed5da6d8118UL ,
+ 0xd807aa98a3030242UL , 0x12835b0145706fbeUL ,
+ 0x243185be4ee4b28cUL , 0x550c7dc3d5ffb4e2UL ,
+ 0x72be5d74f27b896fUL , 0x80deb1fe3b1696b1UL ,
+ 0x9bdc06a725c71235UL , 0xc19bf174cf692694UL ,
+ 0xe49b69c19ef14ad2UL , 0xefbe4786384f25e3UL ,
+ 0x0fc19dc68b8cd5b5UL , 0x240ca1cc77ac9c65UL ,
+ 0x2de92c6f592b0275UL , 0x4a7484aa6ea6e483UL ,
+ 0x5cb0a9dcbd41fbd4UL , 0x76f988da831153b5UL ,
+ 0x983e5152ee66dfabUL , 0xa831c66d2db43210UL ,
+ 0xb00327c898fb213fUL , 0xbf597fc7beef0ee4UL ,
+ 0xc6e00bf33da88fc2UL , 0xd5a79147930aa725UL ,
+ 0x06ca6351e003826fUL , 0x142929670a0e6e70UL ,
+ 0x27b70a8546d22ffcUL , 0x2e1b21385c26c926UL ,
+ 0x4d2c6dfc5ac42aedUL , 0x53380d139d95b3dfUL ,
+ 0x650a73548baf63deUL , 0x766a0abb3c77b2a8UL ,
+ 0x81c2c92e47edaee6UL , 0x92722c851482353bUL ,
+ 0xa2bfe8a14cf10364UL , 0xa81a664bbc423001UL ,
+ 0xc24b8b70d0f89791UL , 0xc76c51a30654be30UL ,
+ 0xd192e819d6ef5218UL , 0xd69906245565a910UL ,
+ 0xf40e35855771202aUL , 0x106aa07032bbd1b8UL ,
+ 0x19a4c116b8d2d0c8UL , 0x1e376c085141ab53UL ,
+ 0x2748774cdf8eeb99UL , 0x34b0bcb5e19b48a8UL ,
+ 0x391c0cb3c5c95a63UL , 0x4ed8aa4ae3418acbUL ,
+ 0x5b9cca4f7763e373UL , 0x682e6ff3d6b2b8a3UL ,
+ 0x748f82ee5defb2fcUL , 0x78a5636f43172f60UL ,
+ 0x84c87814a1f0ab72UL , 0x8cc702081a6439ecUL ,
+ 0x90befffa23631e28UL , 0xa4506cebde82bde9UL ,
+ 0xbef9a3f7b2c67915UL , 0xc67178f2e372532bUL ,
+ 0xca273eceea26619cUL , 0xd186b8c721c0c207UL ,
+ 0xeada7dd6cde0eb1eUL , 0xf57d4f7fee6ed178UL ,
+ 0x06f067aa72176fbaUL , 0x0a637dc5a2c898a6UL ,
+ 0x113f9804bef90daeUL , 0x1b710b35131c471bUL ,
+ 0x28db77f523047d84UL , 0x32caab7b40c72493UL ,
+ 0x3c9ebe0a15c9bebcUL , 0x431d67c49c100d4cUL ,
+ 0x4cc5d4becb3e42b6UL , 0x597f299cfc657e2aUL ,
+ 0x5fcb6fab3ad6faecUL , 0x6c44198c4a475817UL
+#else
+ ULLC(428a2f98,d728ae22), ULLC(71374491,23ef65cd),
+ ULLC(b5c0fbcf,ec4d3b2f), ULLC(e9b5dba5,8189dbbc),
+ ULLC(3956c25b,f348b538), ULLC(59f111f1,b605d019),
+ ULLC(923f82a4,af194f9b), ULLC(ab1c5ed5,da6d8118),
+ ULLC(d807aa98,a3030242), ULLC(12835b01,45706fbe),
+ ULLC(243185be,4ee4b28c), ULLC(550c7dc3,d5ffb4e2),
+ ULLC(72be5d74,f27b896f), ULLC(80deb1fe,3b1696b1),
+ ULLC(9bdc06a7,25c71235), ULLC(c19bf174,cf692694),
+ ULLC(e49b69c1,9ef14ad2), ULLC(efbe4786,384f25e3),
+ ULLC(0fc19dc6,8b8cd5b5), ULLC(240ca1cc,77ac9c65),
+ ULLC(2de92c6f,592b0275), ULLC(4a7484aa,6ea6e483),
+ ULLC(5cb0a9dc,bd41fbd4), ULLC(76f988da,831153b5),
+ ULLC(983e5152,ee66dfab), ULLC(a831c66d,2db43210),
+ ULLC(b00327c8,98fb213f), ULLC(bf597fc7,beef0ee4),
+ ULLC(c6e00bf3,3da88fc2), ULLC(d5a79147,930aa725),
+ ULLC(06ca6351,e003826f), ULLC(14292967,0a0e6e70),
+ ULLC(27b70a85,46d22ffc), ULLC(2e1b2138,5c26c926),
+ ULLC(4d2c6dfc,5ac42aed), ULLC(53380d13,9d95b3df),
+ ULLC(650a7354,8baf63de), ULLC(766a0abb,3c77b2a8),
+ ULLC(81c2c92e,47edaee6), ULLC(92722c85,1482353b),
+ ULLC(a2bfe8a1,4cf10364), ULLC(a81a664b,bc423001),
+ ULLC(c24b8b70,d0f89791), ULLC(c76c51a3,0654be30),
+ ULLC(d192e819,d6ef5218), ULLC(d6990624,5565a910),
+ ULLC(f40e3585,5771202a), ULLC(106aa070,32bbd1b8),
+ ULLC(19a4c116,b8d2d0c8), ULLC(1e376c08,5141ab53),
+ ULLC(2748774c,df8eeb99), ULLC(34b0bcb5,e19b48a8),
+ ULLC(391c0cb3,c5c95a63), ULLC(4ed8aa4a,e3418acb),
+ ULLC(5b9cca4f,7763e373), ULLC(682e6ff3,d6b2b8a3),
+ ULLC(748f82ee,5defb2fc), ULLC(78a5636f,43172f60),
+ ULLC(84c87814,a1f0ab72), ULLC(8cc70208,1a6439ec),
+ ULLC(90befffa,23631e28), ULLC(a4506ceb,de82bde9),
+ ULLC(bef9a3f7,b2c67915), ULLC(c67178f2,e372532b),
+ ULLC(ca273ece,ea26619c), ULLC(d186b8c7,21c0c207),
+ ULLC(eada7dd6,cde0eb1e), ULLC(f57d4f7f,ee6ed178),
+ ULLC(06f067aa,72176fba), ULLC(0a637dc5,a2c898a6),
+ ULLC(113f9804,bef90dae), ULLC(1b710b35,131c471b),
+ ULLC(28db77f5,23047d84), ULLC(32caab7b,40c72493),
+ ULLC(3c9ebe0a,15c9bebc), ULLC(431d67c4,9c100d4c),
+ ULLC(4cc5d4be,cb3e42b6), ULLC(597f299c,fc657e2a),
+ ULLC(5fcb6fab,3ad6faec), ULLC(6c44198c,4a475817)
+#endif
+};
+
+struct SHA512ContextStr {
+ union {
+ PRUint64 w[80]; /* message schedule, input buffer, plus 64 words */
+ PRUint32 l[160];
+ PRUint8 b[640];
+ } u;
+ PRUint64 h[8]; /* 8 state variables */
+ PRUint64 sizeLo; /* 64-bit count of hashed bytes. */
+};
+
+/* =========== SHA512 implementation ===================================== */
+
+/* SHA-512 initial hash values */
+static const PRUint64 H512[8] = {
+#if PR_BYTES_PER_LONG == 8
+ 0x6a09e667f3bcc908UL , 0xbb67ae8584caa73bUL ,
+ 0x3c6ef372fe94f82bUL , 0xa54ff53a5f1d36f1UL ,
+ 0x510e527fade682d1UL , 0x9b05688c2b3e6c1fUL ,
+ 0x1f83d9abfb41bd6bUL , 0x5be0cd19137e2179UL
+#else
+ ULLC(6a09e667,f3bcc908), ULLC(bb67ae85,84caa73b),
+ ULLC(3c6ef372,fe94f82b), ULLC(a54ff53a,5f1d36f1),
+ ULLC(510e527f,ade682d1), ULLC(9b05688c,2b3e6c1f),
+ ULLC(1f83d9ab,fb41bd6b), ULLC(5be0cd19,137e2179)
+#endif
+};
+
+
+SHA512Context *
+SHA512_NewContext(void)
+{
+ SHA512Context *ctx = PORT_New(SHA512Context);
+ return ctx;
+}
+
+void
+SHA512_DestroyContext(SHA512Context *ctx, PRBool freeit)
+{
+ if (freeit) {
+ PORT_ZFree(ctx, sizeof *ctx);
+ }
+}
+
+void
+SHA512_Begin(SHA512Context *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ memcpy(H, H512, sizeof H512);
+}
+
+#if defined(SHA512_TRACE)
+#if defined(HAVE_LONG_LONG)
+#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %016lx, %s = %016lx\n", \
+ n, #e, d, #a, h);
+#else
+#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %08x%08x, %s = %08x%08x\n", \
+ n, #e, d.hi, d.lo, #a, h.hi, h.lo);
+#endif
+#else
+#define DUMP(n,a,d,e,h)
+#endif
+
+#if defined(HAVE_LONG_LONG)
+
+#define ADDTO(x,y) y += x
+
+#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16])
+
+#define ROUND(n,a,b,c,d,e,f,g,h) \
+ h += S1(e) + Ch(e,f,g) + K512[n] + W[n]; \
+ d += h; \
+ h += S0(a) + Maj(a,b,c); \
+ DUMP(n,a,d,e,h)
+
+#else /* use only 32-bit variables, and don't unroll loops */
+
+#undef NOUNROLL512
+#define NOUNROLL512 1
+
+#define ADDTO(x,y) y.lo += x.lo; y.hi += x.hi + (x.lo > y.lo)
+
+#define ROTR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n))
+#define ROTR64A(x,n,lo,hi) (x.lo << (64-n) | x.hi >> (n-32))
+#define SHR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n))
+
+/* Capitol Sigma and lower case sigma functions */
+#define s0lo(x) (ROTR64a(x,1,lo,hi) ^ ROTR64a(x,8,lo,hi) ^ SHR64a(x,7,lo,hi))
+#define s0hi(x) (ROTR64a(x,1,hi,lo) ^ ROTR64a(x,8,hi,lo) ^ (x.hi >> 7))
+
+#define s1lo(x) (ROTR64a(x,19,lo,hi) ^ ROTR64A(x,61,lo,hi) ^ SHR64a(x,6,lo,hi))
+#define s1hi(x) (ROTR64a(x,19,hi,lo) ^ ROTR64A(x,61,hi,lo) ^ (x.hi >> 6))
+
+#define S0lo(x)(ROTR64a(x,28,lo,hi) ^ ROTR64A(x,34,lo,hi) ^ ROTR64A(x,39,lo,hi))
+#define S0hi(x)(ROTR64a(x,28,hi,lo) ^ ROTR64A(x,34,hi,lo) ^ ROTR64A(x,39,hi,lo))
+
+#define S1lo(x)(ROTR64a(x,14,lo,hi) ^ ROTR64a(x,18,lo,hi) ^ ROTR64A(x,41,lo,hi))
+#define S1hi(x)(ROTR64a(x,14,hi,lo) ^ ROTR64a(x,18,hi,lo) ^ ROTR64A(x,41,hi,lo))
+
+/* 32-bit versions of Ch and Maj */
+#define Chxx(x,y,z,lo) ((x.lo & y.lo) ^ (~x.lo & z.lo))
+#define Majx(x,y,z,lo) ((x.lo & y.lo) ^ (x.lo & z.lo) ^ (y.lo & z.lo))
+
+#define INITW(t) \
+ do { \
+ PRUint32 lo, tm; \
+ PRUint32 cy = 0; \
+ lo = s1lo(W[t-2]); \
+ lo += (tm = W[t-7].lo); if (lo < tm) cy++; \
+ lo += (tm = s0lo(W[t-15])); if (lo < tm) cy++; \
+ lo += (tm = W[t-16].lo); if (lo < tm) cy++; \
+ W[t].lo = lo; \
+ W[t].hi = cy + s1hi(W[t-2]) + W[t-7].hi + s0hi(W[t-15]) + W[t-16].hi; \
+ } while (0)
+
+#define ROUND(n,a,b,c,d,e,f,g,h) \
+ { \
+ PRUint32 lo, tm, cy; \
+ lo = S1lo(e); \
+ lo += (tm = Chxx(e,f,g,lo)); cy = (lo < tm); \
+ lo += (tm = K512[n].lo); if (lo < tm) cy++; \
+ lo += (tm = W[n].lo); if (lo < tm) cy++; \
+ h.lo += lo; if (h.lo < lo) cy++; \
+ h.hi += cy + S1hi(e) + Chxx(e,f,g,hi) + K512[n].hi + W[n].hi; \
+ d.lo += h.lo; \
+ d.hi += h.hi + (d.lo < h.lo); \
+ lo = S0lo(a); \
+ lo += (tm = Majx(a,b,c,lo)); cy = (lo < tm); \
+ h.lo += lo; if (h.lo < lo) cy++; \
+ h.hi += cy + S0hi(a) + Majx(a,b,c,hi); \
+ DUMP(n,a,d,e,h) \
+ }
+#endif
+
+static void
+SHA512_Compress(SHA512Context *ctx)
+{
+#if defined(IS_LITTLE_ENDIAN)
+ {
+#if defined(HAVE_LONG_LONG)
+ PRUint64 t1;
+#else
+ PRUint32 t1;
+#endif
+ BYTESWAP8(W[0]);
+ BYTESWAP8(W[1]);
+ BYTESWAP8(W[2]);
+ BYTESWAP8(W[3]);
+ BYTESWAP8(W[4]);
+ BYTESWAP8(W[5]);
+ BYTESWAP8(W[6]);
+ BYTESWAP8(W[7]);
+ BYTESWAP8(W[8]);
+ BYTESWAP8(W[9]);
+ BYTESWAP8(W[10]);
+ BYTESWAP8(W[11]);
+ BYTESWAP8(W[12]);
+ BYTESWAP8(W[13]);
+ BYTESWAP8(W[14]);
+ BYTESWAP8(W[15]);
+ }
+#endif
+
+ {
+ PRUint64 t1, t2;
+#ifdef NOUNROLL512
+ {
+ /* prepare the "message schedule" */
+ int t;
+ for (t = 16; t < 80; ++t) {
+ INITW(t);
+ }
+ }
+#else
+ INITW(16);
+ INITW(17);
+ INITW(18);
+ INITW(19);
+
+ INITW(20);
+ INITW(21);
+ INITW(22);
+ INITW(23);
+ INITW(24);
+ INITW(25);
+ INITW(26);
+ INITW(27);
+ INITW(28);
+ INITW(29);
+
+ INITW(30);
+ INITW(31);
+ INITW(32);
+ INITW(33);
+ INITW(34);
+ INITW(35);
+ INITW(36);
+ INITW(37);
+ INITW(38);
+ INITW(39);
+
+ INITW(40);
+ INITW(41);
+ INITW(42);
+ INITW(43);
+ INITW(44);
+ INITW(45);
+ INITW(46);
+ INITW(47);
+ INITW(48);
+ INITW(49);
+
+ INITW(50);
+ INITW(51);
+ INITW(52);
+ INITW(53);
+ INITW(54);
+ INITW(55);
+ INITW(56);
+ INITW(57);
+ INITW(58);
+ INITW(59);
+
+ INITW(60);
+ INITW(61);
+ INITW(62);
+ INITW(63);
+ INITW(64);
+ INITW(65);
+ INITW(66);
+ INITW(67);
+ INITW(68);
+ INITW(69);
+
+ INITW(70);
+ INITW(71);
+ INITW(72);
+ INITW(73);
+ INITW(74);
+ INITW(75);
+ INITW(76);
+ INITW(77);
+ INITW(78);
+ INITW(79);
+#endif
+ }
+#ifdef SHA512_TRACE
+ {
+ int i;
+ for (i = 0; i < 80; ++i) {
+#ifdef HAVE_LONG_LONG
+ printf("W[%2d] = %016lx\n", i, W[i]);
+#else
+ printf("W[%2d] = %08x%08x\n", i, W[i].hi, W[i].lo);
+#endif
+ }
+ }
+#endif
+ {
+ PRUint64 a, b, c, d, e, f, g, h;
+
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+#ifdef NOUNROLL512
+ {
+ int t;
+ for (t = 0; t < 80; t+= 8) {
+ ROUND(t+0,a,b,c,d,e,f,g,h)
+ ROUND(t+1,h,a,b,c,d,e,f,g)
+ ROUND(t+2,g,h,a,b,c,d,e,f)
+ ROUND(t+3,f,g,h,a,b,c,d,e)
+ ROUND(t+4,e,f,g,h,a,b,c,d)
+ ROUND(t+5,d,e,f,g,h,a,b,c)
+ ROUND(t+6,c,d,e,f,g,h,a,b)
+ ROUND(t+7,b,c,d,e,f,g,h,a)
+ }
+ }
+#else
+ ROUND( 0,a,b,c,d,e,f,g,h)
+ ROUND( 1,h,a,b,c,d,e,f,g)
+ ROUND( 2,g,h,a,b,c,d,e,f)
+ ROUND( 3,f,g,h,a,b,c,d,e)
+ ROUND( 4,e,f,g,h,a,b,c,d)
+ ROUND( 5,d,e,f,g,h,a,b,c)
+ ROUND( 6,c,d,e,f,g,h,a,b)
+ ROUND( 7,b,c,d,e,f,g,h,a)
+
+ ROUND( 8,a,b,c,d,e,f,g,h)
+ ROUND( 9,h,a,b,c,d,e,f,g)
+ ROUND(10,g,h,a,b,c,d,e,f)
+ ROUND(11,f,g,h,a,b,c,d,e)
+ ROUND(12,e,f,g,h,a,b,c,d)
+ ROUND(13,d,e,f,g,h,a,b,c)
+ ROUND(14,c,d,e,f,g,h,a,b)
+ ROUND(15,b,c,d,e,f,g,h,a)
+
+ ROUND(16,a,b,c,d,e,f,g,h)
+ ROUND(17,h,a,b,c,d,e,f,g)
+ ROUND(18,g,h,a,b,c,d,e,f)
+ ROUND(19,f,g,h,a,b,c,d,e)
+ ROUND(20,e,f,g,h,a,b,c,d)
+ ROUND(21,d,e,f,g,h,a,b,c)
+ ROUND(22,c,d,e,f,g,h,a,b)
+ ROUND(23,b,c,d,e,f,g,h,a)
+
+ ROUND(24,a,b,c,d,e,f,g,h)
+ ROUND(25,h,a,b,c,d,e,f,g)
+ ROUND(26,g,h,a,b,c,d,e,f)
+ ROUND(27,f,g,h,a,b,c,d,e)
+ ROUND(28,e,f,g,h,a,b,c,d)
+ ROUND(29,d,e,f,g,h,a,b,c)
+ ROUND(30,c,d,e,f,g,h,a,b)
+ ROUND(31,b,c,d,e,f,g,h,a)
+
+ ROUND(32,a,b,c,d,e,f,g,h)
+ ROUND(33,h,a,b,c,d,e,f,g)
+ ROUND(34,g,h,a,b,c,d,e,f)
+ ROUND(35,f,g,h,a,b,c,d,e)
+ ROUND(36,e,f,g,h,a,b,c,d)
+ ROUND(37,d,e,f,g,h,a,b,c)
+ ROUND(38,c,d,e,f,g,h,a,b)
+ ROUND(39,b,c,d,e,f,g,h,a)
+
+ ROUND(40,a,b,c,d,e,f,g,h)
+ ROUND(41,h,a,b,c,d,e,f,g)
+ ROUND(42,g,h,a,b,c,d,e,f)
+ ROUND(43,f,g,h,a,b,c,d,e)
+ ROUND(44,e,f,g,h,a,b,c,d)
+ ROUND(45,d,e,f,g,h,a,b,c)
+ ROUND(46,c,d,e,f,g,h,a,b)
+ ROUND(47,b,c,d,e,f,g,h,a)
+
+ ROUND(48,a,b,c,d,e,f,g,h)
+ ROUND(49,h,a,b,c,d,e,f,g)
+ ROUND(50,g,h,a,b,c,d,e,f)
+ ROUND(51,f,g,h,a,b,c,d,e)
+ ROUND(52,e,f,g,h,a,b,c,d)
+ ROUND(53,d,e,f,g,h,a,b,c)
+ ROUND(54,c,d,e,f,g,h,a,b)
+ ROUND(55,b,c,d,e,f,g,h,a)
+
+ ROUND(56,a,b,c,d,e,f,g,h)
+ ROUND(57,h,a,b,c,d,e,f,g)
+ ROUND(58,g,h,a,b,c,d,e,f)
+ ROUND(59,f,g,h,a,b,c,d,e)
+ ROUND(60,e,f,g,h,a,b,c,d)
+ ROUND(61,d,e,f,g,h,a,b,c)
+ ROUND(62,c,d,e,f,g,h,a,b)
+ ROUND(63,b,c,d,e,f,g,h,a)
+
+ ROUND(64,a,b,c,d,e,f,g,h)
+ ROUND(65,h,a,b,c,d,e,f,g)
+ ROUND(66,g,h,a,b,c,d,e,f)
+ ROUND(67,f,g,h,a,b,c,d,e)
+ ROUND(68,e,f,g,h,a,b,c,d)
+ ROUND(69,d,e,f,g,h,a,b,c)
+ ROUND(70,c,d,e,f,g,h,a,b)
+ ROUND(71,b,c,d,e,f,g,h,a)
+
+ ROUND(72,a,b,c,d,e,f,g,h)
+ ROUND(73,h,a,b,c,d,e,f,g)
+ ROUND(74,g,h,a,b,c,d,e,f)
+ ROUND(75,f,g,h,a,b,c,d,e)
+ ROUND(76,e,f,g,h,a,b,c,d)
+ ROUND(77,d,e,f,g,h,a,b,c)
+ ROUND(78,c,d,e,f,g,h,a,b)
+ ROUND(79,b,c,d,e,f,g,h,a)
+#endif
+
+ ADDTO(a,H[0]);
+ ADDTO(b,H[1]);
+ ADDTO(c,H[2]);
+ ADDTO(d,H[3]);
+ ADDTO(e,H[4]);
+ ADDTO(f,H[5]);
+ ADDTO(g,H[6]);
+ ADDTO(h,H[7]);
+ }
+}
+
+void
+SHA512_Update(SHA512Context *ctx, const unsigned char *input,
+ unsigned int inputLen)
+{
+ unsigned int inBuf;
+ if (!inputLen)
+ return;
+
+#if defined(HAVE_LONG_LONG)
+ inBuf = (unsigned int)ctx->sizeLo & 0x7f;
+ /* Add inputLen into the count of bytes processed, before processing */
+ ctx->sizeLo += inputLen;
+#else
+ inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f;
+ ctx->sizeLo.lo += inputLen;
+ if (ctx->sizeLo.lo < inputLen) ctx->sizeLo.hi++;
+#endif
+
+ /* if data already in buffer, attemp to fill rest of buffer */
+ if (inBuf) {
+ unsigned int todo = SHA512_BLOCK_LENGTH - inBuf;
+ if (inputLen < todo)
+ todo = inputLen;
+ memcpy(B + inBuf, input, todo);
+ input += todo;
+ inputLen -= todo;
+ if (inBuf + todo == SHA512_BLOCK_LENGTH)
+ SHA512_Compress(ctx);
+ }
+
+ /* if enough data to fill one or more whole buffers, process them. */
+ while (inputLen >= SHA512_BLOCK_LENGTH) {
+ memcpy(B, input, SHA512_BLOCK_LENGTH);
+ input += SHA512_BLOCK_LENGTH;
+ inputLen -= SHA512_BLOCK_LENGTH;
+ SHA512_Compress(ctx);
+ }
+ /* if data left over, fill it into buffer */
+ if (inputLen)
+ memcpy(B, input, inputLen);
+}
+
+void
+SHA512_End(SHA512Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+#if defined(HAVE_LONG_LONG)
+ unsigned int inBuf = (unsigned int)ctx->sizeLo & 0x7f;
+ unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf);
+ PRUint64 lo, t1;
+ lo = (ctx->sizeLo << 3);
+#else
+ unsigned int inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f;
+ unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf);
+ PRUint64 lo = ctx->sizeLo;
+ PRUint32 t1;
+ lo.lo <<= 3;
+#endif
+
+ SHA512_Update(ctx, pad, padLen);
+
+#if defined(HAVE_LONG_LONG)
+ W[14] = 0;
+#else
+ W[14].lo = 0;
+ W[14].hi = 0;
+#endif
+
+ W[15] = lo;
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP8(W[15]);
+#endif
+ SHA512_Compress(ctx);
+
+ /* now output the answer */
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP8(H[0]);
+ BYTESWAP8(H[1]);
+ BYTESWAP8(H[2]);
+ BYTESWAP8(H[3]);
+ BYTESWAP8(H[4]);
+ BYTESWAP8(H[5]);
+ BYTESWAP8(H[6]);
+ BYTESWAP8(H[7]);
+#endif
+ padLen = PR_MIN(SHA512_LENGTH, maxDigestLen);
+ memcpy(digest, H, padLen);
+ if (digestLen)
+ *digestLen = padLen;
+}
+
+SECStatus
+SHA512_HashBuf(unsigned char *dest, const unsigned char *src,
+ unsigned int src_length)
+{
+ SHA512Context ctx;
+ unsigned int outLen;
+
+ SHA512_Begin(&ctx);
+ SHA512_Update(&ctx, src, src_length);
+ SHA512_End(&ctx, dest, &outLen, SHA512_LENGTH);
+
+ return SECSuccess;
+}
+
+
+SECStatus
+SHA512_Hash(unsigned char *dest, const char *src)
+{
+ return SHA512_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src));
+}
+
+
+void SHA512_TraceState(SHA512Context *ctx) { }
+
+unsigned int
+SHA512_FlattenSize(SHA512Context *ctx)
+{
+ return sizeof *ctx;
+}
+
+SECStatus
+SHA512_Flatten(SHA512Context *ctx,unsigned char *space)
+{
+ PORT_Memcpy(space, ctx, sizeof *ctx);
+ return SECSuccess;
+}
+
+SHA512Context *
+SHA512_Resurrect(unsigned char *space, void *arg)
+{
+ SHA512Context *ctx = SHA512_NewContext();
+ if (ctx)
+ PORT_Memcpy(ctx, space, sizeof *ctx);
+ return ctx;
+}
+
+void SHA512_Clone(SHA512Context *dest, SHA512Context *src)
+{
+ memcpy(dest, src, sizeof *dest);
+}
+
+/* ======================================================================= */
+/* SHA384 uses a SHA512Context as the real context.
+** The only differences between SHA384 an SHA512 are:
+** a) the intialization values for the context, and
+** b) the number of bytes of data produced as output.
+*/
+
+/* SHA-384 initial hash values */
+static const PRUint64 H384[8] = {
+#if PR_BYTES_PER_LONG == 8
+ 0xcbbb9d5dc1059ed8UL , 0x629a292a367cd507UL ,
+ 0x9159015a3070dd17UL , 0x152fecd8f70e5939UL ,
+ 0x67332667ffc00b31UL , 0x8eb44a8768581511UL ,
+ 0xdb0c2e0d64f98fa7UL , 0x47b5481dbefa4fa4UL
+#else
+ ULLC(cbbb9d5d,c1059ed8), ULLC(629a292a,367cd507),
+ ULLC(9159015a,3070dd17), ULLC(152fecd8,f70e5939),
+ ULLC(67332667,ffc00b31), ULLC(8eb44a87,68581511),
+ ULLC(db0c2e0d,64f98fa7), ULLC(47b5481d,befa4fa4)
+#endif
+};
+
+SHA384Context *
+SHA384_NewContext(void)
+{
+ return SHA512_NewContext();
+}
+
+void
+SHA384_DestroyContext(SHA384Context *ctx, PRBool freeit)
+{
+ SHA512_DestroyContext(ctx, freeit);
+}
+
+void
+SHA384_Begin(SHA384Context *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ memcpy(H, H384, sizeof H384);
+}
+
+void
+SHA384_Update(SHA384Context *ctx, const unsigned char *input,
+ unsigned int inputLen)
+{
+ SHA512_Update(ctx, input, inputLen);
+}
+
+void
+SHA384_End(SHA384Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+#define SHA_MIN(a,b) (a < b ? a : b)
+ unsigned int maxLen = SHA_MIN(maxDigestLen, SHA384_LENGTH);
+ SHA512_End(ctx, digest, digestLen, maxLen);
+}
+
+SECStatus
+SHA384_HashBuf(unsigned char *dest, const unsigned char *src,
+ unsigned int src_length)
+{
+ SHA512Context ctx;
+ unsigned int outLen;
+
+ SHA384_Begin(&ctx);
+ SHA512_Update(&ctx, src, src_length);
+ SHA512_End(&ctx, dest, &outLen, SHA384_LENGTH);
+
+ return SECSuccess;
+}
+
+SECStatus
+SHA384_Hash(unsigned char *dest, const char *src)
+{
+ return SHA384_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src));
+}
+
+void SHA384_TraceState(SHA384Context *ctx) { }
+
+unsigned int
+SHA384_FlattenSize(SHA384Context *ctx)
+{
+ return sizeof(SHA384Context);
+}
+
+SECStatus
+SHA384_Flatten(SHA384Context *ctx,unsigned char *space)
+{
+ return SHA512_Flatten(ctx, space);
+}
+
+SHA384Context *
+SHA384_Resurrect(unsigned char *space, void *arg)
+{
+ return SHA512_Resurrect(space, arg);
+}
+
+void SHA384_Clone(SHA384Context *dest, SHA384Context *src)
+{
+ memcpy(dest, src, sizeof *dest);
+}
+#endif /* Comment out unused code. */
+
+/* ======================================================================= */
+#ifdef SELFTEST
+#include <stdio.h>
+
+static const char abc[] = { "abc" };
+static const char abcdbc[] = {
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+};
+static const char abcdef[] = {
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+ "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+};
+
+void
+dumpHash32(const unsigned char *buf, unsigned int bufLen)
+{
+ unsigned int i;
+ for (i = 0; i < bufLen; i += 4) {
+ printf(" %02x%02x%02x%02x", buf[i], buf[i+1], buf[i+2], buf[i+3]);
+ }
+ printf("\n");
+}
+
+void test256(void)
+{
+ unsigned char outBuf[SHA256_LENGTH];
+
+ printf("SHA256, input = %s\n", abc);
+ SHA256_Hash(outBuf, abc);
+ dumpHash32(outBuf, sizeof outBuf);
+
+ printf("SHA256, input = %s\n", abcdbc);
+ SHA256_Hash(outBuf, abcdbc);
+ dumpHash32(outBuf, sizeof outBuf);
+}
+
+void
+dumpHash64(const unsigned char *buf, unsigned int bufLen)
+{
+ unsigned int i;
+ for (i = 0; i < bufLen; i += 8) {
+ if (i % 32 == 0)
+ printf("\n");
+ printf(" %02x%02x%02x%02x%02x%02x%02x%02x",
+ buf[i ], buf[i+1], buf[i+2], buf[i+3],
+ buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
+ }
+ printf("\n");
+}
+
+void test512(void)
+{
+ unsigned char outBuf[SHA512_LENGTH];
+
+ printf("SHA512, input = %s\n", abc);
+ SHA512_Hash(outBuf, abc);
+ dumpHash64(outBuf, sizeof outBuf);
+
+ printf("SHA512, input = %s\n", abcdef);
+ SHA512_Hash(outBuf, abcdef);
+ dumpHash64(outBuf, sizeof outBuf);
+}
+
+void time512(void)
+{
+ unsigned char outBuf[SHA512_LENGTH];
+
+ SHA512_Hash(outBuf, abc);
+ SHA512_Hash(outBuf, abcdef);
+}
+
+void test384(void)
+{
+ unsigned char outBuf[SHA384_LENGTH];
+
+ printf("SHA384, input = %s\n", abc);
+ SHA384_Hash(outBuf, abc);
+ dumpHash64(outBuf, sizeof outBuf);
+
+ printf("SHA384, input = %s\n", abcdef);
+ SHA384_Hash(outBuf, abcdef);
+ dumpHash64(outBuf, sizeof outBuf);
+}
+
+int main (int argc, char *argv[], char *envp[])
+{
+ int i = 1;
+ if (argc > 1) {
+ i = atoi(argv[1]);
+ }
+ if (i < 2) {
+ test256();
+ test512();
+ test384();
+ } else {
+ while (i-- > 0) {
+ time512();
+ }
+ printf("done\n");
+ }
+ return 0;
+}
+
+#endif
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.cc b/sandbox/linux/bpf_dsl/bpf_dsl.cc
new file mode 100644
index 0000000000..3a35903ec9
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.cc
@@ -0,0 +1,363 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+namespace {
+
+intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) {
+ SANDBOX_DIE(static_cast<char*>(aux));
+}
+
+class AllowResultExprImpl : public internal::ResultExprImpl {
+ public:
+ AllowResultExprImpl() {}
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+
+ bool IsAllow() const override { return true; }
+
+ private:
+ ~AllowResultExprImpl() override {}
+
+ DISALLOW_COPY_AND_ASSIGN(AllowResultExprImpl);
+};
+
+class ErrorResultExprImpl : public internal::ResultExprImpl {
+ public:
+ explicit ErrorResultExprImpl(int err) : err_(err) {
+ CHECK(err_ >= ErrorCode::ERR_MIN_ERRNO && err_ <= ErrorCode::ERR_MAX_ERRNO);
+ }
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return pc->Error(err_);
+ }
+
+ bool IsDeny() const override { return true; }
+
+ private:
+ ~ErrorResultExprImpl() override {}
+
+ int err_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrorResultExprImpl);
+};
+
+class TraceResultExprImpl : public internal::ResultExprImpl {
+ public:
+ TraceResultExprImpl(uint16_t aux) : aux_(aux) {}
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return ErrorCode(ErrorCode::ERR_TRACE + aux_);
+ }
+
+ private:
+ ~TraceResultExprImpl() override {}
+
+ uint16_t aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceResultExprImpl);
+};
+
+class TrapResultExprImpl : public internal::ResultExprImpl {
+ public:
+ TrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg, bool safe)
+ : func_(func), arg_(arg), safe_(safe) {
+ DCHECK(func_);
+ }
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return pc->Trap(func_, arg_, safe_);
+ }
+
+ bool HasUnsafeTraps() const override { return safe_ == false; }
+
+ bool IsDeny() const override { return true; }
+
+ private:
+ ~TrapResultExprImpl() override {}
+
+ TrapRegistry::TrapFnc func_;
+ const void* arg_;
+ bool safe_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrapResultExprImpl);
+};
+
+class IfThenResultExprImpl : public internal::ResultExprImpl {
+ public:
+ IfThenResultExprImpl(const BoolExpr& cond,
+ const ResultExpr& then_result,
+ const ResultExpr& else_result)
+ : cond_(cond), then_result_(then_result), else_result_(else_result) {}
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return cond_->Compile(
+ pc, then_result_->Compile(pc), else_result_->Compile(pc));
+ }
+
+ bool HasUnsafeTraps() const override {
+ return then_result_->HasUnsafeTraps() || else_result_->HasUnsafeTraps();
+ }
+
+ private:
+ ~IfThenResultExprImpl() override {}
+
+ BoolExpr cond_;
+ ResultExpr then_result_;
+ ResultExpr else_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(IfThenResultExprImpl);
+};
+
+class ConstBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ ConstBoolExprImpl(bool value) : value_(value) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return value_ ? true_ec : false_ec;
+ }
+
+ private:
+ ~ConstBoolExprImpl() override {}
+
+ bool value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstBoolExprImpl);
+};
+
+class PrimitiveBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ PrimitiveBoolExprImpl(int argno,
+ ErrorCode::ArgType is_32bit,
+ uint64_t mask,
+ uint64_t value)
+ : argno_(argno), is_32bit_(is_32bit), mask_(mask), value_(value) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return pc->CondMaskedEqual(
+ argno_, is_32bit_, mask_, value_, true_ec, false_ec);
+ }
+
+ private:
+ ~PrimitiveBoolExprImpl() override {}
+
+ int argno_;
+ ErrorCode::ArgType is_32bit_;
+ uint64_t mask_;
+ uint64_t value_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveBoolExprImpl);
+};
+
+class NegateBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ explicit NegateBoolExprImpl(const BoolExpr& cond) : cond_(cond) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return cond_->Compile(pc, false_ec, true_ec);
+ }
+
+ private:
+ ~NegateBoolExprImpl() override {}
+
+ BoolExpr cond_;
+
+ DISALLOW_COPY_AND_ASSIGN(NegateBoolExprImpl);
+};
+
+class AndBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ AndBoolExprImpl(const BoolExpr& lhs, const BoolExpr& rhs)
+ : lhs_(lhs), rhs_(rhs) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return lhs_->Compile(pc, rhs_->Compile(pc, true_ec, false_ec), false_ec);
+ }
+
+ private:
+ ~AndBoolExprImpl() override {}
+
+ BoolExpr lhs_;
+ BoolExpr rhs_;
+
+ DISALLOW_COPY_AND_ASSIGN(AndBoolExprImpl);
+};
+
+class OrBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ OrBoolExprImpl(const BoolExpr& lhs, const BoolExpr& rhs)
+ : lhs_(lhs), rhs_(rhs) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return lhs_->Compile(pc, true_ec, rhs_->Compile(pc, true_ec, false_ec));
+ }
+
+ private:
+ ~OrBoolExprImpl() override {}
+
+ BoolExpr lhs_;
+ BoolExpr rhs_;
+
+ DISALLOW_COPY_AND_ASSIGN(OrBoolExprImpl);
+};
+
+} // namespace
+
+namespace internal {
+
+bool ResultExprImpl::HasUnsafeTraps() const {
+ return false;
+}
+
+bool ResultExprImpl::IsAllow() const {
+ return false;
+}
+
+bool ResultExprImpl::IsDeny() const {
+ return false;
+}
+
+uint64_t DefaultMask(size_t size) {
+ switch (size) {
+ case 4:
+ return std::numeric_limits<uint32_t>::max();
+ case 8:
+ return std::numeric_limits<uint64_t>::max();
+ default:
+ CHECK(false) << "Unimplemented DefaultMask case";
+ return 0;
+ }
+}
+
+BoolExpr ArgEq(int num, size_t size, uint64_t mask, uint64_t val) {
+ CHECK(size == 4 || size == 8);
+
+ // TODO(mdempsky): Should we just always use TP_64BIT?
+ const ErrorCode::ArgType arg_type =
+ (size == 4) ? ErrorCode::TP_32BIT : ErrorCode::TP_64BIT;
+
+ return BoolExpr(new const PrimitiveBoolExprImpl(num, arg_type, mask, val));
+}
+
+} // namespace internal
+
+ResultExpr Allow() {
+ return ResultExpr(new const AllowResultExprImpl());
+}
+
+ResultExpr Error(int err) {
+ return ResultExpr(new const ErrorResultExprImpl(err));
+}
+
+ResultExpr Kill(const char* msg) {
+ return Trap(BPFFailure, msg);
+}
+
+ResultExpr Trace(uint16_t aux) {
+ return ResultExpr(new const TraceResultExprImpl(aux));
+}
+
+ResultExpr Trap(TrapRegistry::TrapFnc trap_func, const void* aux) {
+ return ResultExpr(
+ new const TrapResultExprImpl(trap_func, aux, true /* safe */));
+}
+
+ResultExpr UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux) {
+ return ResultExpr(
+ new const TrapResultExprImpl(trap_func, aux, false /* unsafe */));
+}
+
+BoolExpr BoolConst(bool value) {
+ return BoolExpr(new const ConstBoolExprImpl(value));
+}
+
+BoolExpr operator!(const BoolExpr& cond) {
+ return BoolExpr(new const NegateBoolExprImpl(cond));
+}
+
+BoolExpr operator&&(const BoolExpr& lhs, const BoolExpr& rhs) {
+ return BoolExpr(new const AndBoolExprImpl(lhs, rhs));
+}
+
+BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs) {
+ return BoolExpr(new const OrBoolExprImpl(lhs, rhs));
+}
+
+Elser If(const BoolExpr& cond, const ResultExpr& then_result) {
+ return Elser(nullptr).ElseIf(cond, then_result);
+}
+
+Elser::Elser(cons::List<Clause> clause_list) : clause_list_(clause_list) {
+}
+
+Elser::Elser(const Elser& elser) : clause_list_(elser.clause_list_) {
+}
+
+Elser::~Elser() {
+}
+
+Elser Elser::ElseIf(const BoolExpr& cond, const ResultExpr& then_result) const {
+ return Elser(Cons(std::make_pair(cond, then_result), clause_list_));
+}
+
+ResultExpr Elser::Else(const ResultExpr& else_result) const {
+ // We finally have the default result expression for this
+ // if/then/else sequence. Also, we've already accumulated all
+ // if/then pairs into a list of reverse order (i.e., lower priority
+ // conditions are listed before higher priority ones). E.g., an
+ // expression like
+ //
+ // If(b1, e1).ElseIf(b2, e2).ElseIf(b3, e3).Else(e4)
+ //
+ // will have built up a list like
+ //
+ // [(b3, e3), (b2, e2), (b1, e1)].
+ //
+ // Now that we have e4, we can walk the list and create a ResultExpr
+ // tree like:
+ //
+ // expr = e4
+ // expr = (b3 ? e3 : expr) = (b3 ? e3 : e4)
+ // expr = (b2 ? e2 : expr) = (b2 ? e2 : (b3 ? e3 : e4))
+ // expr = (b1 ? e1 : expr) = (b1 ? e1 : (b2 ? e2 : (b3 ? e3 : e4)))
+ //
+ // and end up with an appropriately chained tree.
+
+ ResultExpr expr = else_result;
+ for (const Clause& clause : clause_list_) {
+ expr = ResultExpr(
+ new const IfThenResultExprImpl(clause.first, clause.second, expr));
+ }
+ return expr;
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+template class scoped_refptr<const sandbox::bpf_dsl::internal::BoolExprImpl>;
+template class scoped_refptr<const sandbox::bpf_dsl::internal::ResultExprImpl>;
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.h b/sandbox/linux/bpf_dsl/bpf_dsl.h
new file mode 100644
index 0000000000..365e9b5466
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.h
@@ -0,0 +1,317 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
+
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/linux/bpf_dsl/cons.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/sandbox_export.h"
+
+// The sandbox::bpf_dsl namespace provides a domain-specific language
+// to make writing BPF policies more expressive. In general, the
+// object types all have value semantics (i.e., they can be copied
+// around, returned from or passed to function calls, etc. without any
+// surprising side effects), though not all support assignment.
+//
+// An idiomatic and demonstrative (albeit silly) example of this API
+// would be:
+//
+// #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+//
+// using namespace sandbox::bpf_dsl;
+//
+// class SillyPolicy : public Policy {
+// public:
+// SillyPolicy() {}
+// ~SillyPolicy() override {}
+// ResultExpr EvaluateSyscall(int sysno) const override {
+// if (sysno == __NR_fcntl) {
+// Arg<int> fd(0), cmd(1);
+// Arg<unsigned long> flags(2);
+// const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK;
+// return If(fd == 0 && cmd == F_SETFL && (flags & ~kGoodFlags) == 0,
+// Allow())
+// .ElseIf(cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC,
+// Error(EMFILE))
+// .Else(Trap(SetFlagHandler, NULL));
+// } else {
+// return Allow();
+// }
+// }
+//
+// private:
+// DISALLOW_COPY_AND_ASSIGN(SillyPolicy);
+// };
+//
+// More generally, the DSL currently supports the following grammar:
+//
+// result = Allow() | Error(errno) | Kill(msg) | Trace(aux)
+// | Trap(trap_func, aux) | UnsafeTrap(trap_func, aux)
+// | If(bool, result)[.ElseIf(bool, result)].Else(result)
+// | Switch(arg)[.Case(val, result)].Default(result)
+// bool = BoolConst(boolean) | !bool | bool && bool | bool || bool
+// | arg == val | arg != val
+// arg = Arg<T>(num) | arg & mask
+//
+// The semantics of each function and operator are intended to be
+// intuitive, but are described in more detail below.
+//
+// (Credit to Sean Parent's "Inheritance is the Base Class of Evil"
+// talk at Going Native 2013 for promoting value semantics via shared
+// pointers to immutable state.)
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// ResultExpr is an opaque reference to an immutable result expression tree.
+typedef scoped_refptr<const internal::ResultExprImpl> ResultExpr;
+
+// BoolExpr is an opaque reference to an immutable boolean expression tree.
+typedef scoped_refptr<const internal::BoolExprImpl> BoolExpr;
+
+// Allow specifies a result that the system call should be allowed to
+// execute normally.
+SANDBOX_EXPORT ResultExpr Allow();
+
+// Error specifies a result that the system call should fail with
+// error number |err|. As a special case, Error(0) will result in the
+// system call appearing to have succeeded, but without having any
+// side effects.
+SANDBOX_EXPORT ResultExpr Error(int err);
+
+// Kill specifies a result to kill the program and print an error message.
+SANDBOX_EXPORT ResultExpr Kill(const char* msg);
+
+// Trace specifies a result to notify a tracing process via the
+// PTRACE_EVENT_SECCOMP event and allow it to change or skip the system call.
+// The value of |aux| will be available to the tracer via PTRACE_GETEVENTMSG.
+SANDBOX_EXPORT ResultExpr Trace(uint16_t aux);
+
+// Trap specifies a result that the system call should be handled by
+// trapping back into userspace and invoking |trap_func|, passing
+// |aux| as the second parameter.
+SANDBOX_EXPORT ResultExpr
+ Trap(TrapRegistry::TrapFnc trap_func, const void* aux);
+
+// UnsafeTrap is like Trap, except the policy is marked as "unsafe"
+// and allowed to use SandboxSyscall to invoke any system call.
+//
+// NOTE: This feature, by definition, disables all security features of
+// the sandbox. It should never be used in production, but it can be
+// very useful to diagnose code that is incompatible with the sandbox.
+// If even a single system call returns "UnsafeTrap", the security of
+// entire sandbox should be considered compromised.
+SANDBOX_EXPORT ResultExpr
+ UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux);
+
+// BoolConst converts a bool value into a BoolExpr.
+SANDBOX_EXPORT BoolExpr BoolConst(bool value);
+
+// Various ways to combine boolean expressions into more complex expressions.
+// They follow standard boolean algebra laws.
+SANDBOX_EXPORT BoolExpr operator!(const BoolExpr& cond);
+SANDBOX_EXPORT BoolExpr operator&&(const BoolExpr& lhs, const BoolExpr& rhs);
+SANDBOX_EXPORT BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs);
+
+template <typename T>
+class SANDBOX_EXPORT Arg {
+ public:
+ // Initializes the Arg to represent the |num|th system call
+ // argument (indexed from 0), which is of type |T|.
+ explicit Arg(int num);
+
+ Arg(const Arg& arg) : num_(arg.num_), mask_(arg.mask_) {}
+
+ // Returns an Arg representing the current argument, but after
+ // bitwise-and'ing it with |rhs|.
+ friend Arg operator&(const Arg& lhs, uint64_t rhs) {
+ return Arg(lhs.num_, lhs.mask_ & rhs);
+ }
+
+ // Returns a boolean expression comparing whether the system call argument
+ // (after applying any bitmasks, if appropriate) equals |rhs|.
+ friend BoolExpr operator==(const Arg& lhs, T rhs) { return lhs.EqualTo(rhs); }
+
+ // Returns a boolean expression comparing whether the system call argument
+ // (after applying any bitmasks, if appropriate) does not equal |rhs|.
+ friend BoolExpr operator!=(const Arg& lhs, T rhs) { return !(lhs == rhs); }
+
+ private:
+ Arg(int num, uint64_t mask) : num_(num), mask_(mask) {}
+
+ BoolExpr EqualTo(T val) const;
+
+ int num_;
+ uint64_t mask_;
+
+ DISALLOW_ASSIGN(Arg);
+};
+
+// If begins a conditional result expression predicated on the
+// specified boolean expression.
+SANDBOX_EXPORT Elser If(const BoolExpr& cond, const ResultExpr& then_result);
+
+class SANDBOX_EXPORT Elser {
+ public:
+ Elser(const Elser& elser);
+ ~Elser();
+
+ // ElseIf extends the conditional result expression with another
+ // "if then" clause, predicated on the specified boolean expression.
+ Elser ElseIf(const BoolExpr& cond, const ResultExpr& then_result) const;
+
+ // Else terminates a conditional result expression using |else_result| as
+ // the default fallback result expression.
+ ResultExpr Else(const ResultExpr& else_result) const;
+
+ private:
+ typedef std::pair<BoolExpr, ResultExpr> Clause;
+
+ explicit Elser(cons::List<Clause> clause_list);
+
+ cons::List<Clause> clause_list_;
+
+ friend Elser If(const BoolExpr&, const ResultExpr&);
+ template <typename T>
+ friend Caser<T> Switch(const Arg<T>&);
+ DISALLOW_ASSIGN(Elser);
+};
+
+// Switch begins a switch expression dispatched according to the
+// specified argument value.
+template <typename T>
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg);
+
+template <typename T>
+class SANDBOX_EXPORT Caser {
+ public:
+ Caser(const Caser<T>& caser) : arg_(caser.arg_), elser_(caser.elser_) {}
+ ~Caser() {}
+
+ // Case adds a single-value "case" clause to the switch.
+ Caser<T> Case(T value, ResultExpr result) const;
+
+ // Cases adds a multiple-value "case" clause to the switch.
+ // See also the SANDBOX_BPF_DSL_CASES macro below for a more idiomatic way
+ // of using this function.
+ Caser<T> Cases(const std::vector<T>& values, ResultExpr result) const;
+
+ // Terminate the switch with a "default" clause.
+ ResultExpr Default(ResultExpr result) const;
+
+ private:
+ Caser(const Arg<T>& arg, Elser elser) : arg_(arg), elser_(elser) {}
+
+ Arg<T> arg_;
+ Elser elser_;
+
+ template <typename U>
+ friend Caser<U> Switch(const Arg<U>&);
+ DISALLOW_ASSIGN(Caser);
+};
+
+// Recommended usage is to put
+// #define CASES SANDBOX_BPF_DSL_CASES
+// near the top of the .cc file (e.g., nearby any "using" statements), then
+// use like:
+// Switch(arg).CASES((3, 5, 7), result)...;
+#define SANDBOX_BPF_DSL_CASES(values, result) \
+ Cases(SANDBOX_BPF_DSL_CASES_HELPER values, result)
+
+// Helper macro to construct a std::vector from an initializer list.
+// TODO(mdempsky): Convert to use C++11 initializer lists instead.
+#define SANDBOX_BPF_DSL_CASES_HELPER(value, ...) \
+ ({ \
+ const __typeof__(value) bpf_dsl_cases_values[] = {value, __VA_ARGS__}; \
+ std::vector<__typeof__(value)>( \
+ bpf_dsl_cases_values, \
+ bpf_dsl_cases_values + arraysize(bpf_dsl_cases_values)); \
+ })
+
+// =====================================================================
+// Official API ends here.
+// =====================================================================
+
+namespace internal {
+
+// Make argument-dependent lookup work. This is necessary because although
+// BoolExpr is defined in bpf_dsl, since it's merely a typedef for
+// scoped_refptr<const internal::BoolExplImpl>, argument-dependent lookup only
+// searches the "internal" nested namespace.
+using bpf_dsl::operator!;
+using bpf_dsl::operator||;
+using bpf_dsl::operator&&;
+
+// Returns a boolean expression that represents whether system call
+// argument |num| of size |size| is equal to |val|, when masked
+// according to |mask|. Users should use the Arg template class below
+// instead of using this API directly.
+SANDBOX_EXPORT BoolExpr
+ ArgEq(int num, size_t size, uint64_t mask, uint64_t val);
+
+// Returns the default mask for a system call argument of the specified size.
+SANDBOX_EXPORT uint64_t DefaultMask(size_t size);
+
+} // namespace internal
+
+template <typename T>
+Arg<T>::Arg(int num)
+ : num_(num), mask_(internal::DefaultMask(sizeof(T))) {
+}
+
+// Definition requires ArgEq to have been declared. Moved out-of-line
+// to minimize how much internal clutter users have to ignore while
+// reading the header documentation.
+//
+// Additionally, we use this helper member function to avoid linker errors
+// caused by defining operator== out-of-line. For a more detailed explanation,
+// see http://www.parashift.com/c++-faq-lite/template-friends.html.
+template <typename T>
+BoolExpr Arg<T>::EqualTo(T val) const {
+ return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(val));
+}
+
+template <typename T>
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) {
+ return Caser<T>(arg, Elser(nullptr));
+}
+
+template <typename T>
+Caser<T> Caser<T>::Case(T value, ResultExpr result) const {
+ return SANDBOX_BPF_DSL_CASES((value), result);
+}
+
+template <typename T>
+Caser<T> Caser<T>::Cases(const std::vector<T>& values,
+ ResultExpr result) const {
+ // Theoretically we could evaluate arg_ just once and emit a more efficient
+ // dispatch table, but for now we simply translate into an equivalent
+ // If/ElseIf/Else chain.
+
+ typedef typename std::vector<T>::const_iterator Iter;
+ BoolExpr test = BoolConst(false);
+ for (Iter i = values.begin(), end = values.end(); i != end; ++i) {
+ test = test || (arg_ == *i);
+ }
+ return Caser<T>(arg_, elser_.ElseIf(test, result));
+}
+
+template <typename T>
+ResultExpr Caser<T>::Default(ResultExpr result) const {
+ return elser_.Else(result);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
new file mode 100644
index 0000000000..398ec59ef1
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
@@ -0,0 +1,486 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <map>
+#include <utility>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/linux/bpf_dsl/verifier.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define CASES SANDBOX_BPF_DSL_CASES
+
+namespace sandbox {
+namespace bpf_dsl {
+namespace {
+
+// Helper function to construct fake arch_seccomp_data objects.
+struct arch_seccomp_data FakeSyscall(int nr,
+ uint64_t p0 = 0,
+ uint64_t p1 = 0,
+ uint64_t p2 = 0,
+ uint64_t p3 = 0,
+ uint64_t p4 = 0,
+ uint64_t p5 = 0) {
+ // Made up program counter for syscall address.
+ const uint64_t kFakePC = 0x543210;
+
+ struct arch_seccomp_data data = {
+ nr,
+ SECCOMP_ARCH,
+ kFakePC,
+ {
+ p0, p1, p2, p3, p4, p5,
+ },
+ };
+
+ return data;
+}
+
+class FakeTrapRegistry : public TrapRegistry {
+ public:
+ FakeTrapRegistry() : map_() {}
+ virtual ~FakeTrapRegistry() {}
+
+ uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override {
+ EXPECT_TRUE(safe);
+
+ const uint16_t next_id = map_.size() + 1;
+ return map_.insert(std::make_pair(Key(fnc, aux), next_id)).first->second;
+ }
+
+ bool EnableUnsafeTraps() override {
+ ADD_FAILURE() << "Unimplemented";
+ return false;
+ }
+
+ private:
+ using Key = std::pair<TrapFnc, const void*>;
+
+ std::map<Key, uint16_t> map_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeTrapRegistry);
+};
+
+intptr_t FakeTrapFuncOne(const arch_seccomp_data& data, void* aux) { return 1; }
+intptr_t FakeTrapFuncTwo(const arch_seccomp_data& data, void* aux) { return 2; }
+
+// Test that FakeTrapRegistry correctly assigns trap IDs to trap handlers.
+TEST(FakeTrapRegistry, TrapIDs) {
+ struct {
+ TrapRegistry::TrapFnc fnc;
+ const void* aux;
+ } funcs[] = {
+ {FakeTrapFuncOne, nullptr},
+ {FakeTrapFuncTwo, nullptr},
+ {FakeTrapFuncOne, funcs},
+ {FakeTrapFuncTwo, funcs},
+ };
+
+ FakeTrapRegistry traps;
+
+ // Add traps twice to test that IDs are reused correctly.
+ for (int i = 0; i < 2; ++i) {
+ for (size_t j = 0; j < arraysize(funcs); ++j) {
+ // Trap IDs start at 1.
+ EXPECT_EQ(j + 1, traps.Add(funcs[j].fnc, funcs[j].aux, true));
+ }
+ }
+}
+
+class PolicyEmulator {
+ public:
+ explicit PolicyEmulator(const Policy* policy) : program_(), traps_() {
+ program_ = *PolicyCompiler(policy, &traps_).Compile(true /* verify */);
+ }
+ ~PolicyEmulator() {}
+
+ uint32_t Emulate(const struct arch_seccomp_data& data) const {
+ const char* err = nullptr;
+ uint32_t res = Verifier::EvaluateBPF(program_, data, &err);
+ if (err) {
+ ADD_FAILURE() << err;
+ return 0;
+ }
+ return res;
+ }
+
+ void ExpectAllow(const struct arch_seccomp_data& data) const {
+ EXPECT_EQ(SECCOMP_RET_ALLOW, Emulate(data));
+ }
+
+ void ExpectErrno(uint16_t err, const struct arch_seccomp_data& data) const {
+ EXPECT_EQ(SECCOMP_RET_ERRNO | err, Emulate(data));
+ }
+
+ private:
+ CodeGen::Program program_;
+ FakeTrapRegistry traps_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyEmulator);
+};
+
+class BasicPolicy : public Policy {
+ public:
+ BasicPolicy() {}
+ ~BasicPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_getpgid) {
+ const Arg<pid_t> pid(0);
+ return If(pid == 0, Error(EPERM)).Else(Error(EINVAL));
+ }
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If(uid != 42, Error(ESRCH)).Else(Error(ENOMEM));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicPolicy);
+};
+
+TEST(BPFDSL, Basic) {
+ BasicPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_getpgid, 0));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_getpgid, 1));
+
+ emulator.ExpectErrno(ENOMEM, FakeSyscall(__NR_setuid, 42));
+ emulator.ExpectErrno(ESRCH, FakeSyscall(__NR_setuid, 43));
+}
+
+/* On IA-32, socketpair() is implemented via socketcall(). :-( */
+#if !defined(ARCH_CPU_X86)
+class BooleanLogicPolicy : public Policy {
+ public:
+ BooleanLogicPolicy() {}
+ ~BooleanLogicPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_socketpair) {
+ const Arg<int> domain(0), type(1), protocol(2);
+ return If(domain == AF_UNIX &&
+ (type == SOCK_STREAM || type == SOCK_DGRAM) &&
+ protocol == 0,
+ Error(EPERM)).Else(Error(EINVAL));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy);
+};
+
+TEST(BPFDSL, BooleanLogic) {
+ BooleanLogicPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ const intptr_t kFakeSV = 0x12345;
+
+ // Acceptable combinations that should return EPERM.
+ emulator.ExpectErrno(
+ EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_STREAM, 0, kFakeSV));
+ emulator.ExpectErrno(
+ EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_DGRAM, 0, kFakeSV));
+
+ // Combinations that are invalid for only one reason; should return EINVAL.
+ emulator.ExpectErrno(
+ EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_STREAM, 0, kFakeSV));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX,
+ SOCK_SEQPACKET, 0, kFakeSV));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX,
+ SOCK_STREAM, IPPROTO_TCP, kFakeSV));
+
+ // Completely unacceptable combination; should also return EINVAL.
+ emulator.ExpectErrno(
+ EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_SEQPACKET, IPPROTO_UDP,
+ kFakeSV));
+}
+#endif // !ARCH_CPU_X86
+
+class MoreBooleanLogicPolicy : public Policy {
+ public:
+ MoreBooleanLogicPolicy() {}
+ ~MoreBooleanLogicPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_setresuid) {
+ const Arg<uid_t> ruid(0), euid(1), suid(2);
+ return If(ruid == 0 || euid == 0 || suid == 0, Error(EPERM))
+ .ElseIf(ruid == 1 && euid == 1 && suid == 1, Error(EAGAIN))
+ .Else(Error(EINVAL));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy);
+};
+
+TEST(BPFDSL, MoreBooleanLogic) {
+ MoreBooleanLogicPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ // Expect EPERM if any set to 0.
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 0, 5, 5));
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 0, 5));
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 5, 0));
+
+ // Expect EAGAIN if all set to 1.
+ emulator.ExpectErrno(EAGAIN, FakeSyscall(__NR_setresuid, 1, 1, 1));
+
+ // Expect EINVAL for anything else.
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 5, 1, 1));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 5, 1));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 1, 5));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 3, 4, 5));
+}
+
+static const uintptr_t kDeadBeefAddr =
+ static_cast<uintptr_t>(0xdeadbeefdeadbeefULL);
+
+class ArgSizePolicy : public Policy {
+ public:
+ ArgSizePolicy() {}
+ ~ArgSizePolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_uname) {
+ const Arg<uintptr_t> addr(0);
+ return If(addr == kDeadBeefAddr, Error(EPERM)).Else(Allow());
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy);
+};
+
+TEST(BPFDSL, ArgSizeTest) {
+ ArgSizePolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ emulator.ExpectAllow(FakeSyscall(__NR_uname, 0));
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_uname, kDeadBeefAddr));
+}
+
+#if 0
+// TODO(mdempsky): This is really an integration test.
+
+class TrappingPolicy : public Policy {
+ public:
+ TrappingPolicy() {}
+ ~TrappingPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_uname) {
+ return Trap(UnameTrap, &count_);
+ }
+ return Allow();
+ }
+
+ private:
+ static intptr_t count_;
+
+ static intptr_t UnameTrap(const struct arch_seccomp_data& data, void* aux) {
+ BPF_ASSERT_EQ(&count_, aux);
+ return ++count_;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TrappingPolicy);
+};
+
+intptr_t TrappingPolicy::count_;
+
+BPF_TEST_C(BPFDSL, TrapTest, TrappingPolicy) {
+ ASSERT_SYSCALL_RESULT(1, uname, NULL);
+ ASSERT_SYSCALL_RESULT(2, uname, NULL);
+ ASSERT_SYSCALL_RESULT(3, uname, NULL);
+}
+#endif
+
+class MaskingPolicy : public Policy {
+ public:
+ MaskingPolicy() {}
+ ~MaskingPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If((uid & 0xf) == 0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ if (sysno == __NR_setgid) {
+ const Arg<gid_t> gid(0);
+ return If((gid & 0xf0) == 0xf0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ if (sysno == __NR_setpgid) {
+ const Arg<pid_t> pid(0);
+ return If((pid & 0xa5) == 0xa0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MaskingPolicy);
+};
+
+TEST(BPFDSL, MaskTest) {
+ MaskingPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ for (uid_t uid = 0; uid < 0x100; ++uid) {
+ const int expect_errno = (uid & 0xf) == 0 ? EINVAL : EACCES;
+ emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setuid, uid));
+ }
+
+ for (gid_t gid = 0; gid < 0x100; ++gid) {
+ const int expect_errno = (gid & 0xf0) == 0xf0 ? EINVAL : EACCES;
+ emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setgid, gid));
+ }
+
+ for (pid_t pid = 0; pid < 0x100; ++pid) {
+ const int expect_errno = (pid & 0xa5) == 0xa0 ? EINVAL : EACCES;
+ emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setpgid, pid, 0));
+ }
+}
+
+class ElseIfPolicy : public Policy {
+ public:
+ ElseIfPolicy() {}
+ ~ElseIfPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If((uid & 0xfff) == 0, Error(0))
+ .ElseIf((uid & 0xff0) == 0, Error(EINVAL))
+ .ElseIf((uid & 0xf00) == 0, Error(EEXIST))
+ .Else(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy);
+};
+
+TEST(BPFDSL, ElseIfTest) {
+ ElseIfPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ emulator.ExpectErrno(0, FakeSyscall(__NR_setuid, 0));
+
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0001));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0002));
+
+ emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0011));
+ emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0022));
+
+ emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0111));
+ emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0222));
+}
+
+class SwitchPolicy : public Policy {
+ public:
+ SwitchPolicy() {}
+ ~SwitchPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_fcntl) {
+ const Arg<int> cmd(1);
+ const Arg<unsigned long> long_arg(2);
+ return Switch(cmd)
+ .CASES((F_GETFL, F_GETFD), Error(ENOENT))
+ .Case(F_SETFD, If(long_arg == O_CLOEXEC, Allow()).Else(Error(EINVAL)))
+ .Case(F_SETFL, Error(EPERM))
+ .Default(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SwitchPolicy);
+};
+
+TEST(BPFDSL, SwitchTest) {
+ SwitchPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ const int kFakeSockFD = 42;
+
+ emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFD));
+ emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFL));
+
+ emulator.ExpectAllow(
+ FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, O_CLOEXEC));
+ emulator.ExpectErrno(EINVAL,
+ FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, 0));
+
+ emulator.ExpectErrno(EPERM,
+ FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFL, O_RDONLY));
+
+ emulator.ExpectErrno(EACCES,
+ FakeSyscall(__NR_fcntl, kFakeSockFD, F_DUPFD, 0));
+}
+
+static intptr_t DummyTrap(const struct arch_seccomp_data& data, void* aux) {
+ return 0;
+}
+
+TEST(BPFDSL, IsAllowDeny) {
+ ResultExpr allow = Allow();
+ EXPECT_TRUE(allow->IsAllow());
+ EXPECT_FALSE(allow->IsDeny());
+
+ ResultExpr error = Error(ENOENT);
+ EXPECT_FALSE(error->IsAllow());
+ EXPECT_TRUE(error->IsDeny());
+
+ ResultExpr trace = Trace(42);
+ EXPECT_FALSE(trace->IsAllow());
+ EXPECT_FALSE(trace->IsDeny());
+
+ ResultExpr trap = Trap(DummyTrap, nullptr);
+ EXPECT_FALSE(trap->IsAllow());
+ EXPECT_TRUE(trap->IsDeny());
+
+ const Arg<int> arg(0);
+ ResultExpr maybe = If(arg == 0, Allow()).Else(Error(EPERM));
+ EXPECT_FALSE(maybe->IsAllow());
+ EXPECT_FALSE(maybe->IsDeny());
+}
+
+TEST(BPFDSL, HasUnsafeTraps) {
+ ResultExpr allow = Allow();
+ EXPECT_FALSE(allow->HasUnsafeTraps());
+
+ ResultExpr safe = Trap(DummyTrap, nullptr);
+ EXPECT_FALSE(safe->HasUnsafeTraps());
+
+ ResultExpr unsafe = UnsafeTrap(DummyTrap, nullptr);
+ EXPECT_TRUE(unsafe->HasUnsafeTraps());
+
+ const Arg<int> arg(0);
+ ResultExpr maybe = If(arg == 0, allow).Else(unsafe);
+ EXPECT_TRUE(maybe->HasUnsafeTraps());
+}
+
+} // namespace
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/codegen.cc b/sandbox/linux/bpf_dsl/codegen.cc
new file mode 100644
index 0000000000..2d5c8e406e
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/codegen.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/codegen.h"
+
+#include <limits>
+#include <utility>
+
+#include "base/logging.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+
+// This CodeGen implementation strives for simplicity while still
+// generating acceptable BPF programs under typical usage patterns
+// (e.g., by PolicyCompiler).
+//
+// The key to its simplicity is that BPF programs only support forward
+// jumps/branches, which allows constraining the DAG construction API
+// to make instruction nodes immutable. Immutable nodes admits a
+// simple greedy approach of emitting new instructions as needed and
+// then reusing existing ones that have already been emitted. This
+// cleanly avoids any need to compute basic blocks or apply
+// topological sorting because the API effectively sorts instructions
+// for us (e.g., before MakeInstruction() can be called to emit a
+// branch instruction, it must have already been called for each
+// branch path).
+//
+// This greedy algorithm is not without (theoretical) weakness though:
+//
+// 1. In the general case, we don't eliminate dead code. If needed,
+// we could trace back through the program in Compile() and elide
+// any unneeded instructions, but in practice we only emit live
+// instructions anyway.
+//
+// 2. By not dividing instructions into basic blocks and sorting, we
+// lose an opportunity to move non-branch/non-return instructions
+// adjacent to their successor instructions, which means we might
+// need to emit additional jumps. But in practice, they'll
+// already be nearby as long as callers don't go out of their way
+// to interleave MakeInstruction() calls for unrelated code
+// sequences.
+
+namespace sandbox {
+
+// kBranchRange is the maximum value that can be stored in
+// sock_filter's 8-bit jt and jf fields.
+const size_t kBranchRange = std::numeric_limits<uint8_t>::max();
+
+const CodeGen::Node CodeGen::kNullNode;
+
+CodeGen::CodeGen() : program_(), equivalent_(), memos_() {
+}
+
+CodeGen::~CodeGen() {
+}
+
+void CodeGen::Compile(CodeGen::Node head, Program* out) {
+ DCHECK(out);
+ out->assign(program_.rbegin() + Offset(head), program_.rend());
+}
+
+CodeGen::Node CodeGen::MakeInstruction(uint16_t code,
+ uint32_t k,
+ Node jt,
+ Node jf) {
+ // To avoid generating redundant code sequences, we memoize the
+ // results from AppendInstruction().
+ auto res = memos_.insert(std::make_pair(MemoKey(code, k, jt, jf), kNullNode));
+ CodeGen::Node* node = &res.first->second;
+ if (res.second) { // Newly inserted memo entry.
+ *node = AppendInstruction(code, k, jt, jf);
+ }
+ return *node;
+}
+
+CodeGen::Node CodeGen::AppendInstruction(uint16_t code,
+ uint32_t k,
+ Node jt,
+ Node jf) {
+ if (BPF_CLASS(code) == BPF_JMP) {
+ CHECK_NE(BPF_JA, BPF_OP(code)) << "CodeGen inserts JAs as needed";
+
+ // Optimally adding jumps is rather tricky, so we use a quick
+ // approximation: by artificially reducing |jt|'s range, |jt| will
+ // stay within its true range even if we add a jump for |jf|.
+ jt = WithinRange(jt, kBranchRange - 1);
+ jf = WithinRange(jf, kBranchRange);
+ return Append(code, k, Offset(jt), Offset(jf));
+ }
+
+ CHECK_EQ(kNullNode, jf) << "Non-branch instructions shouldn't provide jf";
+ if (BPF_CLASS(code) == BPF_RET) {
+ CHECK_EQ(kNullNode, jt) << "Return instructions shouldn't provide jt";
+ } else {
+ // For non-branch/non-return instructions, execution always
+ // proceeds to the next instruction; so we need to arrange for
+ // that to be |jt|.
+ jt = WithinRange(jt, 0);
+ CHECK_EQ(0U, Offset(jt)) << "ICE: Failed to setup next instruction";
+ }
+ return Append(code, k, 0, 0);
+}
+
+CodeGen::Node CodeGen::WithinRange(Node target, size_t range) {
+ // Just use |target| if it's already within range.
+ if (Offset(target) <= range) {
+ return target;
+ }
+
+ // Alternatively, look for an equivalent instruction within range.
+ if (Offset(equivalent_.at(target)) <= range) {
+ return equivalent_.at(target);
+ }
+
+ // Otherwise, fall back to emitting a jump instruction.
+ Node jump = Append(BPF_JMP | BPF_JA, Offset(target), 0, 0);
+ equivalent_.at(target) = jump;
+ return jump;
+}
+
+CodeGen::Node CodeGen::Append(uint16_t code, uint32_t k, size_t jt, size_t jf) {
+ if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) {
+ CHECK_LE(jt, kBranchRange);
+ CHECK_LE(jf, kBranchRange);
+ } else {
+ CHECK_EQ(0U, jt);
+ CHECK_EQ(0U, jf);
+ }
+
+ CHECK_LT(program_.size(), static_cast<size_t>(BPF_MAXINSNS));
+ CHECK_EQ(program_.size(), equivalent_.size());
+
+ Node res = program_.size();
+ program_.push_back(sock_filter{
+ code, static_cast<uint8_t>(jt), static_cast<uint8_t>(jf), k});
+ equivalent_.push_back(res);
+ return res;
+}
+
+size_t CodeGen::Offset(Node target) const {
+ CHECK_LT(target, program_.size()) << "Bogus offset target node";
+ return (program_.size() - 1) - target;
+}
+
+// TODO(mdempsky): Move into a general base::Tuple helper library.
+bool CodeGen::MemoKeyLess::operator()(const MemoKey& lhs,
+ const MemoKey& rhs) const {
+ if (base::get<0>(lhs) != base::get<0>(rhs))
+ return base::get<0>(lhs) < base::get<0>(rhs);
+ if (base::get<1>(lhs) != base::get<1>(rhs))
+ return base::get<1>(lhs) < base::get<1>(rhs);
+ if (base::get<2>(lhs) != base::get<2>(rhs))
+ return base::get<2>(lhs) < base::get<2>(rhs);
+ if (base::get<3>(lhs) != base::get<3>(rhs))
+ return base::get<3>(lhs) < base::get<3>(rhs);
+ return false;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/codegen.h b/sandbox/linux/bpf_dsl/codegen.h
new file mode 100644
index 0000000000..9d898030b9
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/codegen.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_CODEGEN_H__
+#define SANDBOX_LINUX_BPF_DSL_CODEGEN_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/tuple.h"
+#include "sandbox/sandbox_export.h"
+
+struct sock_filter;
+
+namespace sandbox {
+
+// The code generator implements a basic assembler that can convert a
+// graph of BPF instructions into a well-formed array of BPF
+// instructions. Most notably, it ensures that jumps are always
+// forward and don't exceed the limit of 255 instructions imposed by
+// the instruction set.
+//
+// Callers would typically create a new CodeGen object and then use it
+// to build a DAG of instruction nodes. They'll eventually call
+// Compile() to convert this DAG to a Program.
+//
+// CodeGen gen;
+// CodeGen::Node allow, branch, dag;
+//
+// allow =
+// gen.MakeInstruction(BPF_RET+BPF_K,
+// ErrorCode(ErrorCode::ERR_ALLOWED).err()));
+// branch =
+// gen.MakeInstruction(BPF_JMP+BPF_EQ+BPF_K, __NR_getpid,
+// Trap(GetPidHandler, NULL), allow);
+// dag =
+// gen.MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+// offsetof(struct arch_seccomp_data, nr), branch);
+//
+// // Simplified code follows; in practice, it is important to avoid calling
+// // any C++ destructors after starting the sandbox.
+// CodeGen::Program program;
+// gen.Compile(dag, program);
+// const struct sock_fprog prog = {
+// static_cast<unsigned short>(program->size()), &program[0] };
+// prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+//
+class SANDBOX_EXPORT CodeGen {
+ public:
+ // A vector of BPF instructions that need to be installed as a filter
+ // program in the kernel.
+ typedef std::vector<struct sock_filter> Program;
+
+ // Node represents a node within the instruction DAG being compiled.
+ using Node = Program::size_type;
+
+ // kNullNode represents the "null" node; i.e., the reserved node
+ // value guaranteed to not equal any actual nodes.
+ static const Node kNullNode = -1;
+
+ CodeGen();
+ ~CodeGen();
+
+ // MakeInstruction creates a node representing the specified
+ // instruction, or returns and existing equivalent node if one
+ // exists. For details on the possible parameters refer to
+ // https://www.kernel.org/doc/Documentation/networking/filter.txt.
+ // TODO(mdempsky): Reconsider using default arguments here.
+ Node MakeInstruction(uint16_t code,
+ uint32_t k,
+ Node jt = kNullNode,
+ Node jf = kNullNode);
+
+ // Compile linearizes the instruction DAG rooted at |head| into a
+ // program that can be executed by a BPF virtual machine.
+ void Compile(Node head, Program* program);
+
+ private:
+ using MemoKey = base::Tuple<uint16_t, uint32_t, Node, Node>;
+ struct MemoKeyLess {
+ bool operator()(const MemoKey& lhs, const MemoKey& rhs) const;
+ };
+
+ // AppendInstruction adds a new instruction, ensuring that |jt| and
+ // |jf| are within range as necessary for |code|.
+ Node AppendInstruction(uint16_t code, uint32_t k, Node jt, Node jf);
+
+ // WithinRange returns a node equivalent to |next| that is at most
+ // |range| instructions away from the (logical) beginning of the
+ // program.
+ Node WithinRange(Node next, size_t range);
+
+ // Append appends a new instruction to the physical end (i.e.,
+ // logical beginning) of |program_|.
+ Node Append(uint16_t code, uint32_t k, size_t jt, size_t jf);
+
+ // Offset returns how many instructions exist in |program_| after |target|.
+ size_t Offset(Node target) const;
+
+ // NOTE: program_ is the compiled program in *reverse*, so that
+ // indices remain stable as we add instructions.
+ Program program_;
+
+ // equivalent_ stores the most recent semantically-equivalent node for each
+ // instruction in program_. A node is defined as semantically-equivalent to N
+ // if it has the same instruction code and constant as N and its successor
+ // nodes (if any) are semantically-equivalent to N's successor nodes, or
+ // if it's an unconditional jump to a node semantically-equivalent to N.
+ std::vector<Node> equivalent_;
+
+ std::map<MemoKey, Node, MemoKeyLess> memos_;
+
+ DISALLOW_COPY_AND_ASSIGN(CodeGen);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_CODEGEN_H__
diff --git a/sandbox/linux/bpf_dsl/codegen_unittest.cc b/sandbox/linux/bpf_dsl/codegen_unittest.cc
new file mode 100644
index 0000000000..5961822123
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/codegen_unittest.cc
@@ -0,0 +1,402 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/codegen.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/md5.h"
+#include "base/strings/string_piece.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+// Hash provides an abstraction for building "hash trees" from BPF
+// control flow graphs, and efficiently identifying equivalent graphs.
+//
+// For simplicity, we use MD5, because base happens to provide a
+// convenient API for its use. However, any collision-resistant hash
+// should suffice.
+class Hash {
+ public:
+ static const Hash kZero;
+
+ Hash() : digest_() {}
+
+ Hash(uint16_t code,
+ uint32_t k,
+ const Hash& jt = kZero,
+ const Hash& jf = kZero)
+ : digest_() {
+ base::MD5Context ctx;
+ base::MD5Init(&ctx);
+ HashValue(&ctx, code);
+ HashValue(&ctx, k);
+ HashValue(&ctx, jt);
+ HashValue(&ctx, jf);
+ base::MD5Final(&digest_, &ctx);
+ }
+
+ Hash(const Hash& hash) = default;
+ Hash& operator=(const Hash& rhs) = default;
+
+ friend bool operator==(const Hash& lhs, const Hash& rhs) {
+ return lhs.Base16() == rhs.Base16();
+ }
+ friend bool operator!=(const Hash& lhs, const Hash& rhs) {
+ return !(lhs == rhs);
+ }
+
+ private:
+ template <typename T>
+ void HashValue(base::MD5Context* ctx, const T& value) {
+ base::MD5Update(ctx,
+ base::StringPiece(reinterpret_cast<const char*>(&value),
+ sizeof(value)));
+ }
+
+ std::string Base16() const {
+ return base::MD5DigestToBase16(digest_);
+ }
+
+ base::MD5Digest digest_;
+};
+
+const Hash Hash::kZero;
+
+// Sanity check that equality and inequality work on Hash as required.
+TEST(CodeGen, HashSanity) {
+ std::vector<Hash> hashes;
+
+ // Push a bunch of logically distinct hashes.
+ hashes.push_back(Hash::kZero);
+ for (int i = 0; i < 4; ++i) {
+ hashes.push_back(Hash(i & 1, i & 2));
+ }
+ for (int i = 0; i < 16; ++i) {
+ hashes.push_back(Hash(i & 1, i & 2, Hash(i & 4, i & 8)));
+ }
+ for (int i = 0; i < 64; ++i) {
+ hashes.push_back(
+ Hash(i & 1, i & 2, Hash(i & 4, i & 8), Hash(i & 16, i & 32)));
+ }
+
+ for (const Hash& a : hashes) {
+ for (const Hash& b : hashes) {
+ // Hashes should equal themselves, but not equal all others.
+ if (&a == &b) {
+ EXPECT_EQ(a, b);
+ } else {
+ EXPECT_NE(a, b);
+ }
+ }
+ }
+}
+
+// ProgramTest provides a fixture for writing compiling sample
+// programs with CodeGen and verifying the linearized output matches
+// the input DAG.
+class ProgramTest : public ::testing::Test {
+ protected:
+ ProgramTest() : gen_(), node_hashes_() {}
+
+ // MakeInstruction calls CodeGen::MakeInstruction() and associated
+ // the returned address with a hash of the instruction.
+ CodeGen::Node MakeInstruction(uint16_t code,
+ uint32_t k,
+ CodeGen::Node jt = CodeGen::kNullNode,
+ CodeGen::Node jf = CodeGen::kNullNode) {
+ CodeGen::Node res = gen_.MakeInstruction(code, k, jt, jf);
+ EXPECT_NE(CodeGen::kNullNode, res);
+
+ Hash digest(code, k, Lookup(jt), Lookup(jf));
+ auto it = node_hashes_.insert(std::make_pair(res, digest));
+ EXPECT_EQ(digest, it.first->second);
+
+ return res;
+ }
+
+ // RunTest compiles the program and verifies that the output matches
+ // what is expected. It should be called at the end of each program
+ // test case.
+ void RunTest(CodeGen::Node head) {
+ // Compile the program
+ CodeGen::Program program;
+ gen_.Compile(head, &program);
+
+ // Walk the program backwards, and compute the hash for each instruction.
+ std::vector<Hash> prog_hashes(program.size());
+ for (size_t i = program.size(); i > 0; --i) {
+ const sock_filter& insn = program.at(i - 1);
+ Hash& hash = prog_hashes.at(i - 1);
+
+ if (BPF_CLASS(insn.code) == BPF_JMP) {
+ if (BPF_OP(insn.code) == BPF_JA) {
+ // The compiler adds JA instructions as needed, so skip them.
+ hash = prog_hashes.at(i + insn.k);
+ } else {
+ hash = Hash(insn.code, insn.k, prog_hashes.at(i + insn.jt),
+ prog_hashes.at(i + insn.jf));
+ }
+ } else if (BPF_CLASS(insn.code) == BPF_RET) {
+ hash = Hash(insn.code, insn.k);
+ } else {
+ hash = Hash(insn.code, insn.k, prog_hashes.at(i));
+ }
+ }
+
+ EXPECT_EQ(Lookup(head), prog_hashes.at(0));
+ }
+
+ private:
+ const Hash& Lookup(CodeGen::Node next) const {
+ if (next == CodeGen::kNullNode) {
+ return Hash::kZero;
+ }
+ auto it = node_hashes_.find(next);
+ if (it == node_hashes_.end()) {
+ ADD_FAILURE() << "No hash found for node " << next;
+ return Hash::kZero;
+ }
+ return it->second;
+ }
+
+ CodeGen gen_;
+ std::map<CodeGen::Node, Hash> node_hashes_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProgramTest);
+};
+
+TEST_F(ProgramTest, OneInstruction) {
+ // Create the most basic valid BPF program:
+ // RET 0
+ CodeGen::Node head = MakeInstruction(BPF_RET + BPF_K, 0);
+ RunTest(head);
+}
+
+TEST_F(ProgramTest, SimpleBranch) {
+ // Create a program with a single branch:
+ // JUMP if eq 42 then $0 else $1
+ // 0: RET 1
+ // 1: RET 0
+ CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42,
+ MakeInstruction(BPF_RET + BPF_K, 1),
+ MakeInstruction(BPF_RET + BPF_K, 0));
+ RunTest(head);
+}
+
+TEST_F(ProgramTest, AtypicalBranch) {
+ // Create a program with a single branch:
+ // JUMP if eq 42 then $0 else $0
+ // 0: RET 0
+
+ CodeGen::Node ret = MakeInstruction(BPF_RET + BPF_K, 0);
+ CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret);
+
+ // N.B.: As the instructions in both sides of the branch are already
+ // the same object, we do not actually have any "mergeable" branches.
+ // This needs to be reflected in our choice of "flags".
+ RunTest(head);
+}
+
+TEST_F(ProgramTest, Complex) {
+ // Creates a basic BPF program that we'll use to test some of the code:
+ // JUMP if eq 42 the $0 else $1 (insn6)
+ // 0: LD 23 (insn5)
+ // 1: JUMP if eq 42 then $2 else $4 (insn4)
+ // 2: JUMP to $3 (insn2)
+ // 3: LD 42 (insn1)
+ // RET 42 (insn0)
+ // 4: LD 42 (insn3)
+ // RET 42 (insn3+)
+ CodeGen::Node insn0 = MakeInstruction(BPF_RET + BPF_K, 42);
+ CodeGen::Node insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0);
+ CodeGen::Node insn2 = insn1; // Implicit JUMP
+
+ // We explicitly duplicate instructions to test that they're merged.
+ CodeGen::Node insn3 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42,
+ MakeInstruction(BPF_RET + BPF_K, 42));
+ EXPECT_EQ(insn2, insn3);
+
+ CodeGen::Node insn4 =
+ MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3);
+ CodeGen::Node insn5 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4);
+
+ // Force a basic block that ends in neither a jump instruction nor a return
+ // instruction. It only contains "insn5". This exercises one of the less
+ // common code paths in the topo-sort algorithm.
+ // This also gives us a diamond-shaped pattern in our graph, which stresses
+ // another aspect of the topo-sort algorithm (namely, the ability to
+ // correctly count the incoming branches for subtrees that are not disjunct).
+ CodeGen::Node insn6 =
+ MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4);
+
+ RunTest(insn6);
+}
+
+TEST_F(ProgramTest, ConfusingTails) {
+ // This simple program demonstrates https://crbug.com/351103/
+ // The two "LOAD 0" instructions are blocks of their own. MergeTails() could
+ // be tempted to merge them since they are the same. However, they are
+ // not mergeable because they fall-through to non semantically equivalent
+ // blocks.
+ // Without the fix for this bug, this program should trigger the check in
+ // CompileAndCompare: the serialized graphs from the program and its compiled
+ // version will differ.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) LOAD 0 // System call number
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) LOAD 0 // System call number
+ // 5) if A == 0x1; then JMP 6 else JMP 7
+ // 6) RET 0
+ // 7) RET 1
+
+ CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1);
+ CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0);
+ CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ RunTest(i0);
+}
+
+TEST_F(ProgramTest, ConfusingTailsBasic) {
+ // Without the fix for https://crbug.com/351103/, (see
+ // SampleProgramConfusingTails()), this would generate a cyclic graph and
+ // crash as the two "LOAD 0" instructions would get merged.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) LOAD 0 // System call number
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) LOAD 0 // System call number
+ // 5) RET 1
+
+ CodeGen::Node i5 = MakeInstruction(BPF_RET + BPF_K, 1);
+ CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ RunTest(i0);
+}
+
+TEST_F(ProgramTest, ConfusingTailsMergeable) {
+ // This is similar to SampleProgramConfusingTails(), except that
+ // instructions 2 and 4 are now RET instructions.
+ // In PointerCompare(), this exercises the path where two blocks are of the
+ // same length and identical and the last instruction is a JMP or RET, so the
+ // following blocks don't need to be looked at and the blocks are mergeable.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) RET 42
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) RET 42
+ // 5) if A == 0x1; then JMP 6 else JMP 7
+ // 6) RET 0
+ // 7) RET 1
+
+ CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1);
+ CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0);
+ CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ CodeGen::Node i4 = MakeInstruction(BPF_RET + BPF_K, 42);
+ CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ CodeGen::Node i2 = MakeInstruction(BPF_RET + BPF_K, 42);
+ CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ RunTest(i0);
+}
+
+TEST_F(ProgramTest, InstructionFolding) {
+ // Check that simple instructions are folded as expected.
+ CodeGen::Node a = MakeInstruction(BPF_RET + BPF_K, 0);
+ EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0));
+ CodeGen::Node b = MakeInstruction(BPF_RET + BPF_K, 1);
+ EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0));
+ EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1));
+ EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1));
+
+ // Check that complex sequences are folded too.
+ CodeGen::Node c =
+ MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0,
+ MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b));
+ EXPECT_EQ(c, MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, 0,
+ MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b)));
+
+ RunTest(c);
+}
+
+TEST_F(ProgramTest, FarBranches) {
+ // BPF instructions use 8-bit fields for branch offsets, which means
+ // branch targets must be within 255 instructions of the branch
+ // instruction. CodeGen abstracts away this detail by inserting jump
+ // instructions as needed, which we test here by generating programs
+ // that should trigger any interesting boundary conditions.
+
+ // Populate with 260 initial instruction nodes.
+ std::vector<CodeGen::Node> nodes;
+ nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0));
+ for (size_t i = 1; i < 260; ++i) {
+ nodes.push_back(
+ MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back()));
+ }
+
+ // Exhaustively test branch offsets near BPF's limits.
+ for (size_t jt = 250; jt < 260; ++jt) {
+ for (size_t jf = 250; jf < 260; ++jf) {
+ nodes.push_back(MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0,
+ nodes.rbegin()[jt], nodes.rbegin()[jf]));
+ RunTest(nodes.back());
+ }
+ }
+}
+
+TEST_F(ProgramTest, JumpReuse) {
+ // As a code size optimization, we try to reuse jumps when possible
+ // instead of emitting new ones. Here we make sure that optimization
+ // is working as intended.
+ //
+ // NOTE: To simplify testing, we rely on implementation details
+ // about what CodeGen::Node values indicate (i.e., vector indices),
+ // but CodeGen users should treat them as opaque values.
+
+ // Populate with 260 initial instruction nodes.
+ std::vector<CodeGen::Node> nodes;
+ nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0));
+ for (size_t i = 1; i < 260; ++i) {
+ nodes.push_back(
+ MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back()));
+ }
+
+ // Branching to nodes[0] and nodes[1] should require 3 new
+ // instructions: two far jumps plus the branch itself.
+ CodeGen::Node one =
+ MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0, nodes[0], nodes[1]);
+ EXPECT_EQ(nodes.back() + 3, one); // XXX: Implementation detail!
+ RunTest(one);
+
+ // Branching again to the same target nodes should require only one
+ // new instruction, as we can reuse the previous branch's jumps.
+ CodeGen::Node two =
+ MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, nodes[0], nodes[1]);
+ EXPECT_EQ(one + 1, two); // XXX: Implementation detail!
+ RunTest(two);
+}
+
+} // namespace
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/dump_bpf.cc b/sandbox/linux/bpf_dsl/dump_bpf.cc
new file mode 100644
index 0000000000..d0c8f75073
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/dump_bpf.cc
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/dump_bpf.h"
+
+#include <stdio.h>
+
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+void DumpBPF::PrintProgram(const CodeGen::Program& program) {
+ for (CodeGen::Program::const_iterator iter = program.begin();
+ iter != program.end();
+ ++iter) {
+ int ip = (int)(iter - program.begin());
+ fprintf(stderr, "%3d) ", ip);
+ switch (BPF_CLASS(iter->code)) {
+ case BPF_LD:
+ if (iter->code == BPF_LD + BPF_W + BPF_ABS) {
+ fprintf(stderr, "LOAD %d // ", (int)iter->k);
+ if (iter->k == offsetof(struct arch_seccomp_data, nr)) {
+ fprintf(stderr, "System call number\n");
+ } else if (iter->k == offsetof(struct arch_seccomp_data, arch)) {
+ fprintf(stderr, "Architecture\n");
+ } else if (iter->k ==
+ offsetof(struct arch_seccomp_data, instruction_pointer)) {
+ fprintf(stderr, "Instruction pointer (LSB)\n");
+ } else if (iter->k ==
+ offsetof(struct arch_seccomp_data, instruction_pointer) +
+ 4) {
+ fprintf(stderr, "Instruction pointer (MSB)\n");
+ } else if (iter->k >= offsetof(struct arch_seccomp_data, args) &&
+ iter->k < offsetof(struct arch_seccomp_data, args) + 48 &&
+ (iter->k - offsetof(struct arch_seccomp_data, args)) % 4 ==
+ 0) {
+ fprintf(
+ stderr,
+ "Argument %d (%cSB)\n",
+ (int)(iter->k - offsetof(struct arch_seccomp_data, args)) / 8,
+ (iter->k - offsetof(struct arch_seccomp_data, args)) % 8 ? 'M'
+ : 'L');
+ } else {
+ fprintf(stderr, "???\n");
+ }
+ } else {
+ fprintf(stderr, "LOAD ???\n");
+ }
+ break;
+ case BPF_JMP:
+ if (BPF_OP(iter->code) == BPF_JA) {
+ fprintf(stderr, "JMP %d\n", ip + iter->k + 1);
+ } else {
+ fprintf(stderr, "if A %s 0x%x; then JMP %d else JMP %d\n",
+ BPF_OP(iter->code) == BPF_JSET ? "&" :
+ BPF_OP(iter->code) == BPF_JEQ ? "==" :
+ BPF_OP(iter->code) == BPF_JGE ? ">=" :
+ BPF_OP(iter->code) == BPF_JGT ? ">" : "???",
+ (int)iter->k,
+ ip + iter->jt + 1, ip + iter->jf + 1);
+ }
+ break;
+ case BPF_RET:
+ fprintf(stderr, "RET 0x%x // ", iter->k);
+ if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) {
+ fprintf(stderr, "Trap #%d\n", iter->k & SECCOMP_RET_DATA);
+ } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+ fprintf(stderr, "errno = %d\n", iter->k & SECCOMP_RET_DATA);
+ } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE) {
+ fprintf(stderr, "Trace #%d\n", iter->k & SECCOMP_RET_DATA);
+ } else if (iter->k == SECCOMP_RET_ALLOW) {
+ fprintf(stderr, "Allowed\n");
+ } else {
+ fprintf(stderr, "???\n");
+ }
+ break;
+ case BPF_ALU:
+ if (BPF_OP(iter->code) == BPF_NEG) {
+ fprintf(stderr, "A := -A\n");
+ } else {
+ fprintf(stderr, "A := A %s 0x%x\n",
+ BPF_OP(iter->code) == BPF_ADD ? "+" :
+ BPF_OP(iter->code) == BPF_SUB ? "-" :
+ BPF_OP(iter->code) == BPF_MUL ? "*" :
+ BPF_OP(iter->code) == BPF_DIV ? "/" :
+ BPF_OP(iter->code) == BPF_MOD ? "%" :
+ BPF_OP(iter->code) == BPF_OR ? "|" :
+ BPF_OP(iter->code) == BPF_XOR ? "^" :
+ BPF_OP(iter->code) == BPF_AND ? "&" :
+ BPF_OP(iter->code) == BPF_LSH ? "<<" :
+ BPF_OP(iter->code) == BPF_RSH ? ">>" : "???",
+ (int)iter->k);
+ }
+ break;
+ default:
+ fprintf(stderr, "???\n");
+ break;
+ }
+ }
+ return;
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/dump_bpf.h b/sandbox/linux/bpf_dsl/dump_bpf.h
new file mode 100644
index 0000000000..cd12be793d
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/dump_bpf.h
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+class SANDBOX_EXPORT DumpBPF {
+ public:
+ // PrintProgram writes |program| in a human-readable format to stderr.
+ static void PrintProgram(const CodeGen::Program& program);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/policy.cc b/sandbox/linux/bpf_dsl/policy.cc
new file mode 100644
index 0000000000..c20edc6da8
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy.cc
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/policy.h"
+
+#include <errno.h>
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+ResultExpr Policy::InvalidSyscall() const {
+ return Error(ENOSYS);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/policy.h b/sandbox/linux/bpf_dsl/policy.h
new file mode 100644
index 0000000000..6c67589456
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_POLICY_H_
+#define SANDBOX_LINUX_BPF_DSL_POLICY_H_
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// Interface to implement to define a BPF sandbox policy.
+class SANDBOX_EXPORT Policy {
+ public:
+ Policy() {}
+ virtual ~Policy() {}
+
+ // User extension point for writing custom sandbox policies.
+ // The returned ResultExpr will control how the kernel responds to the
+ // specified system call number.
+ virtual ResultExpr EvaluateSyscall(int sysno) const = 0;
+
+ // Optional overload for specifying alternate behavior for invalid
+ // system calls. The default is to return ENOSYS.
+ virtual ResultExpr InvalidSyscall() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Policy);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_POLICY_H_
diff --git a/sandbox/linux/bpf_dsl/policy_compiler.cc b/sandbox/linux/bpf_dsl/policy_compiler.cc
new file mode 100644
index 0000000000..f38232f85f
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy_compiler.cc
@@ -0,0 +1,499 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+
+#include <errno.h>
+#include <sys/syscall.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/dump_bpf.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+#include "sandbox/linux/bpf_dsl/verifier.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+namespace {
+
+#if defined(__i386__) || defined(__x86_64__)
+const bool kIsIntel = true;
+#else
+const bool kIsIntel = false;
+#endif
+#if defined(__x86_64__) && defined(__ILP32__)
+const bool kIsX32 = true;
+#else
+const bool kIsX32 = false;
+#endif
+
+const int kSyscallsRequiredForUnsafeTraps[] = {
+ __NR_rt_sigprocmask,
+ __NR_rt_sigreturn,
+#if defined(__NR_sigprocmask)
+ __NR_sigprocmask,
+#endif
+#if defined(__NR_sigreturn)
+ __NR_sigreturn,
+#endif
+};
+
+bool HasExactlyOneBit(uint64_t x) {
+ // Common trick; e.g., see http://stackoverflow.com/a/108329.
+ return x != 0 && (x & (x - 1)) == 0;
+}
+
+// A Trap() handler that returns an "errno" value. The value is encoded
+// in the "aux" parameter.
+intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) {
+ // TrapFnc functions report error by following the native kernel convention
+ // of returning an exit code in the range of -1..-4096. They do not try to
+ // set errno themselves. The glibc wrapper that triggered the SIGSYS will
+ // ultimately do so for us.
+ int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA;
+ return -err;
+}
+
+bool HasUnsafeTraps(const Policy* policy) {
+ DCHECK(policy);
+ for (uint32_t sysnum : SyscallSet::ValidOnly()) {
+ if (policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) {
+ return true;
+ }
+ }
+ return policy->InvalidSyscall()->HasUnsafeTraps();
+}
+
+} // namespace
+
+struct PolicyCompiler::Range {
+ uint32_t from;
+ CodeGen::Node node;
+};
+
+PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry)
+ : policy_(policy),
+ registry_(registry),
+ escapepc_(0),
+ conds_(),
+ gen_(),
+ has_unsafe_traps_(HasUnsafeTraps(policy_)) {
+ DCHECK(policy);
+}
+
+PolicyCompiler::~PolicyCompiler() {
+}
+
+scoped_ptr<CodeGen::Program> PolicyCompiler::Compile(bool verify) {
+ CHECK(policy_->InvalidSyscall()->IsDeny())
+ << "Policies should deny invalid system calls";
+
+ // If our BPF program has unsafe traps, enable support for them.
+ if (has_unsafe_traps_) {
+ CHECK_NE(0U, escapepc_) << "UnsafeTrap() requires a valid escape PC";
+
+ for (int sysnum : kSyscallsRequiredForUnsafeTraps) {
+ CHECK(policy_->EvaluateSyscall(sysnum)->IsAllow())
+ << "Policies that use UnsafeTrap() must unconditionally allow all "
+ "required system calls";
+ }
+
+ CHECK(registry_->EnableUnsafeTraps())
+ << "We'd rather die than enable unsafe traps";
+ }
+
+ // Assemble the BPF filter program.
+ scoped_ptr<CodeGen::Program> program(new CodeGen::Program());
+ gen_.Compile(AssemblePolicy(), program.get());
+
+ // Make sure compilation resulted in a BPF program that executes
+ // correctly. Otherwise, there is an internal error in our BPF compiler.
+ // There is really nothing the caller can do until the bug is fixed.
+ if (verify) {
+ const char* err = nullptr;
+ if (!Verifier::VerifyBPF(this, *program, *policy_, &err)) {
+ DumpBPF::PrintProgram(*program);
+ LOG(FATAL) << err;
+ }
+ }
+
+ return program.Pass();
+}
+
+void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) {
+ escapepc_ = escapepc;
+}
+
+CodeGen::Node PolicyCompiler::AssemblePolicy() {
+ // A compiled policy consists of three logical parts:
+ // 1. Check that the "arch" field matches the expected architecture.
+ // 2. If the policy involves unsafe traps, check if the syscall was
+ // invoked by Syscall::Call, and then allow it unconditionally.
+ // 3. Check the system call number and jump to the appropriate compiled
+ // system call policy number.
+ return CheckArch(MaybeAddEscapeHatch(DispatchSyscall()));
+}
+
+CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) {
+ // If the architecture doesn't match SECCOMP_ARCH, disallow the
+ // system call.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed,
+ CompileResult(Kill("Invalid audit architecture in BPF filter"))));
+}
+
+CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) {
+ // If no unsafe traps, then simply return |rest|.
+ if (!has_unsafe_traps_) {
+ return rest;
+ }
+
+ // We already enabled unsafe traps in Compile, but enable them again to give
+ // the trap registry a second chance to complain before we add the backdoor.
+ CHECK(registry_->EnableUnsafeTraps());
+
+ // Allow system calls, if they originate from our magic return address.
+ const uint32_t lopc = static_cast<uint32_t>(escapepc_);
+ const uint32_t hipc = static_cast<uint32_t>(escapepc_ >> 32);
+
+ // BPF cannot do native 64-bit comparisons, so we have to compare
+ // both 32-bit halves of the instruction pointer. If they match what
+ // we expect, we return ERR_ALLOWED. If either or both don't match,
+ // we continue evalutating the rest of the sandbox policy.
+ //
+ // For simplicity, we check the full 64-bit instruction pointer even
+ // on 32-bit architectures.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, lopc,
+ gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX,
+ gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hipc,
+ CompileResult(Allow()), rest)),
+ rest));
+}
+
+CodeGen::Node PolicyCompiler::DispatchSyscall() {
+ // Evaluate all possible system calls and group their ErrorCodes into
+ // ranges of identical codes.
+ Ranges ranges;
+ FindRanges(&ranges);
+
+ // Compile the system call ranges to an optimized BPF jumptable
+ CodeGen::Node jumptable = AssembleJumpTable(ranges.begin(), ranges.end());
+
+ // Grab the system call number, so that we can check it and then
+ // execute the jump table.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable));
+}
+
+CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) {
+ if (kIsIntel) {
+ // On Intel architectures, verify that system call numbers are in the
+ // expected number range.
+ CodeGen::Node invalidX32 =
+ CompileResult(Kill("Illegal mixing of system call ABIs"));
+ if (kIsX32) {
+ // The newer x32 API always sets bit 30.
+ return gen_.MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32);
+ } else {
+ // The older i386 and x86-64 APIs clear bit 30 on all system calls.
+ return gen_.MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed);
+ }
+ }
+
+ // TODO(mdempsky): Similar validation for other architectures?
+ return passed;
+}
+
+void PolicyCompiler::FindRanges(Ranges* ranges) {
+ // Please note that "struct seccomp_data" defines system calls as a signed
+ // int32_t, but BPF instructions always operate on unsigned quantities. We
+ // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
+ // and then verifying that the rest of the number range (both positive and
+ // negative) all return the same ErrorCode.
+ const CodeGen::Node invalid_node = CompileResult(policy_->InvalidSyscall());
+ uint32_t old_sysnum = 0;
+ CodeGen::Node old_node =
+ SyscallSet::IsValid(old_sysnum)
+ ? CompileResult(policy_->EvaluateSyscall(old_sysnum))
+ : invalid_node;
+
+ for (uint32_t sysnum : SyscallSet::All()) {
+ CodeGen::Node node =
+ SyscallSet::IsValid(sysnum)
+ ? CompileResult(policy_->EvaluateSyscall(static_cast<int>(sysnum)))
+ : invalid_node;
+ // N.B., here we rely on CodeGen folding (i.e., returning the same
+ // node value for) identical code sequences, otherwise our jump
+ // table will blow up in size.
+ if (node != old_node) {
+ ranges->push_back(Range{old_sysnum, old_node});
+ old_sysnum = sysnum;
+ old_node = node;
+ }
+ }
+ ranges->push_back(Range{old_sysnum, old_node});
+}
+
+CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start,
+ Ranges::const_iterator stop) {
+ // We convert the list of system call ranges into jump table that performs
+ // a binary search over the ranges.
+ // As a sanity check, we need to have at least one distinct ranges for us
+ // to be able to build a jump table.
+ CHECK(start < stop) << "Invalid iterator range";
+ const auto n = stop - start;
+ if (n == 1) {
+ // If we have narrowed things down to a single range object, we can
+ // return from the BPF filter program.
+ return start->node;
+ }
+
+ // Pick the range object that is located at the mid point of our list.
+ // We compare our system call number against the lowest valid system call
+ // number in this range object. If our number is lower, it is outside of
+ // this range object. If it is greater or equal, it might be inside.
+ Ranges::const_iterator mid = start + n / 2;
+
+ // Sub-divide the list of ranges and continue recursively.
+ CodeGen::Node jf = AssembleJumpTable(start, mid);
+ CodeGen::Node jt = AssembleJumpTable(mid, stop);
+ return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf);
+}
+
+CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) {
+ return RetExpression(res->Compile(this));
+}
+
+CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) {
+ switch (err.error_type()) {
+ case ErrorCode::ET_COND:
+ return CondExpression(err);
+ case ErrorCode::ET_SIMPLE:
+ case ErrorCode::ET_TRAP:
+ return gen_.MakeInstruction(BPF_RET + BPF_K, err.err());
+ default:
+ LOG(FATAL)
+ << "ErrorCode is not suitable for returning from a BPF program";
+ return CodeGen::kNullNode;
+ }
+}
+
+CodeGen::Node PolicyCompiler::CondExpression(const ErrorCode& cond) {
+ // Sanity check that |cond| makes sense.
+ CHECK(cond.argno_ >= 0 && cond.argno_ < 6) << "Invalid argument number "
+ << cond.argno_;
+ CHECK(cond.width_ == ErrorCode::TP_32BIT ||
+ cond.width_ == ErrorCode::TP_64BIT)
+ << "Invalid argument width " << cond.width_;
+ CHECK_NE(0U, cond.mask_) << "Zero mask is invalid";
+ CHECK_EQ(cond.value_, cond.value_ & cond.mask_)
+ << "Value contains masked out bits";
+ if (sizeof(void*) == 4) {
+ CHECK_EQ(ErrorCode::TP_32BIT, cond.width_)
+ << "Invalid width on 32-bit platform";
+ }
+ if (cond.width_ == ErrorCode::TP_32BIT) {
+ CHECK_EQ(0U, cond.mask_ >> 32) << "Mask exceeds argument size";
+ CHECK_EQ(0U, cond.value_ >> 32) << "Value exceeds argument size";
+ }
+
+ CodeGen::Node passed = RetExpression(*cond.passed_);
+ CodeGen::Node failed = RetExpression(*cond.failed_);
+
+ // We want to emit code to check "(arg & mask) == value" where arg, mask, and
+ // value are 64-bit values, but the BPF machine is only 32-bit. We implement
+ // this by independently testing the upper and lower 32-bits and continuing to
+ // |passed| if both evaluate true, or to |failed| if either evaluate false.
+ return CondExpressionHalf(cond,
+ UpperHalf,
+ CondExpressionHalf(cond, LowerHalf, passed, failed),
+ failed);
+}
+
+CodeGen::Node PolicyCompiler::CondExpressionHalf(const ErrorCode& cond,
+ ArgHalf half,
+ CodeGen::Node passed,
+ CodeGen::Node failed) {
+ if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) {
+ // Special logic for sanity checking the upper 32-bits of 32-bit system
+ // call arguments.
+
+ // TODO(mdempsky): Compile Unexpected64bitArgument() just per program.
+ CodeGen::Node invalid_64bit = RetExpression(Unexpected64bitArgument());
+
+ const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_);
+ const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_);
+
+ if (sizeof(void*) == 4) {
+ // On 32-bit platforms, the upper 32-bits should always be 0:
+ // LDW [upper]
+ // JEQ 0, passed, invalid
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ upper,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, 0, passed, invalid_64bit));
+ }
+
+ // On 64-bit platforms, the upper 32-bits may be 0 or ~0; but we only allow
+ // ~0 if the sign bit of the lower 32-bits is set too:
+ // LDW [upper]
+ // JEQ 0, passed, (next)
+ // JEQ ~0, (next), invalid
+ // LDW [lower]
+ // JSET (1<<31), passed, invalid
+ //
+ // TODO(mdempsky): The JSET instruction could perhaps jump to passed->next
+ // instead, as the first instruction of passed should be "LDW [lower]".
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ upper,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ 0,
+ passed,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ std::numeric_limits<uint32_t>::max(),
+ gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ lower,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
+ 1U << 31,
+ passed,
+ invalid_64bit)),
+ invalid_64bit)));
+ }
+
+ const uint32_t idx = (half == UpperHalf) ? SECCOMP_ARG_MSB_IDX(cond.argno_)
+ : SECCOMP_ARG_LSB_IDX(cond.argno_);
+ const uint32_t mask = (half == UpperHalf) ? cond.mask_ >> 32 : cond.mask_;
+ const uint32_t value = (half == UpperHalf) ? cond.value_ >> 32 : cond.value_;
+
+ // Emit a suitable instruction sequence for (arg & mask) == value.
+
+ // For (arg & 0) == 0, just return passed.
+ if (mask == 0) {
+ CHECK_EQ(0U, value);
+ return passed;
+ }
+
+ // For (arg & ~0) == value, emit:
+ // LDW [idx]
+ // JEQ value, passed, failed
+ if (mask == std::numeric_limits<uint32_t>::max()) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed));
+ }
+
+ // For (arg & mask) == 0, emit:
+ // LDW [idx]
+ // JSET mask, failed, passed
+ // (Note: failed and passed are intentionally swapped.)
+ if (value == 0) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, failed, passed));
+ }
+
+ // For (arg & x) == x where x is a single-bit value, emit:
+ // LDW [idx]
+ // JSET mask, passed, failed
+ if (mask == value && HasExactlyOneBit(mask)) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, passed, failed));
+ }
+
+ // Generic fallback:
+ // LDW [idx]
+ // AND mask
+ // JEQ value, passed, failed
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(
+ BPF_ALU + BPF_AND + BPF_K,
+ mask,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed)));
+}
+
+ErrorCode PolicyCompiler::Unexpected64bitArgument() {
+ return Kill("Unexpected 64bit argument detected")->Compile(this);
+}
+
+ErrorCode PolicyCompiler::Error(int err) {
+ if (has_unsafe_traps_) {
+ // When inside an UnsafeTrap() callback, we want to allow all system calls.
+ // This means, we must conditionally disable the sandbox -- and that's not
+ // something that kernel-side BPF filters can do, as they cannot inspect
+ // any state other than the syscall arguments.
+ // But if we redirect all error handlers to user-space, then we can easily
+ // make this decision.
+ // The performance penalty for this extra round-trip to user-space is not
+ // actually that bad, as we only ever pay it for denied system calls; and a
+ // typical program has very few of these.
+ return Trap(ReturnErrno, reinterpret_cast<void*>(err), true);
+ }
+
+ return ErrorCode(err);
+}
+
+ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc,
+ const void* aux,
+ bool safe) {
+ uint16_t trap_id = registry_->Add(fnc, aux, safe);
+ return ErrorCode(trap_id, fnc, aux, safe);
+}
+
+bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) {
+ for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) {
+ if (sysno == kSyscallsRequiredForUnsafeTraps[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+ErrorCode PolicyCompiler::CondMaskedEqual(int argno,
+ ErrorCode::ArgType width,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode& passed,
+ const ErrorCode& failed) {
+ return ErrorCode(argno,
+ width,
+ mask,
+ value,
+ &*conds_.insert(passed).first,
+ &*conds_.insert(failed).first);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/policy_compiler.h b/sandbox/linux/bpf_dsl/policy_compiler.h
new file mode 100644
index 0000000000..df38d4ccbc
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy_compiler.h
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
+#define SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+class Policy;
+
+// PolicyCompiler implements the bpf_dsl compiler, allowing users to
+// transform bpf_dsl policies into BPF programs to be executed by the
+// Linux kernel.
+class SANDBOX_EXPORT PolicyCompiler {
+ public:
+ PolicyCompiler(const Policy* policy, TrapRegistry* registry);
+ ~PolicyCompiler();
+
+ // Compile registers any trap handlers needed by the policy and
+ // compiles the policy to a BPF program, which it returns.
+ scoped_ptr<CodeGen::Program> Compile(bool verify);
+
+ // DangerousSetEscapePC sets the "escape PC" that is allowed to issue any
+ // system calls, regardless of policy.
+ void DangerousSetEscapePC(uint64_t escapepc);
+
+ // Error returns an ErrorCode to indicate the system call should fail with
+ // the specified error number.
+ ErrorCode Error(int err);
+
+ // Trap returns an ErrorCode to indicate the system call should
+ // instead invoke a trap handler.
+ ErrorCode Trap(TrapRegistry::TrapFnc fnc, const void* aux, bool safe);
+
+ // UnsafeTraps require some syscalls to always be allowed.
+ // This helper function returns true for these calls.
+ static bool IsRequiredForUnsafeTrap(int sysno);
+
+ // We can also use ErrorCode to request evaluation of a conditional
+ // statement based on inspection of system call parameters.
+ // This method wrap an ErrorCode object around the conditional statement.
+ // Argument "argno" (1..6) will be bitwise-AND'd with "mask" and compared
+ // to "value"; if equal, then "passed" will be returned, otherwise "failed".
+ // If "is32bit" is set, the argument must in the range of 0x0..(1u << 32 - 1)
+ // If it is outside this range, the sandbox treats the system call just
+ // the same as any other ABI violation (i.e. it aborts with an error
+ // message).
+ ErrorCode CondMaskedEqual(int argno,
+ ErrorCode::ArgType is_32bit,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode& passed,
+ const ErrorCode& failed);
+
+ // Returns the fatal ErrorCode that is used to indicate that somebody
+ // attempted to pass a 64bit value in a 32bit system call argument.
+ // This method is primarily needed for testing purposes.
+ ErrorCode Unexpected64bitArgument();
+
+ private:
+ struct Range;
+ typedef std::vector<Range> Ranges;
+ typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
+
+ // Used by CondExpressionHalf to track which half of the argument it's
+ // emitting instructions for.
+ enum ArgHalf {
+ LowerHalf,
+ UpperHalf,
+ };
+
+ // Compile the configured policy into a complete instruction sequence.
+ CodeGen::Node AssemblePolicy();
+
+ // Return an instruction sequence that checks the
+ // arch_seccomp_data's "arch" field is valid, and then passes
+ // control to |passed| if so.
+ CodeGen::Node CheckArch(CodeGen::Node passed);
+
+ // If |has_unsafe_traps_| is true, returns an instruction sequence
+ // that allows all system calls from |escapepc_|, and otherwise
+ // passes control to |rest|. Otherwise, simply returns |rest|.
+ CodeGen::Node MaybeAddEscapeHatch(CodeGen::Node rest);
+
+ // Return an instruction sequence that loads and checks the system
+ // call number, performs a binary search, and then dispatches to an
+ // appropriate instruction sequence compiled from the current
+ // policy.
+ CodeGen::Node DispatchSyscall();
+
+ // Return an instruction sequence that checks the system call number
+ // (expected to be loaded in register A) and if valid, passes
+ // control to |passed| (with register A still valid).
+ CodeGen::Node CheckSyscallNumber(CodeGen::Node passed);
+
+ // Finds all the ranges of system calls that need to be handled. Ranges are
+ // sorted in ascending order of system call numbers. There are no gaps in the
+ // ranges. System calls with identical ErrorCodes are coalesced into a single
+ // range.
+ void FindRanges(Ranges* ranges);
+
+ // Returns a BPF program snippet that implements a jump table for the
+ // given range of system call numbers. This function runs recursively.
+ CodeGen::Node AssembleJumpTable(Ranges::const_iterator start,
+ Ranges::const_iterator stop);
+
+ // CompileResult compiles an individual result expression into a
+ // CodeGen node.
+ CodeGen::Node CompileResult(const ResultExpr& res);
+
+ // Returns a BPF program snippet that makes the BPF filter program exit
+ // with the given ErrorCode "err". N.B. the ErrorCode may very well be a
+ // conditional expression; if so, this function will recursively call
+ // CondExpression() and possibly RetExpression() to build a complex set of
+ // instructions.
+ CodeGen::Node RetExpression(const ErrorCode& err);
+
+ // Returns a BPF program that evaluates the conditional expression in
+ // "cond" and returns the appropriate value from the BPF filter program.
+ // This function recursively calls RetExpression(); it should only ever be
+ // called from RetExpression().
+ CodeGen::Node CondExpression(const ErrorCode& cond);
+
+ // Returns a BPF program that evaluates half of a conditional expression;
+ // it should only ever be called from CondExpression().
+ CodeGen::Node CondExpressionHalf(const ErrorCode& cond,
+ ArgHalf half,
+ CodeGen::Node passed,
+ CodeGen::Node failed);
+
+ const Policy* policy_;
+ TrapRegistry* registry_;
+ uint64_t escapepc_;
+
+ Conds conds_;
+ CodeGen gen_;
+ bool has_unsafe_traps_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyCompiler);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
diff --git a/sandbox/linux/bpf_dsl/syscall_set.cc b/sandbox/linux/bpf_dsl/syscall_set.cc
new file mode 100644
index 0000000000..47810e99ac
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/syscall_set.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h"
+
+namespace sandbox {
+
+namespace {
+
+#if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+// This is true for Mips O32 ABI.
+static_assert(MIN_SYSCALL == __NR_Linux, "min syscall number should be 4000");
+#else
+// This true for supported architectures (Intel and ARM EABI).
+static_assert(MIN_SYSCALL == 0u,
+ "min syscall should always be zero");
+#endif
+
+// SyscallRange represents an inclusive range of system call numbers.
+struct SyscallRange {
+ uint32_t first;
+ uint32_t last;
+};
+
+const SyscallRange kValidSyscallRanges[] = {
+ // First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL
+ // on Intel architectures, but leaves room for private syscalls on ARM.
+ {MIN_SYSCALL, MAX_PUBLIC_SYSCALL},
+#if defined(__arm__)
+ // ARM EABI includes "ARM private" system calls starting at
+ // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at
+ // MIN_GHOST_SYSCALL.
+ {MIN_PRIVATE_SYSCALL, MAX_PRIVATE_SYSCALL},
+ {MIN_GHOST_SYSCALL, MAX_SYSCALL},
+#endif
+};
+
+} // namespace
+
+SyscallSet::Iterator SyscallSet::begin() const {
+ return Iterator(set_, false);
+}
+
+SyscallSet::Iterator SyscallSet::end() const {
+ return Iterator(set_, true);
+}
+
+bool SyscallSet::IsValid(uint32_t num) {
+ for (const SyscallRange& range : kValidSyscallRanges) {
+ if (num >= range.first && num <= range.last) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool operator==(const SyscallSet& lhs, const SyscallSet& rhs) {
+ return (lhs.set_ == rhs.set_);
+}
+
+SyscallSet::Iterator::Iterator(Set set, bool done)
+ : set_(set), done_(done), num_(0) {
+ // If the set doesn't contain 0, we need to skip to the next element.
+ if (!done && set_ == (IsValid(num_) ? Set::INVALID_ONLY : Set::VALID_ONLY)) {
+ ++*this;
+ }
+}
+
+uint32_t SyscallSet::Iterator::operator*() const {
+ DCHECK(!done_);
+ return num_;
+}
+
+SyscallSet::Iterator& SyscallSet::Iterator::operator++() {
+ DCHECK(!done_);
+
+ num_ = NextSyscall();
+ if (num_ == 0) {
+ done_ = true;
+ }
+
+ return *this;
+}
+
+// NextSyscall returns the next system call in the iterated system
+// call set after |num_|, or 0 if no such system call exists.
+uint32_t SyscallSet::Iterator::NextSyscall() const {
+ const bool want_valid = (set_ != Set::INVALID_ONLY);
+ const bool want_invalid = (set_ != Set::VALID_ONLY);
+
+ for (const SyscallRange& range : kValidSyscallRanges) {
+ if (want_invalid && range.first > 0 && num_ < range.first - 1) {
+ // Even when iterating invalid syscalls, we only include the end points;
+ // so skip directly to just before the next (valid) range.
+ return range.first - 1;
+ }
+ if (want_valid && num_ < range.first) {
+ return range.first;
+ }
+ if (want_valid && num_ < range.last) {
+ return num_ + 1;
+ }
+ if (want_invalid && num_ <= range.last) {
+ return range.last + 1;
+ }
+ }
+
+ if (want_invalid) {
+ // BPF programs only ever operate on unsigned quantities. So,
+ // that's how we iterate; we return values from
+ // 0..0xFFFFFFFFu. But there are places, where the kernel might
+ // interpret system call numbers as signed quantities, so the
+ // boundaries between signed and unsigned values are potential
+ // problem cases. We want to explicitly return these values from
+ // our iterator.
+ if (num_ < 0x7FFFFFFFu)
+ return 0x7FFFFFFFu;
+ if (num_ < 0x80000000u)
+ return 0x80000000u;
+
+ if (num_ < 0xFFFFFFFFu)
+ return 0xFFFFFFFFu;
+ }
+
+ return 0;
+}
+
+bool operator==(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs) {
+ DCHECK(lhs.set_ == rhs.set_);
+ return (lhs.done_ == rhs.done_) && (lhs.num_ == rhs.num_);
+}
+
+bool operator!=(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/syscall_set.h b/sandbox/linux/bpf_dsl/syscall_set.h
new file mode 100644
index 0000000000..b9f076d932
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/syscall_set.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__
+#define SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__
+
+#include <stdint.h>
+
+#include <iterator>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Iterates over the entire system call range from 0..0xFFFFFFFFu. This
+// iterator is aware of how system calls look like and will skip quickly
+// over ranges that can't contain system calls. It iterates more slowly
+// whenever it reaches a range that is potentially problematic, returning
+// the last invalid value before a valid range of system calls, and the
+// first invalid value after a valid range of syscalls. It iterates over
+// individual values whenever it is in the normal range for system calls
+// (typically MIN_SYSCALL..MAX_SYSCALL).
+//
+// Example usage:
+// for (uint32_t sysnum : SyscallSet::All()) {
+// // Do something with sysnum.
+// }
+class SANDBOX_EXPORT SyscallSet {
+ public:
+ class Iterator;
+
+ SyscallSet(const SyscallSet& ss) : set_(ss.set_) {}
+ ~SyscallSet() {}
+
+ Iterator begin() const;
+ Iterator end() const;
+
+ // All returns a SyscallSet that contains both valid and invalid
+ // system call numbers.
+ static SyscallSet All() { return SyscallSet(Set::ALL); }
+
+ // ValidOnly returns a SyscallSet that contains only valid system
+ // call numbers.
+ static SyscallSet ValidOnly() { return SyscallSet(Set::VALID_ONLY); }
+
+ // InvalidOnly returns a SyscallSet that contains only invalid
+ // system call numbers, but still omits numbers in the middle of a
+ // range of invalid system call numbers.
+ static SyscallSet InvalidOnly() { return SyscallSet(Set::INVALID_ONLY); }
+
+ // IsValid returns whether |num| specifies a valid system call
+ // number.
+ static bool IsValid(uint32_t num);
+
+ private:
+ enum class Set { ALL, VALID_ONLY, INVALID_ONLY };
+
+ explicit SyscallSet(Set set) : set_(set) {}
+
+ Set set_;
+
+ friend bool operator==(const SyscallSet&, const SyscallSet&);
+ DISALLOW_ASSIGN(SyscallSet);
+};
+
+SANDBOX_EXPORT bool operator==(const SyscallSet& lhs, const SyscallSet& rhs);
+
+// Iterator provides C++ input iterator semantics for traversing a
+// SyscallSet.
+class SyscallSet::Iterator
+ : public std::iterator<std::input_iterator_tag, uint32_t> {
+ public:
+ Iterator(const Iterator& it)
+ : set_(it.set_), done_(it.done_), num_(it.num_) {}
+ ~Iterator() {}
+
+ uint32_t operator*() const;
+ Iterator& operator++();
+
+ private:
+ Iterator(Set set, bool done);
+
+ uint32_t NextSyscall() const;
+
+ Set set_;
+ bool done_;
+ uint32_t num_;
+
+ friend SyscallSet;
+ friend bool operator==(const Iterator&, const Iterator&);
+ DISALLOW_ASSIGN(Iterator);
+};
+
+SANDBOX_EXPORT bool operator==(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs);
+SANDBOX_EXPORT bool operator!=(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs);
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__
diff --git a/sandbox/linux/bpf_dsl/syscall_set_unittest.cc b/sandbox/linux/bpf_dsl/syscall_set_unittest.cc
new file mode 100644
index 0000000000..fafb6f6f73
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/syscall_set_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+
+#include <stdint.h>
+
+#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+const SyscallSet kSyscallSets[] = {
+ SyscallSet::All(),
+ SyscallSet::InvalidOnly(),
+};
+
+SANDBOX_TEST(SyscallSet, Monotonous) {
+ for (const SyscallSet& set : kSyscallSets) {
+ uint32_t prev = 0;
+ bool have_prev = false;
+ for (uint32_t sysnum : set) {
+ if (have_prev) {
+ SANDBOX_ASSERT(sysnum > prev);
+ } else if (set == SyscallSet::All()) {
+ // The iterator should start at 0.
+ SANDBOX_ASSERT(sysnum == 0);
+ }
+
+ prev = sysnum;
+ have_prev = true;
+ }
+
+ // The iterator should always return 0xFFFFFFFFu as the last value.
+ SANDBOX_ASSERT(have_prev);
+ SANDBOX_ASSERT(prev == 0xFFFFFFFFu);
+ }
+}
+
+// AssertRange checks that SyscallIterator produces all system call
+// numbers in the inclusive range [min, max].
+void AssertRange(uint32_t min, uint32_t max) {
+ SANDBOX_ASSERT(min < max);
+ uint32_t prev = min - 1;
+ for (uint32_t sysnum : SyscallSet::All()) {
+ if (sysnum >= min && sysnum <= max) {
+ SANDBOX_ASSERT(prev == sysnum - 1);
+ prev = sysnum;
+ }
+ }
+ SANDBOX_ASSERT(prev == max);
+}
+
+SANDBOX_TEST(SyscallSet, ValidSyscallRanges) {
+ AssertRange(MIN_SYSCALL, MAX_PUBLIC_SYSCALL);
+#if defined(__arm__)
+ AssertRange(MIN_PRIVATE_SYSCALL, MAX_PRIVATE_SYSCALL);
+ AssertRange(MIN_GHOST_SYSCALL, MAX_SYSCALL);
+#endif
+}
+
+SANDBOX_TEST(SyscallSet, InvalidSyscalls) {
+ static const uint32_t kExpected[] = {
+#if defined(__mips__)
+ 0,
+ MIN_SYSCALL - 1,
+#endif
+ MAX_PUBLIC_SYSCALL + 1,
+#if defined(__arm__)
+ MIN_PRIVATE_SYSCALL - 1,
+ MAX_PRIVATE_SYSCALL + 1,
+ MIN_GHOST_SYSCALL - 1,
+ MAX_SYSCALL + 1,
+#endif
+ 0x7FFFFFFFu,
+ 0x80000000u,
+ 0xFFFFFFFFu,
+ };
+
+ for (const SyscallSet& set : kSyscallSets) {
+ size_t i = 0;
+ for (uint32_t sysnum : set) {
+ if (!SyscallSet::IsValid(sysnum)) {
+ SANDBOX_ASSERT(i < arraysize(kExpected));
+ SANDBOX_ASSERT(kExpected[i] == sysnum);
+ ++i;
+ }
+ }
+ SANDBOX_ASSERT(i == arraysize(kExpected));
+ }
+}
+
+SANDBOX_TEST(SyscallSet, ValidOnlyIsOnlyValid) {
+ for (uint32_t sysnum : SyscallSet::ValidOnly()) {
+ SANDBOX_ASSERT(SyscallSet::IsValid(sysnum));
+ }
+}
+
+SANDBOX_TEST(SyscallSet, InvalidOnlyIsOnlyInvalid) {
+ for (uint32_t sysnum : SyscallSet::InvalidOnly()) {
+ SANDBOX_ASSERT(!SyscallSet::IsValid(sysnum));
+ }
+}
+
+SANDBOX_TEST(SyscallSet, AllIsValidOnlyPlusInvalidOnly) {
+ std::vector<uint32_t> merged;
+ const SyscallSet valid_only = SyscallSet::ValidOnly();
+ const SyscallSet invalid_only = SyscallSet::InvalidOnly();
+ std::merge(valid_only.begin(),
+ valid_only.end(),
+ invalid_only.begin(),
+ invalid_only.end(),
+ std::back_inserter(merged));
+
+ const SyscallSet all = SyscallSet::All();
+ SANDBOX_ASSERT(merged == std::vector<uint32_t>(all.begin(), all.end()));
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/verifier.cc b/sandbox/linux/bpf_dsl/verifier.cc
new file mode 100644
index 0000000000..417c663e30
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/verifier.cc
@@ -0,0 +1,396 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/verifier.h"
+
+#include <string.h>
+
+#include <limits>
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+namespace {
+
+const uint64_t kLower32Bits = std::numeric_limits<uint32_t>::max();
+const uint64_t kUpper32Bits = static_cast<uint64_t>(kLower32Bits) << 32;
+
+struct State {
+ State(const std::vector<struct sock_filter>& p,
+ const struct arch_seccomp_data& d)
+ : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {}
+ const std::vector<struct sock_filter>& program;
+ const struct arch_seccomp_data& data;
+ unsigned int ip;
+ uint32_t accumulator;
+ bool acc_is_valid;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(State);
+};
+
+uint32_t EvaluateErrorCode(bpf_dsl::PolicyCompiler* compiler,
+ const ErrorCode& code,
+ const struct arch_seccomp_data& data) {
+ if (code.error_type() == ErrorCode::ET_SIMPLE ||
+ code.error_type() == ErrorCode::ET_TRAP) {
+ return code.err();
+ } else if (code.error_type() == ErrorCode::ET_COND) {
+ if (code.width() == ErrorCode::TP_32BIT &&
+ (data.args[code.argno()] >> 32) &&
+ (data.args[code.argno()] & 0xFFFFFFFF80000000ull) !=
+ 0xFFFFFFFF80000000ull) {
+ return compiler->Unexpected64bitArgument().err();
+ }
+ bool equal = (data.args[code.argno()] & code.mask()) == code.value();
+ return EvaluateErrorCode(compiler, equal ? *code.passed() : *code.failed(),
+ data);
+ } else {
+ return SECCOMP_RET_INVALID;
+ }
+}
+
+bool VerifyErrorCode(bpf_dsl::PolicyCompiler* compiler,
+ const std::vector<struct sock_filter>& program,
+ struct arch_seccomp_data* data,
+ const ErrorCode& root_code,
+ const ErrorCode& code,
+ const char** err) {
+ if (code.error_type() == ErrorCode::ET_SIMPLE ||
+ code.error_type() == ErrorCode::ET_TRAP) {
+ const uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err);
+ if (*err) {
+ return false;
+ }
+ const uint32_t policy_ret = EvaluateErrorCode(compiler, root_code, *data);
+ if (computed_ret != policy_ret) {
+ // For efficiency's sake, we'd much rather compare "computed_ret"
+ // against "code.err()". This works most of the time, but it doesn't
+ // always work for nested conditional expressions. The test values
+ // that we generate on the fly to probe expressions can trigger
+ // code flow decisions in multiple nodes of the decision tree, and the
+ // only way to compute the correct error code in that situation is by
+ // calling EvaluateErrorCode().
+ *err = "Exit code from BPF program doesn't match";
+ return false;
+ }
+ } else if (code.error_type() == ErrorCode::ET_COND) {
+ if (code.argno() < 0 || code.argno() >= 6) {
+ *err = "Invalid argument number in error code";
+ return false;
+ }
+
+ // TODO(mdempsky): The test values generated here try to provide good
+ // coverage for generated BPF instructions while avoiding combinatorial
+ // explosion on large policies. Ideally we would instead take a fuzzing-like
+ // approach and generate a bounded number of test cases regardless of policy
+ // size.
+
+ // Verify that we can check a value for simple equality.
+ data->args[code.argno()] = code.value();
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.passed(),
+ err)) {
+ return false;
+ }
+
+ // If mask ignores any bits, verify that setting those bits is still
+ // detected as equality.
+ uint64_t ignored_bits = ~code.mask();
+ if (code.width() == ErrorCode::TP_32BIT) {
+ ignored_bits = static_cast<uint32_t>(ignored_bits);
+ }
+ if ((ignored_bits & kLower32Bits) != 0) {
+ data->args[code.argno()] = code.value() | (ignored_bits & kLower32Bits);
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.passed(),
+ err)) {
+ return false;
+ }
+ }
+ if ((ignored_bits & kUpper32Bits) != 0) {
+ data->args[code.argno()] = code.value() | (ignored_bits & kUpper32Bits);
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.passed(),
+ err)) {
+ return false;
+ }
+ }
+
+ // Verify that changing bits included in the mask is detected as inequality.
+ if ((code.mask() & kLower32Bits) != 0) {
+ data->args[code.argno()] = code.value() ^ (code.mask() & kLower32Bits);
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.failed(),
+ err)) {
+ return false;
+ }
+ }
+ if ((code.mask() & kUpper32Bits) != 0) {
+ data->args[code.argno()] = code.value() ^ (code.mask() & kUpper32Bits);
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.failed(),
+ err)) {
+ return false;
+ }
+ }
+
+ if (code.width() == ErrorCode::TP_32BIT) {
+ // For 32-bit system call arguments, we emit additional instructions to
+ // validate the upper 32-bits. Here we test that validation.
+
+ // Arbitrary 64-bit values should be rejected.
+ data->args[code.argno()] = 1ULL << 32;
+ if (!VerifyErrorCode(compiler, program, data, root_code,
+ compiler->Unexpected64bitArgument(), err)) {
+ return false;
+ }
+
+ // Upper 32-bits set without the MSB of the lower 32-bits set should be
+ // rejected too.
+ data->args[code.argno()] = kUpper32Bits;
+ if (!VerifyErrorCode(compiler, program, data, root_code,
+ compiler->Unexpected64bitArgument(), err)) {
+ return false;
+ }
+ }
+ } else {
+ *err = "Attempting to return invalid error code from BPF program";
+ return false;
+ }
+ return true;
+}
+
+void Ld(State* state, const struct sock_filter& insn, const char** err) {
+ if (BPF_SIZE(insn.code) != BPF_W || BPF_MODE(insn.code) != BPF_ABS ||
+ insn.jt != 0 || insn.jf != 0) {
+ *err = "Invalid BPF_LD instruction";
+ return;
+ }
+ if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) {
+ // We only allow loading of properly aligned 32bit quantities.
+ memcpy(&state->accumulator,
+ reinterpret_cast<const char*>(&state->data) + insn.k, 4);
+ } else {
+ *err = "Invalid operand in BPF_LD instruction";
+ return;
+ }
+ state->acc_is_valid = true;
+ return;
+}
+
+void Jmp(State* state, const struct sock_filter& insn, const char** err) {
+ if (BPF_OP(insn.code) == BPF_JA) {
+ if (state->ip + insn.k + 1 >= state->program.size() ||
+ state->ip + insn.k + 1 <= state->ip) {
+ compilation_failure:
+ *err = "Invalid BPF_JMP instruction";
+ return;
+ }
+ state->ip += insn.k;
+ } else {
+ if (BPF_SRC(insn.code) != BPF_K || !state->acc_is_valid ||
+ state->ip + insn.jt + 1 >= state->program.size() ||
+ state->ip + insn.jf + 1 >= state->program.size()) {
+ goto compilation_failure;
+ }
+ switch (BPF_OP(insn.code)) {
+ case BPF_JEQ:
+ if (state->accumulator == insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JGT:
+ if (state->accumulator > insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JGE:
+ if (state->accumulator >= insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JSET:
+ if (state->accumulator & insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ default:
+ goto compilation_failure;
+ }
+ }
+}
+
+uint32_t Ret(State*, const struct sock_filter& insn, const char** err) {
+ if (BPF_SRC(insn.code) != BPF_K) {
+ *err = "Invalid BPF_RET instruction";
+ return 0;
+ }
+ return insn.k;
+}
+
+void Alu(State* state, const struct sock_filter& insn, const char** err) {
+ if (BPF_OP(insn.code) == BPF_NEG) {
+ state->accumulator = -state->accumulator;
+ return;
+ } else {
+ if (BPF_SRC(insn.code) != BPF_K) {
+ *err = "Unexpected source operand in arithmetic operation";
+ return;
+ }
+ switch (BPF_OP(insn.code)) {
+ case BPF_ADD:
+ state->accumulator += insn.k;
+ break;
+ case BPF_SUB:
+ state->accumulator -= insn.k;
+ break;
+ case BPF_MUL:
+ state->accumulator *= insn.k;
+ break;
+ case BPF_DIV:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator /= insn.k;
+ break;
+ case BPF_MOD:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator %= insn.k;
+ break;
+ case BPF_OR:
+ state->accumulator |= insn.k;
+ break;
+ case BPF_XOR:
+ state->accumulator ^= insn.k;
+ break;
+ case BPF_AND:
+ state->accumulator &= insn.k;
+ break;
+ case BPF_LSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator <<= insn.k;
+ break;
+ case BPF_RSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator >>= insn.k;
+ break;
+ default:
+ *err = "Invalid operator in arithmetic operation";
+ break;
+ }
+ }
+}
+
+} // namespace
+
+bool Verifier::VerifyBPF(bpf_dsl::PolicyCompiler* compiler,
+ const std::vector<struct sock_filter>& program,
+ const bpf_dsl::Policy& policy,
+ const char** err) {
+ *err = NULL;
+ for (uint32_t sysnum : SyscallSet::All()) {
+ // We ideally want to iterate over the full system call range and values
+ // just above and just below this range. This gives us the full result set
+ // of the "evaluators".
+ // On Intel systems, this can fail in a surprising way, as a cleared bit 30
+ // indicates either i386 or x86-64; and a set bit 30 indicates x32. And
+ // unless we pay attention to setting this bit correctly, an early check in
+ // our BPF program will make us fail with a misleading error code.
+ struct arch_seccomp_data data = {static_cast<int>(sysnum),
+ static_cast<uint32_t>(SECCOMP_ARCH)};
+#if defined(__i386__) || defined(__x86_64__)
+#if defined(__x86_64__) && defined(__ILP32__)
+ if (!(sysnum & 0x40000000u)) {
+ continue;
+ }
+#else
+ if (sysnum & 0x40000000u) {
+ continue;
+ }
+#endif
+#endif
+ ErrorCode code = SyscallSet::IsValid(sysnum)
+ ? policy.EvaluateSyscall(sysnum)->Compile(compiler)
+ : policy.InvalidSyscall()->Compile(compiler);
+ if (!VerifyErrorCode(compiler, program, &data, code, code, err)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
+ const struct arch_seccomp_data& data,
+ const char** err) {
+ *err = NULL;
+ if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
+ *err = "Invalid program length";
+ return 0;
+ }
+ for (State state(program, data); !*err; ++state.ip) {
+ if (state.ip >= program.size()) {
+ *err = "Invalid instruction pointer in BPF program";
+ break;
+ }
+ const struct sock_filter& insn = program[state.ip];
+ switch (BPF_CLASS(insn.code)) {
+ case BPF_LD:
+ Ld(&state, insn, err);
+ break;
+ case BPF_JMP:
+ Jmp(&state, insn, err);
+ break;
+ case BPF_RET: {
+ uint32_t r = Ret(&state, insn, err);
+ switch (r & SECCOMP_RET_ACTION) {
+ case SECCOMP_RET_TRAP:
+ case SECCOMP_RET_ERRNO:
+ case SECCOMP_RET_TRACE:
+ case SECCOMP_RET_ALLOW:
+ break;
+ case SECCOMP_RET_KILL: // We don't ever generate this
+ case SECCOMP_RET_INVALID: // Should never show up in BPF program
+ default:
+ *err = "Unexpected return code found in BPF program";
+ return 0;
+ }
+ return r;
+ }
+ case BPF_ALU:
+ Alu(&state, insn, err);
+ break;
+ default:
+ *err = "Unexpected instruction in BPF program";
+ break;
+ }
+ }
+ return 0;
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/verifier.h b/sandbox/linux/bpf_dsl/verifier.h
new file mode 100644
index 0000000000..b0435d1aa1
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/verifier.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_VERIFIER_H__
+#define SANDBOX_LINUX_BPF_DSL_VERIFIER_H__
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+struct sock_filter;
+
+namespace sandbox {
+struct arch_seccomp_data;
+
+namespace bpf_dsl {
+class Policy;
+class PolicyCompiler;
+
+class SANDBOX_EXPORT Verifier {
+ public:
+ // Evaluate the BPF program for all possible inputs and verify that it
+ // computes the correct result. We use the "evaluators" to determine
+ // the full set of possible inputs that we have to iterate over.
+ // Returns success, if the BPF filter accurately reflects the rules
+ // set by the "evaluators".
+ // Upon success, "err" is set to NULL. Upon failure, it contains a static
+ // error message that does not need to be free()'d.
+ static bool VerifyBPF(bpf_dsl::PolicyCompiler* compiler,
+ const std::vector<struct sock_filter>& program,
+ const bpf_dsl::Policy& policy,
+ const char** err);
+
+ // Evaluate a given BPF program for a particular set of system call
+ // parameters. If evaluation failed for any reason, "err" will be set to
+ // a non-NULL error string. Otherwise, the BPF program's result will be
+ // returned by the function and "err" is NULL.
+ // We do not actually implement the full BPF state machine, but only the
+ // parts that can actually be generated by our BPF compiler. If this code
+ // is used for purposes other than verifying the output of the sandbox's
+ // BPF compiler, we might have to extend this BPF interpreter.
+ static uint32_t EvaluateBPF(const std::vector<struct sock_filter>& program,
+ const struct arch_seccomp_data& data,
+ const char** err);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_VERIFIER_H__
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
new file mode 100644
index 0000000000..8c679a3d41
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -0,0 +1,270 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
+
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+// Changing this implementation will have an effect on *all* policies.
+// Currently this means: Renderer/Worker, GPU, Flash and NaCl.
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Arg;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::If;
+using sandbox::bpf_dsl::ResultExpr;
+
+namespace sandbox {
+
+namespace {
+
+bool IsBaselinePolicyAllowed(int sysno) {
+ return SyscallSets::IsAllowedAddressSpaceAccess(sysno) ||
+ SyscallSets::IsAllowedBasicScheduler(sysno) ||
+ SyscallSets::IsAllowedEpoll(sysno) ||
+ SyscallSets::IsAllowedFileSystemAccessViaFd(sysno) ||
+ SyscallSets::IsAllowedFutex(sysno) ||
+ SyscallSets::IsAllowedGeneralIo(sysno) ||
+ SyscallSets::IsAllowedGetOrModifySocket(sysno) ||
+ SyscallSets::IsAllowedGettime(sysno) ||
+ SyscallSets::IsAllowedProcessStartOrDeath(sysno) ||
+ SyscallSets::IsAllowedSignalHandling(sysno) ||
+ SyscallSets::IsGetSimpleId(sysno) ||
+ SyscallSets::IsKernelInternalApi(sysno) ||
+#if defined(__arm__)
+ SyscallSets::IsArmPrivate(sysno) ||
+#endif
+#if defined(__mips__)
+ SyscallSets::IsMipsPrivate(sysno) ||
+#endif
+ SyscallSets::IsAllowedOperationOnFd(sysno);
+}
+
+// System calls that will trigger the crashing SIGSYS handler.
+bool IsBaselinePolicyWatched(int sysno) {
+ return SyscallSets::IsAdminOperation(sysno) ||
+ SyscallSets::IsAdvancedScheduler(sysno) ||
+ SyscallSets::IsAdvancedTimer(sysno) ||
+ SyscallSets::IsAsyncIo(sysno) ||
+ SyscallSets::IsDebug(sysno) ||
+ SyscallSets::IsEventFd(sysno) ||
+ SyscallSets::IsExtendedAttributes(sysno) ||
+ SyscallSets::IsFaNotify(sysno) ||
+ SyscallSets::IsFsControl(sysno) ||
+ SyscallSets::IsGlobalFSViewChange(sysno) ||
+ SyscallSets::IsGlobalProcessEnvironment(sysno) ||
+ SyscallSets::IsGlobalSystemStatus(sysno) ||
+ SyscallSets::IsInotify(sysno) ||
+ SyscallSets::IsKernelModule(sysno) ||
+ SyscallSets::IsKeyManagement(sysno) ||
+ SyscallSets::IsKill(sysno) ||
+ SyscallSets::IsMessageQueue(sysno) ||
+ SyscallSets::IsMisc(sysno) ||
+#if defined(__x86_64__)
+ SyscallSets::IsNetworkSocketInformation(sysno) ||
+#endif
+ SyscallSets::IsNuma(sysno) ||
+ SyscallSets::IsPrctl(sysno) ||
+ SyscallSets::IsProcessGroupOrSession(sysno) ||
+#if defined(__i386__) || defined(__mips__)
+ SyscallSets::IsSocketCall(sysno) ||
+#endif
+#if defined(__arm__)
+ SyscallSets::IsArmPciConfig(sysno) ||
+#endif
+#if defined(__mips__)
+ SyscallSets::IsMipsMisc(sysno) ||
+#endif
+ SyscallSets::IsTimer(sysno);
+}
+
+// |fs_denied_errno| is the errno return for denied filesystem access.
+ResultExpr EvaluateSyscallImpl(int fs_denied_errno,
+ pid_t current_pid,
+ int sysno) {
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
+ defined(MEMORY_SANITIZER)
+ // TCGETS is required by the sanitizers on failure.
+ if (sysno == __NR_ioctl) {
+ return RestrictIoctl();
+ }
+
+ if (sysno == __NR_sched_getaffinity) {
+ return Allow();
+ }
+
+ // Used when RSS limiting is enabled in sanitizers.
+ if (sysno == __NR_getrusage) {
+ return RestrictGetrusage();
+ }
+
+ if (sysno == __NR_sigaltstack) {
+ // Required for better stack overflow detection in ASan. Disallowed in
+ // non-ASan builds.
+ return Allow();
+ }
+#endif // defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||
+ // defined(MEMORY_SANITIZER)
+
+ if (IsBaselinePolicyAllowed(sysno)) {
+ return Allow();
+ }
+
+#if defined(OS_ANDROID)
+ // Needed for thread creation.
+ if (sysno == __NR_sigaltstack)
+ return Allow();
+#endif
+
+ if (sysno == __NR_clock_gettime) {
+ return RestrictClockID();
+ }
+
+ if (sysno == __NR_clone) {
+ return RestrictCloneToThreadsAndEPERMFork();
+ }
+
+ if (sysno == __NR_fcntl)
+ return RestrictFcntlCommands();
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ if (sysno == __NR_fcntl64)
+ return RestrictFcntlCommands();
+#endif
+
+#if !defined(__aarch64__)
+ // fork() is never used as a system call (clone() is used instead), but we
+ // have seen it in fallback code on Android.
+ if (sysno == __NR_fork) {
+ return Error(EPERM);
+ }
+#endif
+
+ if (sysno == __NR_futex)
+ return RestrictFutex();
+
+ if (sysno == __NR_set_robust_list)
+ return Error(EPERM);
+
+ if (sysno == __NR_getpriority || sysno ==__NR_setpriority)
+ return RestrictGetSetpriority(current_pid);
+
+ if (sysno == __NR_madvise) {
+ // Only allow MADV_DONTNEED (aka MADV_FREE).
+ const Arg<int> advice(2);
+ return If(advice == MADV_DONTNEED, Allow()).Else(Error(EPERM));
+ }
+
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ if (sysno == __NR_mmap)
+ return RestrictMmapFlags();
+#endif
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ if (sysno == __NR_mmap2)
+ return RestrictMmapFlags();
+#endif
+
+ if (sysno == __NR_mprotect)
+ return RestrictMprotectFlags();
+
+ if (sysno == __NR_prctl)
+ return RestrictPrctl();
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ if (sysno == __NR_socketpair) {
+ // Only allow AF_UNIX, PF_UNIX. Crash if anything else is seen.
+ static_assert(AF_UNIX == PF_UNIX,
+ "af_unix and pf_unix should not be different");
+ const Arg<int> domain(0);
+ return If(domain == AF_UNIX, Allow()).Else(CrashSIGSYS());
+ }
+#endif
+
+ if (SyscallSets::IsKill(sysno)) {
+ return RestrictKillTarget(current_pid, sysno);
+ }
+
+ if (SyscallSets::IsFileSystem(sysno) ||
+ SyscallSets::IsCurrentDirectory(sysno)) {
+ return Error(fs_denied_errno);
+ }
+
+ if (SyscallSets::IsSeccomp(sysno))
+ return Error(EPERM);
+
+ if (SyscallSets::IsAnySystemV(sysno)) {
+ return Error(EPERM);
+ }
+
+ if (SyscallSets::IsUmask(sysno) ||
+ SyscallSets::IsDeniedFileSystemAccessViaFd(sysno) ||
+ SyscallSets::IsDeniedGetOrModifySocket(sysno) ||
+ SyscallSets::IsProcessPrivilegeChange(sysno)) {
+ return Error(EPERM);
+ }
+
+#if defined(__i386__) || defined(__mips__)
+ if (SyscallSets::IsSocketCall(sysno))
+ return RestrictSocketcallCommand();
+#endif
+
+ if (IsBaselinePolicyWatched(sysno)) {
+ // Previously unseen syscalls. TODO(jln): some of these should
+ // be denied gracefully right away.
+ return CrashSIGSYS();
+ }
+
+ // In any other case crash the program with our SIGSYS handler.
+ return CrashSIGSYS();
+}
+
+} // namespace.
+
+// Unfortunately C++03 doesn't allow delegated constructors.
+// Call other constructor when C++11 lands.
+BaselinePolicy::BaselinePolicy() : BaselinePolicy(EPERM) {}
+
+BaselinePolicy::BaselinePolicy(int fs_denied_errno)
+ : fs_denied_errno_(fs_denied_errno), policy_pid_(sys_getpid()) {
+}
+
+BaselinePolicy::~BaselinePolicy() {
+ // Make sure that this policy is created, used and destroyed by a single
+ // process.
+ DCHECK_EQ(sys_getpid(), policy_pid_);
+}
+
+ResultExpr BaselinePolicy::EvaluateSyscall(int sysno) const {
+ // Sanity check that we're only called with valid syscall numbers.
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Make sure that this policy is used in the creating process.
+ if (1 == sysno) {
+ DCHECK_EQ(sys_getpid(), policy_pid_);
+ }
+ return EvaluateSyscallImpl(fs_denied_errno_, policy_pid_, sysno);
+}
+
+ResultExpr BaselinePolicy::InvalidSyscall() const {
+ return CrashSIGSYS();
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h
new file mode 100644
index 0000000000..4169d9c3e2
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This is a helper to build seccomp-bpf policies, i.e. policies for a sandbox
+// that reduces the Linux kernel's attack surface. Given its nature, it doesn't
+// have a clear semantics and is mostly "implementation-defined".
+//
+// This class implements the Policy interface with a "baseline"
+// policy for use within Chromium.
+// The "baseline" policy is somewhat arbitrary. All Chromium policies are an
+// alteration of it, and it represents a reasonable common ground to run most
+// code in a sandboxed environment.
+// A baseline policy is only valid for the process for which this object was
+// instantiated (so do not fork() and use it in a child).
+class SANDBOX_EXPORT BaselinePolicy : public bpf_dsl::Policy {
+ public:
+ BaselinePolicy();
+ // |fs_denied_errno| is the errno returned when a filesystem access system
+ // call is denied.
+ explicit BaselinePolicy(int fs_denied_errno);
+ ~BaselinePolicy() override;
+
+ bpf_dsl::ResultExpr EvaluateSyscall(int system_call_number) const override;
+ bpf_dsl::ResultExpr InvalidSyscall() const override;
+ pid_t policy_pid() const { return policy_pid_; }
+
+ private:
+ int fs_denied_errno_;
+
+ // The PID that the policy applies to (should be equal to the current pid).
+ pid_t policy_pid_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaselinePolicy);
+};
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
new file mode 100644
index 0000000000..614849f61c
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -0,0 +1,334 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/system_headers/linux_futex.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+// This also tests that read(), write() and fstat() are allowed.
+void TestPipeOrSocketPair(base::ScopedFD read_end, base::ScopedFD write_end) {
+ BPF_ASSERT_LE(0, read_end.get());
+ BPF_ASSERT_LE(0, write_end.get());
+ struct stat stat_buf;
+ int sys_ret = fstat(read_end.get(), &stat_buf);
+ BPF_ASSERT_EQ(0, sys_ret);
+ BPF_ASSERT(S_ISFIFO(stat_buf.st_mode) || S_ISSOCK(stat_buf.st_mode));
+
+ const ssize_t kTestTransferSize = 4;
+ static const char kTestString[kTestTransferSize] = {'T', 'E', 'S', 'T'};
+ ssize_t transfered = 0;
+
+ transfered =
+ HANDLE_EINTR(write(write_end.get(), kTestString, kTestTransferSize));
+ BPF_ASSERT_EQ(kTestTransferSize, transfered);
+ char read_buf[kTestTransferSize + 1] = {0};
+ transfered = HANDLE_EINTR(read(read_end.get(), read_buf, sizeof(read_buf)));
+ BPF_ASSERT_EQ(kTestTransferSize, transfered);
+ BPF_ASSERT_EQ(0, memcmp(kTestString, read_buf, kTestTransferSize));
+}
+
+// Test that a few easy-to-test system calls are allowed.
+BPF_TEST_C(BaselinePolicy, BaselinePolicyBasicAllowed, BaselinePolicy) {
+ BPF_ASSERT_EQ(0, sched_yield());
+
+ int pipefd[2];
+ int sys_ret = pipe(pipefd);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(pipefd[0]), base::ScopedFD(pipefd[1]));
+
+ BPF_ASSERT_LE(1, getpid());
+ BPF_ASSERT_LE(0, getuid());
+}
+
+BPF_TEST_C(BaselinePolicy, FchmodErrno, BaselinePolicy) {
+ int ret = fchmod(-1, 07777);
+ BPF_ASSERT_EQ(-1, ret);
+ // Without the sandbox, this would EBADF instead.
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkErrno, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = fork();
+ const int fork_errno = errno;
+ TestUtils::HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+pid_t ForkX86Glibc() {
+ static pid_t ptid;
+ return sys_clone(CLONE_PARENT_SETTID | SIGCHLD, nullptr, &ptid, nullptr,
+ nullptr);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkX86Eperm, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = ForkX86Glibc();
+ const int fork_errno = errno;
+ TestUtils::HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+pid_t ForkARMGlibc() {
+ static pid_t ctid;
+ return sys_clone(CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, nullptr,
+ nullptr, &ctid, nullptr);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkArmEperm, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = ForkARMGlibc();
+ const int fork_errno = errno;
+ TestUtils::HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+BPF_TEST_C(BaselinePolicy, CreateThread, BaselinePolicy) {
+ base::Thread thread("sandbox_tests");
+ BPF_ASSERT(thread.Start());
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ DisallowedCloneFlagCrashes,
+ DEATH_SEGV_MESSAGE(GetCloneErrorMessageContentForTests()),
+ BaselinePolicy) {
+ pid_t pid = sys_clone(CLONE_THREAD | SIGCHLD);
+ TestUtils::HandlePostForkReturn(pid);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ DisallowedKillCrashes,
+ DEATH_SEGV_MESSAGE(GetKillErrorMessageContentForTests()),
+ BaselinePolicy) {
+ BPF_ASSERT_NE(1, getpid());
+ kill(1, 0);
+ _exit(0);
+}
+
+BPF_TEST_C(BaselinePolicy, CanKillSelf, BaselinePolicy) {
+ int sys_ret = kill(getpid(), 0);
+ BPF_ASSERT_EQ(0, sys_ret);
+}
+
+BPF_TEST_C(BaselinePolicy, Socketpair, BaselinePolicy) {
+ int sv[2];
+ int sys_ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sv);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1]));
+
+ sys_ret = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sv);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1]));
+}
+
+// Not all architectures can restrict the domain for socketpair().
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+BPF_DEATH_TEST_C(BaselinePolicy,
+ SocketpairWrongDomain,
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ int sv[2];
+ ignore_result(socketpair(AF_INET, SOCK_STREAM, 0, sv));
+ _exit(1);
+}
+#endif // defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+
+BPF_TEST_C(BaselinePolicy, EPERM_open, BaselinePolicy) {
+ errno = 0;
+ int sys_ret = open("/proc/cpuinfo", O_RDONLY);
+ BPF_ASSERT_EQ(-1, sys_ret);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, EPERM_access, BaselinePolicy) {
+ errno = 0;
+ int sys_ret = access("/proc/cpuinfo", R_OK);
+ BPF_ASSERT_EQ(-1, sys_ret);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, EPERM_getcwd, BaselinePolicy) {
+ errno = 0;
+ char buf[1024];
+ char* cwd = getcwd(buf, sizeof(buf));
+ BPF_ASSERT_EQ(NULL, cwd);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ SIGSYS_InvalidSyscall,
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ Syscall::InvalidCall();
+}
+
+// A failing test using this macro could be problematic since we perform
+// system calls by passing "0" as every argument.
+// The kernel could SIGSEGV the process or the system call itself could reboot
+// the machine. Some thoughts have been given when hand-picking the system
+// calls below to limit any potential side effects outside of the current
+// process.
+#define TEST_BASELINE_SIGSYS(sysno) \
+ BPF_DEATH_TEST_C(BaselinePolicy, \
+ SIGSYS_##sysno, \
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), \
+ BaselinePolicy) { \
+ syscall(sysno, 0, 0, 0, 0, 0, 0); \
+ _exit(1); \
+ }
+
+TEST_BASELINE_SIGSYS(__NR_acct);
+TEST_BASELINE_SIGSYS(__NR_chroot);
+TEST_BASELINE_SIGSYS(__NR_fanotify_init);
+TEST_BASELINE_SIGSYS(__NR_fgetxattr);
+TEST_BASELINE_SIGSYS(__NR_getcpu);
+TEST_BASELINE_SIGSYS(__NR_getitimer);
+TEST_BASELINE_SIGSYS(__NR_init_module);
+TEST_BASELINE_SIGSYS(__NR_io_cancel);
+TEST_BASELINE_SIGSYS(__NR_keyctl);
+TEST_BASELINE_SIGSYS(__NR_mq_open);
+TEST_BASELINE_SIGSYS(__NR_ptrace);
+TEST_BASELINE_SIGSYS(__NR_sched_setaffinity);
+TEST_BASELINE_SIGSYS(__NR_setpgid);
+TEST_BASELINE_SIGSYS(__NR_swapon);
+TEST_BASELINE_SIGSYS(__NR_sysinfo);
+TEST_BASELINE_SIGSYS(__NR_syslog);
+TEST_BASELINE_SIGSYS(__NR_timer_create);
+
+#if !defined(__aarch64__)
+TEST_BASELINE_SIGSYS(__NR_eventfd);
+TEST_BASELINE_SIGSYS(__NR_inotify_init);
+TEST_BASELINE_SIGSYS(__NR_vserver);
+#endif
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithRequeuePriorityInheritence,
+ DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithRequeuePriorityInheritencePrivate,
+ DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI_PRIVATE, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithUnlockPIPrivate,
+ DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_UNLOCK_PI_PRIVATE, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_TEST_C(BaselinePolicy, PrctlDumpable, BaselinePolicy) {
+ const int is_dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
+ BPF_ASSERT(is_dumpable == 1 || is_dumpable == 0);
+ const int prctl_ret = prctl(PR_SET_DUMPABLE, is_dumpable, 0, 0, 0, 0);
+ BPF_ASSERT_EQ(0, prctl_ret);
+}
+
+// Workaround incomplete Android headers.
+#if !defined(PR_CAPBSET_READ)
+#define PR_CAPBSET_READ 23
+#endif
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ PrctlSigsys,
+ DEATH_SEGV_MESSAGE(GetPrctlErrorMessageContentForTests()),
+ BaselinePolicy) {
+ prctl(PR_CAPBSET_READ, 0, 0, 0, 0);
+ _exit(1);
+}
+
+BPF_TEST_C(BaselinePolicy, GetOrSetPriority, BaselinePolicy) {
+ errno = 0;
+ const int original_prio = getpriority(PRIO_PROCESS, 0);
+ // Check errno instead of the return value since this system call can return
+ // -1 as a valid value.
+ BPF_ASSERT_EQ(0, errno);
+
+ errno = 0;
+ int rc = getpriority(PRIO_PROCESS, getpid());
+ BPF_ASSERT_EQ(0, errno);
+
+ rc = getpriority(PRIO_PROCESS, getpid() + 1);
+ BPF_ASSERT_EQ(-1, rc);
+ BPF_ASSERT_EQ(EPERM, errno);
+
+ rc = setpriority(PRIO_PROCESS, 0, original_prio);
+ BPF_ASSERT_EQ(0, rc);
+
+ rc = setpriority(PRIO_PROCESS, getpid(), original_prio);
+ BPF_ASSERT_EQ(0, rc);
+
+ errno = 0;
+ rc = setpriority(PRIO_PROCESS, getpid() + 1, original_prio);
+ BPF_ASSERT_EQ(-1, rc);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ GetPrioritySigsys,
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ getpriority(PRIO_USER, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ ClockGettimeWithDisallowedClockCrashes,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
new file mode 100644
index 0000000000..05250d147f
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note: any code in this file MUST be async-signal safe.
+
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+#if defined(__mips__)
+// __NR_Linux, is defined in <asm/unistd.h>.
+#include <asm/unistd.h>
+#endif
+
+#define SECCOMP_MESSAGE_COMMON_CONTENT "seccomp-bpf failure"
+#define SECCOMP_MESSAGE_CLONE_CONTENT "clone() failure"
+#define SECCOMP_MESSAGE_PRCTL_CONTENT "prctl() failure"
+#define SECCOMP_MESSAGE_IOCTL_CONTENT "ioctl() failure"
+#define SECCOMP_MESSAGE_KILL_CONTENT "(tg)kill() failure"
+#define SECCOMP_MESSAGE_FUTEX_CONTENT "futex() failure"
+
+namespace {
+
+inline bool IsArchitectureX86_64() {
+#if defined(__x86_64__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+// Write |error_message| to stderr. Similar to RawLog(), but a bit more careful
+// about async-signal safety. |size| is the size to write and should typically
+// not include a terminating \0.
+void WriteToStdErr(const char* error_message, size_t size) {
+ while (size > 0) {
+ // TODO(jln): query the current policy to check if send() is available and
+ // use it to perform a non-blocking write.
+ const int ret = HANDLE_EINTR(write(STDERR_FILENO, error_message, size));
+ // We can't handle any type of error here.
+ if (ret <= 0 || static_cast<size_t>(ret) > size) break;
+ size -= ret;
+ error_message += ret;
+ }
+}
+
+// Invalid syscall values are truncated to zero.
+// On architectures where base value is zero (Intel and Arm),
+// syscall number is the same as offset from base.
+// This function returns values between 0 and 1023 on all architectures.
+// On architectures where base value is different than zero (currently only
+// Mips), we are truncating valid syscall values to offset from base.
+uint32_t SyscallNumberToOffsetFromBase(uint32_t sysno) {
+#if defined(__mips__)
+ // On MIPS syscall numbers are in different range than on x86 and ARM.
+ // Valid MIPS O32 ABI syscall __NR_syscall will be truncated to zero for
+ // simplicity.
+ sysno = sysno - __NR_Linux;
+#endif
+
+ if (sysno >= 1024)
+ sysno = 0;
+
+ return sysno;
+}
+
+// Print a seccomp-bpf failure to handle |sysno| to stderr in an
+// async-signal safe way.
+void PrintSyscallError(uint32_t sysno) {
+ if (sysno >= 1024)
+ sysno = 0;
+ // TODO(markus): replace with async-signal safe snprintf when available.
+ const size_t kNumDigits = 4;
+ char sysno_base10[kNumDigits];
+ uint32_t rem = sysno;
+ uint32_t mod = 0;
+ for (int i = kNumDigits - 1; i >= 0; i--) {
+ mod = rem % 10;
+ rem /= 10;
+ sysno_base10[i] = '0' + mod;
+ }
+#if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+ static const char kSeccompErrorPrefix[] = __FILE__
+ ":**CRASHING**:" SECCOMP_MESSAGE_COMMON_CONTENT " in syscall 4000 + ";
+#else
+ static const char kSeccompErrorPrefix[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_COMMON_CONTENT " in syscall ";
+#endif
+ static const char kSeccompErrorPostfix[] = "\n";
+ WriteToStdErr(kSeccompErrorPrefix, sizeof(kSeccompErrorPrefix) - 1);
+ WriteToStdErr(sysno_base10, sizeof(sysno_base10));
+ WriteToStdErr(kSeccompErrorPostfix, sizeof(kSeccompErrorPostfix) - 1);
+}
+
+} // namespace.
+
+namespace sandbox {
+
+intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux) {
+ uint32_t syscall = SyscallNumberToOffsetFromBase(args.nr);
+
+ PrintSyscallError(syscall);
+
+ // Encode 8-bits of the 1st two arguments too, so we can discern which socket
+ // type, which fcntl, ... etc., without being likely to hit a mapped
+ // address.
+ // Do not encode more bits here without thinking about increasing the
+ // likelihood of collision with mapped pages.
+ syscall |= ((args.args[0] & 0xffUL) << 12);
+ syscall |= ((args.args[1] & 0xffUL) << 20);
+ // Purposefully dereference the syscall as an address so it'll show up very
+ // clearly and easily in crash dumps.
+ volatile char* addr = reinterpret_cast<volatile char*>(syscall);
+ *addr = '\0';
+ // In case we hit a mapped address, hit the null page with just the syscall,
+ // for paranoia.
+ syscall &= 0xfffUL;
+ addr = reinterpret_cast<volatile char*>(syscall);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+// TODO(jln): refactor the reporting functions.
+
+intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux) {
+ static const char kSeccompCloneError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_CLONE_CONTENT "\n";
+ WriteToStdErr(kSeccompCloneError, sizeof(kSeccompCloneError) - 1);
+ // "flags" is the first argument in the kernel's clone().
+ // Mark as volatile to be able to find the value on the stack in a minidump.
+ volatile uint64_t clone_flags = args.args[0];
+ volatile char* addr;
+ if (IsArchitectureX86_64()) {
+ addr = reinterpret_cast<volatile char*>(clone_flags & 0xFFFFFF);
+ *addr = '\0';
+ }
+ // Hit the NULL page if this fails to fault.
+ addr = reinterpret_cast<volatile char*>(clone_flags & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompPrctlError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_PRCTL_CONTENT "\n";
+ WriteToStdErr(kSeccompPrctlError, sizeof(kSeccompPrctlError) - 1);
+ // Mark as volatile to be able to find the value on the stack in a minidump.
+ volatile uint64_t option = args.args[0];
+ volatile char* addr =
+ reinterpret_cast<volatile char*>(option & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompIoctlError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_IOCTL_CONTENT "\n";
+ WriteToStdErr(kSeccompIoctlError, sizeof(kSeccompIoctlError) - 1);
+ // Make "request" volatile so that we can see it on the stack in a minidump.
+ volatile uint64_t request = args.args[1];
+ volatile char* addr = reinterpret_cast<volatile char*>(request & 0xFFFF);
+ *addr = '\0';
+ // Hit the NULL page if this fails.
+ addr = reinterpret_cast<volatile char*>(request & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSKillFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompKillError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_KILL_CONTENT "\n";
+ WriteToStdErr(kSeccompKillError, sizeof(kSeccompKillError) - 1);
+ // Make "pid" volatile so that we can see it on the stack in a minidump.
+ volatile uint64_t my_pid = sys_getpid();
+ volatile char* addr = reinterpret_cast<volatile char*>(my_pid & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSFutexFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompFutexError[] =
+ __FILE__ ":**CRASHING**:" SECCOMP_MESSAGE_FUTEX_CONTENT "\n";
+ WriteToStdErr(kSeccompFutexError, sizeof(kSeccompFutexError) - 1);
+ volatile int futex_op = args.args[1];
+ volatile char* addr = reinterpret_cast<volatile char*>(futex_op & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSSchedHandler(const struct arch_seccomp_data& args,
+ void* aux) {
+ switch (args.nr) {
+ case __NR_sched_getaffinity:
+ case __NR_sched_getattr:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_rr_get_interval:
+ case __NR_sched_setaffinity:
+ case __NR_sched_setattr:
+ case __NR_sched_setparam:
+ case __NR_sched_setscheduler:
+ const pid_t tid = sys_gettid();
+ // The first argument is the pid. If is our thread id, then replace it
+ // with 0, which is equivalent and allowed by the policy.
+ if (args.args[0] == static_cast<uint64_t>(tid)) {
+ return Syscall::Call(args.nr,
+ 0,
+ static_cast<intptr_t>(args.args[1]),
+ static_cast<intptr_t>(args.args[2]),
+ static_cast<intptr_t>(args.args[3]),
+ static_cast<intptr_t>(args.args[4]),
+ static_cast<intptr_t>(args.args[5]));
+ }
+ break;
+ }
+
+ CrashSIGSYS_Handler(args, aux);
+
+ // Should never be reached.
+ RAW_CHECK(false);
+ return -ENOSYS;
+}
+
+bpf_dsl::ResultExpr CrashSIGSYS() {
+ return bpf_dsl::Trap(CrashSIGSYS_Handler, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSClone() {
+ return bpf_dsl::Trap(SIGSYSCloneFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSPrctl() {
+ return bpf_dsl::Trap(SIGSYSPrctlFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSIoctl() {
+ return bpf_dsl::Trap(SIGSYSIoctlFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSKill() {
+ return bpf_dsl::Trap(SIGSYSKillFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSFutex() {
+ return bpf_dsl::Trap(SIGSYSFutexFailure, NULL);
+}
+
+bpf_dsl::ResultExpr RewriteSchedSIGSYS() {
+ return bpf_dsl::Trap(SIGSYSSchedHandler, NULL);
+}
+
+const char* GetErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_COMMON_CONTENT;
+}
+
+const char* GetCloneErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_CLONE_CONTENT;
+}
+
+const char* GetPrctlErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_PRCTL_CONTENT;
+}
+
+const char* GetIoctlErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_IOCTL_CONTENT;
+}
+
+const char* GetKillErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_KILL_CONTENT;
+}
+
+const char* GetFutexErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_FUTEX_CONTENT;
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
new file mode 100644
index 0000000000..c64e994172
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_
+
+#include <stdint.h>
+
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/sandbox_export.h"
+
+// The handlers are suitable for use in Trap() error codes. They are
+// guaranteed to be async-signal safe.
+// See sandbox/linux/seccomp-bpf/trap.h to see how they work.
+
+namespace sandbox {
+
+struct arch_seccomp_data;
+
+// This handler will crash the currently running process. The crashing address
+// will be the number of the current system call, extracted from |args|.
+// This handler will also print to stderr the number of the crashing syscall.
+SANDBOX_EXPORT intptr_t
+ CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux);
+
+// The following three handlers are suitable to report failures with the
+// clone(), prctl() and ioctl() system calls respectively.
+
+// The crashing address will be (clone_flags & 0xFFFFFF), where clone_flags is
+// the clone(2) argument, extracted from |args|.
+SANDBOX_EXPORT intptr_t
+ SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (option & 0xFFF), where option is the prctl(2)
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSPrctlFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be request & 0xFFFF, where request is the ioctl(2)
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSIoctlFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (pid & 0xFFF), where pid is the first
+// argument (and can be a tid).
+SANDBOX_EXPORT intptr_t
+ SIGSYSKillFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (op & 0xFFF), where op is the second
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSFutexFailure(const struct arch_seccomp_data& args, void* aux);
+// If the syscall is not being called on the current tid, crashes in the same
+// way as CrashSIGSYS_Handler. Otherwise, returns the result of calling the
+// syscall with the pid argument set to 0 (which for these calls means the
+// current thread). The following syscalls are supported:
+//
+// sched_getaffinity(), sched_getattr(), sched_getparam(), sched_getscheduler(),
+// sched_rr_get_interval(), sched_setaffinity(), sched_setattr(),
+// sched_setparam(), sched_setscheduler()
+SANDBOX_EXPORT intptr_t
+ SIGSYSSchedHandler(const struct arch_seccomp_data& args, void* aux);
+
+// Variants of the above functions for use with bpf_dsl.
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYS();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSClone();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSPrctl();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSIoctl();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSKill();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSFutex();
+SANDBOX_EXPORT bpf_dsl::ResultExpr RewriteSchedSIGSYS();
+
+// Following four functions return substrings of error messages used
+// in the above four functions. They are useful in death tests.
+SANDBOX_EXPORT const char* GetErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetCloneErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetPrctlErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetIoctlErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetKillErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetFutexErrorMessageContentForTests();
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
new file mode 100644
index 0000000000..58ffb843a8
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -0,0 +1,319 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <fcntl.h>
+#include <linux/net.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/system_headers/linux_futex.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/system_headers/linux_time.h"
+
+// PNaCl toolchain does not provide sys/ioctl.h header.
+#if !defined(OS_NACL_NONSFI)
+#include <sys/ioctl.h>
+#endif
+
+#if defined(OS_ANDROID)
+
+#if !defined(F_DUPFD_CLOEXEC)
+#define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
+#endif
+
+// https://android.googlesource.com/platform/bionic/+/lollipop-release/libc/private/bionic_prctl.h
+#if !defined(PR_SET_VMA)
+#define PR_SET_VMA 0x53564d41
+#endif
+
+// https://android.googlesource.com/platform/system/core/+/lollipop-release/libcutils/sched_policy.c
+#if !defined(PR_SET_TIMERSLACK_PID)
+#define PR_SET_TIMERSLACK_PID 41
+#endif
+
+#endif // defined(OS_ANDROID)
+
+#if defined(__arm__) && !defined(MAP_STACK)
+#define MAP_STACK 0x20000 // Daisy build environment has old headers.
+#endif
+
+#if defined(__mips__) && !defined(MAP_STACK)
+#define MAP_STACK 0x40000
+#endif
+namespace {
+
+inline bool IsArchitectureX86_64() {
+#if defined(__x86_64__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool IsArchitectureI386() {
+#if defined(__i386__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool IsAndroid() {
+#if defined(OS_ANDROID)
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool IsArchitectureMips() {
+#if defined(__mips__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+} // namespace.
+
+#define CASES SANDBOX_BPF_DSL_CASES
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Arg;
+using sandbox::bpf_dsl::BoolExpr;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::If;
+using sandbox::bpf_dsl::ResultExpr;
+
+namespace sandbox {
+
+#if !defined(OS_NACL_NONSFI)
+// Allow Glibc's and Android pthread creation flags, crash on any other
+// thread creation attempts and EPERM attempts to use neither
+// CLONE_VM, nor CLONE_THREAD, which includes all fork() implementations.
+ResultExpr RestrictCloneToThreadsAndEPERMFork() {
+ const Arg<unsigned long> flags(0);
+
+ // TODO(mdempsky): Extend DSL to support (flags & ~mask1) == mask2.
+ const uint64_t kAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM;
+ const uint64_t kObsoleteAndroidCloneMask = kAndroidCloneMask | CLONE_DETACHED;
+
+ const uint64_t kGlibcPthreadFlags =
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
+ const BoolExpr glibc_test = flags == kGlibcPthreadFlags;
+
+ const BoolExpr android_test = flags == kAndroidCloneMask ||
+ flags == kObsoleteAndroidCloneMask ||
+ flags == kGlibcPthreadFlags;
+
+ return If(IsAndroid() ? android_test : glibc_test, Allow())
+ .ElseIf((flags & (CLONE_VM | CLONE_THREAD)) == 0, Error(EPERM))
+ .Else(CrashSIGSYSClone());
+}
+
+ResultExpr RestrictPrctl() {
+ // Will need to add seccomp compositing in the future. PR_SET_PTRACER is
+ // used by breakpad but not needed anymore.
+ const Arg<int> option(0);
+ return Switch(option)
+ .CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE),
+ Allow())
+#if defined(OS_ANDROID)
+ .CASES((PR_SET_VMA, PR_SET_TIMERSLACK_PID), Allow())
+#endif
+ .Default(CrashSIGSYSPrctl());
+}
+
+ResultExpr RestrictIoctl() {
+ const Arg<int> request(1);
+ return Switch(request).CASES((TCGETS, FIONREAD), Allow()).Default(
+ CrashSIGSYSIoctl());
+}
+
+ResultExpr RestrictMmapFlags() {
+ // The flags you see are actually the allowed ones, and the variable is a
+ // "denied" mask because of the negation operator.
+ // Significantly, we don't permit MAP_HUGETLB, or the newer flags such as
+ // MAP_POPULATE.
+ // TODO(davidung), remove MAP_DENYWRITE with updated Tegra libraries.
+ const uint64_t kAllowedMask = MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS |
+ MAP_STACK | MAP_NORESERVE | MAP_FIXED |
+ MAP_DENYWRITE;
+ const Arg<int> flags(3);
+ return If((flags & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictMprotectFlags() {
+ // The flags you see are actually the allowed ones, and the variable is a
+ // "denied" mask because of the negation operator.
+ // Significantly, we don't permit weird undocumented flags such as
+ // PROT_GROWSDOWN.
+ const uint64_t kAllowedMask = PROT_READ | PROT_WRITE | PROT_EXEC;
+ const Arg<int> prot(2);
+ return If((prot & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictFcntlCommands() {
+ // We also restrict the flags in F_SETFL. We don't want to permit flags with
+ // a history of trouble such as O_DIRECT. The flags you see are actually the
+ // allowed ones, and the variable is a "denied" mask because of the negation
+ // operator.
+ // Glibc overrides the kernel's O_LARGEFILE value. Account for this.
+ uint64_t kOLargeFileFlag = O_LARGEFILE;
+ if (IsArchitectureX86_64() || IsArchitectureI386() || IsArchitectureMips())
+ kOLargeFileFlag = 0100000;
+
+ const Arg<int> cmd(1);
+ const Arg<long> long_arg(2);
+
+ const uint64_t kAllowedMask = O_ACCMODE | O_APPEND | O_NONBLOCK | O_SYNC |
+ kOLargeFileFlag | O_CLOEXEC | O_NOATIME;
+ return Switch(cmd)
+ .CASES((F_GETFL,
+ F_GETFD,
+ F_SETFD,
+ F_SETLK,
+ F_SETLKW,
+ F_GETLK,
+ F_DUPFD,
+ F_DUPFD_CLOEXEC),
+ Allow())
+ .Case(F_SETFL,
+ If((long_arg & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()))
+ .Default(CrashSIGSYS());
+}
+
+#if defined(__i386__) || defined(__mips__)
+ResultExpr RestrictSocketcallCommand() {
+ // Unfortunately, we are unable to restrict the first parameter to
+ // socketpair(2). Whilst initially sounding bad, it's noteworthy that very
+ // few protocols actually support socketpair(2). The scary call that we're
+ // worried about, socket(2), remains blocked.
+ const Arg<int> call(0);
+ return Switch(call)
+ .CASES((SYS_SOCKETPAIR,
+ SYS_SHUTDOWN,
+ SYS_RECV,
+ SYS_SEND,
+ SYS_RECVFROM,
+ SYS_SENDTO,
+ SYS_RECVMSG,
+ SYS_SENDMSG),
+ Allow())
+ .Default(Error(EPERM));
+}
+#endif
+
+ResultExpr RestrictKillTarget(pid_t target_pid, int sysno) {
+ switch (sysno) {
+ case __NR_kill:
+ case __NR_tgkill: {
+ const Arg<pid_t> pid(0);
+ return If(pid == target_pid, Allow()).Else(CrashSIGSYSKill());
+ }
+ case __NR_tkill:
+ return CrashSIGSYSKill();
+ default:
+ NOTREACHED();
+ return CrashSIGSYS();
+ }
+}
+
+ResultExpr RestrictFutex() {
+ const uint64_t kAllowedFutexFlags = FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME;
+ const Arg<int> op(1);
+ return Switch(op & ~kAllowedFutexFlags)
+ .CASES((FUTEX_WAIT,
+ FUTEX_WAKE,
+ FUTEX_REQUEUE,
+ FUTEX_CMP_REQUEUE,
+ FUTEX_WAKE_OP,
+ FUTEX_WAIT_BITSET,
+ FUTEX_WAKE_BITSET),
+ Allow())
+ .Default(CrashSIGSYSFutex());
+}
+
+ResultExpr RestrictGetSetpriority(pid_t target_pid) {
+ const Arg<int> which(0);
+ const Arg<int> who(1);
+ return If(which == PRIO_PROCESS,
+ If(who == 0 || who == target_pid, Allow()).Else(Error(EPERM)))
+ .Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictSchedTarget(pid_t target_pid, int sysno) {
+ switch (sysno) {
+ case __NR_sched_getaffinity:
+ case __NR_sched_getattr:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_rr_get_interval:
+ case __NR_sched_setaffinity:
+ case __NR_sched_setattr:
+ case __NR_sched_setparam:
+ case __NR_sched_setscheduler: {
+ const Arg<pid_t> pid(0);
+ return If(pid == 0 || pid == target_pid, Allow())
+ .Else(RewriteSchedSIGSYS());
+ }
+ default:
+ NOTREACHED();
+ return CrashSIGSYS();
+ }
+}
+
+ResultExpr RestrictPrlimit64(pid_t target_pid) {
+ const Arg<pid_t> pid(0);
+ return If(pid == 0 || pid == target_pid, Allow()).Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictGetrusage() {
+ const Arg<int> who(0);
+ return If(who == RUSAGE_SELF, Allow()).Else(CrashSIGSYS());
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+ResultExpr RestrictClockID() {
+ static_assert(4 == sizeof(clockid_t), "clockid_t is not 32bit");
+ const Arg<clockid_t> clockid(0);
+ return If(
+#if defined(OS_CHROMEOS)
+ // Allow the special clock for Chrome OS used by Chrome tracing.
+ clockid == base::TraceTicks::kClockSystemTrace ||
+#endif
+ clockid == CLOCK_MONOTONIC ||
+ clockid == CLOCK_MONOTONIC_COARSE ||
+ clockid == CLOCK_PROCESS_CPUTIME_ID ||
+ clockid == CLOCK_REALTIME ||
+ clockid == CLOCK_REALTIME_COARSE ||
+ clockid == CLOCK_THREAD_CPUTIME_ID,
+ Allow()).Else(CrashSIGSYS());
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
new file mode 100644
index 0000000000..9eb35d10e0
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
+
+#include <unistd.h>
+
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/sandbox_export.h"
+
+// These are helpers to build seccomp-bpf policies, i.e. policies for a
+// sandbox that reduces the Linux kernel's attack surface. They return a
+// bpf_dsl::ResultExpr suitable to restrict certain system call parameters.
+
+namespace sandbox {
+
+// Allow clone(2) for threads.
+// Reject fork(2) attempts with EPERM.
+// Don't restrict on ASAN.
+// Crash if anything else is attempted.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictCloneToThreadsAndEPERMFork();
+
+// Allow PR_SET_NAME, PR_SET_DUMPABLE, PR_GET_DUMPABLE.
+// Crash if anything else is attempted.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrctl();
+
+// Allow TCGETS and FIONREAD.
+// Crash if anything else is attempted.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictIoctl();
+
+// Restrict the flags argument in mmap(2).
+// Only allow: MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS |
+// MAP_STACK | MAP_NORESERVE | MAP_FIXED | MAP_DENYWRITE.
+// Crash if any other flag is used.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMmapFlags();
+
+// Restrict the prot argument in mprotect(2).
+// Only allow: PROT_READ | PROT_WRITE | PROT_EXEC.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMprotectFlags();
+
+// Restrict fcntl(2) cmd argument to:
+// We allow F_GETFL, F_SETFL, F_GETFD, F_SETFD, F_DUPFD, F_DUPFD_CLOEXEC,
+// F_SETLK, F_SETLKW and F_GETLK.
+// Also, in F_SETFL, restrict the allowed flags to: O_ACCMODE | O_APPEND |
+// O_NONBLOCK | O_SYNC | O_LARGEFILE | O_CLOEXEC | O_NOATIME.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictFcntlCommands();
+
+#if defined(__i386__) || defined(__mips__)
+// Restrict socketcall(2) to only allow socketpair(2), send(2), recv(2),
+// sendto(2), recvfrom(2), shutdown(2), sendmsg(2) and recvmsg(2).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictSocketcallCommand();
+#endif
+
+// Restrict |sysno| (which must be kill, tkill or tgkill) by allowing tgkill or
+// kill iff the first parameter is |target_pid|, crashing otherwise or if
+// |sysno| is tkill.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictKillTarget(pid_t target_pid,
+ int sysno);
+
+// Crash if FUTEX_CMP_REQUEUE_PI is used in the second argument of futex(2).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictFutex();
+
+// Crash if |which| is not PRIO_PROCESS. EPERM if |who| is not 0, neither
+// |target_pid| while calling setpriority(2) / getpriority(2).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictGetSetpriority(pid_t target_pid);
+
+// Restricts |pid| for sched_* syscalls which take a pid as the first argument.
+// We only allow calling these syscalls if the pid argument is equal to the pid
+// of the sandboxed process or 0 (indicating the current thread). The following
+// syscalls are supported:
+//
+// sched_getaffinity(), sched_getattr(), sched_getparam(), sched_getscheduler(),
+// sched_rr_get_interval(), sched_setaffinity(), sched_setattr(),
+// sched_setparam(), sched_setscheduler()
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictSchedTarget(pid_t target_pid,
+ int sysno);
+
+// Restricts the |pid| argument of prlimit64 to 0 (meaning the calling process)
+// or target_pid.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrlimit64(pid_t target_pid);
+
+// Restricts the |who| argument of getrusage to RUSAGE_SELF (meaning the calling
+// process).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictGetrusage();
+
+// Restrict |clk_id| for clock_getres(), clock_gettime() and clock_settime().
+// We allow accessing only CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID,
+// CLOCK_REALTIME, and CLOCK_THREAD_CPUTIME_ID. In particular, this disallows
+// access to arbitrary per-{process,thread} CPU-time clock IDs (such as those
+// returned by {clock,pthread}_getcpuclockid), which can leak information
+// about the state of the host OS.
+// On Chrome OS, base::TraceTicks::kClockSystemTrace is also allowed.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictClockID();
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
new file mode 100644
index 0000000000..aaed480d69
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -0,0 +1,282 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
+
+#include <errno.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/sys_info.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/system_headers/linux_time.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+#if !defined(OS_ANDROID)
+#include "third_party/lss/linux_syscall_support.h" // for MAKE_PROCESS_CPUCLOCK
+#endif
+
+namespace sandbox {
+
+namespace {
+
+// NOTE: most of the parameter restrictions are tested in
+// baseline_policy_unittest.cc as a more end-to-end test.
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::ResultExpr;
+
+class RestrictClockIdPolicy : public bpf_dsl::Policy {
+ public:
+ RestrictClockIdPolicy() {}
+ ~RestrictClockIdPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_clock_gettime:
+ case __NR_clock_getres:
+ return RestrictClockID();
+ default:
+ return Allow();
+ }
+ }
+};
+
+void CheckClock(clockid_t clockid) {
+ struct timespec ts;
+ ts.tv_sec = -1;
+ ts.tv_nsec = -1;
+ BPF_ASSERT_EQ(0, clock_getres(clockid, &ts));
+ BPF_ASSERT_EQ(0, ts.tv_sec);
+ BPF_ASSERT_LE(0, ts.tv_nsec);
+ ts.tv_sec = -1;
+ ts.tv_nsec = -1;
+ BPF_ASSERT_EQ(0, clock_gettime(clockid, &ts));
+ BPF_ASSERT_LE(0, ts.tv_sec);
+ BPF_ASSERT_LE(0, ts.tv_nsec);
+}
+
+BPF_TEST_C(ParameterRestrictions,
+ clock_gettime_allowed,
+ RestrictClockIdPolicy) {
+ CheckClock(CLOCK_MONOTONIC);
+ CheckClock(CLOCK_MONOTONIC_COARSE);
+ CheckClock(CLOCK_PROCESS_CPUTIME_ID);
+ CheckClock(CLOCK_REALTIME);
+ CheckClock(CLOCK_REALTIME_COARSE);
+ CheckClock(CLOCK_THREAD_CPUTIME_ID);
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ clock_gettime_crash_monotonic_raw,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictClockIdPolicy) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+}
+
+#if defined(OS_CHROMEOS)
+
+// A custom BPF tester delegate to run IsRunningOnChromeOS() before
+// the sandbox is enabled because we cannot run it with non-SFI BPF
+// sandbox enabled.
+class ClockSystemTesterDelegate : public sandbox::BPFTesterDelegate {
+ public:
+ ClockSystemTesterDelegate()
+ : is_running_on_chromeos_(base::SysInfo::IsRunningOnChromeOS()) {}
+ ~ClockSystemTesterDelegate() override {}
+
+ scoped_ptr<sandbox::bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ return scoped_ptr<sandbox::bpf_dsl::Policy>(new RestrictClockIdPolicy());
+ }
+ void RunTestFunction() override {
+ if (is_running_on_chromeos_) {
+ CheckClock(base::TraceTicks::kClockSystemTrace);
+ } else {
+ struct timespec ts;
+ // kClockSystemTrace is 11, which is CLOCK_THREAD_CPUTIME_ID of
+ // the init process (pid=1). If kernel supports this feature,
+ // this may succeed even if this is not running on Chrome OS. We
+ // just check this clock_gettime call does not crash.
+ clock_gettime(base::TraceTicks::kClockSystemTrace, &ts);
+ }
+ }
+
+ private:
+ const bool is_running_on_chromeos_;
+ DISALLOW_COPY_AND_ASSIGN(ClockSystemTesterDelegate);
+};
+
+BPF_TEST_D(BPFTest, BPFTestWithDelegateClass, ClockSystemTesterDelegate);
+
+#elif defined(OS_LINUX)
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ clock_gettime_crash_system_trace,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictClockIdPolicy) {
+ struct timespec ts;
+ clock_gettime(base::TraceTicks::kClockSystemTrace, &ts);
+}
+
+#endif // defined(OS_CHROMEOS)
+
+#if !defined(OS_ANDROID)
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ clock_gettime_crash_cpu_clock,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictClockIdPolicy) {
+ // We can't use clock_getcpuclockid() because it's not implemented in newlib,
+ // and it might not work inside the sandbox anyway.
+ const pid_t kInitPID = 1;
+ const clockid_t kInitCPUClockID =
+ MAKE_PROCESS_CPUCLOCK(kInitPID, CPUCLOCK_SCHED);
+
+ struct timespec ts;
+ clock_gettime(kInitCPUClockID, &ts);
+}
+#endif // !defined(OS_ANDROID)
+
+class RestrictSchedPolicy : public bpf_dsl::Policy {
+ public:
+ RestrictSchedPolicy() {}
+ ~RestrictSchedPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_sched_getparam:
+ return RestrictSchedTarget(getpid(), sysno);
+ default:
+ return Allow();
+ }
+ }
+};
+
+void CheckSchedGetParam(pid_t pid, struct sched_param* param) {
+ BPF_ASSERT_EQ(0, sched_getparam(pid, param));
+}
+
+void SchedGetParamThread(base::WaitableEvent* thread_run) {
+ const pid_t pid = getpid();
+ const pid_t tid = sys_gettid();
+ BPF_ASSERT_NE(pid, tid);
+
+ struct sched_param current_pid_param;
+ CheckSchedGetParam(pid, &current_pid_param);
+
+ struct sched_param zero_param;
+ CheckSchedGetParam(0, &zero_param);
+
+ struct sched_param tid_param;
+ CheckSchedGetParam(tid, &tid_param);
+
+ BPF_ASSERT_EQ(zero_param.sched_priority, tid_param.sched_priority);
+
+ // Verify that the SIGSYS handler sets errno properly.
+ errno = 0;
+ BPF_ASSERT_EQ(-1, sched_getparam(tid, NULL));
+ BPF_ASSERT_EQ(EINVAL, errno);
+
+ thread_run->Signal();
+}
+
+BPF_TEST_C(ParameterRestrictions,
+ sched_getparam_allowed,
+ RestrictSchedPolicy) {
+ base::WaitableEvent thread_run(true, false);
+ // Run the actual test in a new thread so that the current pid and tid are
+ // different.
+ base::Thread getparam_thread("sched_getparam_thread");
+ BPF_ASSERT(getparam_thread.Start());
+ getparam_thread.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&SchedGetParamThread, &thread_run));
+ BPF_ASSERT(thread_run.TimedWait(base::TimeDelta::FromMilliseconds(5000)));
+ getparam_thread.Stop();
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ sched_getparam_crash_non_zero,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictSchedPolicy) {
+ const pid_t kInitPID = 1;
+ struct sched_param param;
+ sched_getparam(kInitPID, &param);
+}
+
+class RestrictPrlimit64Policy : public bpf_dsl::Policy {
+ public:
+ RestrictPrlimit64Policy() {}
+ ~RestrictPrlimit64Policy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_prlimit64:
+ return RestrictPrlimit64(getpid());
+ default:
+ return Allow();
+ }
+ }
+};
+
+BPF_TEST_C(ParameterRestrictions, prlimit64_allowed, RestrictPrlimit64Policy) {
+ BPF_ASSERT_EQ(0, sys_prlimit64(0, RLIMIT_AS, NULL, NULL));
+ BPF_ASSERT_EQ(0, sys_prlimit64(getpid(), RLIMIT_AS, NULL, NULL));
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ prlimit64_crash_not_self,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictPrlimit64Policy) {
+ const pid_t kInitPID = 1;
+ BPF_ASSERT_NE(kInitPID, getpid());
+ sys_prlimit64(kInitPID, RLIMIT_AS, NULL, NULL);
+}
+
+class RestrictGetrusagePolicy : public bpf_dsl::Policy {
+ public:
+ RestrictGetrusagePolicy() {}
+ ~RestrictGetrusagePolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_getrusage:
+ return RestrictGetrusage();
+ default:
+ return Allow();
+ }
+ }
+};
+
+BPF_TEST_C(ParameterRestrictions, getrusage_allowed, RestrictGetrusagePolicy) {
+ struct rusage usage;
+ BPF_ASSERT_EQ(0, getrusage(RUSAGE_SELF, &usage));
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ getrusage_crash_not_self,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictGetrusagePolicy) {
+ struct rusage usage;
+ getrusage(RUSAGE_CHILDREN, &usage);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
new file mode 100644
index 0000000000..c217d47e2d
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
@@ -0,0 +1,1060 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
+
+#include "build/build_config.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+
+// The functions below cover all existing i386, x86_64, and ARM system calls;
+// excluding syscalls made obsolete in ARM EABI.
+// The implicitly defined sets form a partition of the sets of
+// system calls.
+
+bool SyscallSets::IsKill(int sysno) {
+ switch (sysno) {
+ case __NR_kill:
+ case __NR_tgkill:
+ case __NR_tkill: // Deprecated.
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedGettime(int sysno) {
+ switch (sysno) {
+ case __NR_gettimeofday:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_time:
+#endif
+ return true;
+ case __NR_adjtimex: // Privileged.
+ case __NR_clock_adjtime: // Privileged.
+ case __NR_clock_getres: // Could be allowed.
+ case __NR_clock_gettime:
+ case __NR_clock_nanosleep: // Could be allowed.
+ case __NR_clock_settime: // Privileged.
+#if defined(__i386__) || defined(__mips__)
+ case __NR_ftime: // Obsolete.
+#endif
+ case __NR_settimeofday: // Privileged.
+#if defined(__i386__) || defined(__mips__)
+ case __NR_stime:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsCurrentDirectory(int sysno) {
+ switch (sysno) {
+ case __NR_getcwd:
+ case __NR_chdir:
+ case __NR_fchdir:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsUmask(int sysno) {
+ switch (sysno) {
+ case __NR_umask:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// System calls that directly access the file system. They might acquire
+// a new file descriptor or otherwise perform an operation directly
+// via a path.
+// Both EPERM and ENOENT are valid errno unless otherwise noted in comment.
+bool SyscallSets::IsFileSystem(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_access: // EPERM not a valid errno.
+ case __NR_chmod:
+ case __NR_chown:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_chown32:
+#endif
+ case __NR_creat:
+ case __NR_futimesat: // Should be called utimesat ?
+ case __NR_lchown:
+ case __NR_link:
+ case __NR_lstat: // EPERM not a valid errno.
+ case __NR_mkdir:
+ case __NR_mknod:
+ case __NR_open:
+ case __NR_readlink: // EPERM not a valid errno.
+ case __NR_rename:
+ case __NR_rmdir:
+ case __NR_stat: // EPERM not a valid errno.
+ case __NR_symlink:
+ case __NR_unlink:
+ case __NR_uselib: // Neither EPERM, nor ENOENT are valid errno.
+ case __NR_ustat: // Same as above. Deprecated.
+ case __NR_utimes:
+#endif // !defined(__aarch64__)
+
+ case __NR_execve:
+ case __NR_faccessat: // EPERM not a valid errno.
+ case __NR_fchmodat:
+ case __NR_fchownat: // Should be called chownat ?
+#if defined(__x86_64__) || defined(__aarch64__)
+ case __NR_newfstatat: // fstatat(). EPERM not a valid errno.
+#elif defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fstatat64:
+#endif
+#if defined(__i386__) || defined(__arm__)
+ case __NR_lchown32:
+#endif
+ case __NR_linkat:
+ case __NR_lookup_dcookie: // ENOENT not a valid errno.
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_lstat64:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
+ case __NR_memfd_create:
+#endif
+ case __NR_mkdirat:
+ case __NR_mknodat:
+#if defined(__i386__)
+ case __NR_oldlstat:
+ case __NR_oldstat:
+#endif
+ case __NR_openat:
+ case __NR_readlinkat:
+ case __NR_renameat:
+ case __NR_renameat2:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_stat64:
+#endif
+ case __NR_statfs: // EPERM not a valid errno.
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_statfs64:
+#endif
+ case __NR_symlinkat:
+ case __NR_truncate:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_truncate64:
+#endif
+ case __NR_unlinkat:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_utime:
+#endif
+ case __NR_utimensat: // New.
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedFileSystemAccessViaFd(int sysno) {
+ switch (sysno) {
+ case __NR_fstat:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fstat64:
+#endif
+ return true;
+// TODO(jln): these should be denied gracefully as well (moved below).
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_fadvise64: // EPERM not a valid errno.
+#endif
+#if defined(__i386__)
+ case __NR_fadvise64_64:
+#endif
+#if defined(__arm__)
+ case __NR_arm_fadvise64_64:
+#endif
+ case __NR_fdatasync: // EPERM not a valid errno.
+ case __NR_flock: // EPERM not a valid errno.
+ case __NR_fstatfs: // Give information about the whole filesystem.
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fstatfs64:
+#endif
+ case __NR_fsync: // EPERM not a valid errno.
+#if defined(__i386__)
+ case __NR_oldfstat:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_sync_file_range: // EPERM not a valid errno.
+#elif defined(__arm__)
+ case __NR_arm_sync_file_range: // EPERM not a valid errno.
+#endif
+ default:
+ return false;
+ }
+}
+
+// EPERM is a good errno for any of these.
+bool SyscallSets::IsDeniedFileSystemAccessViaFd(int sysno) {
+ switch (sysno) {
+ case __NR_fallocate:
+ case __NR_fchmod:
+ case __NR_fchown:
+ case __NR_ftruncate:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_fchown32:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_ftruncate64:
+#endif
+#if !defined(__aarch64__)
+ case __NR_getdents: // EPERM not a valid errno.
+#endif
+ case __NR_getdents64: // EPERM not a valid errno.
+#if defined(__i386__) || defined(__mips__)
+ case __NR_readdir:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGetSimpleId(int sysno) {
+ switch (sysno) {
+ case __NR_capget:
+ case __NR_getegid:
+ case __NR_geteuid:
+ case __NR_getgid:
+ case __NR_getgroups:
+ case __NR_getpid:
+ case __NR_getppid:
+ case __NR_getresgid:
+ case __NR_getsid:
+ case __NR_gettid:
+ case __NR_getuid:
+ case __NR_getresuid:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_getegid32:
+ case __NR_geteuid32:
+ case __NR_getgid32:
+ case __NR_getgroups32:
+ case __NR_getresgid32:
+ case __NR_getresuid32:
+ case __NR_getuid32:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsProcessPrivilegeChange(int sysno) {
+ switch (sysno) {
+ case __NR_capset:
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_ioperm: // Intel privilege.
+ case __NR_iopl: // Intel privilege.
+#endif
+ case __NR_setfsgid:
+ case __NR_setfsuid:
+ case __NR_setgid:
+ case __NR_setgroups:
+ case __NR_setregid:
+ case __NR_setresgid:
+ case __NR_setresuid:
+ case __NR_setreuid:
+ case __NR_setuid:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_setfsgid32:
+ case __NR_setfsuid32:
+ case __NR_setgid32:
+ case __NR_setgroups32:
+ case __NR_setregid32:
+ case __NR_setresgid32:
+ case __NR_setresuid32:
+ case __NR_setreuid32:
+ case __NR_setuid32:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsProcessGroupOrSession(int sysno) {
+ switch (sysno) {
+ case __NR_setpgid:
+#if !defined(__aarch64__)
+ case __NR_getpgrp:
+#endif
+ case __NR_setsid:
+ case __NR_getpgid:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedSignalHandling(int sysno) {
+ switch (sysno) {
+ case __NR_rt_sigaction:
+ case __NR_rt_sigprocmask:
+ case __NR_rt_sigreturn:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_sigaction:
+ case __NR_sigprocmask:
+ case __NR_sigreturn:
+#endif
+ return true;
+ case __NR_rt_sigpending:
+ case __NR_rt_sigqueueinfo:
+ case __NR_rt_sigsuspend:
+ case __NR_rt_sigtimedwait:
+ case __NR_rt_tgsigqueueinfo:
+ case __NR_sigaltstack:
+#if !defined(__aarch64__)
+ case __NR_signalfd:
+#endif
+ case __NR_signalfd4:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_sigpending:
+ case __NR_sigsuspend:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_signal:
+ case __NR_sgetmask: // Obsolete.
+ case __NR_ssetmask:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedOperationOnFd(int sysno) {
+ switch (sysno) {
+ case __NR_close:
+ case __NR_dup:
+#if !defined(__aarch64__)
+ case __NR_dup2:
+#endif
+ case __NR_dup3:
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_shutdown:
+#endif
+ return true;
+ case __NR_fcntl:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fcntl64:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsKernelInternalApi(int sysno) {
+ switch (sysno) {
+ case __NR_restart_syscall:
+#if defined(__arm__)
+ case __ARM_NR_cmpxchg:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+// This should be thought through in conjunction with IsFutex().
+bool SyscallSets::IsAllowedProcessStartOrDeath(int sysno) {
+ switch (sysno) {
+ case __NR_exit:
+ case __NR_exit_group:
+ case __NR_wait4:
+ case __NR_waitid:
+#if defined(__i386__)
+ case __NR_waitpid:
+#endif
+ return true;
+ case __NR_clone: // Should be parameter-restricted.
+ case __NR_setns: // Privileged.
+#if !defined(__aarch64__)
+ case __NR_fork:
+#endif
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_get_thread_area:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_set_thread_area:
+#endif
+ case __NR_set_tid_address:
+ case __NR_unshare:
+#if !defined(__mips__) && !defined(__aarch64__)
+ case __NR_vfork:
+#endif
+ default:
+ return false;
+ }
+}
+
+// It's difficult to restrict those, but there is attack surface here.
+bool SyscallSets::IsAllowedFutex(int sysno) {
+ switch (sysno) {
+ case __NR_get_robust_list:
+ case __NR_set_robust_list:
+ case __NR_futex:
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedEpoll(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_epoll_create:
+ case __NR_epoll_wait:
+#endif
+ case __NR_epoll_create1:
+ case __NR_epoll_ctl:
+ return true;
+ default:
+#if defined(__x86_64__)
+ case __NR_epoll_ctl_old:
+#endif
+ case __NR_epoll_pwait:
+#if defined(__x86_64__)
+ case __NR_epoll_wait_old:
+#endif
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedGetOrModifySocket(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_pipe:
+#endif
+ case __NR_pipe2:
+ return true;
+ default:
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_socketpair: // We will want to inspect its argument.
+#endif
+ return false;
+ }
+}
+
+bool SyscallSets::IsDeniedGetOrModifySocket(int sysno) {
+ switch (sysno) {
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_accept:
+ case __NR_accept4:
+ case __NR_bind:
+ case __NR_connect:
+ case __NR_socket:
+ case __NR_listen:
+ return true;
+#endif
+ default:
+ return false;
+ }
+}
+
+#if defined(__i386__) || defined(__mips__)
+// Big multiplexing system call for sockets.
+bool SyscallSets::IsSocketCall(int sysno) {
+ switch (sysno) {
+ case __NR_socketcall:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__)
+bool SyscallSets::IsNetworkSocketInformation(int sysno) {
+ switch (sysno) {
+ case __NR_getpeername:
+ case __NR_getsockname:
+ case __NR_getsockopt:
+ case __NR_setsockopt:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+bool SyscallSets::IsAllowedAddressSpaceAccess(int sysno) {
+ switch (sysno) {
+ case __NR_brk:
+ case __NR_mlock:
+ case __NR_munlock:
+ case __NR_munmap:
+ return true;
+ case __NR_madvise:
+ case __NR_mincore:
+ case __NR_mlockall:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_mmap:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_mmap2:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_modify_ldt:
+#endif
+ case __NR_mprotect:
+ case __NR_mremap:
+ case __NR_msync:
+ case __NR_munlockall:
+ case __NR_readahead:
+ case __NR_remap_file_pages:
+#if defined(__i386__)
+ case __NR_vm86:
+ case __NR_vm86old:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedGeneralIo(int sysno) {
+ switch (sysno) {
+ case __NR_lseek:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR__llseek:
+#endif
+#if !defined(__aarch64__)
+ case __NR_poll:
+#endif
+ case __NR_ppoll:
+ case __NR_pselect6:
+ case __NR_read:
+ case __NR_readv:
+#if defined(__arm__) || defined(__mips__)
+ case __NR_recv:
+#endif
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_recvfrom: // Could specify source.
+ case __NR_recvmsg: // Could specify source.
+#endif
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_select:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR__newselect:
+#endif
+#if defined(__arm__)
+ case __NR_send:
+#endif
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_sendmsg: // Could specify destination.
+ case __NR_sendto: // Could specify destination.
+#endif
+ case __NR_write:
+ case __NR_writev:
+ return true;
+ case __NR_ioctl: // Can be very powerful.
+ case __NR_pread64:
+ case __NR_preadv:
+ case __NR_pwrite64:
+ case __NR_pwritev:
+ case __NR_recvmmsg: // Could specify source.
+ case __NR_sendfile:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_sendfile64:
+#endif
+ case __NR_sendmmsg: // Could specify destination.
+ case __NR_splice:
+ case __NR_tee:
+ case __NR_vmsplice:
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsPrctl(int sysno) {
+ switch (sysno) {
+#if defined(__x86_64__)
+ case __NR_arch_prctl:
+#endif
+ case __NR_prctl:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsSeccomp(int sysno) {
+ switch (sysno) {
+ case __NR_seccomp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedBasicScheduler(int sysno) {
+ switch (sysno) {
+ case __NR_sched_yield:
+#if !defined(__aarch64__)
+ case __NR_pause:
+#endif
+ case __NR_nanosleep:
+ return true;
+ case __NR_getpriority:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_nice:
+#endif
+ case __NR_setpriority:
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAdminOperation(int sysno) {
+ switch (sysno) {
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_bdflush:
+#endif
+ case __NR_kexec_load:
+ case __NR_reboot:
+ case __NR_setdomainname:
+ case __NR_sethostname:
+ case __NR_syslog:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsKernelModule(int sysno) {
+ switch (sysno) {
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_create_module:
+ case __NR_get_kernel_syms: // Should ENOSYS.
+ case __NR_query_module:
+#endif
+ case __NR_delete_module:
+ case __NR_init_module:
+ case __NR_finit_module:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGlobalFSViewChange(int sysno) {
+ switch (sysno) {
+ case __NR_pivot_root:
+ case __NR_chroot:
+ case __NR_sync:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsFsControl(int sysno) {
+ switch (sysno) {
+ case __NR_mount:
+ case __NR_nfsservctl:
+ case __NR_quotactl:
+ case __NR_swapoff:
+ case __NR_swapon:
+#if defined(__i386__) || defined(__mips__)
+ case __NR_umount:
+#endif
+ case __NR_umount2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsNuma(int sysno) {
+ switch (sysno) {
+ case __NR_get_mempolicy:
+ case __NR_getcpu:
+ case __NR_mbind:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_migrate_pages:
+#endif
+ case __NR_move_pages:
+ case __NR_set_mempolicy:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsMessageQueue(int sysno) {
+ switch (sysno) {
+ case __NR_mq_getsetattr:
+ case __NR_mq_notify:
+ case __NR_mq_open:
+ case __NR_mq_timedreceive:
+ case __NR_mq_timedsend:
+ case __NR_mq_unlink:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGlobalProcessEnvironment(int sysno) {
+ switch (sysno) {
+ case __NR_acct: // Privileged.
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_getrlimit:
+#endif
+#if defined(__i386__) || defined(__arm__)
+ case __NR_ugetrlimit:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_ulimit:
+#endif
+ case __NR_getrusage:
+ case __NR_personality: // Can change its personality as well.
+ case __NR_prlimit64: // Like setrlimit / getrlimit.
+ case __NR_setrlimit:
+ case __NR_times:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsDebug(int sysno) {
+ switch (sysno) {
+ case __NR_ptrace:
+ case __NR_process_vm_readv:
+ case __NR_process_vm_writev:
+ case __NR_kcmp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGlobalSystemStatus(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR__sysctl:
+ case __NR_sysfs:
+#endif
+ case __NR_sysinfo:
+ case __NR_uname:
+#if defined(__i386__)
+ case __NR_olduname:
+ case __NR_oldolduname:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsEventFd(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_eventfd:
+#endif
+ case __NR_eventfd2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Asynchronous I/O API.
+bool SyscallSets::IsAsyncIo(int sysno) {
+ switch (sysno) {
+ case __NR_io_cancel:
+ case __NR_io_destroy:
+ case __NR_io_getevents:
+ case __NR_io_setup:
+ case __NR_io_submit:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsKeyManagement(int sysno) {
+ switch (sysno) {
+ case __NR_add_key:
+ case __NR_keyctl:
+ case __NR_request_key:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+bool SyscallSets::IsSystemVSemaphores(int sysno) {
+ switch (sysno) {
+ case __NR_semctl:
+ case __NR_semget:
+ case __NR_semop:
+ case __NR_semtimedop:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+// These give a lot of ambient authority and bypass the setuid sandbox.
+bool SyscallSets::IsSystemVSharedMemory(int sysno) {
+ switch (sysno) {
+ case __NR_shmat:
+ case __NR_shmctl:
+ case __NR_shmdt:
+ case __NR_shmget:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+bool SyscallSets::IsSystemVMessageQueue(int sysno) {
+ switch (sysno) {
+ case __NR_msgctl:
+ case __NR_msgget:
+ case __NR_msgrcv:
+ case __NR_msgsnd:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__i386__) || defined(__mips__)
+// Big system V multiplexing system call.
+bool SyscallSets::IsSystemVIpc(int sysno) {
+ switch (sysno) {
+ case __NR_ipc:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+bool SyscallSets::IsAnySystemV(int sysno) {
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ return IsSystemVMessageQueue(sysno) || IsSystemVSemaphores(sysno) ||
+ IsSystemVSharedMemory(sysno);
+#elif defined(__i386__) || defined(__mips__)
+ return IsSystemVIpc(sysno);
+#endif
+}
+
+bool SyscallSets::IsAdvancedScheduler(int sysno) {
+ switch (sysno) {
+ case __NR_ioprio_get: // IO scheduler.
+ case __NR_ioprio_set:
+ case __NR_sched_get_priority_max:
+ case __NR_sched_get_priority_min:
+ case __NR_sched_getaffinity:
+ case __NR_sched_getattr:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_rr_get_interval:
+ case __NR_sched_setaffinity:
+ case __NR_sched_setattr:
+ case __NR_sched_setparam:
+ case __NR_sched_setscheduler:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsInotify(int sysno) {
+ switch (sysno) {
+ case __NR_inotify_add_watch:
+#if !defined(__aarch64__)
+ case __NR_inotify_init:
+#endif
+ case __NR_inotify_init1:
+ case __NR_inotify_rm_watch:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsFaNotify(int sysno) {
+ switch (sysno) {
+ case __NR_fanotify_init:
+ case __NR_fanotify_mark:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsTimer(int sysno) {
+ switch (sysno) {
+ case __NR_getitimer:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_alarm:
+#endif
+ case __NR_setitimer:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAdvancedTimer(int sysno) {
+ switch (sysno) {
+ case __NR_timer_create:
+ case __NR_timer_delete:
+ case __NR_timer_getoverrun:
+ case __NR_timer_gettime:
+ case __NR_timer_settime:
+ case __NR_timerfd_create:
+ case __NR_timerfd_gettime:
+ case __NR_timerfd_settime:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsExtendedAttributes(int sysno) {
+ switch (sysno) {
+ case __NR_fgetxattr:
+ case __NR_flistxattr:
+ case __NR_fremovexattr:
+ case __NR_fsetxattr:
+ case __NR_getxattr:
+ case __NR_lgetxattr:
+ case __NR_listxattr:
+ case __NR_llistxattr:
+ case __NR_lremovexattr:
+ case __NR_lsetxattr:
+ case __NR_removexattr:
+ case __NR_setxattr:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Various system calls that need to be researched.
+// TODO(jln): classify this better.
+bool SyscallSets::IsMisc(int sysno) {
+ switch (sysno) {
+#if !defined(__mips__)
+ case __NR_getrandom:
+#endif
+ case __NR_name_to_handle_at:
+ case __NR_open_by_handle_at:
+ case __NR_perf_event_open:
+ case __NR_syncfs:
+ case __NR_vhangup:
+// The system calls below are not implemented.
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_afs_syscall:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_break:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_getpmsg:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_gtty:
+ case __NR_idle:
+ case __NR_lock:
+ case __NR_mpx:
+ case __NR_prof:
+ case __NR_profil:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_putpmsg:
+#endif
+#if defined(__x86_64__)
+ case __NR_security:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_stty:
+#endif
+#if defined(__x86_64__)
+ case __NR_tuxcall:
+#endif
+#if !defined(__aarch64__)
+ case __NR_vserver:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+#if defined(__arm__)
+bool SyscallSets::IsArmPciConfig(int sysno) {
+ switch (sysno) {
+ case __NR_pciconfig_iobase:
+ case __NR_pciconfig_read:
+ case __NR_pciconfig_write:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsArmPrivate(int sysno) {
+ switch (sysno) {
+ case __ARM_NR_breakpoint:
+ case __ARM_NR_cacheflush:
+ case __ARM_NR_set_tls:
+ case __ARM_NR_usr26:
+ case __ARM_NR_usr32:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif // defined(__arm__)
+
+#if defined(__mips__)
+bool SyscallSets::IsMipsPrivate(int sysno) {
+ switch (sysno) {
+ case __NR_cacheflush:
+ case __NR_cachectl:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsMipsMisc(int sysno) {
+ switch (sysno) {
+ case __NR_sysmips:
+ case __NR_unused150:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif // defined(__mips__)
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h
new file mode 100644
index 0000000000..5ba6335a95
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/sandbox_export.h"
+
+// These are helpers to build seccomp-bpf policies, i.e. policies for a
+// sandbox that reduces the Linux kernel's attack surface. Given their
+// nature, they don't have any clear semantics and are completely
+// "implementation-defined".
+
+namespace sandbox {
+
+class SANDBOX_EXPORT SyscallSets {
+ public:
+ static bool IsKill(int sysno);
+ static bool IsAllowedGettime(int sysno);
+ static bool IsCurrentDirectory(int sysno);
+ static bool IsUmask(int sysno);
+ // System calls that directly access the file system. They might acquire
+ // a new file descriptor or otherwise perform an operation directly
+ // via a path.
+ static bool IsFileSystem(int sysno);
+ static bool IsAllowedFileSystemAccessViaFd(int sysno);
+ static bool IsDeniedFileSystemAccessViaFd(int sysno);
+ static bool IsGetSimpleId(int sysno);
+ static bool IsProcessPrivilegeChange(int sysno);
+ static bool IsProcessGroupOrSession(int sysno);
+ static bool IsAllowedSignalHandling(int sysno);
+ static bool IsAllowedOperationOnFd(int sysno);
+ static bool IsKernelInternalApi(int sysno);
+ // This should be thought through in conjunction with IsFutex().
+ static bool IsAllowedProcessStartOrDeath(int sysno);
+ // It's difficult to restrict those, but there is attack surface here.
+ static bool IsAllowedFutex(int sysno);
+ static bool IsAllowedEpoll(int sysno);
+ static bool IsAllowedGetOrModifySocket(int sysno);
+ static bool IsDeniedGetOrModifySocket(int sysno);
+
+#if defined(__i386__) || defined(__mips__)
+ // Big multiplexing system call for sockets.
+ static bool IsSocketCall(int sysno);
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ static bool IsNetworkSocketInformation(int sysno);
+#endif
+
+ static bool IsAllowedAddressSpaceAccess(int sysno);
+ static bool IsAllowedGeneralIo(int sysno);
+ static bool IsPrctl(int sysno);
+ static bool IsSeccomp(int sysno);
+ static bool IsAllowedBasicScheduler(int sysno);
+ static bool IsAdminOperation(int sysno);
+ static bool IsKernelModule(int sysno);
+ static bool IsGlobalFSViewChange(int sysno);
+ static bool IsFsControl(int sysno);
+ static bool IsNuma(int sysno);
+ static bool IsMessageQueue(int sysno);
+ static bool IsGlobalProcessEnvironment(int sysno);
+ static bool IsDebug(int sysno);
+ static bool IsGlobalSystemStatus(int sysno);
+ static bool IsEventFd(int sysno);
+ // Asynchronous I/O API.
+ static bool IsAsyncIo(int sysno);
+ static bool IsKeyManagement(int sysno);
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ static bool IsSystemVSemaphores(int sysno);
+#endif
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ // These give a lot of ambient authority and bypass the setuid sandbox.
+ static bool IsSystemVSharedMemory(int sysno);
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ static bool IsSystemVMessageQueue(int sysno);
+#endif
+
+#if defined(__i386__) || defined(__mips__)
+ // Big system V multiplexing system call.
+ static bool IsSystemVIpc(int sysno);
+#endif
+
+ static bool IsAnySystemV(int sysno);
+ static bool IsAdvancedScheduler(int sysno);
+ static bool IsInotify(int sysno);
+ static bool IsFaNotify(int sysno);
+ static bool IsTimer(int sysno);
+ static bool IsAdvancedTimer(int sysno);
+ static bool IsExtendedAttributes(int sysno);
+ static bool IsMisc(int sysno);
+#if defined(__arm__)
+ static bool IsArmPciConfig(int sysno);
+ static bool IsArmPrivate(int sysno);
+#endif // defined(__arm__)
+#if defined(__mips__)
+ static bool IsMipsPrivate(int sysno);
+ static bool IsMipsMisc(int sysno);
+#endif // defined(__mips__)
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SyscallSets);
+};
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_
diff --git a/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
new file mode 100644
index 0000000000..63e1814c90
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
@@ -0,0 +1,153 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::ResultExpr;
+
+namespace sandbox {
+
+namespace {
+
+class FourtyTwo {
+ public:
+ static const int kMagicValue = 42;
+ FourtyTwo() : value_(kMagicValue) {}
+ int value() { return value_; }
+
+ private:
+ int value_;
+ DISALLOW_COPY_AND_ASSIGN(FourtyTwo);
+};
+
+class EmptyClassTakingPolicy : public bpf_dsl::Policy {
+ public:
+ explicit EmptyClassTakingPolicy(FourtyTwo* fourty_two) {
+ BPF_ASSERT(fourty_two);
+ BPF_ASSERT(FourtyTwo::kMagicValue == fourty_two->value());
+ }
+ ~EmptyClassTakingPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ return Allow();
+ }
+};
+
+BPF_TEST(BPFTest,
+ BPFAUXPointsToClass,
+ EmptyClassTakingPolicy,
+ FourtyTwo /* *BPF_AUX */) {
+ // BPF_AUX should point to an instance of FourtyTwo.
+ BPF_ASSERT(BPF_AUX);
+ BPF_ASSERT(FourtyTwo::kMagicValue == BPF_AUX->value());
+}
+
+void DummyTestFunction(FourtyTwo *fourty_two) {
+}
+
+TEST(BPFTest, BPFTesterCompatibilityDelegateLeakTest) {
+ // Don't do anything, simply gives dynamic tools an opportunity to detect
+ // leaks.
+ {
+ BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>
+ simple_delegate(DummyTestFunction);
+ }
+ {
+ // Test polymorphism.
+ scoped_ptr<BPFTesterDelegate> simple_delegate(
+ new BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>(
+ DummyTestFunction));
+ }
+}
+
+class EnosysPtracePolicy : public bpf_dsl::Policy {
+ public:
+ EnosysPtracePolicy() { my_pid_ = sys_getpid(); }
+ ~EnosysPtracePolicy() override {
+ // Policies should be able to bind with the process on which they are
+ // created. They should never be created in a parent process.
+ BPF_ASSERT_EQ(my_pid_, sys_getpid());
+ }
+
+ ResultExpr EvaluateSyscall(int system_call_number) const override {
+ CHECK(SandboxBPF::IsValidSyscallNumber(system_call_number));
+ if (system_call_number == __NR_ptrace) {
+ // The EvaluateSyscall function should run in the process that created
+ // the current object.
+ BPF_ASSERT_EQ(my_pid_, sys_getpid());
+ return Error(ENOSYS);
+ } else {
+ return Allow();
+ }
+ }
+
+ private:
+ pid_t my_pid_;
+ DISALLOW_COPY_AND_ASSIGN(EnosysPtracePolicy);
+};
+
+class BasicBPFTesterDelegate : public BPFTesterDelegate {
+ public:
+ BasicBPFTesterDelegate() {}
+ ~BasicBPFTesterDelegate() override {}
+
+ scoped_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ return scoped_ptr<bpf_dsl::Policy>(new EnosysPtracePolicy());
+ }
+ void RunTestFunction() override {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicBPFTesterDelegate);
+};
+
+// This is the most powerful and complex way to create a BPF test, but it
+// requires a full class definition (BasicBPFTesterDelegate).
+BPF_TEST_D(BPFTest, BPFTestWithDelegateClass, BasicBPFTesterDelegate);
+
+// This is the simplest form of BPF tests.
+BPF_TEST_C(BPFTest, BPFTestWithInlineTest, EnosysPtracePolicy) {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+}
+
+const char kHelloMessage[] = "Hello";
+
+BPF_DEATH_TEST_C(BPFTest,
+ BPFDeathTestWithInlineTest,
+ DEATH_MESSAGE(kHelloMessage),
+ EnosysPtracePolicy) {
+ LOG(ERROR) << kHelloMessage;
+ _exit(1);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/die.cc b/sandbox/linux/seccomp-bpf/die.cc
new file mode 100644
index 0000000000..3baf1f13d9
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/die.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/die.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+
+namespace sandbox {
+
+void Die::ExitGroup() {
+ // exit_group() should exit our program. After all, it is defined as a
+ // function that doesn't return. But things can theoretically go wrong.
+ // Especially, since we are dealing with system call filters. Continuing
+ // execution would be very bad in most cases where ExitGroup() gets called.
+ // So, we'll try a few other strategies too.
+ Syscall::Call(__NR_exit_group, 1);
+
+ // We have no idea what our run-time environment looks like. So, signal
+ // handlers might or might not do the right thing. Try to reset settings
+ // to a defined state; but we have not way to verify whether we actually
+ // succeeded in doing so. Nonetheless, triggering a fatal signal could help
+ // us terminate.
+ struct sigaction sa = {};
+ sa.sa_handler = LINUX_SIG_DFL;
+ sa.sa_flags = LINUX_SA_RESTART;
+ sys_sigaction(LINUX_SIGSEGV, &sa, nullptr);
+ Syscall::Call(__NR_prctl, PR_SET_DUMPABLE, (void*)0, (void*)0, (void*)0);
+ if (*(volatile char*)0) {
+ }
+
+ // If there is no way for us to ask for the program to exit, the next
+ // best thing we can do is to loop indefinitely. Maybe, somebody will notice
+ // and file a bug...
+ // We in fact retry the system call inside of our loop so that it will
+ // stand out when somebody tries to diagnose the problem by using "strace".
+ for (;;) {
+ Syscall::Call(__NR_exit_group, 1);
+ }
+}
+
+void Die::SandboxDie(const char* msg, const char* file, int line) {
+ if (simple_exit_) {
+ LogToStderr(msg, file, line);
+ } else {
+ logging::LogMessage(file, line, logging::LOG_FATAL).stream() << msg;
+ }
+ ExitGroup();
+}
+
+void Die::RawSandboxDie(const char* msg) {
+ if (!msg)
+ msg = "";
+ RAW_LOG(FATAL, msg);
+ ExitGroup();
+}
+
+void Die::SandboxInfo(const char* msg, const char* file, int line) {
+ if (!suppress_info_) {
+ logging::LogMessage(file, line, logging::LOG_INFO).stream() << msg;
+ }
+}
+
+void Die::LogToStderr(const char* msg, const char* file, int line) {
+ if (msg) {
+ char buf[40];
+ snprintf(buf, sizeof(buf), "%d", line);
+ std::string s = std::string(file) + ":" + buf + ":" + msg + "\n";
+
+ // No need to loop. Short write()s are unlikely and if they happen we
+ // probably prefer them over a loop that blocks.
+ ignore_result(
+ HANDLE_EINTR(Syscall::Call(__NR_write, 2, s.c_str(), s.length())));
+ }
+}
+
+bool Die::simple_exit_ = false;
+bool Die::suppress_info_ = false;
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/die.h b/sandbox/linux/seccomp-bpf/die.h
new file mode 100644
index 0000000000..b3f3f72c2f
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/die.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This is the main API for using this file. Prints a error message and
+// exits with a fatal error. This is not async-signal safe.
+#define SANDBOX_DIE(m) sandbox::Die::SandboxDie(m, __FILE__, __LINE__)
+
+// An async signal safe version of the same API. Won't print the filename
+// and line numbers.
+#define RAW_SANDBOX_DIE(m) sandbox::Die::RawSandboxDie(m)
+
+// Adds an informational message to the log file or stderr as appropriate.
+#define SANDBOX_INFO(m) sandbox::Die::SandboxInfo(m, __FILE__, __LINE__)
+
+class SANDBOX_EXPORT Die {
+ public:
+ // Terminate the program, even if the current sandbox policy prevents some
+ // of the more commonly used functions used for exiting.
+ // Most users would want to call SANDBOX_DIE() instead, as it logs extra
+ // information. But calling ExitGroup() is correct and in some rare cases
+ // preferable. So, we make it part of the public API.
+ static void ExitGroup() __attribute__((noreturn));
+
+ // This method gets called by SANDBOX_DIE(). There is normally no reason
+ // to call it directly unless you are defining your own exiting macro.
+ static void SandboxDie(const char* msg, const char* file, int line)
+ __attribute__((noreturn));
+
+ static void RawSandboxDie(const char* msg) __attribute__((noreturn));
+
+ // This method gets called by SANDBOX_INFO(). There is normally no reason
+ // to call it directly unless you are defining your own logging macro.
+ static void SandboxInfo(const char* msg, const char* file, int line);
+
+ // Writes a message to stderr. Used as a fall-back choice, if we don't have
+ // any other way to report an error.
+ static void LogToStderr(const char* msg, const char* file, int line);
+
+ // We generally want to run all exit handlers. This means, on SANDBOX_DIE()
+ // we should be calling LOG(FATAL). But there are some situations where
+ // we just need to print a message and then terminate. This would typically
+ // happen in cases where we consume the error message internally (e.g. in
+ // unit tests or in the supportsSeccompSandbox() method).
+ static void EnableSimpleExit() { simple_exit_ = true; }
+
+ // Sometimes we need to disable all informational messages (e.g. from within
+ // unittests).
+ static void SuppressInfoMessages(bool flag) { suppress_info_ = flag; }
+
+ private:
+ static bool simple_exit_;
+ static bool suppress_info_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Die);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
new file mode 100644
index 0000000000..3e4f0adf53
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -0,0 +1,279 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+
+// Some headers on Android are missing cdefs: crbug.com/172337.
+// (We can't use OS_ANDROID here since build_config.h is not included).
+#if defined(ANDROID)
+#include <sys/cdefs.h>
+#endif
+
+#include <errno.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/compiler_specific.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "third_party/valgrind/valgrind.h"
+
+namespace sandbox {
+
+namespace {
+
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
+bool IsSingleThreaded(int proc_fd) {
+ return ThreadHelpers::IsSingleThreaded(proc_fd);
+}
+
+// Check if the kernel supports seccomp-filter (a.k.a. seccomp mode 2) via
+// prctl().
+bool KernelSupportsSeccompBPF() {
+ errno = 0;
+ const int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr);
+
+ if (rv == -1 && EFAULT == errno) {
+ return true;
+ }
+ return false;
+}
+
+// LG introduced a buggy syscall, sys_set_media_ext, with the same number as
+// seccomp. Return true if the current kernel has this buggy syscall.
+//
+// We want this to work with upcoming versions of seccomp, so we pass bogus
+// flags that are unlikely to ever be used by the kernel. A normal kernel would
+// return -EINVAL, but a buggy LG kernel would return 1.
+bool KernelHasLGBug() {
+#if defined(OS_ANDROID)
+ // sys_set_media will see this as NULL, which should be a safe (non-crashing)
+ // way to invoke it. A genuine seccomp syscall will see it as
+ // SECCOMP_SET_MODE_STRICT.
+ const unsigned int operation = 0;
+ // Chosen by fair dice roll. Guaranteed to be random.
+ const unsigned int flags = 0xf7a46a5c;
+ const int rv = sys_seccomp(operation, flags, nullptr);
+ // A genuine kernel would return -EINVAL (which would set rv to -1 and errno
+ // to EINVAL), or at the very least return some kind of error (which would
+ // set rv to -1). Any other behavior indicates that whatever code received
+ // our syscall was not the real seccomp.
+ if (rv != -1) {
+ return true;
+ }
+#endif // defined(OS_ANDROID)
+
+ return false;
+}
+
+// Check if the kernel supports seccomp-filter via the seccomp system call
+// and the TSYNC feature to enable seccomp on all threads.
+bool KernelSupportsSeccompTsync() {
+ if (KernelHasLGBug()) {
+ return false;
+ }
+
+ errno = 0;
+ const int rv =
+ sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, nullptr);
+
+ if (rv == -1 && errno == EFAULT) {
+ return true;
+ } else {
+ // TODO(jln): turn these into DCHECK after 417888 is considered fixed.
+ CHECK_EQ(-1, rv);
+ CHECK(ENOSYS == errno || EINVAL == errno);
+ return false;
+ }
+}
+
+uint64_t EscapePC() {
+ intptr_t rv = Syscall::Call(-1);
+ if (rv == -1 && errno == ENOSYS) {
+ return 0;
+ }
+ return static_cast<uint64_t>(static_cast<uintptr_t>(rv));
+}
+
+} // namespace
+
+SandboxBPF::SandboxBPF(bpf_dsl::Policy* policy)
+ : proc_fd_(), sandbox_has_started_(false), policy_(policy) {
+}
+
+SandboxBPF::~SandboxBPF() {
+}
+
+// static
+bool SandboxBPF::SupportsSeccompSandbox(SeccompLevel level) {
+ // Never pretend to support seccomp with Valgrind, as it
+ // throws the tool off.
+ if (IsRunningOnValgrind()) {
+ return false;
+ }
+
+ switch (level) {
+ case SeccompLevel::SINGLE_THREADED:
+ return KernelSupportsSeccompBPF();
+ case SeccompLevel::MULTI_THREADED:
+ return KernelSupportsSeccompTsync();
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
+ DCHECK(policy_);
+ CHECK(seccomp_level == SeccompLevel::SINGLE_THREADED ||
+ seccomp_level == SeccompLevel::MULTI_THREADED);
+
+ if (sandbox_has_started_) {
+ SANDBOX_DIE(
+ "Cannot repeatedly start sandbox. Create a separate Sandbox "
+ "object instead.");
+ return false;
+ }
+
+ if (!proc_fd_.is_valid()) {
+ SetProcFd(ProcUtil::OpenProc());
+ }
+
+ const bool supports_tsync = KernelSupportsSeccompTsync();
+
+ if (seccomp_level == SeccompLevel::SINGLE_THREADED) {
+ // Wait for /proc/self/task/ to update if needed and assert the
+ // process is single threaded.
+ ThreadHelpers::AssertSingleThreaded(proc_fd_.get());
+ } else if (seccomp_level == SeccompLevel::MULTI_THREADED) {
+ if (IsSingleThreaded(proc_fd_.get())) {
+ SANDBOX_DIE("Cannot start sandbox; "
+ "process may be single-threaded when reported as not");
+ return false;
+ }
+ if (!supports_tsync) {
+ SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing "
+ "filters for a threadgroup");
+ return false;
+ }
+ }
+
+ // We no longer need access to any files in /proc. We want to do this
+ // before installing the filters, just in case that our policy denies
+ // close().
+ if (proc_fd_.is_valid()) {
+ proc_fd_.reset();
+ }
+
+ // Install the filters.
+ InstallFilter(supports_tsync ||
+ seccomp_level == SeccompLevel::MULTI_THREADED);
+
+ return true;
+}
+
+void SandboxBPF::SetProcFd(base::ScopedFD proc_fd) {
+ proc_fd_.swap(proc_fd);
+}
+
+// static
+bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
+ return SyscallSet::IsValid(sysnum);
+}
+
+// static
+bool SandboxBPF::IsRequiredForUnsafeTrap(int sysno) {
+ return bpf_dsl::PolicyCompiler::IsRequiredForUnsafeTrap(sysno);
+}
+
+// static
+intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) {
+ return Syscall::Call(
+ args.nr, static_cast<intptr_t>(args.args[0]),
+ static_cast<intptr_t>(args.args[1]), static_cast<intptr_t>(args.args[2]),
+ static_cast<intptr_t>(args.args[3]), static_cast<intptr_t>(args.args[4]),
+ static_cast<intptr_t>(args.args[5]));
+}
+
+scoped_ptr<CodeGen::Program> SandboxBPF::AssembleFilter(
+ bool force_verification) {
+#if !defined(NDEBUG)
+ force_verification = true;
+#endif
+ DCHECK(policy_);
+
+ bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry());
+ if (Trap::SandboxDebuggingAllowedByUser()) {
+ compiler.DangerousSetEscapePC(EscapePC());
+ }
+ return compiler.Compile(force_verification);
+}
+
+void SandboxBPF::InstallFilter(bool must_sync_threads) {
+ // We want to be very careful in not imposing any requirements on the
+ // policies that are set with SetSandboxPolicy(). This means, as soon as
+ // the sandbox is active, we shouldn't be relying on libraries that could
+ // be making system calls. This, for example, means we should avoid
+ // using the heap and we should avoid using STL functions.
+ // Temporarily copy the contents of the "program" vector into a
+ // stack-allocated array; and then explicitly destroy that object.
+ // This makes sure we don't ex- or implicitly call new/delete after we
+ // installed the BPF filter program in the kernel. Depending on the
+ // system memory allocator that is in effect, these operators can result
+ // in system calls to things like munmap() or brk().
+ CodeGen::Program* program = AssembleFilter(false).release();
+
+ struct sock_filter bpf[program->size()];
+ const struct sock_fprog prog = {static_cast<unsigned short>(program->size()),
+ bpf};
+ memcpy(bpf, &(*program)[0], sizeof(bpf));
+ delete program;
+
+ // Make an attempt to release memory that is no longer needed here, rather
+ // than in the destructor. Try to avoid as much as possible to presume of
+ // what will be possible to do in the new (sandboxed) execution environment.
+ policy_.reset();
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ SANDBOX_DIE("Kernel refuses to enable no-new-privs");
+ }
+
+ // Install BPF filter program. If the thread state indicates multi-threading
+ // support, then the kernel hass the seccomp system call. Otherwise, fall
+ // back on prctl, which requires the process to be single-threaded.
+ if (must_sync_threads) {
+ int rv =
+ sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog);
+ if (rv) {
+ SANDBOX_DIE(
+ "Kernel refuses to turn on and synchronize threads for BPF filters");
+ }
+ } else {
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+ SANDBOX_DIE("Kernel refuses to turn on BPF filters");
+ }
+ }
+
+ sandbox_has_started_ = true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
new file mode 100644
index 0000000000..96cceb5648
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_
+
+#include <stdint.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+struct arch_seccomp_data;
+namespace bpf_dsl {
+class Policy;
+}
+
+// This class can be used to apply a syscall sandboxing policy expressed in a
+// bpf_dsl::Policy object to the current process.
+// Syscall sandboxing policies get inherited by subprocesses and, once applied,
+// can never be removed for the lifetime of the process.
+class SANDBOX_EXPORT SandboxBPF {
+ public:
+ enum class SeccompLevel {
+ SINGLE_THREADED,
+ MULTI_THREADED,
+ };
+
+ // Ownership of |policy| is transfered here to the sandbox object.
+ // nullptr is allowed for unit tests.
+ explicit SandboxBPF(bpf_dsl::Policy* policy);
+ // NOTE: Setting a policy and starting the sandbox is a one-way operation.
+ // The kernel does not provide any option for unloading a loaded sandbox. The
+ // sandbox remains engaged even when the object is destructed.
+ ~SandboxBPF();
+
+ // Detect if the kernel supports the specified seccomp level.
+ // See StartSandbox() for a description of these.
+ static bool SupportsSeccompSandbox(SeccompLevel level);
+
+ // This is the main public entry point. It sets up the resources needed by
+ // the sandbox, and enters Seccomp mode.
+ // The calling process must provide a |level| to tell the sandbox which type
+ // of kernel support it should engage.
+ // SINGLE_THREADED will only sandbox the calling thread. Since it would be a
+ // security risk, the sandbox will also check that the current process is
+ // single threaded and crash if it isn't the case.
+ // MULTI_THREADED requires more recent kernel support and allows to sandbox
+ // all the threads of the current process. Be mindful of potential races,
+ // with other threads using disallowed system calls either before or after
+ // the sandbox is engaged.
+ //
+ // It is possible to stack multiple sandboxes by creating separate "Sandbox"
+ // objects and calling "StartSandbox()" on each of them. Please note, that
+ // this requires special care, though, as newly stacked sandboxes can never
+ // relax restrictions imposed by earlier sandboxes. Furthermore, installing
+ // a new policy requires making system calls, that might already be
+ // disallowed.
+ // Finally, stacking does add more kernel overhead than having a single
+ // combined policy. So, it should only be used if there are no alternatives.
+ bool StartSandbox(SeccompLevel level) WARN_UNUSED_RESULT;
+
+ // The sandbox needs to be able to access files in "/proc/self/". If
+ // this directory is not accessible when "StartSandbox()" gets called, the
+ // caller must provide an already opened file descriptor by calling
+ // "SetProcFd()".
+ // The sandbox becomes the new owner of this file descriptor and will
+ // close it when "StartSandbox()" executes or when the sandbox object
+ // disappears.
+ void SetProcFd(base::ScopedFD proc_fd);
+
+ // Checks whether a particular system call number is valid on the current
+ // architecture.
+ static bool IsValidSyscallNumber(int sysnum);
+
+ // UnsafeTraps require some syscalls to always be allowed.
+ // This helper function returns true for these calls.
+ static bool IsRequiredForUnsafeTrap(int sysno);
+
+ // From within an UnsafeTrap() it is often useful to be able to execute
+ // the system call that triggered the trap. The ForwardSyscall() method
+ // makes this easy. It is more efficient than calling glibc's syscall()
+ // function, as it avoid the extra round-trip to the signal handler. And
+ // it automatically does the correct thing to report kernel-style error
+ // conditions, rather than setting errno. See the comments for TrapFnc for
+ // details. In other words, the return value from ForwardSyscall() is
+ // directly suitable as a return value for a trap handler.
+ static intptr_t ForwardSyscall(const struct arch_seccomp_data& args);
+
+ // Assembles a BPF filter program from the current policy. After calling this
+ // function, you must not call any other sandboxing function.
+ // Typically, AssembleFilter() is only used by unit tests and by sandbox
+ // internals. It should not be used by production code.
+ // For performance reasons, we normally only run the assembled BPF program
+ // through the verifier, iff the program was built in debug mode.
+ // But by setting "force_verification", the caller can request that the
+ // verifier is run unconditionally. This is useful for unittests.
+ scoped_ptr<CodeGen::Program> AssembleFilter(bool force_verification);
+
+ private:
+ // Assembles and installs a filter based on the policy that has previously
+ // been configured with SetSandboxPolicy().
+ void InstallFilter(bool must_sync_threads);
+
+ base::ScopedFD proc_fd_;
+ bool sandbox_has_started_;
+ scoped_ptr<bpf_dsl::Policy> policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPF);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_
diff --git a/sandbox/linux/seccomp-bpf/syscall.cc b/sandbox/linux/seccomp-bpf/syscall.cc
new file mode 100644
index 0000000000..bc6461f117
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall.cc
@@ -0,0 +1,421 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+#include <errno.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+
+namespace sandbox {
+
+namespace {
+
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS_FAMILY)
+// Number that's not currently used by any Linux kernel ABIs.
+const int kInvalidSyscallNumber = 0x351d3;
+#else
+#error Unrecognized architecture
+#endif
+
+asm(// We need to be able to tell the kernel exactly where we made a
+ // system call. The C++ compiler likes to sometimes clone or
+ // inline code, which would inadvertently end up duplicating
+ // the entry point.
+ // "gcc" can suppress code duplication with suitable function
+ // attributes, but "clang" doesn't have this ability.
+ // The "clang" developer mailing list suggested that the correct
+ // and portable solution is a file-scope assembly block.
+ // N.B. We do mark our code as a proper function so that backtraces
+ // work correctly. But we make absolutely no attempt to use the
+ // ABI's calling conventions for passing arguments. We will only
+ // ever be called from assembly code and thus can pick more
+ // suitable calling conventions.
+#if defined(__i386__)
+ ".text\n"
+ ".align 16, 0x90\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+ // Check if "%eax" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "int $0x80". This address can be
+ // used as a marker that BPF code inspects.
+ "test %eax, %eax\n"
+ "jge 1f\n"
+ // Always, make sure that our code is position-independent, or
+ // address space randomization might not work on i386. This means,
+ // we can't use "lea", but instead have to rely on "call/pop".
+ "call 0f; .cfi_adjust_cfa_offset 4\n"
+ "0:pop %eax; .cfi_adjust_cfa_offset -4\n"
+ "addl $2f-0b, %eax\n"
+ "ret\n"
+ // Save register that we don't want to clobber. On i386, we need to
+ // save relatively aggressively, as there are a couple or registers
+ // that are used internally (e.g. %ebx for position-independent
+ // code, and %ebp for the frame pointer), and as we need to keep at
+ // least a few registers available for the register allocator.
+ "1:push %esi; .cfi_adjust_cfa_offset 4; .cfi_rel_offset esi, 0\n"
+ "push %edi; .cfi_adjust_cfa_offset 4; .cfi_rel_offset edi, 0\n"
+ "push %ebx; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebx, 0\n"
+ "push %ebp; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebp, 0\n"
+ // Copy entries from the array holding the arguments into the
+ // correct CPU registers.
+ "movl 0(%edi), %ebx\n"
+ "movl 4(%edi), %ecx\n"
+ "movl 8(%edi), %edx\n"
+ "movl 12(%edi), %esi\n"
+ "movl 20(%edi), %ebp\n"
+ "movl 16(%edi), %edi\n"
+ // Enter the kernel.
+ "int $0x80\n"
+ // This is our "magic" return address that the BPF filter sees.
+ "2:"
+ // Restore any clobbered registers that we didn't declare to the
+ // compiler.
+ "pop %ebp; .cfi_restore ebp; .cfi_adjust_cfa_offset -4\n"
+ "pop %ebx; .cfi_restore ebx; .cfi_adjust_cfa_offset -4\n"
+ "pop %edi; .cfi_restore edi; .cfi_adjust_cfa_offset -4\n"
+ "pop %esi; .cfi_restore esi; .cfi_adjust_cfa_offset -4\n"
+ "ret\n"
+ ".cfi_endproc\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__x86_64__)
+ ".text\n"
+ ".align 16, 0x90\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+ // Check if "%rdi" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "syscall". This address can be
+ // used as a marker that BPF code inspects.
+ "test %rdi, %rdi\n"
+ "jge 1f\n"
+ // Always make sure that our code is position-independent, or the
+ // linker will throw a hissy fit on x86-64.
+ "lea 2f(%rip), %rax\n"
+ "ret\n"
+ // Now we load the registers used to pass arguments to the system
+ // call: system call number in %rax, and arguments in %rdi, %rsi,
+ // %rdx, %r10, %r8, %r9. Note: These are all caller-save registers
+ // (only %rbx, %rbp, %rsp, and %r12-%r15 are callee-save), so no
+ // need to worry here about spilling registers or CFI directives.
+ "1:movq %rdi, %rax\n"
+ "movq 0(%rsi), %rdi\n"
+ "movq 16(%rsi), %rdx\n"
+ "movq 24(%rsi), %r10\n"
+ "movq 32(%rsi), %r8\n"
+ "movq 40(%rsi), %r9\n"
+ "movq 8(%rsi), %rsi\n"
+ // Enter the kernel.
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ "2:ret\n"
+ ".cfi_endproc\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__arm__)
+ // Throughout this file, we use the same mode (ARM vs. thumb)
+ // that the C++ compiler uses. This means, when transfering control
+ // from C++ to assembly code, we do not need to switch modes (e.g.
+ // by using the "bx" instruction). It also means that our assembly
+ // code should not be invoked directly from code that lives in
+ // other compilation units, as we don't bother implementing thumb
+ // interworking. That's OK, as we don't make any of the assembly
+ // symbols public. They are all local to this file.
+ ".text\n"
+ ".align 2\n"
+ ".type SyscallAsm, %function\n"
+#if defined(__thumb__)
+ ".thumb_func\n"
+#else
+ ".arm\n"
+#endif
+ "SyscallAsm:\n"
+#if !defined(__native_client_nonsfi__)
+ // .fnstart and .fnend pseudo operations creates unwind table.
+ // It also creates a reference to the symbol __aeabi_unwind_cpp_pr0, which
+ // is not provided by PNaCl toolchain. Disable it.
+ ".fnstart\n"
+#endif
+ "@ args = 0, pretend = 0, frame = 8\n"
+ "@ frame_needed = 1, uses_anonymous_args = 0\n"
+#if defined(__thumb__)
+ ".cfi_startproc\n"
+ "push {r7, lr}\n"
+ ".save {r7, lr}\n"
+ ".cfi_offset 14, -4\n"
+ ".cfi_offset 7, -8\n"
+ ".cfi_def_cfa_offset 8\n"
+#else
+ "stmfd sp!, {fp, lr}\n"
+ "add fp, sp, #4\n"
+#endif
+ // Check if "r0" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "swi 0". This address can be
+ // used as a marker that BPF code inspects.
+ "cmp r0, #0\n"
+ "bge 1f\n"
+ "adr r0, 2f\n"
+ "b 2f\n"
+ // We declared (almost) all clobbered registers to the compiler. On
+ // ARM there is no particular register pressure. So, we can go
+ // ahead and directly copy the entries from the arguments array
+ // into the appropriate CPU registers.
+ "1:ldr r5, [r6, #20]\n"
+ "ldr r4, [r6, #16]\n"
+ "ldr r3, [r6, #12]\n"
+ "ldr r2, [r6, #8]\n"
+ "ldr r1, [r6, #4]\n"
+ "mov r7, r0\n"
+ "ldr r0, [r6, #0]\n"
+ // Enter the kernel
+ "swi 0\n"
+// Restore the frame pointer. Also restore the program counter from
+// the link register; this makes us return to the caller.
+#if defined(__thumb__)
+ "2:pop {r7, pc}\n"
+ ".cfi_endproc\n"
+#else
+ "2:ldmfd sp!, {fp, pc}\n"
+#endif
+#if !defined(__native_client_nonsfi__)
+ // Do not use .fnstart and .fnend for PNaCl toolchain. See above comment,
+ // for more details.
+ ".fnend\n"
+#endif
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__mips__)
+ ".text\n"
+ ".align 4\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.ent SyscallAsm\n"
+ ".frame $sp, 40, $ra\n"
+ ".set push\n"
+ ".set noreorder\n"
+ "addiu $sp, $sp, -40\n"
+ "sw $ra, 36($sp)\n"
+ // Check if "v0" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "syscall". This address can be
+ // used as a marker that BPF code inspects.
+ "bgez $v0, 1f\n"
+ " nop\n"
+ "la $v0, 2f\n"
+ "b 2f\n"
+ " nop\n"
+ // On MIPS first four arguments go to registers a0 - a3 and any
+ // argument after that goes to stack. We can go ahead and directly
+ // copy the entries from the arguments array into the appropriate
+ // CPU registers and on the stack.
+ "1:lw $a3, 28($a0)\n"
+ "lw $a2, 24($a0)\n"
+ "lw $a1, 20($a0)\n"
+ "lw $t0, 16($a0)\n"
+ "sw $a3, 28($sp)\n"
+ "sw $a2, 24($sp)\n"
+ "sw $a1, 20($sp)\n"
+ "sw $t0, 16($sp)\n"
+ "lw $a3, 12($a0)\n"
+ "lw $a2, 8($a0)\n"
+ "lw $a1, 4($a0)\n"
+ "lw $a0, 0($a0)\n"
+ // Enter the kernel
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ // Restore the return address from the stack.
+ "2:lw $ra, 36($sp)\n"
+ "jr $ra\n"
+ " addiu $sp, $sp, 40\n"
+ ".set pop\n"
+ ".end SyscallAsm\n"
+ ".size SyscallAsm,.-SyscallAsm\n"
+#elif defined(__aarch64__)
+ ".text\n"
+ ".align 2\n"
+ ".type SyscallAsm, %function\n"
+ "SyscallAsm:\n"
+ ".cfi_startproc\n"
+ "cmp x0, #0\n"
+ "b.ge 1f\n"
+ "adr x0,2f\n"
+ "b 2f\n"
+ "1:ldr x5, [x6, #40]\n"
+ "ldr x4, [x6, #32]\n"
+ "ldr x3, [x6, #24]\n"
+ "ldr x2, [x6, #16]\n"
+ "ldr x1, [x6, #8]\n"
+ "mov x8, x0\n"
+ "ldr x0, [x6, #0]\n"
+ // Enter the kernel
+ "svc 0\n"
+ "2:ret\n"
+ ".cfi_endproc\n"
+ ".size SyscallAsm, .-SyscallAsm\n"
+#endif
+ ); // asm
+
+#if defined(__x86_64__)
+extern "C" {
+intptr_t SyscallAsm(intptr_t nr, const intptr_t args[6]);
+}
+#endif
+
+} // namespace
+
+intptr_t Syscall::InvalidCall() {
+ // Explicitly pass eight zero arguments just in case.
+ return Call(kInvalidSyscallNumber, 0, 0, 0, 0, 0, 0, 0, 0);
+}
+
+intptr_t Syscall::Call(int nr,
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5,
+ intptr_t p6,
+ intptr_t p7) {
+ // We rely on "intptr_t" to be the exact size as a "void *". This is
+ // typically true, but just in case, we add a check. The language
+ // specification allows platforms some leeway in cases, where
+ // "sizeof(void *)" is not the same as "sizeof(void (*)())". We expect
+ // that this would only be an issue for IA64, which we are currently not
+ // planning on supporting. And it is even possible that this would work
+ // on IA64, but for lack of actual hardware, I cannot test.
+ static_assert(sizeof(void*) == sizeof(intptr_t),
+ "pointer types and intptr_t must be exactly the same size");
+
+ // TODO(nedeljko): Enable use of more than six parameters on architectures
+ // where that makes sense.
+#if defined(__mips__)
+ const intptr_t args[8] = {p0, p1, p2, p3, p4, p5, p6, p7};
+#else
+ DCHECK_EQ(p6, 0) << " Support for syscalls with more than six arguments not "
+ "added for this architecture";
+ DCHECK_EQ(p7, 0) << " Support for syscalls with more than six arguments not "
+ "added for this architecture";
+ const intptr_t args[6] = {p0, p1, p2, p3, p4, p5};
+#endif // defined(__mips__)
+
+// Invoke our file-scope assembly code. The constraints have been picked
+// carefully to match what the rest of the assembly code expects in input,
+// output, and clobbered registers.
+#if defined(__i386__)
+ intptr_t ret = nr;
+ asm volatile(
+ "call SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=a"(ret)
+ : "0"(ret), "D"(args)
+ : "cc", "esp", "memory", "ecx", "edx");
+#elif defined(__x86_64__)
+ intptr_t ret = SyscallAsm(nr, args);
+#elif defined(__arm__)
+ intptr_t ret;
+ {
+ register intptr_t inout __asm__("r0") = nr;
+ register const intptr_t* data __asm__("r6") = args;
+ asm volatile(
+ "bl SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=r"(inout)
+ : "0"(inout), "r"(data)
+ : "cc",
+ "lr",
+ "memory",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5"
+#if !defined(__thumb__)
+ // In thumb mode, we cannot use "r7" as a general purpose register, as
+ // it is our frame pointer. We have to manually manage and preserve
+ // it.
+ // In ARM mode, we have a dedicated frame pointer register and "r7" is
+ // thus available as a general purpose register. We don't preserve it,
+ // but instead mark it as clobbered.
+ ,
+ "r7"
+#endif // !defined(__thumb__)
+ );
+ ret = inout;
+ }
+#elif defined(__mips__)
+ int err_status;
+ intptr_t ret = Syscall::SandboxSyscallRaw(nr, args, &err_status);
+
+ if (err_status) {
+ // On error, MIPS returns errno from syscall instead of -errno.
+ // The purpose of this negation is for SandboxSyscall() to behave
+ // more like it would on other architectures.
+ ret = -ret;
+ }
+#elif defined(__aarch64__)
+ intptr_t ret;
+ {
+ register intptr_t inout __asm__("x0") = nr;
+ register const intptr_t* data __asm__("x6") = args;
+ asm volatile("bl SyscallAsm\n"
+ : "=r"(inout)
+ : "0"(inout), "r"(data)
+ : "memory", "x1", "x2", "x3", "x4", "x5", "x8", "x30");
+ ret = inout;
+ }
+
+#else
+#error "Unimplemented architecture"
+#endif
+ return ret;
+}
+
+void Syscall::PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx) {
+#if defined(__mips__)
+ // Mips ABI states that on error a3 CPU register has non zero value and if
+ // there is no error, it should be zero.
+ if (ret_val <= -1 && ret_val >= -4095) {
+ // |ret_val| followes the Syscall::Call() convention of being -errno on
+ // errors. In order to write correct value to return register this sign
+ // needs to be changed back.
+ ret_val = -ret_val;
+ SECCOMP_PARM4(ctx) = 1;
+ } else
+ SECCOMP_PARM4(ctx) = 0;
+#endif
+ SECCOMP_RESULT(ctx) = static_cast<greg_t>(ret_val);
+}
+
+#if defined(__mips__)
+intptr_t Syscall::SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_ret) {
+ register intptr_t ret __asm__("v0") = nr;
+ // a3 register becomes non zero on error.
+ register intptr_t err_stat __asm__("a3") = 0;
+ {
+ register const intptr_t* data __asm__("a0") = args;
+ asm volatile(
+ "la $t9, SyscallAsm\n"
+ "jalr $t9\n"
+ " nop\n"
+ : "=r"(ret), "=r"(err_stat)
+ : "0"(ret),
+ "r"(data)
+ // a2 is in the clober list so inline assembly can not change its
+ // value.
+ : "memory", "ra", "t9", "a2");
+ }
+
+ // Set an error status so it can be used outside of this function
+ *err_ret = err_stat;
+
+ return ret;
+}
+#endif // defined(__mips__)
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/syscall.h b/sandbox/linux/seccomp-bpf/syscall.h
new file mode 100644
index 0000000000..ccfc88dcb3
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+
+#include <signal.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This purely static class can be used to perform system calls with some
+// low-level control.
+class SANDBOX_EXPORT Syscall {
+ public:
+ // InvalidCall() invokes Call() with a platform-appropriate syscall
+ // number that is guaranteed to not be implemented (i.e., normally
+ // returns -ENOSYS).
+ // This is primarily meant to be useful for writing sandbox policy
+ // unit tests.
+ static intptr_t InvalidCall();
+
+ // System calls can take up to six parameters (up to eight on some
+ // architectures). Traditionally, glibc
+ // implements this property by using variadic argument lists. This works, but
+ // confuses modern tools such as valgrind, because we are nominally passing
+ // uninitialized data whenever we call through this function and pass less
+ // than the full six arguments.
+ // So, instead, we use C++'s template system to achieve a very similar
+ // effect. C++ automatically sets the unused parameters to zero for us, and
+ // it also does the correct type expansion (e.g. from 32bit to 64bit) where
+ // necessary.
+ // We have to use C-style cast operators as we want to be able to accept both
+ // integer and pointer types.
+ template <class T0,
+ class T1,
+ class T2,
+ class T3,
+ class T4,
+ class T5,
+ class T6,
+ class T7>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ (intptr_t)p6,
+ (intptr_t)p7);
+ }
+
+ template <class T0,
+ class T1,
+ class T2,
+ class T3,
+ class T4,
+ class T5,
+ class T6>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ (intptr_t)p6,
+ 0);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4, class T5>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ 0,
+ 0);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4) {
+ return Call(nr, p0, p1, p2, p3, p4, 0, 0, 0);
+ }
+
+ template <class T0, class T1, class T2, class T3>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3) {
+ return Call(nr, p0, p1, p2, p3, 0, 0, 0, 0);
+ }
+
+ template <class T0, class T1, class T2>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2) {
+ return Call(nr, p0, p1, p2, 0, 0, 0, 0, 0);
+ }
+
+ template <class T0, class T1>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1) {
+ return Call(nr, p0, p1, 0, 0, 0, 0, 0, 0);
+ }
+
+ template <class T0>
+ static inline intptr_t Call(int nr, T0 p0) {
+ return Call(nr, p0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ static inline intptr_t Call(int nr) {
+ return Call(nr, 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ // Set the registers in |ctx| to match what they would be after a system call
+ // returning |ret_val|. |ret_val| must follow the Syscall::Call() convention
+ // of being -errno on errors.
+ static void PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx);
+
+ private:
+ // This performs system call |nr| with the arguments p0 to p7 from a constant
+ // userland address, which is for instance observable by seccomp-bpf filters.
+ // The constant userland address from which these system calls are made will
+ // be returned if |nr| is passed as -1.
+ // On error, this function will return a value between -1 and -4095 which
+ // should be interpreted as -errno.
+ static intptr_t Call(int nr,
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5,
+ intptr_t p6,
+ intptr_t p7);
+
+#if defined(__mips__)
+ // This function basically does on MIPS what SandboxSyscall() is doing on
+ // other architectures. However, because of specificity of MIPS regarding
+ // handling syscall errors, SandboxSyscall() is made as a wrapper for this
+ // function in order for SandboxSyscall() to behave more like on other
+ // architectures on places where return value from SandboxSyscall() is used
+ // directly (like in most tests).
+ // The syscall "nr" is called with arguments that are set in an array on which
+ // pointer "args" points to and an information weather there is an error or no
+ // is returned to SandboxSyscall() by err_stat.
+ static intptr_t SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_stat);
+#endif // defined(__mips__)
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Syscall);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
diff --git a/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
new file mode 100644
index 0000000000..5fdee6c495
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -0,0 +1,240 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+#include <asm/unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::ResultExpr;
+using sandbox::bpf_dsl::Trap;
+
+namespace sandbox {
+
+namespace {
+
+// Different platforms use different symbols for the six-argument version
+// of the mmap() system call. Test for the correct symbol at compile time.
+#ifdef __NR_mmap2
+const int kMMapNr = __NR_mmap2;
+#else
+const int kMMapNr = __NR_mmap;
+#endif
+
+TEST(Syscall, InvalidCallReturnsENOSYS) {
+ EXPECT_EQ(-ENOSYS, Syscall::InvalidCall());
+}
+
+TEST(Syscall, WellKnownEntryPoint) {
+// Test that Syscall::Call(-1) is handled specially. Don't do this on ARM,
+// where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
+// are still testing ARM code in the next set of tests.
+#if !defined(__arm__) && !defined(__aarch64__)
+ EXPECT_NE(Syscall::Call(-1), syscall(-1));
+#endif
+
+// If possible, test that Syscall::Call(-1) returns the address right
+// after
+// a kernel entry point.
+#if defined(__i386__)
+ EXPECT_EQ(0x80CDu, ((uint16_t*)Syscall::Call(-1))[-1]); // INT 0x80
+#elif defined(__x86_64__)
+ EXPECT_EQ(0x050Fu, ((uint16_t*)Syscall::Call(-1))[-1]); // SYSCALL
+#elif defined(__arm__)
+#if defined(__thumb__)
+ EXPECT_EQ(0xDF00u, ((uint16_t*)Syscall::Call(-1))[-1]); // SWI 0
+#else
+ EXPECT_EQ(0xEF000000u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
+#endif
+#elif defined(__mips__)
+ // Opcode for MIPS sycall is in the lower 16-bits
+ EXPECT_EQ(0x0cu, (((uint32_t*)Syscall::Call(-1))[-1]) & 0x0000FFFF);
+#elif defined(__aarch64__)
+ EXPECT_EQ(0xD4000001u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
+#else
+#warning Incomplete test case; need port for target platform
+#endif
+}
+
+TEST(Syscall, TrivialSyscallNoArgs) {
+ // Test that we can do basic system calls
+ EXPECT_EQ(Syscall::Call(__NR_getpid), syscall(__NR_getpid));
+}
+
+TEST(Syscall, TrivialSyscallOneArg) {
+ int new_fd;
+ // Duplicate standard error and close it.
+ ASSERT_GE(new_fd = Syscall::Call(__NR_dup, 2), 0);
+ int close_return_value = IGNORE_EINTR(Syscall::Call(__NR_close, new_fd));
+ ASSERT_EQ(close_return_value, 0);
+}
+
+TEST(Syscall, TrivialFailingSyscall) {
+ errno = -42;
+ int ret = Syscall::Call(__NR_dup, -1);
+ ASSERT_EQ(-EBADF, ret);
+ // Verify that Syscall::Call does not touch errno.
+ ASSERT_EQ(-42, errno);
+}
+
+// SIGSYS trap handler that will be called on __NR_uname.
+intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) {
+ // |aux| is our BPF_AUX pointer.
+ std::vector<uint64_t>* const seen_syscall_args =
+ static_cast<std::vector<uint64_t>*>(aux);
+ BPF_ASSERT(arraysize(args.args) == 6);
+ seen_syscall_args->assign(args.args, args.args + arraysize(args.args));
+ return -ENOMEM;
+}
+
+class CopyAllArgsOnUnamePolicy : public bpf_dsl::Policy {
+ public:
+ explicit CopyAllArgsOnUnamePolicy(std::vector<uint64_t>* aux) : aux_(aux) {}
+ ~CopyAllArgsOnUnamePolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ return Trap(CopySyscallArgsToAux, aux_);
+ } else {
+ return Allow();
+ }
+ }
+
+ private:
+ std::vector<uint64_t>* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(CopyAllArgsOnUnamePolicy);
+};
+
+// We are testing Syscall::Call() by making use of a BPF filter that
+// allows us
+// to inspect the system call arguments that the kernel saw.
+BPF_TEST(Syscall,
+ SyntheticSixArgs,
+ CopyAllArgsOnUnamePolicy,
+ std::vector<uint64_t> /* (*BPF_AUX) */) {
+ const int kExpectedValue = 42;
+ // In this test we only pass integers to the kernel. We might want to make
+ // additional tests to try other types. What we will see depends on
+ // implementation details of kernel BPF filters and we will need to document
+ // the expected behavior very clearly.
+ int syscall_args[6];
+ for (size_t i = 0; i < arraysize(syscall_args); ++i) {
+ syscall_args[i] = kExpectedValue + i;
+ }
+
+ // We could use pretty much any system call we don't need here. uname() is
+ // nice because it doesn't have any dangerous side effects.
+ BPF_ASSERT(Syscall::Call(__NR_uname,
+ syscall_args[0],
+ syscall_args[1],
+ syscall_args[2],
+ syscall_args[3],
+ syscall_args[4],
+ syscall_args[5]) == -ENOMEM);
+
+ // We expect the trap handler to have copied the 6 arguments.
+ BPF_ASSERT(BPF_AUX->size() == 6);
+
+ // Don't loop here so that we can see which argument does cause the failure
+ // easily from the failing line.
+ // uint64_t is the type passed to our SIGSYS handler.
+ BPF_ASSERT((*BPF_AUX)[0] == static_cast<uint64_t>(syscall_args[0]));
+ BPF_ASSERT((*BPF_AUX)[1] == static_cast<uint64_t>(syscall_args[1]));
+ BPF_ASSERT((*BPF_AUX)[2] == static_cast<uint64_t>(syscall_args[2]));
+ BPF_ASSERT((*BPF_AUX)[3] == static_cast<uint64_t>(syscall_args[3]));
+ BPF_ASSERT((*BPF_AUX)[4] == static_cast<uint64_t>(syscall_args[4]));
+ BPF_ASSERT((*BPF_AUX)[5] == static_cast<uint64_t>(syscall_args[5]));
+}
+
+TEST(Syscall, ComplexSyscallSixArgs) {
+ int fd;
+ ASSERT_LE(0,
+ fd = Syscall::Call(__NR_openat, AT_FDCWD, "/dev/null", O_RDWR, 0L));
+
+ // Use mmap() to allocate some read-only memory
+ char* addr0;
+ ASSERT_NE(
+ (char*)NULL,
+ addr0 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
+ (void*)NULL,
+ 4096,
+ PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ fd,
+ 0L)));
+
+ // Try to replace the existing mapping with a read-write mapping
+ char* addr1;
+ ASSERT_EQ(addr0,
+ addr1 = reinterpret_cast<char*>(
+ Syscall::Call(kMMapNr,
+ addr0,
+ 4096L,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ fd,
+ 0L)));
+ ++*addr1; // This should not seg fault
+
+ // Clean up
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr1, 4096L));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
+
+ // Check that the offset argument (i.e. the sixth argument) is processed
+ // correctly.
+ ASSERT_GE(
+ fd = Syscall::Call(__NR_openat, AT_FDCWD, "/proc/self/exe", O_RDONLY, 0L),
+ 0);
+ char* addr2, *addr3;
+ ASSERT_NE((char*)NULL,
+ addr2 = reinterpret_cast<char*>(Syscall::Call(
+ kMMapNr, (void*)NULL, 8192L, PROT_READ, MAP_PRIVATE, fd, 0L)));
+ ASSERT_NE((char*)NULL,
+ addr3 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
+ (void*)NULL,
+ 4096L,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+#if defined(__NR_mmap2)
+ 1L
+#else
+ 4096L
+#endif
+ )));
+ EXPECT_EQ(0, memcmp(addr2 + 4096, addr3, 4096));
+
+ // Just to be absolutely on the safe side, also verify that the file
+ // contents matches what we are getting from a read() operation.
+ char buf[8192];
+ EXPECT_EQ(8192, Syscall::Call(__NR_read, fd, buf, 8192L));
+ EXPECT_EQ(0, memcmp(addr2, buf, 8192));
+
+ // Clean up
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr2, 8192L));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr3, 4096L));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/trap.cc b/sandbox/linux/seccomp-bpf/trap.cc
new file mode 100644
index 0000000000..8f559e53b1
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap.cc
@@ -0,0 +1,390 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/syscall.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+
+namespace {
+
+struct arch_sigsys {
+ void* ip;
+ int nr;
+ unsigned int arch;
+};
+
+const int kCapacityIncrement = 20;
+
+// Unsafe traps can only be turned on, if the user explicitly allowed them
+// by setting the CHROME_SANDBOX_DEBUGGING environment variable.
+const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
+
+// We need to tell whether we are performing a "normal" callback, or
+// whether we were called recursively from within a UnsafeTrap() callback.
+// This is a little tricky to do, because we need to somehow get access to
+// per-thread data from within a signal context. Normal TLS storage is not
+// safely accessible at this time. We could roll our own, but that involves
+// a lot of complexity. Instead, we co-opt one bit in the signal mask.
+// If BUS is blocked, we assume that we have been called recursively.
+// There is a possibility for collision with other code that needs to do
+// this, but in practice the risks are low.
+// If SIGBUS turns out to be a problem, we could instead co-opt one of the
+// realtime signals. There are plenty of them. Unfortunately, there is no
+// way to mark a signal as allocated. So, the potential for collision is
+// possibly even worse.
+bool GetIsInSigHandler(const ucontext_t* ctx) {
+ // Note: on Android, sigismember does not take a pointer to const.
+ return sigismember(const_cast<sigset_t*>(&ctx->uc_sigmask), LINUX_SIGBUS);
+}
+
+void SetIsInSigHandler() {
+ sigset_t mask;
+ if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGBUS) ||
+ sandbox::sys_sigprocmask(LINUX_SIG_BLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to block SIGBUS");
+ }
+}
+
+bool IsDefaultSignalAction(const struct sigaction& sa) {
+ if (sa.sa_flags & SA_SIGINFO || sa.sa_handler != SIG_DFL) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+Trap::Trap()
+ : trap_array_(NULL),
+ trap_array_size_(0),
+ trap_array_capacity_(0),
+ has_unsafe_traps_(false) {
+ // Set new SIGSYS handler
+ struct sigaction sa = {};
+ // In some toolchain, sa_sigaction is not declared in struct sigaction.
+ // So, here cast the pointer to the sa_handler's type. This works because
+ // |sa_handler| and |sa_sigaction| shares the same memory.
+ sa.sa_handler = reinterpret_cast<void (*)(int)>(SigSysAction);
+ sa.sa_flags = LINUX_SA_SIGINFO | LINUX_SA_NODEFER;
+ struct sigaction old_sa = {};
+ if (sys_sigaction(LINUX_SIGSYS, &sa, &old_sa) < 0) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+
+ if (!IsDefaultSignalAction(old_sa)) {
+ static const char kExistingSIGSYSMsg[] =
+ "Existing signal handler when trying to install SIGSYS. SIGSYS needs "
+ "to be reserved for seccomp-bpf.";
+ DLOG(FATAL) << kExistingSIGSYSMsg;
+ LOG(ERROR) << kExistingSIGSYSMsg;
+ }
+
+ // Unmask SIGSYS
+ sigset_t mask;
+ if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGSYS) ||
+ sys_sigprocmask(LINUX_SIG_UNBLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+}
+
+bpf_dsl::TrapRegistry* Trap::Registry() {
+ // Note: This class is not thread safe. It is the caller's responsibility
+ // to avoid race conditions. Normally, this is a non-issue as the sandbox
+ // can only be initialized if there are no other threads present.
+ // Also, this is not a normal singleton. Once created, the global trap
+ // object must never be destroyed again.
+ if (!global_trap_) {
+ global_trap_ = new Trap();
+ if (!global_trap_) {
+ SANDBOX_DIE("Failed to allocate global trap handler");
+ }
+ }
+ return global_trap_;
+}
+
+void Trap::SigSysAction(int nr, LinuxSigInfo* info, void* void_context) {
+ if (info) {
+ MSAN_UNPOISON(info, sizeof(*info));
+ }
+
+ // Obtain the signal context. This, most notably, gives us access to
+ // all CPU registers at the time of the signal.
+ ucontext_t* ctx = reinterpret_cast<ucontext_t*>(void_context);
+ if (ctx) {
+ MSAN_UNPOISON(ctx, sizeof(*ctx));
+ }
+
+ if (!global_trap_) {
+ RAW_SANDBOX_DIE(
+ "This can't happen. Found no global singleton instance "
+ "for Trap() handling.");
+ }
+ global_trap_->SigSys(nr, info, ctx);
+}
+
+void Trap::SigSys(int nr, LinuxSigInfo* info, ucontext_t* ctx) {
+ // Signal handlers should always preserve "errno". Otherwise, we could
+ // trigger really subtle bugs.
+ const int old_errno = errno;
+
+ // Various sanity checks to make sure we actually received a signal
+ // triggered by a BPF filter. If something else triggered SIGSYS
+ // (e.g. kill()), there is really nothing we can do with this signal.
+ if (nr != LINUX_SIGSYS || info->si_code != SYS_SECCOMP || !ctx ||
+ info->si_errno <= 0 ||
+ static_cast<size_t>(info->si_errno) > trap_array_size_) {
+ // ATI drivers seem to send SIGSYS, so this cannot be FATAL.
+ // See crbug.com/178166.
+ // TODO(jln): add a DCHECK or move back to FATAL.
+ RAW_LOG(ERROR, "Unexpected SIGSYS received.");
+ errno = old_errno;
+ return;
+ }
+
+
+ // Obtain the siginfo information that is specific to SIGSYS. Unfortunately,
+ // most versions of glibc don't include this information in siginfo_t. So,
+ // we need to explicitly copy it into a arch_sigsys structure.
+ struct arch_sigsys sigsys;
+ memcpy(&sigsys, &info->_sifields, sizeof(sigsys));
+
+#if defined(__mips__)
+ // When indirect syscall (syscall(__NR_foo, ...)) is made on Mips, the
+ // number in register SECCOMP_SYSCALL(ctx) is always __NR_syscall and the
+ // real number of a syscall (__NR_foo) is in SECCOMP_PARM1(ctx)
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) &&
+ sigsys.nr != static_cast<int>(SECCOMP_PARM1(ctx));
+#else
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx));
+#endif
+
+ // Some more sanity checks.
+ if (sigsys.ip != reinterpret_cast<void*>(SECCOMP_IP(ctx)) ||
+ sigsys_nr_is_bad || sigsys.arch != SECCOMP_ARCH) {
+ // TODO(markus):
+ // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal
+ // safe and can lead to bugs. We should eventually implement a different
+ // logging and reporting mechanism that is safe to be called from
+ // the sigSys() handler.
+ RAW_SANDBOX_DIE("Sanity checks are failing after receiving SIGSYS.");
+ }
+
+ intptr_t rc;
+ if (has_unsafe_traps_ && GetIsInSigHandler(ctx)) {
+ errno = old_errno;
+ if (sigsys.nr == __NR_clone) {
+ RAW_SANDBOX_DIE("Cannot call clone() from an UnsafeTrap() handler.");
+ }
+#if defined(__mips__)
+ // Mips supports up to eight arguments for syscall.
+ // However, seccomp bpf can filter only up to six arguments, so using eight
+ // arguments has sense only when using UnsafeTrap() handler.
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx),
+ SECCOMP_PARM7(ctx),
+ SECCOMP_PARM8(ctx));
+#else
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx));
+#endif // defined(__mips__)
+ } else {
+ const TrapKey& trap = trap_array_[info->si_errno - 1];
+ if (!trap.safe) {
+ SetIsInSigHandler();
+ }
+
+ // Copy the seccomp-specific data into a arch_seccomp_data structure. This
+ // is what we are showing to TrapFnc callbacks that the system call
+ // evaluator registered with the sandbox.
+ struct arch_seccomp_data data = {
+ static_cast<int>(SECCOMP_SYSCALL(ctx)),
+ SECCOMP_ARCH,
+ reinterpret_cast<uint64_t>(sigsys.ip),
+ {static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM6(ctx))}};
+
+ // Now call the TrapFnc callback associated with this particular instance
+ // of SECCOMP_RET_TRAP.
+ rc = trap.fnc(data, const_cast<void*>(trap.aux));
+ }
+
+ // Update the CPU register that stores the return code of the system call
+ // that we just handled, and restore "errno" to the value that it had
+ // before entering the signal handler.
+ Syscall::PutValueInUcontext(rc, ctx);
+ errno = old_errno;
+
+ return;
+}
+
+bool Trap::TrapKey::operator<(const TrapKey& o) const {
+ if (fnc != o.fnc) {
+ return fnc < o.fnc;
+ } else if (aux != o.aux) {
+ return aux < o.aux;
+ } else {
+ return safe < o.safe;
+ }
+}
+
+uint16_t Trap::Add(TrapFnc fnc, const void* aux, bool safe) {
+ if (!safe && !SandboxDebuggingAllowedByUser()) {
+ // Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable,
+ // we never return an ErrorCode that is marked as "unsafe". This also
+ // means, the BPF compiler will never emit code that allow unsafe system
+ // calls to by-pass the filter (because they use the magic return address
+ // from Syscall::Call(-1)).
+
+ // This SANDBOX_DIE() can optionally be removed. It won't break security,
+ // but it might make error messages from the BPF compiler a little harder
+ // to understand. Removing the SANDBOX_DIE() allows callers to easily check
+ // whether unsafe traps are supported (by checking whether the returned
+ // ErrorCode is ET_INVALID).
+ SANDBOX_DIE(
+ "Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING "
+ "is enabled");
+
+ return 0;
+ }
+
+ // Each unique pair of TrapFnc and auxiliary data make up a distinct instance
+ // of a SECCOMP_RET_TRAP.
+ TrapKey key(fnc, aux, safe);
+
+ // We return unique identifiers together with SECCOMP_RET_TRAP. This allows
+ // us to associate trap with the appropriate handler. The kernel allows us
+ // identifiers in the range from 0 to SECCOMP_RET_DATA (0xFFFF). We want to
+ // avoid 0, as it could be confused for a trap without any specific id.
+ // The nice thing about sequentially numbered identifiers is that we can also
+ // trivially look them up from our signal handler without making any system
+ // calls that might be async-signal-unsafe.
+ // In order to do so, we store all of our traps in a C-style trap_array_.
+
+ TrapIds::const_iterator iter = trap_ids_.find(key);
+ if (iter != trap_ids_.end()) {
+ // We have seen this pair before. Return the same id that we assigned
+ // earlier.
+ return iter->second;
+ }
+
+ // This is a new pair. Remember it and assign a new id.
+ if (trap_array_size_ >= SECCOMP_RET_DATA /* 0xFFFF */ ||
+ trap_array_size_ >= std::numeric_limits<uint16_t>::max()) {
+ // In practice, this is pretty much impossible to trigger, as there
+ // are other kernel limitations that restrict overall BPF program sizes.
+ SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
+ }
+
+ // Our callers ensure that there are no other threads accessing trap_array_
+ // concurrently (typically this is done by ensuring that we are single-
+ // threaded while the sandbox is being set up). But we nonetheless are
+ // modifying a live data structure that could be accessed any time a
+ // system call is made; as system calls could be triggering SIGSYS.
+ // So, we have to be extra careful that we update trap_array_ atomically.
+ // In particular, this means we shouldn't be using realloc() to resize it.
+ // Instead, we allocate a new array, copy the values, and then switch the
+ // pointer. We only really care about the pointer being updated atomically
+ // and the data that is pointed to being valid, as these are the only
+ // values accessed from the signal handler. It is OK if trap_array_size_
+ // is inconsistent with the pointer, as it is monotonously increasing.
+ // Also, we only care about compiler barriers, as the signal handler is
+ // triggered synchronously from a system call. We don't have to protect
+ // against issues with the memory model or with completely asynchronous
+ // events.
+ if (trap_array_size_ >= trap_array_capacity_) {
+ trap_array_capacity_ += kCapacityIncrement;
+ TrapKey* old_trap_array = trap_array_;
+ TrapKey* new_trap_array = new TrapKey[trap_array_capacity_];
+ std::copy_n(old_trap_array, trap_array_size_, new_trap_array);
+
+ // Language specs are unclear on whether the compiler is allowed to move
+ // the "delete[]" above our preceding assignments and/or memory moves,
+ // iff the compiler believes that "delete[]" doesn't have any other
+ // global side-effects.
+ // We insert optimization barriers to prevent this from happening.
+ // The first barrier is probably not needed, but better be explicit in
+ // what we want to tell the compiler.
+ // The clang developer mailing list couldn't answer whether this is a
+ // legitimate worry; but they at least thought that the barrier is
+ // sufficient to prevent the (so far hypothetical) problem of re-ordering
+ // of instructions by the compiler.
+ //
+ // TODO(mdempsky): Try to clean this up using base/atomicops or C++11
+ // atomics; see crbug.com/414363.
+ asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory");
+ trap_array_ = new_trap_array;
+ asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory");
+
+ delete[] old_trap_array;
+ }
+
+ uint16_t id = trap_array_size_ + 1;
+ trap_ids_[key] = id;
+ trap_array_[trap_array_size_] = key;
+ trap_array_size_++;
+ return id;
+}
+
+bool Trap::SandboxDebuggingAllowedByUser() {
+ const char* debug_flag = getenv(kSandboxDebuggingEnv);
+ return debug_flag && *debug_flag;
+}
+
+bool Trap::EnableUnsafeTraps() {
+ if (!has_unsafe_traps_) {
+ // Unsafe traps are a one-way fuse. Once enabled, they can never be turned
+ // off again.
+ // We only allow enabling unsafe traps, if the user explicitly set an
+ // appropriate environment variable. This prevents bugs that accidentally
+ // disable all sandboxing for all users.
+ if (SandboxDebuggingAllowedByUser()) {
+ // We only ever print this message once, when we enable unsafe traps the
+ // first time.
+ SANDBOX_INFO("WARNING! Disabling sandbox for debugging purposes");
+ has_unsafe_traps_ = true;
+ } else {
+ SANDBOX_INFO(
+ "Cannot disable sandbox and use unsafe traps unless "
+ "CHROME_SANDBOX_DEBUGGING is turned on first");
+ }
+ }
+ // Returns the, possibly updated, value of has_unsafe_traps_.
+ return has_unsafe_traps_;
+}
+
+Trap* Trap::global_trap_;
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/trap.h b/sandbox/linux/seccomp-bpf/trap.h
new file mode 100644
index 0000000000..50ac3fd1c3
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+
+#include <stdint.h>
+
+#include <map>
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// The Trap class allows a BPF filter program to branch out to user space by
+// raising a SIGSYS signal.
+// N.B.: This class does not perform any synchronization operations. If
+// modifications are made to any of the traps, it is the caller's
+// responsibility to ensure that this happens in a thread-safe fashion.
+// Preferably, that means that no other threads should be running at that
+// time. For the purposes of our sandbox, this assertion should always be
+// true. Threads are incompatible with the seccomp sandbox anyway.
+class SANDBOX_EXPORT Trap : public bpf_dsl::TrapRegistry {
+ public:
+ uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override;
+
+ bool EnableUnsafeTraps() override;
+
+ // Registry returns the trap registry used by Trap's SIGSYS handler,
+ // creating it if necessary.
+ static bpf_dsl::TrapRegistry* Registry();
+
+ // SandboxDebuggingAllowedByUser returns whether the
+ // "CHROME_SANDBOX_DEBUGGING" environment variable is set.
+ static bool SandboxDebuggingAllowedByUser();
+
+ private:
+ struct TrapKey {
+ TrapKey() : fnc(NULL), aux(NULL), safe(false) {}
+ TrapKey(TrapFnc f, const void* a, bool s) : fnc(f), aux(a), safe(s) {}
+ TrapFnc fnc;
+ const void* aux;
+ bool safe;
+ bool operator<(const TrapKey&) const;
+ };
+ typedef std::map<TrapKey, uint16_t> TrapIds;
+
+ // Our constructor is private. A shared global instance is created
+ // automatically as needed.
+ Trap();
+
+ // The destructor is unimplemented as destroying this object would
+ // break subsequent system calls that trigger a SIGSYS.
+ ~Trap() = delete;
+
+ static void SigSysAction(int nr, LinuxSigInfo* info, void* void_context);
+
+ // Make sure that SigSys is not inlined in order to get slightly better crash
+ // dumps.
+ void SigSys(int nr, LinuxSigInfo* info, ucontext_t* ctx)
+ __attribute__((noinline));
+ // We have a global singleton that handles all of our SIGSYS traps. This
+ // variable must never be deallocated after it has been set up initially, as
+ // there is no way to reset in-kernel BPF filters that generate SIGSYS
+ // events.
+ static Trap* global_trap_;
+
+ TrapIds trap_ids_; // Maps from TrapKeys to numeric ids
+ TrapKey* trap_array_; // Array of TrapKeys indexed by ids
+ size_t trap_array_size_; // Currently used size of array
+ size_t trap_array_capacity_; // Currently allocated capacity of array
+ bool has_unsafe_traps_; // Whether unsafe traps have been enabled
+
+ // Copying and assigning is unimplemented. It doesn't make sense for a
+ // singleton.
+ DISALLOW_COPY_AND_ASSIGN(Trap);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
diff --git a/sandbox/linux/seccomp-bpf/trap_unittest.cc b/sandbox/linux/seccomp-bpf/trap_unittest.cc
new file mode 100644
index 0000000000..99f94bfb3a
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
+#include <signal.h>
+
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+SANDBOX_TEST_ALLOW_NOISE(Trap, SigSysAction) {
+ // This creates a global Trap instance, and registers the signal handler
+ // (Trap::SigSysAction).
+ Trap::Registry();
+
+ // Send SIGSYS to self. If signal handler (SigSysAction) is not registered,
+ // the process will be terminated with status code -SIGSYS.
+ // Note that, SigSysAction handler would output an error message
+ // "Unexpected SIGSYS received." so it is necessary to allow the noise.
+ raise(SIGSYS);
+}
+
+} // namespace
+} // namespace sandbox
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
new file mode 100644
index 0000000000..c77fd9efde
--- /dev/null
+++ b/sandbox/linux/services/credentials.cc
@@ -0,0 +1,299 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/credentials.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/template_util.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/namespace_utils.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/system_headers/capability.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "third_party/valgrind/valgrind.h"
+
+namespace sandbox {
+
+namespace {
+
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
+// Checks that the set of RES-uids and the set of RES-gids have
+// one element each and return that element in |resuid| and |resgid|
+// respectively. It's ok to pass NULL as one or both of the ids.
+bool GetRESIds(uid_t* resuid, gid_t* resgid) {
+ uid_t ruid, euid, suid;
+ gid_t rgid, egid, sgid;
+ PCHECK(sys_getresuid(&ruid, &euid, &suid) == 0);
+ PCHECK(sys_getresgid(&rgid, &egid, &sgid) == 0);
+ const bool uids_are_equal = (ruid == euid) && (ruid == suid);
+ const bool gids_are_equal = (rgid == egid) && (rgid == sgid);
+ if (!uids_are_equal || !gids_are_equal) return false;
+ if (resuid) *resuid = euid;
+ if (resgid) *resgid = egid;
+ return true;
+}
+
+const int kExitSuccess = 0;
+
+int ChrootToSelfFdinfo(void*) {
+ RAW_CHECK(sys_chroot("/proc/self/fdinfo/") == 0);
+
+ // CWD is essentially an implicit file descriptor, so be careful to not
+ // leave it behind.
+ RAW_CHECK(chdir("/") == 0);
+ _exit(kExitSuccess);
+}
+
+// chroot() to an empty dir that is "safe". To be safe, it must not contain
+// any subdirectory (chroot-ing there would allow a chroot escape) and it must
+// be impossible to create an empty directory there.
+// We achieve this by doing the following:
+// 1. We create a new process sharing file system information.
+// 2. In the child, we chroot to /proc/self/fdinfo/
+// This is already "safe", since fdinfo/ does not contain another directory and
+// one cannot create another directory there.
+// 3. The process dies
+// After (3) happens, the directory is not available anymore in /proc.
+bool ChrootToSafeEmptyDir() {
+ // We need to chroot to a fdinfo that is unique to a process and have that
+ // process die.
+ // 1. We don't want to simply fork() because duplicating the page tables is
+ // slow with a big address space.
+ // 2. We do not use a regular thread (that would unshare CLONE_FILES) because
+ // when we are in a PID namespace, we cannot easily get a handle to the
+ // /proc/tid directory for the thread (since /proc may not be aware of the
+ // PID namespace). With a process, we can just use /proc/self.
+ pid_t pid = -1;
+ char stack_buf[PTHREAD_STACK_MIN];
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
+ // The stack grows downward.
+ void* stack = stack_buf + sizeof(stack_buf);
+#else
+#error "Unsupported architecture"
+#endif
+
+ pid = clone(ChrootToSelfFdinfo, stack,
+ CLONE_VM | CLONE_VFORK | CLONE_FS | LINUX_SIGCHLD, nullptr,
+ nullptr, nullptr, nullptr);
+ PCHECK(pid != -1);
+
+ int status = -1;
+ PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
+
+ return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess;
+}
+
+// CHECK() that an attempt to move to a new user namespace raised an expected
+// errno.
+void CheckCloneNewUserErrno(int error) {
+ // EPERM can happen if already in a chroot. EUSERS if too many nested
+ // namespaces are used. EINVAL for kernels that don't support the feature.
+ // Valgrind will ENOSYS unshare().
+ PCHECK(error == EPERM || error == EUSERS || error == EINVAL ||
+ error == ENOSYS);
+}
+
+// Converts a Capability to the corresponding Linux CAP_XXX value.
+int CapabilityToKernelValue(Credentials::Capability cap) {
+ switch (cap) {
+ case Credentials::Capability::SYS_CHROOT:
+ return CAP_SYS_CHROOT;
+ case Credentials::Capability::SYS_ADMIN:
+ return CAP_SYS_ADMIN;
+ }
+
+ LOG(FATAL) << "Invalid Capability: " << static_cast<int>(cap);
+ return 0;
+}
+
+} // namespace.
+
+// static
+bool Credentials::DropAllCapabilities(int proc_fd) {
+ if (!SetCapabilities(proc_fd, std::vector<Capability>())) {
+ return false;
+ }
+
+ CHECK(!HasAnyCapability());
+ return true;
+}
+
+// static
+bool Credentials::DropAllCapabilities() {
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+ return Credentials::DropAllCapabilities(proc_fd.get());
+}
+
+// static
+bool Credentials::DropAllCapabilitiesOnCurrentThread() {
+ return SetCapabilitiesOnCurrentThread(std::vector<Capability>());
+}
+
+// static
+bool Credentials::SetCapabilitiesOnCurrentThread(
+ const std::vector<Capability>& caps) {
+ struct cap_hdr hdr = {};
+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
+ struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}};
+
+ // Initially, cap has no capability flags set. Enable the effective and
+ // permitted flags only for the requested capabilities.
+ for (const Capability cap : caps) {
+ const int cap_num = CapabilityToKernelValue(cap);
+ const size_t index = CAP_TO_INDEX(cap_num);
+ const uint32_t mask = CAP_TO_MASK(cap_num);
+ data[index].effective |= mask;
+ data[index].permitted |= mask;
+ }
+
+ return sys_capset(&hdr, data) == 0;
+}
+
+// static
+bool Credentials::SetCapabilities(int proc_fd,
+ const std::vector<Capability>& caps) {
+ DCHECK_LE(0, proc_fd);
+
+#if !defined(THREAD_SANITIZER)
+ // With TSAN, accept to break the security model as it is a testing
+ // configuration.
+ CHECK(ThreadHelpers::IsSingleThreaded(proc_fd));
+#endif
+
+ return SetCapabilitiesOnCurrentThread(caps);
+}
+
+bool Credentials::HasAnyCapability() {
+ struct cap_hdr hdr = {};
+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
+ struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}};
+
+ PCHECK(sys_capget(&hdr, data) == 0);
+
+ for (size_t i = 0; i < arraysize(data); ++i) {
+ if (data[i].effective || data[i].permitted || data[i].inheritable) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Credentials::HasCapability(Capability cap) {
+ struct cap_hdr hdr = {};
+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
+ struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}};
+
+ PCHECK(sys_capget(&hdr, data) == 0);
+
+ const int cap_num = CapabilityToKernelValue(cap);
+ const size_t index = CAP_TO_INDEX(cap_num);
+ const uint32_t mask = CAP_TO_MASK(cap_num);
+
+ return (data[index].effective | data[index].permitted |
+ data[index].inheritable) &
+ mask;
+}
+
+// static
+bool Credentials::CanCreateProcessInNewUserNS() {
+ // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
+ // so always consider UserNS unsupported there.
+ if (IsRunningOnValgrind()) {
+ return false;
+ }
+
+#if defined(THREAD_SANITIZER)
+ // With TSAN, processes will always have threads running and can never
+ // enter a new user namespace with MoveToNewUserNS().
+ return false;
+#endif
+
+ // This is roughly a fork().
+ const pid_t pid = sys_clone(CLONE_NEWUSER | SIGCHLD, 0, 0, 0, 0);
+
+ if (pid == -1) {
+ CheckCloneNewUserErrno(errno);
+ return false;
+ }
+
+ // The parent process could have had threads. In the child, these threads
+ // have disappeared. Make sure to not do anything in the child, as this is a
+ // fragile execution environment.
+ if (pid == 0) {
+ _exit(kExitSuccess);
+ }
+
+ // Always reap the child.
+ int status = -1;
+ PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kExitSuccess, WEXITSTATUS(status));
+
+ // clone(2) succeeded, we can use CLONE_NEWUSER.
+ return true;
+}
+
+bool Credentials::MoveToNewUserNS() {
+ uid_t uid;
+ gid_t gid;
+ if (!GetRESIds(&uid, &gid)) {
+ // If all the uids (or gids) are not equal to each other, the security
+ // model will most likely confuse the caller, abort.
+ DVLOG(1) << "uids or gids differ!";
+ return false;
+ }
+ int ret = sys_unshare(CLONE_NEWUSER);
+ if (ret) {
+ const int unshare_errno = errno;
+ VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available "
+ << "on this kernel.";
+ CheckCloneNewUserErrno(unshare_errno);
+ return false;
+ }
+
+ if (NamespaceUtils::KernelSupportsDenySetgroups()) {
+ PCHECK(NamespaceUtils::DenySetgroups());
+ }
+
+ // The current {r,e,s}{u,g}id is now an overflow id (c.f.
+ // /proc/sys/kernel/overflowuid). Setup the uid and gid maps.
+ DCHECK(GetRESIds(NULL, NULL));
+ const char kGidMapFile[] = "/proc/self/gid_map";
+ const char kUidMapFile[] = "/proc/self/uid_map";
+ PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
+ PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
+ DCHECK(GetRESIds(NULL, NULL));
+ return true;
+}
+
+bool Credentials::DropFileSystemAccess(int proc_fd) {
+ CHECK_LE(0, proc_fd);
+
+ CHECK(ChrootToSafeEmptyDir());
+ CHECK(!base::DirectoryExists(base::FilePath("/proc")));
+ CHECK(!ProcUtil::HasOpenDirectory(proc_fd));
+ // We never let this function fail.
+ return true;
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/credentials.h b/sandbox/linux/services/credentials.h
new file mode 100644
index 0000000000..0001dc7328
--- /dev/null
+++ b/sandbox/linux/services/credentials.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_CREDENTIALS_H_
+#define SANDBOX_LINUX_SERVICES_CREDENTIALS_H_
+
+#include "build/build_config.h"
+// Link errors are tedious to track, raise a compile-time error instead.
+#if defined(OS_ANDROID)
+#error "Android is not supported."
+#endif // defined(OS_ANDROID).
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/system_headers/capability.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This class should be used to manipulate the current process' credentials.
+// It is currently a stub used to manipulate POSIX.1e capabilities as
+// implemented by the Linux kernel.
+class SANDBOX_EXPORT Credentials {
+ public:
+ // For brevity, we only expose enums for the subset of capabilities we use.
+ // This can be expanded as the need arises.
+ enum class Capability {
+ SYS_CHROOT,
+ SYS_ADMIN,
+ };
+
+ // Drop all capabilities in the effective, inheritable and permitted sets for
+ // the current thread. For security reasons, since capabilities are
+ // per-thread, the caller is responsible for ensuring it is single-threaded
+ // when calling this API.
+ // |proc_fd| must be a file descriptor to /proc/ and remains owned by
+ // the caller.
+ static bool DropAllCapabilities(int proc_fd) WARN_UNUSED_RESULT;
+ // A similar API which assumes that it can open /proc/self/ by itself.
+ static bool DropAllCapabilities() WARN_UNUSED_RESULT;
+ // Sets the effective and permitted capability sets for the current thread to
+ // the list of capabiltiies in |caps|. All other capability flags are cleared.
+ static bool SetCapabilities(int proc_fd,
+ const std::vector<Capability>& caps)
+ WARN_UNUSED_RESULT;
+
+ // Versions of the above functions which do not check that the process is
+ // single-threaded. After calling these functions, capabilities of other
+ // threads will not be changed. This is dangerous, do not use unless you nkow
+ // what you are doing.
+ static bool DropAllCapabilitiesOnCurrentThread() WARN_UNUSED_RESULT;
+ static bool SetCapabilitiesOnCurrentThread(
+ const std::vector<Capability>& caps) WARN_UNUSED_RESULT;
+
+ // Returns true if the current thread has either the effective, permitted, or
+ // inheritable flag set for the given capability.
+ static bool HasCapability(Capability cap);
+
+ // Return true iff there is any capability in any of the capabilities sets
+ // of the current thread.
+ static bool HasAnyCapability();
+
+ // Returns whether the kernel supports CLONE_NEWUSER and whether it would be
+ // possible to immediately move to a new user namespace. There is no point
+ // in using this method right before calling MoveToNewUserNS(), simply call
+ // MoveToNewUserNS() immediately. This method is only useful to test the
+ // ability to move to a user namespace ahead of time.
+ static bool CanCreateProcessInNewUserNS();
+
+ // Move the current process to a new "user namespace" as supported by Linux
+ // 3.8+ (CLONE_NEWUSER).
+ // The uid map will be set-up so that the perceived uid and gid will not
+ // change.
+ // If this call succeeds, the current process will be granted a full set of
+ // capabilities in the new namespace.
+ // This will fail if the process is not mono-threaded.
+ static bool MoveToNewUserNS() WARN_UNUSED_RESULT;
+
+ // Remove the ability of the process to access the file system. File
+ // descriptors which are already open prior to calling this API remain
+ // available.
+ // The implementation currently uses chroot(2) and requires CAP_SYS_CHROOT.
+ // CAP_SYS_CHROOT can be acquired by using the MoveToNewUserNS() API.
+ // |proc_fd| must be a file descriptor to /proc/ and must be the only open
+ // directory file descriptor of the process.
+ //
+ // CRITICAL:
+ // - the caller must close |proc_fd| eventually or access to the file
+ // system can be recovered.
+ // - DropAllCapabilities() must be called to prevent escapes.
+ static bool DropFileSystemAccess(int proc_fd) WARN_UNUSED_RESULT;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Credentials);
+};
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SERVICES_CREDENTIALS_H_
diff --git a/sandbox/linux/services/credentials_unittest.cc b/sandbox/linux/services/credentials_unittest.cc
new file mode 100644
index 0000000000..6b93c86c3e
--- /dev/null
+++ b/sandbox/linux/services/credentials_unittest.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/credentials.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/capability.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/capability.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+struct CapFreeDeleter {
+ inline void operator()(cap_t cap) const {
+ int ret = cap_free(cap);
+ CHECK_EQ(0, ret);
+ }
+};
+
+// Wrapper to manage libcap2's cap_t type.
+typedef scoped_ptr<typeof(*((cap_t)0)), CapFreeDeleter> ScopedCap;
+
+bool WorkingDirectoryIsRoot() {
+ char current_dir[PATH_MAX];
+ char* cwd = getcwd(current_dir, sizeof(current_dir));
+ PCHECK(cwd);
+ if (strcmp("/", cwd)) return false;
+
+ // The current directory is the root. Add a few paranoid checks.
+ struct stat current;
+ CHECK_EQ(0, stat(".", &current));
+ struct stat parrent;
+ CHECK_EQ(0, stat("..", &parrent));
+ CHECK_EQ(current.st_dev, parrent.st_dev);
+ CHECK_EQ(current.st_ino, parrent.st_ino);
+ CHECK_EQ(current.st_mode, parrent.st_mode);
+ CHECK_EQ(current.st_uid, parrent.st_uid);
+ CHECK_EQ(current.st_gid, parrent.st_gid);
+ return true;
+}
+
+SANDBOX_TEST(Credentials, DropAllCaps) {
+ CHECK(Credentials::DropAllCapabilities());
+ CHECK(!Credentials::HasAnyCapability());
+}
+
+SANDBOX_TEST(Credentials, MoveToNewUserNS) {
+ CHECK(Credentials::DropAllCapabilities());
+ bool moved_to_new_ns = Credentials::MoveToNewUserNS();
+ fprintf(stdout,
+ "Unprivileged CLONE_NEWUSER supported: %s\n",
+ moved_to_new_ns ? "true." : "false.");
+ fflush(stdout);
+ if (!moved_to_new_ns) {
+ fprintf(stdout, "This kernel does not support unprivileged namespaces. "
+ "USERNS tests will succeed without running.\n");
+ fflush(stdout);
+ return;
+ }
+ CHECK(Credentials::HasAnyCapability());
+ CHECK(Credentials::DropAllCapabilities());
+ CHECK(!Credentials::HasAnyCapability());
+}
+
+SANDBOX_TEST(Credentials, CanCreateProcessInNewUserNS) {
+ CHECK(Credentials::DropAllCapabilities());
+ bool user_ns_supported = Credentials::CanCreateProcessInNewUserNS();
+ bool moved_to_new_ns = Credentials::MoveToNewUserNS();
+ CHECK_EQ(user_ns_supported, moved_to_new_ns);
+}
+
+SANDBOX_TEST(Credentials, UidIsPreserved) {
+ CHECK(Credentials::DropAllCapabilities());
+ uid_t old_ruid, old_euid, old_suid;
+ gid_t old_rgid, old_egid, old_sgid;
+ PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid));
+ PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid));
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS()) return;
+ uid_t new_ruid, new_euid, new_suid;
+ PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid));
+ CHECK(old_ruid == new_ruid);
+ CHECK(old_euid == new_euid);
+ CHECK(old_suid == new_suid);
+
+ gid_t new_rgid, new_egid, new_sgid;
+ PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid));
+ CHECK(old_rgid == new_rgid);
+ CHECK(old_egid == new_egid);
+ CHECK(old_sgid == new_sgid);
+}
+
+bool NewUserNSCycle() {
+ if (!Credentials::MoveToNewUserNS() ||
+ !Credentials::HasAnyCapability() ||
+ !Credentials::DropAllCapabilities() ||
+ Credentials::HasAnyCapability()) {
+ return false;
+ }
+ return true;
+}
+
+SANDBOX_TEST(Credentials, NestedUserNS) {
+ CHECK(Credentials::DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS()) return;
+ CHECK(Credentials::DropAllCapabilities());
+ // As of 3.12, the kernel has a limit of 32. See create_user_ns().
+ const int kNestLevel = 10;
+ for (int i = 0; i < kNestLevel; ++i) {
+ CHECK(NewUserNSCycle()) << "Creating new user NS failed at iteration "
+ << i << ".";
+ }
+}
+
+// Test the WorkingDirectoryIsRoot() helper.
+SANDBOX_TEST(Credentials, CanDetectRoot) {
+ PCHECK(0 == chdir("/proc/"));
+ CHECK(!WorkingDirectoryIsRoot());
+ PCHECK(0 == chdir("/"));
+ CHECK(WorkingDirectoryIsRoot());
+}
+
+// Disabled on ASAN because of crbug.com/451603.
+SANDBOX_TEST(Credentials, DISABLE_ON_ASAN(DropFileSystemAccessIsSafe)) {
+ CHECK(Credentials::DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS()) return;
+ CHECK(Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
+ CHECK(!base::DirectoryExists(base::FilePath("/proc")));
+ CHECK(WorkingDirectoryIsRoot());
+ CHECK(base::IsDirectoryEmpty(base::FilePath("/")));
+ // We want the chroot to never have a subdirectory. A subdirectory
+ // could allow a chroot escape.
+ CHECK_NE(0, mkdir("/test", 0700));
+}
+
+// Check that after dropping filesystem access and dropping privileges
+// it is not possible to regain capabilities.
+SANDBOX_TEST(Credentials, DISABLE_ON_ASAN(CannotRegainPrivileges)) {
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+ CHECK(Credentials::DropAllCapabilities(proc_fd.get()));
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS()) return;
+ CHECK(Credentials::DropFileSystemAccess(proc_fd.get()));
+ CHECK(Credentials::DropAllCapabilities(proc_fd.get()));
+
+ // The kernel should now prevent us from regaining capabilities because we
+ // are in a chroot.
+ CHECK(!Credentials::CanCreateProcessInNewUserNS());
+ CHECK(!Credentials::MoveToNewUserNS());
+}
+
+SANDBOX_TEST(Credentials, SetCapabilities) {
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS())
+ return;
+
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+
+ CHECK(Credentials::HasCapability(Credentials::Capability::SYS_ADMIN));
+ CHECK(Credentials::HasCapability(Credentials::Capability::SYS_CHROOT));
+
+ std::vector<Credentials::Capability> caps;
+ caps.push_back(Credentials::Capability::SYS_CHROOT);
+ CHECK(Credentials::SetCapabilities(proc_fd.get(), caps));
+
+ CHECK(!Credentials::HasCapability(Credentials::Capability::SYS_ADMIN));
+ CHECK(Credentials::HasCapability(Credentials::Capability::SYS_CHROOT));
+
+ const std::vector<Credentials::Capability> no_caps;
+ CHECK(Credentials::SetCapabilities(proc_fd.get(), no_caps));
+ CHECK(!Credentials::HasAnyCapability());
+}
+
+SANDBOX_TEST(Credentials, SetCapabilitiesAndChroot) {
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS())
+ return;
+
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+
+ CHECK(Credentials::HasCapability(Credentials::Capability::SYS_CHROOT));
+ PCHECK(chroot("/") == 0);
+
+ std::vector<Credentials::Capability> caps;
+ caps.push_back(Credentials::Capability::SYS_CHROOT);
+ CHECK(Credentials::SetCapabilities(proc_fd.get(), caps));
+ PCHECK(chroot("/") == 0);
+
+ CHECK(Credentials::DropAllCapabilities());
+ PCHECK(chroot("/") == -1 && errno == EPERM);
+}
+
+SANDBOX_TEST(Credentials, SetCapabilitiesMatchesLibCap2) {
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS())
+ return;
+
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+
+ std::vector<Credentials::Capability> caps;
+ caps.push_back(Credentials::Capability::SYS_CHROOT);
+ CHECK(Credentials::SetCapabilities(proc_fd.get(), caps));
+
+ ScopedCap actual_cap(cap_get_proc());
+ PCHECK(actual_cap != nullptr);
+
+ ScopedCap expected_cap(cap_init());
+ PCHECK(expected_cap != nullptr);
+
+ const cap_value_t allowed_cap = CAP_SYS_CHROOT;
+ for (const cap_flag_t flag : {CAP_EFFECTIVE, CAP_PERMITTED}) {
+ PCHECK(cap_set_flag(expected_cap.get(), flag, 1, &allowed_cap, CAP_SET) ==
+ 0);
+ }
+
+ CHECK_EQ(0, cap_compare(expected_cap.get(), actual_cap.get()));
+}
+
+} // namespace.
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/init_process_reaper.cc b/sandbox/linux/services/init_process_reaper.cc
new file mode 100644
index 0000000000..2e0b90b7b5
--- /dev/null
+++ b/sandbox/linux/services/init_process_reaper.cc
@@ -0,0 +1,101 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/init_process_reaper.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace sandbox {
+
+namespace {
+
+void DoNothingSignalHandler(int signal) {}
+
+} // namespace
+
+bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback) {
+ int sync_fds[2];
+ // We want to use send, so we can't use a pipe
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
+ PLOG(ERROR) << "Failed to create socketpair";
+ return false;
+ }
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ int close_ret;
+ close_ret = IGNORE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ close_ret = IGNORE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+ return false;
+ }
+ if (child_pid) {
+ // In the parent, assuming the role of an init process.
+ // The disposition for SIGCHLD cannot be SIG_IGN or wait() will only return
+ // once all of our childs are dead. Since we're init we need to reap childs
+ // as they come.
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = &DoNothingSignalHandler;
+ CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
+
+ int close_ret;
+ close_ret = IGNORE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ close_ret = shutdown(sync_fds[1], SHUT_RD);
+ DPCHECK(!close_ret);
+ if (post_fork_parent_callback)
+ post_fork_parent_callback->Run();
+ // Tell the child to continue
+ CHECK(HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) == 1);
+ close_ret = IGNORE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+
+ for (;;) {
+ // Loop until we have reaped our one natural child
+ siginfo_t reaped_child_info;
+ int wait_ret =
+ HANDLE_EINTR(waitid(P_ALL, 0, &reaped_child_info, WEXITED));
+ if (wait_ret)
+ _exit(1);
+ if (reaped_child_info.si_pid == child_pid) {
+ int exit_code = 0;
+ // We're done waiting
+ if (reaped_child_info.si_code == CLD_EXITED) {
+ exit_code = reaped_child_info.si_status;
+ }
+ // Exit with the same exit code as our parent. Exit with 0 if we got
+ // signaled.
+ _exit(exit_code);
+ }
+ }
+ } else {
+ // The child needs to wait for the parent to run the callback to avoid a
+ // race condition.
+ int close_ret;
+ close_ret = IGNORE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+ close_ret = shutdown(sync_fds[0], SHUT_WR);
+ DPCHECK(!close_ret);
+ char should_continue;
+ int read_ret = HANDLE_EINTR(read(sync_fds[0], &should_continue, 1));
+ close_ret = IGNORE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ if (read_ret == 1)
+ return true;
+ else
+ return false;
+ }
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/init_process_reaper.h b/sandbox/linux/services/init_process_reaper.h
new file mode 100644
index 0000000000..840f6fcda7
--- /dev/null
+++ b/sandbox/linux/services/init_process_reaper.h
@@ -0,0 +1,25 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
+#define SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
+
+#include "base/callback_forward.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// The current process will fork(). The parent will become a process reaper
+// like init(1). The child will continue normally (after this function
+// returns).
+// If not NULL, |post_fork_parent_callback| will run in the parent almost
+// immediately after fork().
+// Since this function calls fork(), it's very important that the caller has
+// only one thread running.
+SANDBOX_EXPORT bool CreateInitProcessReaper(
+ base::Closure* post_fork_parent_callback);
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
diff --git a/sandbox/linux/services/namespace_sandbox.cc b/sandbox/linux/services/namespace_sandbox.cc
new file mode 100644
index 0000000000..23796446f3
--- /dev/null
+++ b/sandbox/linux/services/namespace_sandbox.cc
@@ -0,0 +1,208 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/namespace_sandbox.h"
+
+#include <sched.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/namespace_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+const char kSandboxUSERNSEnvironmentVarName[] = "SBX_USER_NS";
+const char kSandboxPIDNSEnvironmentVarName[] = "SBX_PID_NS";
+const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS";
+
+#if !defined(OS_NACL_NONSFI)
+class WriteUidGidMapDelegate : public base::LaunchOptions::PreExecDelegate {
+ public:
+ WriteUidGidMapDelegate()
+ : uid_(getuid()),
+ gid_(getgid()),
+ supports_deny_setgroups_(
+ NamespaceUtils::KernelSupportsDenySetgroups()) {}
+
+ ~WriteUidGidMapDelegate() override {}
+
+ void RunAsyncSafe() override {
+ if (supports_deny_setgroups_) {
+ RAW_CHECK(NamespaceUtils::DenySetgroups());
+ }
+ RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/uid_map", uid_));
+ RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/gid_map", gid_));
+ }
+
+ private:
+ const uid_t uid_;
+ const gid_t gid_;
+ const bool supports_deny_setgroups_;
+ DISALLOW_COPY_AND_ASSIGN(WriteUidGidMapDelegate);
+};
+
+void SetEnvironForNamespaceType(base::EnvironmentMap* environ,
+ base::NativeEnvironmentString env_var,
+ bool value) {
+ // An empty string causes the env var to be unset in the child process.
+ (*environ)[env_var] = value ? "1" : "";
+}
+
+// Linux supports up to 64 signals. This should be updated if that ever changes.
+int g_signal_exit_codes[64];
+
+void TerminationSignalHandler(int sig) {
+ // Return a special exit code so that the process is detected as terminated by
+ // a signal.
+ const size_t sig_idx = static_cast<size_t>(sig);
+ if (sig_idx < arraysize(g_signal_exit_codes)) {
+ _exit(g_signal_exit_codes[sig_idx]);
+ }
+
+ _exit(NamespaceSandbox::kDefaultExitCode);
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+} // namespace
+
+#if !defined(OS_NACL_NONSFI)
+// static
+base::Process NamespaceSandbox::LaunchProcess(
+ const base::CommandLine& cmdline,
+ const base::LaunchOptions& options) {
+ return LaunchProcess(cmdline.argv(), options);
+}
+
+// static
+base::Process NamespaceSandbox::LaunchProcess(
+ const std::vector<std::string>& argv,
+ const base::LaunchOptions& options) {
+ int clone_flags = 0;
+ int ns_types[] = {CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWNET};
+ for (const int ns_type : ns_types) {
+ if (NamespaceUtils::KernelSupportsUnprivilegedNamespace(ns_type)) {
+ clone_flags |= ns_type;
+ }
+ }
+ CHECK(clone_flags & CLONE_NEWUSER);
+
+ // These fields may not be set by the caller.
+ CHECK(options.pre_exec_delegate == nullptr);
+ CHECK_EQ(0, options.clone_flags);
+
+ WriteUidGidMapDelegate write_uid_gid_map_delegate;
+
+ base::LaunchOptions launch_options = options;
+ launch_options.pre_exec_delegate = &write_uid_gid_map_delegate;
+ launch_options.clone_flags = clone_flags;
+
+ const std::pair<int, const char*> clone_flag_environ[] = {
+ std::make_pair(CLONE_NEWUSER, kSandboxUSERNSEnvironmentVarName),
+ std::make_pair(CLONE_NEWPID, kSandboxPIDNSEnvironmentVarName),
+ std::make_pair(CLONE_NEWNET, kSandboxNETNSEnvironmentVarName),
+ };
+
+ base::EnvironmentMap* environ = &launch_options.environ;
+ for (const auto& entry : clone_flag_environ) {
+ const int flag = entry.first;
+ const char* environ_name = entry.second;
+ SetEnvironForNamespaceType(environ, environ_name, clone_flags & flag);
+ }
+
+ return base::LaunchProcess(argv, launch_options);
+}
+
+// static
+pid_t NamespaceSandbox::ForkInNewPidNamespace(bool drop_capabilities_in_child) {
+ const pid_t pid =
+ base::ForkWithFlags(CLONE_NEWPID | SIGCHLD, nullptr, nullptr);
+ if (pid < 0) {
+ return pid;
+ }
+
+ if (pid == 0) {
+ DCHECK_EQ(1, getpid());
+ if (drop_capabilities_in_child) {
+ // Since we just forked, we are single-threaded, so this should be safe.
+ CHECK(Credentials::DropAllCapabilitiesOnCurrentThread());
+ }
+ return 0;
+ }
+
+ return pid;
+}
+
+// static
+void NamespaceSandbox::InstallDefaultTerminationSignalHandlers() {
+ static const int kDefaultTermSignals[] = {
+ SIGHUP, SIGINT, SIGABRT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2,
+ };
+
+ for (const int sig : kDefaultTermSignals) {
+ InstallTerminationSignalHandler(sig, kDefaultExitCode);
+ }
+}
+
+// static
+bool NamespaceSandbox::InstallTerminationSignalHandler(
+ int sig,
+ int exit_code) {
+ struct sigaction old_action;
+ PCHECK(sigaction(sig, nullptr, &old_action) == 0);
+
+ if (old_action.sa_flags & SA_SIGINFO &&
+ old_action.sa_sigaction != nullptr) {
+ return false;
+ } else if (old_action.sa_handler != SIG_DFL) {
+ return false;
+ }
+
+ const size_t sig_idx = static_cast<size_t>(sig);
+ CHECK_LT(sig_idx, arraysize(g_signal_exit_codes));
+
+ DCHECK_GE(exit_code, 0);
+ DCHECK_LT(exit_code, 256);
+
+ g_signal_exit_codes[sig_idx] = exit_code;
+
+ struct sigaction action = {};
+ action.sa_handler = &TerminationSignalHandler;
+ PCHECK(sigaction(sig, &action, nullptr) == 0);
+ return true;
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+// static
+bool NamespaceSandbox::InNewUserNamespace() {
+ return getenv(kSandboxUSERNSEnvironmentVarName) != nullptr;
+}
+
+// static
+bool NamespaceSandbox::InNewPidNamespace() {
+ return getenv(kSandboxPIDNSEnvironmentVarName) != nullptr;
+}
+
+// static
+bool NamespaceSandbox::InNewNetNamespace() {
+ return getenv(kSandboxNETNSEnvironmentVarName) != nullptr;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/namespace_sandbox.h b/sandbox/linux/services/namespace_sandbox.h
new file mode 100644
index 0000000000..80097fb16a
--- /dev/null
+++ b/sandbox/linux/services/namespace_sandbox.h
@@ -0,0 +1,101 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_NAMESPACE_SANDBOX_H_
+#define SANDBOX_LINUX_SERVICES_NAMESPACE_SANDBOX_H_
+
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Helper class for starting a process inside a new user, PID, and network
+// namespace. Before using a namespace sandbox, check for namespaces support
+// using Credentials::CanCreateProcessInNewUserNS.
+//
+// A typical use for "A" launching a sandboxed process "B" would be:
+// 1. A sets up a command line and launch options for process B.
+// 2. A launches B with LaunchProcess.
+// 3. B should be prepared to assume the role of init(1). In particular, apart
+// from SIGKILL and SIGSTOP, B cannot receive any signal for which it does
+// not have an explicit signal handler registered.
+// If B dies, all the processes in the namespace will die.
+// B can fork() and the parent can assume the role of init(1), by using
+// CreateInitProcessReaper().
+// 4. B chroots using Credentials::MoveToNewUserNS() and
+// Credentials::DropFileSystemAccess()
+// 5. B drops capabilities gained by entering the new user namespace with
+// Credentials::DropAllCapabilities().
+class SANDBOX_EXPORT NamespaceSandbox {
+ public:
+#if !defined(OS_NACL_NONSFI)
+ static const int kDefaultExitCode = 1;
+
+ // Launch a new process inside its own user/PID/network namespaces (depending
+ // on kernel support). Requires at a minimum that user namespaces are
+ // supported (use Credentials::CanCreateProcessInNewUserNS to check this).
+ //
+ // pre_exec_delegate and clone_flags fields of LaunchOptions should be nullptr
+ // and 0, respectively, since this function makes a copy of options and
+ // overrides them.
+ static base::Process LaunchProcess(const base::CommandLine& cmdline,
+ const base::LaunchOptions& options);
+ static base::Process LaunchProcess(const std::vector<std::string>& argv,
+ const base::LaunchOptions& options);
+
+ // Forks a process in its own PID namespace. The child process is the init
+ // process inside of the PID namespace, so if the child needs to fork further,
+ // it should call CreateInitProcessReaper, which turns the init process into a
+ // reaper process.
+ //
+ // Otherwise, the child should setup handlers for signals which should
+ // terminate the process using InstallDefaultTerminationSignalHandlers or
+ // InstallTerminationSignalHandler. This works around the fact that init
+ // processes ignore such signals unless they have an explicit handler set.
+ //
+ // This function requries CAP_SYS_ADMIN. If |drop_capabilities_in_child| is
+ // true, then capabilities are dropped in the child.
+ static pid_t ForkInNewPidNamespace(bool drop_capabilities_in_child);
+
+ // Installs a signal handler for:
+ //
+ // SIGHUP, SIGINT, SIGABRT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2
+ //
+ // that exits with kDefaultExitCode. These are signals whose default action is
+ // to terminate the program (apart from SIGILL, SIGFPE, and SIGSEGV, which
+ // will still terminate the process if e.g. an illegal instruction is
+ // encountered, etc.).
+ //
+ // If any of these already had a signal handler installed, this function will
+ // not override them.
+ static void InstallDefaultTerminationSignalHandlers();
+
+ // Installs a signal handler for |sig| which exits with |exit_code|. If a
+ // signal handler was already present for |sig|, does nothing and returns
+ // false.
+ static bool InstallTerminationSignalHandler(int sig, int exit_code);
+#endif // !defined(OS_NACL_NONSFI)
+
+ // Returns whether the namespace sandbox created a new user, PID, and network
+ // namespace. In particular, InNewUserNamespace should return true iff the
+ // process was started via this class.
+ static bool InNewUserNamespace();
+ static bool InNewPidNamespace();
+ static bool InNewNetNamespace();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceSandbox);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_NAMESPACE_SANDBOX_H_
diff --git a/sandbox/linux/services/namespace_sandbox_unittest.cc b/sandbox/linux/services/namespace_sandbox_unittest.cc
new file mode 100644
index 0000000000..547ef6728c
--- /dev/null
+++ b/sandbox/linux/services/namespace_sandbox_unittest.cc
@@ -0,0 +1,217 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/namespace_sandbox.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/test/multiprocess_test.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/namespace_utils.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace sandbox {
+
+namespace {
+
+bool RootDirectoryIsEmpty() {
+ base::FilePath root("/");
+ int file_type =
+ base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES;
+ base::FileEnumerator enumerator_before(root, false, file_type);
+ return enumerator_before.Next().empty();
+}
+
+class NamespaceSandboxTest : public base::MultiProcessTest {
+ public:
+ void TestProc(const std::string& procname) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ base::FileHandleMappingVector fds_to_remap = {
+ std::make_pair(STDOUT_FILENO, STDOUT_FILENO),
+ std::make_pair(STDERR_FILENO, STDERR_FILENO),
+ };
+ base::LaunchOptions launch_options;
+ launch_options.fds_to_remap = &fds_to_remap;
+
+ base::Process process =
+ NamespaceSandbox::LaunchProcess(MakeCmdLine(procname), launch_options);
+ ASSERT_TRUE(process.IsValid());
+
+ const int kDummyExitCode = 42;
+ int exit_code = kDummyExitCode;
+ EXPECT_TRUE(process.WaitForExit(&exit_code));
+ EXPECT_EQ(0, exit_code);
+ }
+};
+
+MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ bool in_user_ns = NamespaceSandbox::InNewUserNamespace();
+ bool in_pid_ns = NamespaceSandbox::InNewPidNamespace();
+ bool in_net_ns = NamespaceSandbox::InNewNetNamespace();
+ CHECK(in_user_ns);
+ CHECK_EQ(in_pid_ns,
+ NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWPID));
+ CHECK_EQ(in_net_ns,
+ NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWNET));
+ if (in_pid_ns) {
+ CHECK_EQ(1, getpid());
+ }
+ return 0;
+}
+
+TEST_F(NamespaceSandboxTest, BasicUsage) {
+ TestProc("SimpleChildProcess");
+}
+
+MULTIPROCESS_TEST_MAIN(ChrootMe) {
+ CHECK(!RootDirectoryIsEmpty());
+ CHECK(sandbox::Credentials::MoveToNewUserNS());
+ CHECK(sandbox::Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
+ CHECK(RootDirectoryIsEmpty());
+ return 0;
+}
+
+// Temporarily disabled on ASAN due to crbug.com/451603.
+TEST_F(NamespaceSandboxTest, DISABLE_ON_ASAN(ChrootAndDropCapabilities)) {
+ TestProc("ChrootMe");
+}
+
+MULTIPROCESS_TEST_MAIN(NestedNamespaceSandbox) {
+ base::FileHandleMappingVector fds_to_remap = {
+ std::make_pair(STDOUT_FILENO, STDOUT_FILENO),
+ std::make_pair(STDERR_FILENO, STDERR_FILENO),
+ };
+ base::LaunchOptions launch_options;
+ launch_options.fds_to_remap = &fds_to_remap;
+ base::Process process = NamespaceSandbox::LaunchProcess(
+ base::CommandLine(base::FilePath("/bin/true")), launch_options);
+ CHECK(process.IsValid());
+
+ const int kDummyExitCode = 42;
+ int exit_code = kDummyExitCode;
+ CHECK(process.WaitForExit(&exit_code));
+ CHECK_EQ(0, exit_code);
+ return 0;
+}
+
+TEST_F(NamespaceSandboxTest, NestedNamespaceSandbox) {
+ TestProc("NestedNamespaceSandbox");
+}
+
+const int kNormalExitCode = 0;
+const int kSignalTerminationExitCode = 255;
+
+// Ensure that CHECK(false) is distinguishable from _exit(kNormalExitCode).
+// Allowing noise since CHECK(false) will write a stack trace to stderr.
+SANDBOX_TEST_ALLOW_NOISE(ForkInNewPidNamespace, CheckDoesNotReturnZero) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ CHECK(sandbox::Credentials::MoveToNewUserNS());
+ const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
+ /*drop_capabilities_in_child=*/true);
+ CHECK_GE(pid, 0);
+
+ if (pid == 0) {
+ CHECK(false);
+ _exit(kNormalExitCode);
+ }
+
+ int status;
+ PCHECK(waitpid(pid, &status, 0) == pid);
+ if (WIFEXITED(status)) {
+ CHECK_NE(kNormalExitCode, WEXITSTATUS(status));
+ }
+}
+
+SANDBOX_TEST(ForkInNewPidNamespace, BasicUsage) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ CHECK(sandbox::Credentials::MoveToNewUserNS());
+ const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
+ /*drop_capabilities_in_child=*/true);
+ CHECK_GE(pid, 0);
+
+ if (pid == 0) {
+ CHECK_EQ(1, getpid());
+ CHECK(!Credentials::HasAnyCapability());
+ _exit(kNormalExitCode);
+ }
+
+ int status;
+ PCHECK(waitpid(pid, &status, 0) == pid);
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kNormalExitCode, WEXITSTATUS(status));
+}
+
+SANDBOX_TEST(ForkInNewPidNamespace, ExitWithSignal) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ CHECK(sandbox::Credentials::MoveToNewUserNS());
+ const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
+ /*drop_capabilities_in_child=*/true);
+ CHECK_GE(pid, 0);
+
+ if (pid == 0) {
+ CHECK_EQ(1, getpid());
+ CHECK(!Credentials::HasAnyCapability());
+ CHECK(NamespaceSandbox::InstallTerminationSignalHandler(
+ SIGTERM, kSignalTerminationExitCode));
+ while (true) {
+ raise(SIGTERM);
+ }
+ }
+
+ int status;
+ PCHECK(waitpid(pid, &status, 0) == pid);
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kSignalTerminationExitCode, WEXITSTATUS(status));
+}
+
+volatile sig_atomic_t signal_handler_called;
+void ExitSuccessfully(int sig) {
+ signal_handler_called = 1;
+}
+
+SANDBOX_TEST(InstallTerminationSignalHandler, DoesNotOverrideExistingHandlers) {
+ struct sigaction action = {};
+ action.sa_handler = &ExitSuccessfully;
+ PCHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
+
+ NamespaceSandbox::InstallDefaultTerminationSignalHandlers();
+ CHECK(!NamespaceSandbox::InstallTerminationSignalHandler(
+ SIGUSR1, kSignalTerminationExitCode));
+
+ raise(SIGUSR1);
+ CHECK_EQ(1, signal_handler_called);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/namespace_utils.cc b/sandbox/linux/services/namespace_utils.cc
new file mode 100644
index 0000000000..29b649c078
--- /dev/null
+++ b/sandbox/linux/services/namespace_utils.cc
@@ -0,0 +1,117 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/namespace_utils.h"
+
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/strings/safe_sprintf.h"
+#include "third_party/valgrind/valgrind.h"
+
+namespace sandbox {
+
+namespace {
+bool IsRunningOnValgrind() {
+ return RUNNING_ON_VALGRIND;
+}
+
+const char kProcSelfSetgroups[] = "/proc/self/setgroups";
+} // namespace
+
+// static
+bool NamespaceUtils::WriteToIdMapFile(const char* map_file, generic_id_t id) {
+ // This function needs to be async-signal-safe, as it may be called in between
+ // fork and exec.
+
+ int fd = HANDLE_EINTR(open(map_file, O_WRONLY));
+ if (fd == -1) {
+ return false;
+ }
+
+ const generic_id_t inside_id = id;
+ const generic_id_t outside_id = id;
+
+ char mapping[64];
+ const ssize_t len =
+ base::strings::SafeSPrintf(mapping, "%d %d 1\n", inside_id, outside_id);
+ const ssize_t rc = HANDLE_EINTR(write(fd, mapping, len));
+ RAW_CHECK(IGNORE_EINTR(close(fd)) == 0);
+ return rc == len;
+}
+
+// static
+bool NamespaceUtils::KernelSupportsUnprivilegedNamespace(int type) {
+ // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
+ // so always consider namespaces unsupported there.
+ if (IsRunningOnValgrind()) {
+ return false;
+ }
+
+ // As of Linux 3.8, /proc/self/ns/* files exist for all namespace types. Since
+ // user namespaces were added in 3.8, it is OK to rely on the existence of
+ // /proc/self/ns/*.
+ if (!base::PathExists(base::FilePath("/proc/self/ns/user"))) {
+ return false;
+ }
+
+ const char* path;
+ switch (type) {
+ case CLONE_NEWUSER:
+ return true;
+ case CLONE_NEWIPC:
+ path = "/proc/self/ns/ipc";
+ break;
+ case CLONE_NEWNET:
+ path = "/proc/self/ns/net";
+ break;
+ case CLONE_NEWNS:
+ path = "/proc/self/ns/mnt";
+ break;
+ case CLONE_NEWPID:
+ path = "/proc/self/ns/pid";
+ break;
+ case CLONE_NEWUTS:
+ path = "/proc/self/ns/uts";
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ return base::PathExists(base::FilePath(path));
+}
+
+// static
+bool NamespaceUtils::KernelSupportsDenySetgroups() {
+ return base::PathExists(base::FilePath(kProcSelfSetgroups));
+}
+
+// static
+bool NamespaceUtils::DenySetgroups() {
+ // This function needs to be async-signal-safe.
+ int fd = HANDLE_EINTR(open(kProcSelfSetgroups, O_WRONLY));
+ if (fd == -1) {
+ return false;
+ }
+
+ static const char kDeny[] = "deny";
+ const ssize_t len = sizeof(kDeny) - 1;
+ const ssize_t rc = HANDLE_EINTR(write(fd, kDeny, len));
+ RAW_CHECK(IGNORE_EINTR(close(fd)) == 0);
+ return rc == len;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/namespace_utils.h b/sandbox/linux/services/namespace_utils.h
new file mode 100644
index 0000000000..f3c88a9452
--- /dev/null
+++ b/sandbox/linux/services/namespace_utils.h
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_NAMESPACE_UTILS_H_
+#define SANDBOX_LINUX_SERVICES_NAMESPACE_UTILS_H_
+
+#include <sys/types.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/template_util.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Utility functions for using Linux namepaces.
+class SANDBOX_EXPORT NamespaceUtils {
+ public:
+ COMPILE_ASSERT((base::is_same<uid_t, gid_t>::value), UidAndGidAreSameType);
+ // generic_id_t can be used for either uid_t or gid_t.
+ typedef uid_t generic_id_t;
+
+ // Write a uid or gid mapping from |id| to |id| in |map_file|. This function
+ // is async-signal-safe.
+ static bool WriteToIdMapFile(const char* map_file,
+ generic_id_t id) WARN_UNUSED_RESULT;
+
+ // Returns true if unprivileged namespaces of type |type| is supported
+ // (meaning that both CLONE_NEWUSER and type are are supported). |type| must
+ // be one of CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID,
+ // CLONE_NEWUSER, or CLONE_NEWUTS. This relies on access to /proc, so it will
+ // not work from within a sandbox.
+ static bool KernelSupportsUnprivilegedNamespace(int type);
+
+ // Returns true if the kernel supports denying setgroups in a user namespace.
+ // On kernels where this is supported, DenySetgroups must be called before a
+ // gid mapping can be added.
+ static bool KernelSupportsDenySetgroups();
+
+ // Disables setgroups() within the current user namespace. On Linux 3.18.2 and
+ // later, this is required in order to write to /proc/self/gid_map without
+ // having CAP_SETGID. Callers can determine whether is this needed with
+ // KernelSupportsDenySetgroups. This function is async-signal-safe.
+ static bool DenySetgroups() WARN_UNUSED_RESULT;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceUtils);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_NAMESPACE_UTILS_H_
diff --git a/sandbox/linux/services/namespace_utils_unittest.cc b/sandbox/linux/services/namespace_utils_unittest.cc
new file mode 100644
index 0000000000..41ed7e89a6
--- /dev/null
+++ b/sandbox/linux/services/namespace_utils_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/namespace_utils.h"
+
+#include <errno.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+SANDBOX_TEST(NamespaceUtils, KernelSupportsUnprivilegedNamespace) {
+ const bool can_create_user_ns = Credentials::CanCreateProcessInNewUserNS();
+ const bool supports_user_ns =
+ NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWUSER);
+ // can_create_user_ns implies supports_user_ns, but the converse is not
+ // necessarily true, as creating a user namespace can fail for various
+ // reasons.
+ if (can_create_user_ns) {
+ SANDBOX_ASSERT(supports_user_ns);
+ }
+}
+
+SANDBOX_TEST(NamespaceUtils, WriteToIdMapFile) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ const uid_t uid = getuid();
+ const gid_t gid = getgid();
+
+ const bool supports_deny_setgroups =
+ NamespaceUtils::KernelSupportsDenySetgroups();
+
+ const pid_t pid =
+ base::ForkWithFlags(CLONE_NEWUSER | SIGCHLD, nullptr, nullptr);
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ if (supports_deny_setgroups) {
+ RAW_CHECK(NamespaceUtils::DenySetgroups());
+ }
+
+ RAW_CHECK(getuid() != uid);
+ RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/uid_map", uid));
+ RAW_CHECK(getuid() == uid);
+
+ RAW_CHECK(getgid() != gid);
+ RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/gid_map", gid));
+ RAW_CHECK(getgid() == gid);
+
+ _exit(0);
+ }
+
+ int status = 42;
+ SANDBOX_ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+ SANDBOX_ASSERT_EQ(0, status);
+}
+
+} // namespace.
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/proc_util.cc b/sandbox/linux/services/proc_util.cc
new file mode 100644
index 0000000000..d3f755f9a1
--- /dev/null
+++ b/sandbox/linux/services/proc_util.cc
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/proc_util.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_number_conversions.h"
+
+namespace sandbox {
+namespace {
+
+struct DIRCloser {
+ void operator()(DIR* d) const {
+ DCHECK(d);
+ PCHECK(0 == closedir(d));
+ }
+};
+
+typedef scoped_ptr<DIR, DIRCloser> ScopedDIR;
+
+base::ScopedFD OpenDirectory(const char* path) {
+ DCHECK(path);
+ base::ScopedFD directory_fd(
+ HANDLE_EINTR(open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC)));
+ PCHECK(directory_fd.is_valid());
+ return directory_fd.Pass();
+}
+
+} // namespace
+
+int ProcUtil::CountOpenFds(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ int proc_self_fd = HANDLE_EINTR(
+ openat(proc_fd, "self/fd/", O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ PCHECK(0 <= proc_self_fd);
+
+ // Ownership of proc_self_fd is transferred here, it must not be closed
+ // or modified afterwards except via dir.
+ ScopedDIR dir(fdopendir(proc_self_fd));
+ CHECK(dir);
+
+ int count = 0;
+ struct dirent e;
+ struct dirent* de;
+ while (!readdir_r(dir.get(), &e, &de) && de) {
+ if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
+ continue;
+ }
+
+ int fd_num;
+ CHECK(base::StringToInt(e.d_name, &fd_num));
+ if (fd_num == proc_fd || fd_num == proc_self_fd) {
+ continue;
+ }
+
+ ++count;
+ }
+ return count;
+}
+
+bool ProcUtil::HasOpenDirectory(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ int proc_self_fd =
+ openat(proc_fd, "self/fd/", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+
+ PCHECK(0 <= proc_self_fd);
+
+ // Ownership of proc_self_fd is transferred here, it must not be closed
+ // or modified afterwards except via dir.
+ ScopedDIR dir(fdopendir(proc_self_fd));
+ CHECK(dir);
+
+ struct dirent e;
+ struct dirent* de;
+ while (!readdir_r(dir.get(), &e, &de) && de) {
+ if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
+ continue;
+ }
+
+ int fd_num;
+ CHECK(base::StringToInt(e.d_name, &fd_num));
+ if (fd_num == proc_fd || fd_num == proc_self_fd) {
+ continue;
+ }
+
+ struct stat s;
+ // It's OK to use proc_self_fd here, fstatat won't modify it.
+ CHECK(fstatat(proc_self_fd, e.d_name, &s, 0) == 0);
+ if (S_ISDIR(s.st_mode)) {
+ return true;
+ }
+ }
+
+ // No open unmanaged directories found.
+ return false;
+}
+
+bool ProcUtil::HasOpenDirectory() {
+ base::ScopedFD proc_fd(
+ HANDLE_EINTR(open("/proc/", O_DIRECTORY | O_RDONLY | O_CLOEXEC)));
+ return HasOpenDirectory(proc_fd.get());
+}
+
+//static
+base::ScopedFD ProcUtil::OpenProc() {
+ return OpenDirectory("/proc/");
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/proc_util.h b/sandbox/linux/services/proc_util.h
new file mode 100644
index 0000000000..bc14c5ef2a
--- /dev/null
+++ b/sandbox/linux/services/proc_util.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_PROC_UTIL_H_
+#define SANDBOX_LINUX_SERVICES_PROC_UTIL_H_
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+class SANDBOX_EXPORT ProcUtil {
+ public:
+ // Returns the number of file descriptors in the current process's FD
+ // table, excluding |proc_fd|, which should be a file descriptor for
+ // /proc/.
+ static int CountOpenFds(int proc_fd);
+
+ // Checks whether the current process has any directory file descriptor open.
+ // Directory file descriptors are "capabilities" that would let a process use
+ // system calls such as openat() to bypass restrictions such as
+ // DropFileSystemAccess().
+ // Sometimes it's useful to call HasOpenDirectory() after file system access
+ // has been dropped. In this case, |proc_fd| should be a file descriptor to
+ // /proc/. The file descriptor in |proc_fd| will be ignored by
+ // HasOpenDirectory() and remains owned by the caller. It is very important
+ // for the caller to close it.
+ static bool HasOpenDirectory(int proc_fd) WARN_UNUSED_RESULT;
+ static bool HasOpenDirectory() WARN_UNUSED_RESULT;
+
+ // Open /proc/ or crash if not possible.
+ static base::ScopedFD OpenProc();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcUtil);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_PROC_UTIL_H_
diff --git a/sandbox/linux/services/proc_util_unittest.cc b/sandbox/linux/services/proc_util_unittest.cc
new file mode 100644
index 0000000000..bf25151956
--- /dev/null
+++ b/sandbox/linux/services/proc_util_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/proc_util.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "base/files/scoped_file.h"
+#include "base/posix/eintr_wrapper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+TEST(ProcUtil, CountOpenFds) {
+ base::ScopedFD proc_fd(open("/proc/", O_RDONLY | O_DIRECTORY));
+ ASSERT_TRUE(proc_fd.is_valid());
+ int fd_count = ProcUtil::CountOpenFds(proc_fd.get());
+ int fd = open("/dev/null", O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(fd_count + 1, ProcUtil::CountOpenFds(proc_fd.get()));
+ ASSERT_EQ(0, IGNORE_EINTR(close(fd)));
+ EXPECT_EQ(fd_count, ProcUtil::CountOpenFds(proc_fd.get()));
+}
+
+TEST(ProcUtil, HasOpenDirectory) {
+ // No open directory should exist at startup.
+ EXPECT_FALSE(ProcUtil::HasOpenDirectory());
+ {
+ // Have a "/proc" file descriptor around.
+ int proc_fd = open("/proc/", O_RDONLY | O_DIRECTORY);
+ base::ScopedFD proc_fd_closer(proc_fd);
+ EXPECT_TRUE(ProcUtil::HasOpenDirectory());
+ }
+ EXPECT_FALSE(ProcUtil::HasOpenDirectory());
+}
+
+TEST(ProcUtil, HasOpenDirectoryWithFD) {
+ int proc_fd = open("/proc/", O_RDONLY | O_DIRECTORY);
+ base::ScopedFD proc_fd_closer(proc_fd);
+ ASSERT_LE(0, proc_fd);
+
+ // Don't pass |proc_fd|, an open directory (proc_fd) should
+ // be detected.
+ EXPECT_TRUE(ProcUtil::HasOpenDirectory());
+ // Pass |proc_fd| and no open directory should be detected.
+ EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
+
+ {
+ // Have a directory file descriptor around.
+ int open_directory_fd = open("/proc/self/", O_RDONLY | O_DIRECTORY);
+ base::ScopedFD open_directory_fd_closer(open_directory_fd);
+ EXPECT_TRUE(ProcUtil::HasOpenDirectory(proc_fd));
+ }
+
+ // The "/proc/" file descriptor should now be closed, |proc_fd| is the
+ // only directory file descriptor open.
+ EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/resource_limits.cc b/sandbox/linux/services/resource_limits.cc
new file mode 100644
index 0000000000..1ec11295d1
--- /dev/null
+++ b/sandbox/linux/services/resource_limits.cc
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/resource_limits.h"
+
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <algorithm>
+
+namespace sandbox {
+
+// static
+bool ResourceLimits::Lower(int resource, rlim_t limit) {
+ struct rlimit old_rlimit;
+ if (getrlimit(resource, &old_rlimit))
+ return false;
+ // Make sure we don't raise the existing limit.
+ const struct rlimit new_rlimit = {std::min(old_rlimit.rlim_cur, limit),
+ std::min(old_rlimit.rlim_max, limit)};
+ int rc = setrlimit(resource, &new_rlimit);
+ return rc == 0;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/resource_limits.h b/sandbox/linux/services/resource_limits.h
new file mode 100644
index 0000000000..3464dab679
--- /dev/null
+++ b/sandbox/linux/services/resource_limits.h
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_RESOURCE_LIMITS_H_
+#define SANDBOX_LINUX_SERVICES_RESOURCE_LIMITS_H_
+
+#include <sys/resource.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This class provides a small wrapper around setrlimit().
+class SANDBOX_EXPORT ResourceLimits {
+ public:
+ // Lower the soft and hard limit of |resource| to |limit|. If the current
+ // limit is lower than |limit|, keep it.
+ static bool Lower(int resource, rlim_t limit) WARN_UNUSED_RESULT;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ResourceLimits);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_RESOURCE_LIMITS_H_
diff --git a/sandbox/linux/services/resource_limits_unittests.cc b/sandbox/linux/services/resource_limits_unittests.cc
new file mode 100644
index 0000000000..910c740f7b
--- /dev/null
+++ b/sandbox/linux/services/resource_limits_unittests.cc
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/resource_limits.h"
+
+#include <errno.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+// Fails on Android: crbug.com/459158
+#if !defined(OS_ANDROID)
+#define MAYBE_NoFork DISABLE_ON_ASAN(NoFork)
+#else
+#define MAYBE_NoFork DISABLED_NoFork
+#endif // OS_ANDROID
+
+// Not being able to fork breaks LeakSanitizer, so disable on
+// all ASAN builds.
+SANDBOX_TEST(ResourceLimits, MAYBE_NoFork) {
+ // Make sure that fork will fail with EAGAIN.
+ SANDBOX_ASSERT(ResourceLimits::Lower(RLIMIT_NPROC, 0));
+ errno = 0;
+ pid_t pid = fork();
+ // Reap any child if fork succeeded.
+ TestUtils::HandlePostForkReturn(pid);
+ SANDBOX_ASSERT_EQ(-1, pid);
+ CHECK_EQ(EAGAIN, errno);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/scoped_process.cc b/sandbox/linux/services/scoped_process.cc
new file mode 100644
index 0000000000..65af4873a4
--- /dev/null
+++ b/sandbox/linux/services/scoped_process.cc
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/scoped_process.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+
+namespace sandbox {
+
+namespace {
+
+const char kSynchronisationChar[] = "D";
+
+void WaitForever() {
+ while(true) {
+ pause();
+ }
+}
+
+} // namespace
+
+ScopedProcess::ScopedProcess(const base::Closure& child_callback)
+ : child_process_id_(-1), process_id_(getpid()) {
+ PCHECK(0 == pipe(pipe_fds_));
+#if !defined(THREAD_SANITIZER)
+ // Make sure that we can safely fork().
+ CHECK(ThreadHelpers::IsSingleThreaded());
+#endif
+ child_process_id_ = fork();
+ PCHECK(0 <= child_process_id_);
+
+ if (0 == child_process_id_) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+ pipe_fds_[0] = -1;
+ child_callback.Run();
+ // Notify the parent that the closure has run.
+ CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds_[1], kSynchronisationChar, 1)));
+ WaitForever();
+ NOTREACHED();
+ _exit(1);
+ }
+
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+ pipe_fds_[1] = -1;
+}
+
+ScopedProcess::~ScopedProcess() {
+ CHECK(IsOriginalProcess());
+ if (child_process_id_ >= 0) {
+ PCHECK(0 == kill(child_process_id_, SIGKILL));
+ siginfo_t process_info;
+
+ PCHECK(0 == HANDLE_EINTR(
+ waitid(P_PID, child_process_id_, &process_info, WEXITED)));
+ }
+ if (pipe_fds_[0] >= 0) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+ }
+ if (pipe_fds_[1] >= 0) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+ }
+}
+
+int ScopedProcess::WaitForExit(bool* got_signaled) {
+ DCHECK(got_signaled);
+ CHECK(IsOriginalProcess());
+ siginfo_t process_info;
+ // WNOWAIT to make sure that the destructor can wait on the child.
+ int ret = HANDLE_EINTR(
+ waitid(P_PID, child_process_id_, &process_info, WEXITED | WNOWAIT));
+ PCHECK(0 == ret) << "Did something else wait on the child?";
+
+ if (process_info.si_code == CLD_EXITED) {
+ *got_signaled = false;
+ } else if (process_info.si_code == CLD_KILLED ||
+ process_info.si_code == CLD_DUMPED) {
+ *got_signaled = true;
+ } else {
+ CHECK(false) << "ScopedProcess needs to be extended for si_code "
+ << process_info.si_code;
+ }
+ return process_info.si_status;
+}
+
+bool ScopedProcess::WaitForClosureToRun() {
+ char c = 0;
+ int ret = HANDLE_EINTR(read(pipe_fds_[0], &c, 1));
+ PCHECK(ret >= 0);
+ if (0 == ret)
+ return false;
+
+ CHECK_EQ(c, kSynchronisationChar[0]);
+ return true;
+}
+
+// It would be problematic if after a fork(), another process would start using
+// this object.
+// This method allows to assert it is not happening.
+bool ScopedProcess::IsOriginalProcess() {
+ // Make a direct syscall to bypass glibc caching of PIDs.
+ pid_t pid = sys_getpid();
+ return pid == process_id_;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/scoped_process.h b/sandbox/linux/services/scoped_process.h
new file mode 100644
index 0000000000..bddbd5529b
--- /dev/null
+++ b/sandbox/linux/services/scoped_process.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/process/process_handle.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// fork() a child process that will run a Closure.
+// After the Closure has run, the child will pause forever. If this object
+// is detroyed, the child will be destroyed, even if the closure did not
+// finish running. It's ok to signal the child from outside of this class to
+// destroy it.
+// This class cannot be instanciated from a multi-threaded process, as it needs
+// to fork().
+class SANDBOX_EXPORT ScopedProcess {
+ public:
+ // A new process will be created and |child_callback| will run in the child
+ // process. This callback is allowed to terminate the process or to simply
+ // return. If the callback returns, the process will wait forever.
+ explicit ScopedProcess(const base::Closure& child_callback);
+ ~ScopedProcess();
+
+ // Wait for the process to exit.
+ // |got_signaled| tells how to interpret the return value: either as an exit
+ // code, or as a signal number.
+ // When this returns, the process will still not have been reaped and will
+ // survive as a zombie for the lifetime of this object. This method can be
+ // called multiple times.
+ int WaitForExit(bool* got_signaled);
+
+ // Wait for the |child_callback| passed at construction to run. Return false
+ // if |child_callback| did not finish running and we know it never will (for
+ // instance the child crashed or used _exit()).
+ bool WaitForClosureToRun();
+ base::ProcessId GetPid() { return child_process_id_; }
+
+ private:
+ bool IsOriginalProcess();
+
+ base::ProcessId child_process_id_;
+ base::ProcessId process_id_;
+ int pipe_fds_[2];
+ DISALLOW_COPY_AND_ASSIGN(ScopedProcess);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
diff --git a/sandbox/linux/services/scoped_process_unittest.cc b/sandbox/linux/services/scoped_process_unittest.cc
new file mode 100644
index 0000000000..8bd2847997
--- /dev/null
+++ b/sandbox/linux/services/scoped_process_unittest.cc
@@ -0,0 +1,130 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/scoped_process.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+void DoExit() { _exit(0); }
+
+void ExitWithCode(int exit_code) { _exit(exit_code); }
+
+void RaiseAndExit(int signal) {
+ PCHECK(0 == raise(signal));
+ _exit(0);
+}
+
+void DoNothing() {}
+
+TEST(ScopedProcess, ScopedProcessNormalExit) {
+ const int kCustomExitCode = 12;
+ ScopedProcess process(base::Bind(&ExitWithCode, kCustomExitCode));
+ bool got_signaled = true;
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_FALSE(got_signaled);
+ EXPECT_EQ(kCustomExitCode, exit_code);
+
+ // Verify that WaitForExit() can be called multiple times on the same
+ // process.
+ bool got_signaled2 = true;
+ int exit_code2 = process.WaitForExit(&got_signaled2);
+ EXPECT_FALSE(got_signaled2);
+ EXPECT_EQ(kCustomExitCode, exit_code2);
+}
+
+// Disable this test on Android, SIGABRT is funky there.
+TEST(ScopedProcess, DISABLE_ON_ANDROID(ScopedProcessAbort)) {
+ PCHECK(SIG_ERR != signal(SIGABRT, SIG_DFL));
+ ScopedProcess process(base::Bind(&RaiseAndExit, SIGABRT));
+ bool got_signaled = false;
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_TRUE(got_signaled);
+ EXPECT_EQ(SIGABRT, exit_code);
+}
+
+TEST(ScopedProcess, ScopedProcessSignaled) {
+ ScopedProcess process(base::Bind(&DoNothing));
+ bool got_signaled = false;
+ ASSERT_EQ(0, kill(process.GetPid(), SIGKILL));
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_TRUE(got_signaled);
+ EXPECT_EQ(SIGKILL, exit_code);
+}
+
+TEST(ScopedProcess, DiesForReal) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_end_closer(pipe_fds[0]);
+ base::ScopedFD write_end_closer(pipe_fds[1]);
+
+ { ScopedProcess process(base::Bind(&DoExit)); }
+
+ // Close writing end of the pipe.
+ write_end_closer.reset();
+ pipe_fds[1] = -1;
+
+ ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+ char c;
+ // If the child process is dead for real, there will be no writing end
+ // for this pipe left and read will EOF instead of returning EWOULDBLOCK.
+ ASSERT_EQ(0, read(pipe_fds[0], &c, 1));
+}
+
+TEST(ScopedProcess, SynchronizationBasic) {
+ ScopedProcess process1(base::Bind(&DoNothing));
+ EXPECT_TRUE(process1.WaitForClosureToRun());
+
+ ScopedProcess process2(base::Bind(&DoExit));
+ // The closure didn't finish running normally. This case is simple enough
+ // that process.WaitForClosureToRun() should return false, even though the
+ // API does not guarantees that it will return at all.
+ EXPECT_FALSE(process2.WaitForClosureToRun());
+}
+
+void SleepInMsAndWriteOneByte(int time_to_sleep, int fd) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(time_to_sleep));
+ CHECK(1 == write(fd, "1", 1));
+}
+
+TEST(ScopedProcess, SynchronizationWorks) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_end_closer(pipe_fds[0]);
+ base::ScopedFD write_end_closer(pipe_fds[1]);
+
+ // Start a process with a closure that takes a little bit to run.
+ ScopedProcess process(
+ base::Bind(&SleepInMsAndWriteOneByte, 100, pipe_fds[1]));
+ EXPECT_TRUE(process.WaitForClosureToRun());
+
+ // Verify that the closure did, indeed, run.
+ ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+ char c = 0;
+ EXPECT_EQ(1, read(pipe_fds[0], &c, 1));
+ EXPECT_EQ('1', c);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/syscall_wrappers.cc b/sandbox/linux/services/syscall_wrappers.cc
new file mode 100644
index 0000000000..b6e87655a3
--- /dev/null
+++ b/sandbox/linux/services/syscall_wrappers.cc
@@ -0,0 +1,246 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/syscall_wrappers.h"
+
+#include <pthread.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <cstring>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "sandbox/linux/system_headers/capability.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "third_party/valgrind/valgrind.h"
+
+namespace sandbox {
+
+pid_t sys_getpid(void) {
+ return syscall(__NR_getpid);
+}
+
+pid_t sys_gettid(void) {
+ return syscall(__NR_gettid);
+}
+
+long sys_clone(unsigned long flags,
+ decltype(nullptr) child_stack,
+ pid_t* ptid,
+ pid_t* ctid,
+ decltype(nullptr) tls) {
+ const bool clone_tls_used = flags & CLONE_SETTLS;
+ const bool invalid_ctid =
+ (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) && !ctid;
+ const bool invalid_ptid = (flags & CLONE_PARENT_SETTID) && !ptid;
+
+ // We do not support CLONE_VM.
+ const bool clone_vm_used = flags & CLONE_VM;
+ if (clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used) {
+ RAW_LOG(FATAL, "Invalid usage of sys_clone");
+ }
+
+ if (ptid) MSAN_UNPOISON(ptid, sizeof(*ptid));
+ if (ctid) MSAN_UNPOISON(ctid, sizeof(*ctid));
+ // See kernel/fork.c in Linux. There is different ordering of sys_clone
+ // parameters depending on CONFIG_CLONE_BACKWARDS* configuration options.
+#if defined(ARCH_CPU_X86_64)
+ return syscall(__NR_clone, flags, child_stack, ptid, ctid, tls);
+#elif defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY)
+ // CONFIG_CLONE_BACKWARDS defined.
+ return syscall(__NR_clone, flags, child_stack, ptid, tls, ctid);
+#endif
+}
+
+long sys_clone(unsigned long flags) {
+ return sys_clone(flags, nullptr, nullptr, nullptr, nullptr);
+}
+
+void sys_exit_group(int status) {
+ syscall(__NR_exit_group, status);
+}
+
+int sys_seccomp(unsigned int operation,
+ unsigned int flags,
+ const struct sock_fprog* args) {
+ return syscall(__NR_seccomp, operation, flags, args);
+}
+
+int sys_prlimit64(pid_t pid,
+ int resource,
+ const struct rlimit64* new_limit,
+ struct rlimit64* old_limit) {
+ int res = syscall(__NR_prlimit64, pid, resource, new_limit, old_limit);
+ if (res == 0 && old_limit) MSAN_UNPOISON(old_limit, sizeof(*old_limit));
+ return res;
+}
+
+int sys_capget(cap_hdr* hdrp, cap_data* datap) {
+ int res = syscall(__NR_capget, hdrp, datap);
+ if (res == 0) {
+ if (hdrp) MSAN_UNPOISON(hdrp, sizeof(*hdrp));
+ if (datap) MSAN_UNPOISON(datap, sizeof(*datap));
+ }
+ return res;
+}
+
+int sys_capset(cap_hdr* hdrp, const cap_data* datap) {
+ return syscall(__NR_capset, hdrp, datap);
+}
+
+int sys_getresuid(uid_t* ruid, uid_t* euid, uid_t* suid) {
+ int res;
+#if defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARMEL)
+ // On 32-bit x86 or 32-bit arm, getresuid supports 16bit values only.
+ // Use getresuid32 instead.
+ res = syscall(__NR_getresuid32, ruid, euid, suid);
+#else
+ res = syscall(__NR_getresuid, ruid, euid, suid);
+#endif
+ if (res == 0) {
+ if (ruid) MSAN_UNPOISON(ruid, sizeof(*ruid));
+ if (euid) MSAN_UNPOISON(euid, sizeof(*euid));
+ if (suid) MSAN_UNPOISON(suid, sizeof(*suid));
+ }
+ return res;
+}
+
+int sys_getresgid(gid_t* rgid, gid_t* egid, gid_t* sgid) {
+ int res;
+#if defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARMEL)
+ // On 32-bit x86 or 32-bit arm, getresgid supports 16bit values only.
+ // Use getresgid32 instead.
+ res = syscall(__NR_getresgid32, rgid, egid, sgid);
+#else
+ res = syscall(__NR_getresgid, rgid, egid, sgid);
+#endif
+ if (res == 0) {
+ if (rgid) MSAN_UNPOISON(rgid, sizeof(*rgid));
+ if (egid) MSAN_UNPOISON(egid, sizeof(*egid));
+ if (sgid) MSAN_UNPOISON(sgid, sizeof(*sgid));
+ }
+ return res;
+}
+
+int sys_chroot(const char* path) {
+ return syscall(__NR_chroot, path);
+}
+
+int sys_unshare(int flags) {
+ return syscall(__NR_unshare, flags);
+}
+
+int sys_sigprocmask(int how, const sigset_t* set, decltype(nullptr) oldset) {
+ // In some toolchain (in particular Android and PNaCl toolchain),
+ // sigset_t is 32 bits, but Linux ABI requires 64 bits.
+ uint64_t linux_value = 0;
+ std::memcpy(&linux_value, set, std::min(sizeof(sigset_t), sizeof(uint64_t)));
+ return syscall(__NR_rt_sigprocmask, how, &linux_value, nullptr,
+ sizeof(linux_value));
+}
+
+#if (defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
+ (defined(ARCH_CPU_X86_64) && !defined(__clang__))) && \
+ !defined(OS_NACL_NONSFI)
+// If MEMORY_SANITIZER or THREAD_SANITIZER is enabled, it is necessary to call
+// sigaction() here, rather than the direct syscall (sys_sigaction() defined
+// by ourselves).
+// It is because, if MEMORY_SANITIZER or THREAD_SANITIZER is enabled, sigaction
+// is wrapped, and |act->sa_handler| is injected in order to unpoisonize the
+// memory passed via callback's arguments for MEMORY_SANITIZER, or handle
+// signals to check thread consistency for THREAD_SANITIZER. Please see
+// msan_interceptors.cc and tsan_interceptors.cc for more details.
+// So, specifically, if MEMORY_SANITIZER is enabled while the direct syscall is
+// used, as MEMORY_SANITIZER does not know about it, sigaction() invocation in
+// other places would be broken (in more precise, returned |oldact| would have
+// a broken |sa_handler| callback).
+// Practically, it would break NaCl's signal handler installation.
+// cf) native_client/src/trusted/service_runtime/linux/nacl_signal.c.
+// As for THREAD_SANITIZER, the intercepted signal handlers are processed more
+// in other libc functions' interceptors (such as for raise()), so that it
+// would not work properly.
+//
+// Also on x86_64 architecture, we need naked function for rt_sigreturn.
+// However, there is no simple way to define it with GCC. Note that the body
+// of function is actually very small (only two instructions), but we need to
+// define much debug information in addition, otherwise backtrace() used by
+// base::StackTrace would not work so that some tests would fail.
+//
+// When this is built with PNaCl toolchain, we should always use sys_sigaction
+// below, because sigaction() provided by the toolchain is incompatible with
+// Linux's ABI. So, otherwise, it would just fail. Note that it is not
+// necessary to think about sigaction() invocation in other places even with
+// MEMORY_SANITIZER or THREAD_SANITIZER, because it would just fail there.
+int sys_sigaction(int signum,
+ const struct sigaction* act,
+ struct sigaction* oldact) {
+ return sigaction(signum, act, oldact);
+}
+#else
+// struct sigaction is different ABI from the Linux's.
+struct KernelSigAction {
+ void (*kernel_handler)(int);
+ uint32_t sa_flags;
+ void (*sa_restorer)(void);
+ uint64_t sa_mask;
+};
+
+// On X86_64 arch, it is necessary to set sa_restorer always.
+#if defined(ARCH_CPU_X86_64)
+#if !defined(SA_RESTORER)
+#define SA_RESTORER 0x04000000
+#endif
+
+// rt_sigreturn is a special system call that interacts with the user land
+// stack. Thus, here prologue must not be created, which implies syscall()
+// does not work properly, too. Note that rt_sigreturn will never return.
+static __attribute__((naked)) void sys_rt_sigreturn() {
+ // Just invoke rt_sigreturn system call.
+ asm volatile ("syscall\n"
+ :: "a"(__NR_rt_sigreturn));
+}
+#endif
+
+int sys_sigaction(int signum,
+ const struct sigaction* act,
+ struct sigaction* oldact) {
+ KernelSigAction kernel_act = {};
+ if (act) {
+ kernel_act.kernel_handler = act->sa_handler;
+ std::memcpy(&kernel_act.sa_mask, &act->sa_mask,
+ std::min(sizeof(kernel_act.sa_mask), sizeof(act->sa_mask)));
+ kernel_act.sa_flags = act->sa_flags;
+
+#if defined(ARCH_CPU_X86_64)
+ if (!(kernel_act.sa_flags & SA_RESTORER)) {
+ kernel_act.sa_flags |= SA_RESTORER;
+ kernel_act.sa_restorer = sys_rt_sigreturn;
+ }
+#endif
+ }
+
+ KernelSigAction kernel_oldact = {};
+ int result = syscall(__NR_rt_sigaction, signum, act ? &kernel_act : nullptr,
+ oldact ? &kernel_oldact : nullptr, sizeof(uint64_t));
+ if (result == 0 && oldact) {
+ oldact->sa_handler = kernel_oldact.kernel_handler;
+ sigemptyset(&oldact->sa_mask);
+ std::memcpy(&oldact->sa_mask, &kernel_oldact.sa_mask,
+ std::min(sizeof(kernel_act.sa_mask), sizeof(act->sa_mask)));
+ oldact->sa_flags = kernel_oldact.sa_flags;
+ }
+ return result;
+}
+
+#endif // defined(MEMORY_SANITIZER)
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/syscall_wrappers.h b/sandbox/linux/services/syscall_wrappers.h
new file mode 100644
index 0000000000..581425a367
--- /dev/null
+++ b/sandbox/linux/services/syscall_wrappers.h
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
+#define SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "sandbox/sandbox_export.h"
+
+struct sock_fprog;
+struct rlimit64;
+struct cap_hdr;
+struct cap_data;
+
+namespace sandbox {
+
+// Provide direct system call wrappers for a few common system calls.
+// These are guaranteed to perform a system call and do not rely on things such
+// as caching the current pid (c.f. getpid()) unless otherwise specified.
+
+SANDBOX_EXPORT pid_t sys_getpid(void);
+
+SANDBOX_EXPORT pid_t sys_gettid(void);
+
+SANDBOX_EXPORT long sys_clone(unsigned long flags);
+
+// |regs| is not supported and must be passed as nullptr. |child_stack| must be
+// nullptr, since otherwise this function cannot safely return. As a
+// consequence, this function does not support CLONE_VM.
+SANDBOX_EXPORT long sys_clone(unsigned long flags,
+ decltype(nullptr) child_stack,
+ pid_t* ptid,
+ pid_t* ctid,
+ decltype(nullptr) regs);
+
+SANDBOX_EXPORT void sys_exit_group(int status);
+
+// The official system call takes |args| as void* (in order to be extensible),
+// but add more typing for the cases that are currently used.
+SANDBOX_EXPORT int sys_seccomp(unsigned int operation,
+ unsigned int flags,
+ const struct sock_fprog* args);
+
+// Some libcs do not expose a prlimit64 wrapper.
+SANDBOX_EXPORT int sys_prlimit64(pid_t pid,
+ int resource,
+ const struct rlimit64* new_limit,
+ struct rlimit64* old_limit);
+
+// Some libcs do not expose capget/capset wrappers. We want to use these
+// directly in order to avoid pulling in libcap2.
+SANDBOX_EXPORT int sys_capget(struct cap_hdr* hdrp, struct cap_data* datap);
+SANDBOX_EXPORT int sys_capset(struct cap_hdr* hdrp,
+ const struct cap_data* datap);
+
+// Some libcs do not expose getresuid/getresgid wrappers.
+SANDBOX_EXPORT int sys_getresuid(uid_t* ruid, uid_t* euid, uid_t* suid);
+SANDBOX_EXPORT int sys_getresgid(gid_t* rgid, gid_t* egid, gid_t* sgid);
+
+// Some libcs do not expose a chroot wrapper.
+SANDBOX_EXPORT int sys_chroot(const char* path);
+
+// Some libcs do not expose a unshare wrapper.
+SANDBOX_EXPORT int sys_unshare(int flags);
+
+// Some libcs do not expose a sigprocmask. Note that oldset must be a nullptr,
+// because of some ABI gap between toolchain's and Linux's.
+SANDBOX_EXPORT int sys_sigprocmask(int how,
+ const sigset_t* set,
+ decltype(nullptr) oldset);
+
+// Some libcs do not expose a sigaction().
+SANDBOX_EXPORT int sys_sigaction(int signum,
+ const struct sigaction* act,
+ struct sigaction* oldact);
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
diff --git a/sandbox/linux/services/syscall_wrappers_unittest.cc b/sandbox/linux/services/syscall_wrappers_unittest.cc
new file mode 100644
index 0000000000..249d9ae1da
--- /dev/null
+++ b/sandbox/linux/services/syscall_wrappers_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/syscall_wrappers.h"
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <cstring>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/valgrind/valgrind.h"
+
+namespace sandbox {
+
+namespace {
+
+TEST(SyscallWrappers, BasicSyscalls) {
+ EXPECT_EQ(getpid(), sys_getpid());
+}
+
+TEST(SyscallWrappers, CloneBasic) {
+ pid_t child = sys_clone(SIGCHLD);
+ TestUtils::HandlePostForkReturn(child);
+ EXPECT_LT(0, child);
+}
+
+TEST(SyscallWrappers, CloneParentSettid) {
+ pid_t ptid = 0;
+ pid_t child = sys_clone(CLONE_PARENT_SETTID | SIGCHLD, nullptr, &ptid,
+ nullptr, nullptr);
+ TestUtils::HandlePostForkReturn(child);
+ EXPECT_LT(0, child);
+ EXPECT_EQ(child, ptid);
+}
+
+TEST(SyscallWrappers, CloneChildSettid) {
+ pid_t ctid = 0;
+ pid_t pid =
+ sys_clone(CLONE_CHILD_SETTID | SIGCHLD, nullptr, nullptr, &ctid, nullptr);
+
+ const int kSuccessExit = 0;
+ if (0 == pid) {
+ // In child.
+ if (sys_getpid() == ctid)
+ _exit(kSuccessExit);
+ _exit(1);
+ }
+
+ ASSERT_NE(-1, pid);
+ int status = 0;
+ ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+ ASSERT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(kSuccessExit, WEXITSTATUS(status));
+}
+
+TEST(SyscallWrappers, GetRESUid) {
+ uid_t ruid, euid, suid;
+ uid_t sys_ruid, sys_euid, sys_suid;
+ ASSERT_EQ(0, getresuid(&ruid, &euid, &suid));
+ ASSERT_EQ(0, sys_getresuid(&sys_ruid, &sys_euid, &sys_suid));
+ EXPECT_EQ(ruid, sys_ruid);
+ EXPECT_EQ(euid, sys_euid);
+ EXPECT_EQ(suid, sys_suid);
+}
+
+TEST(SyscallWrappers, GetRESGid) {
+ gid_t rgid, egid, sgid;
+ gid_t sys_rgid, sys_egid, sys_sgid;
+ ASSERT_EQ(0, getresgid(&rgid, &egid, &sgid));
+ ASSERT_EQ(0, sys_getresgid(&sys_rgid, &sys_egid, &sys_sgid));
+ EXPECT_EQ(rgid, sys_rgid);
+ EXPECT_EQ(egid, sys_egid);
+ EXPECT_EQ(sgid, sys_sgid);
+}
+
+TEST(SyscallWrappers, LinuxSigSet) {
+ sigset_t sigset;
+ ASSERT_EQ(0, sigemptyset(&sigset));
+ ASSERT_EQ(0, sigaddset(&sigset, LINUX_SIGSEGV));
+ ASSERT_EQ(0, sigaddset(&sigset, LINUX_SIGBUS));
+ uint64_t linux_sigset = 0;
+ std::memcpy(&linux_sigset, &sigset,
+ std::min(sizeof(sigset), sizeof(linux_sigset)));
+ EXPECT_EQ((1ULL << (LINUX_SIGSEGV - 1)) | (1ULL << (LINUX_SIGBUS - 1)),
+ linux_sigset);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/thread_helpers.cc b/sandbox/linux/services/thread_helpers.cc
new file mode 100644
index 0000000000..80766a9bc5
--- /dev/null
+++ b/sandbox/linux/services/thread_helpers.cc
@@ -0,0 +1,157 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/thread_helpers.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "sandbox/linux/services/proc_util.h"
+
+namespace sandbox {
+
+namespace {
+
+const char kAssertSingleThreadedError[] =
+ "Current process is not mono-threaded!";
+
+bool IsSingleThreadedImpl(int proc_fd) {
+ CHECK_LE(0, proc_fd);
+ struct stat task_stat;
+ int fstat_ret = fstatat(proc_fd, "self/task/", &task_stat, 0);
+ PCHECK(0 == fstat_ret);
+
+ // At least "..", "." and the current thread should be present.
+ CHECK_LE(3UL, task_stat.st_nlink);
+ // Counting threads via /proc/self/task could be racy. For the purpose of
+ // determining if the current proces is monothreaded it works: if at any
+ // time it becomes monothreaded, it'll stay so.
+ return task_stat.st_nlink == 3;
+}
+
+bool IsThreadPresentInProcFS(int proc_fd,
+ const std::string& thread_id_dir_str) {
+ struct stat task_stat;
+ const int fstat_ret =
+ fstatat(proc_fd, thread_id_dir_str.c_str(), &task_stat, 0);
+ if (fstat_ret < 0) {
+ PCHECK(ENOENT == errno);
+ return false;
+ }
+ return true;
+}
+
+// Run |cb| in a loop until it returns false. Every time |cb| runs, sleep
+// for an exponentially increasing amount of time. |cb| is expected to return
+// false very quickly and this will crash if it doesn't happen within ~64ms on
+// Debug builds (2s on Release builds).
+// This is guaranteed to not sleep more than twice as much as the bare minimum
+// amount of time.
+void RunWhileTrue(const base::Callback<bool(void)>& cb) {
+#if defined(NDEBUG)
+ // In Release mode, crash after 30 iterations, which means having spent
+ // roughly 2s in
+ // nanosleep(2) cumulatively.
+ const unsigned int kMaxIterations = 30U;
+#else
+ // In practice, this never goes through more than a couple iterations. In
+ // debug mode, crash after 64ms (+ eventually 25 times the granularity of
+ // the clock) in nanosleep(2). This ensures that this is not becoming too
+ // slow.
+ const unsigned int kMaxIterations = 25U;
+#endif
+
+ // Run |cb| with an exponential back-off, sleeping 2^iterations nanoseconds
+ // in nanosleep(2).
+ // Note: the clock may not allow for nanosecond granularity, in this case the
+ // first iterations would sleep a tiny bit more instead, which would not
+ // change the calculations significantly.
+ for (unsigned int i = 0; i < kMaxIterations; ++i) {
+ if (!cb.Run()) {
+ return;
+ }
+
+ // Increase the waiting time exponentially.
+ struct timespec ts = {0, 1L << i /* nanoseconds */};
+ PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts)));
+ }
+
+ LOG(FATAL) << kAssertSingleThreadedError << " (iterations: " << kMaxIterations
+ << ")";
+
+ NOTREACHED();
+}
+
+bool IsMultiThreaded(int proc_fd) {
+ return !ThreadHelpers::IsSingleThreaded(proc_fd);
+}
+
+} // namespace
+
+// static
+bool ThreadHelpers::IsSingleThreaded(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ return IsSingleThreadedImpl(proc_fd);
+}
+
+// static
+bool ThreadHelpers::IsSingleThreaded() {
+ base::ScopedFD task_fd(ProcUtil::OpenProc());
+ return IsSingleThreaded(task_fd.get());
+}
+
+// static
+void ThreadHelpers::AssertSingleThreaded(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ const base::Callback<bool(void)> cb = base::Bind(&IsMultiThreaded, proc_fd);
+ RunWhileTrue(cb);
+}
+
+void ThreadHelpers::AssertSingleThreaded() {
+ base::ScopedFD task_fd(ProcUtil::OpenProc());
+ AssertSingleThreaded(task_fd.get());
+}
+
+// static
+bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_fd,
+ base::Thread* thread) {
+ DCHECK_LE(0, proc_fd);
+ DCHECK(thread);
+ const base::PlatformThreadId thread_id = thread->thread_id();
+ const std::string thread_id_dir_str =
+ "self/task/" + base::IntToString(thread_id) + "/";
+
+ // The kernel is at liberty to wake the thread id futex before updating
+ // /proc. Following Stop(), the thread is joined, but entries in /proc may
+ // not have been updated.
+ thread->Stop();
+
+ const base::Callback<bool(void)> cb =
+ base::Bind(&IsThreadPresentInProcFS, proc_fd, thread_id_dir_str);
+
+ RunWhileTrue(cb);
+
+ return true;
+}
+
+// static
+const char* ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests() {
+ return kAssertSingleThreadedError;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/thread_helpers.h b/sandbox/linux/services/thread_helpers.h
new file mode 100644
index 0000000000..f4abdffd03
--- /dev/null
+++ b/sandbox/linux/services/thread_helpers.h
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
+#define SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace base { class Thread; }
+
+namespace sandbox {
+
+class SANDBOX_EXPORT ThreadHelpers {
+ public:
+ // Check whether the current process is single threaded. |proc_fd|
+ // must be a file descriptor to /proc/ and remains owned by the
+ // caller.
+ static bool IsSingleThreaded(int proc_fd);
+ static bool IsSingleThreaded();
+
+ // Crash if the current process is not single threaded. This will wait
+ // on /proc to be updated. In the case where this doesn't crash, this will
+ // return promptly. In the case where this does crash, this will first wait
+ // for a few ms in Debug mode, a few seconds in Release mode.
+ static void AssertSingleThreaded(int proc_fd);
+ static void AssertSingleThreaded();
+
+ // Stop |thread| and ensure that it does not have an entry in
+ // /proc/self/task/ from the point of view of the current thread. This is
+ // the way to stop threads before calling IsSingleThreaded().
+ static bool StopThreadAndWatchProcFS(int proc_fd, base::Thread* thread);
+
+ static const char* GetAssertSingleThreadedErrorMessageForTests();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadHelpers);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
diff --git a/sandbox/linux/services/thread_helpers_unittests.cc b/sandbox/linux/services/thread_helpers_unittests.cc
new file mode 100644
index 0000000000..7357a0cfa7
--- /dev/null
+++ b/sandbox/linux/services/thread_helpers_unittests.cc
@@ -0,0 +1,147 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/thread_helpers.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_metrics.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::PlatformThread;
+
+namespace sandbox {
+
+namespace {
+
+// These tests fail under ThreadSanitizer, see http://crbug.com/342305
+#if !defined(THREAD_SANITIZER)
+
+int GetRaceTestIterations() {
+ if (IsRunningOnValgrind()) {
+ return 2;
+ } else {
+ return 1000;
+ }
+}
+
+class ScopedProc {
+ public:
+ ScopedProc() : fd_(-1) {
+ fd_ = open("/proc/", O_RDONLY | O_DIRECTORY);
+ CHECK_LE(0, fd_);
+ }
+
+ ~ScopedProc() { PCHECK(0 == IGNORE_EINTR(close(fd_))); }
+
+ int fd() { return fd_; }
+
+ private:
+ int fd_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedProc);
+};
+
+TEST(ThreadHelpers, IsSingleThreadedBasic) {
+ ScopedProc proc_fd;
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded());
+
+ base::Thread thread("sandbox_tests");
+ ASSERT_TRUE(thread.Start());
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded());
+ // Explicitly stop the thread here to not pollute the next test.
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread));
+}
+
+SANDBOX_TEST(ThreadHelpers, AssertSingleThreaded) {
+ ScopedProc proc_fd;
+ SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded());
+
+ ThreadHelpers::AssertSingleThreaded(proc_fd.fd());
+ ThreadHelpers::AssertSingleThreaded();
+}
+
+TEST(ThreadHelpers, IsSingleThreadedIterated) {
+ ScopedProc proc_fd;
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+
+ // Iterate to check for race conditions.
+ for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ base::Thread thread("sandbox_tests");
+ ASSERT_TRUE(thread.Start());
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ // Explicitly stop the thread here to not pollute the next test.
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread));
+ }
+}
+
+TEST(ThreadHelpers, IsSingleThreadedStartAndStop) {
+ ScopedProc proc_fd;
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+
+ base::Thread thread("sandbox_tests");
+ // This is testing for a race condition, so iterate.
+ // Manually, this has been tested with more that 1M iterations.
+ for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ ASSERT_TRUE(thread.Start());
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread));
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ ASSERT_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
+ }
+}
+
+SANDBOX_TEST(ThreadHelpers, AssertSingleThreadedAfterThreadStopped) {
+ SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded());
+
+ base::Thread thread1("sandbox_tests");
+ base::Thread thread2("sandbox_tests");
+
+ for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ SANDBOX_ASSERT(thread1.Start());
+ SANDBOX_ASSERT(thread2.Start());
+ SANDBOX_ASSERT(!ThreadHelpers::IsSingleThreaded());
+
+ thread1.Stop();
+ thread2.Stop();
+ // This will wait on /proc/ to reflect the state of threads in the
+ // process.
+ ThreadHelpers::AssertSingleThreaded();
+ SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded());
+ }
+}
+
+// Only run this test in Debug mode, where AssertSingleThreaded() will return
+// in less than 64ms.
+#if !defined(NDEBUG)
+SANDBOX_DEATH_TEST(
+ ThreadHelpers,
+ AssertSingleThreadedDies,
+ DEATH_MESSAGE(
+ ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests())) {
+ base::Thread thread1("sandbox_tests");
+ SANDBOX_ASSERT(thread1.Start());
+ ThreadHelpers::AssertSingleThreaded();
+}
+#endif // !defined(NDEBUG)
+
+#endif // !defined(THREAD_SANITIZER)
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/yama.cc b/sandbox/linux/services/yama.cc
new file mode 100644
index 0000000000..151f4bd340
--- /dev/null
+++ b/sandbox/linux/services/yama.cc
@@ -0,0 +1,115 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/yama.h"
+
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+#if !defined(PR_SET_PTRACER_ANY)
+#define PR_SET_PTRACER_ANY ((unsigned long)-1)
+#endif
+
+#if !defined(PR_SET_PTRACER)
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+namespace sandbox {
+
+namespace {
+
+// Enable or disable the Yama ptracers restrictions.
+// Return false if Yama is not present on this kernel.
+bool SetYamaPtracersRestriction(bool enable_restrictions) {
+ unsigned long set_ptracer_arg;
+ if (enable_restrictions) {
+ set_ptracer_arg = 0;
+ } else {
+ set_ptracer_arg = PR_SET_PTRACER_ANY;
+ }
+
+ const int ret = prctl(PR_SET_PTRACER, set_ptracer_arg);
+ const int prctl_errno = errno;
+
+ if (0 == ret) {
+ return true;
+ } else {
+ // ENOSYS or EINVAL means Yama is not in the current kernel.
+ CHECK(ENOSYS == prctl_errno || EINVAL == prctl_errno);
+ return false;
+ }
+}
+
+bool CanAccessProcFS() {
+ static const char kProcfsKernelSysPath[] = "/proc/sys/kernel/";
+ int ret = access(kProcfsKernelSysPath, F_OK);
+ if (ret) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+// static
+bool Yama::RestrictPtracersToAncestors() {
+ return SetYamaPtracersRestriction(true /* enable_restrictions */);
+}
+
+// static
+bool Yama::DisableYamaRestrictions() {
+ return SetYamaPtracersRestriction(false /* enable_restrictions */);
+}
+
+// static
+int Yama::GetStatus() {
+ if (!CanAccessProcFS()) {
+ return 0;
+ }
+
+ static const char kPtraceScopePath[] = "/proc/sys/kernel/yama/ptrace_scope";
+
+ base::ScopedFD yama_scope(HANDLE_EINTR(open(kPtraceScopePath, O_RDONLY)));
+
+ if (!yama_scope.is_valid()) {
+ const int open_errno = errno;
+ DCHECK(ENOENT == open_errno);
+ // The status is known, yama is not present.
+ return STATUS_KNOWN;
+ }
+
+ char yama_scope_value = 0;
+ ssize_t num_read = HANDLE_EINTR(read(yama_scope.get(), &yama_scope_value, 1));
+ PCHECK(1 == num_read);
+
+ switch (yama_scope_value) {
+ case '0':
+ return STATUS_KNOWN | STATUS_PRESENT;
+ case '1':
+ return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING;
+ case '2':
+ case '3':
+ return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING |
+ STATUS_STRICT_ENFORCING;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+// static
+bool Yama::IsPresent() { return GetStatus() & STATUS_PRESENT; }
+
+// static
+bool Yama::IsEnforcing() { return GetStatus() & STATUS_ENFORCING; }
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/yama.h b/sandbox/linux/services/yama.h
new file mode 100644
index 0000000000..e6c5c45b2a
--- /dev/null
+++ b/sandbox/linux/services/yama.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_YAMA_H_
+#define SANDBOX_LINUX_SERVICES_YAMA_H_
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Yama is a LSM kernel module which can restrict ptrace().
+// This class provides ways to detect if Yama is present and enabled
+// and to restrict which processes can ptrace the current process.
+class SANDBOX_EXPORT Yama {
+ public:
+ // This enum should be used to set or check a bitmask.
+ // A value of 0 would indicate that the status is not known.
+ enum GlobalStatus {
+ STATUS_KNOWN = 1 << 0,
+ STATUS_PRESENT = 1 << 1,
+ STATUS_ENFORCING = 1 << 2,
+ // STATUS_STRICT_ENFORCING corresponds to either mode 2 or mode 3 of Yama.
+ // Ptrace could be entirely denied, or restricted to CAP_SYS_PTRACE
+ // and PTRACE_TRACEME.
+ STATUS_STRICT_ENFORCING = 1 << 3
+ };
+
+ // Restrict who can ptrace() the current process to its ancestors.
+ // If this succeeds, then Yama is available on this kernel.
+ // However, Yama may not be enforcing at this time.
+ static bool RestrictPtracersToAncestors();
+
+ // Disable Yama restrictions for the current process.
+ // This will fail if Yama is not available on this kernel.
+ // This is meant for testing only. If you need this, implement
+ // a per-pid authorization instead.
+ static bool DisableYamaRestrictions();
+
+ // Checks if Yama is currently in enforcing mode for the machine (not the
+ // current process). This requires access to the filesystem and will use
+ // /proc/sys/kernel/yama/ptrace_scope.
+ static int GetStatus();
+
+ // Helper for checking for STATUS_PRESENT in GetStatus().
+ static bool IsPresent();
+ // Helper for checkking for STATUS_ENFORCING in GetStatus().
+ static bool IsEnforcing();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Yama);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_YAMA_H_
diff --git a/sandbox/linux/services/yama_unittests.cc b/sandbox/linux/services/yama_unittests.cc
new file mode 100644
index 0000000000..204cfd6a44
--- /dev/null
+++ b/sandbox/linux/services/yama_unittests.cc
@@ -0,0 +1,172 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_util.h"
+#include "base/sys_info.h"
+#include "sandbox/linux/services/scoped_process.h"
+#include "sandbox/linux/services/yama.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool HasLinux32Bug() {
+#if defined(__i386__)
+ // On 3.2 kernels, yama doesn't work for 32-bit binaries on 64-bit kernels.
+ // This is fixed in 3.4.
+ bool is_kernel_64bit =
+ base::SysInfo::OperatingSystemArchitecture() == "x86_64";
+ bool is_linux = base::SysInfo::OperatingSystemName() == "Linux";
+ bool is_3_dot_2 = base::StartsWithASCII(
+ base::SysInfo::OperatingSystemVersion(), "3.2", /*case_sensitive=*/false);
+ if (is_kernel_64bit && is_linux && is_3_dot_2)
+ return true;
+#endif // defined(__i386__)
+ return false;
+}
+
+bool CanPtrace(pid_t pid) {
+ int ret;
+ ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
+ if (ret == -1) {
+ CHECK_EQ(EPERM, errno);
+ return false;
+ }
+ // Wait for the process to be stopped so that it can be detached.
+ siginfo_t process_info;
+ int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
+ PCHECK(0 == wait_ret);
+ PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
+ return true;
+}
+
+// _exit(0) if pid can be ptraced by the current process.
+// _exit(1) otherwise.
+void ExitZeroIfCanPtrace(pid_t pid) {
+ if (CanPtrace(pid)) {
+ _exit(0);
+ } else {
+ _exit(1);
+ }
+}
+
+bool CanSubProcessPtrace(pid_t pid) {
+ ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid));
+ bool signaled;
+ int exit_code = process.WaitForExit(&signaled);
+ CHECK(!signaled);
+ return 0 == exit_code;
+}
+
+// The tests below assume that the system-level configuration will not change
+// while they run.
+
+TEST(Yama, GetStatus) {
+ int status1 = Yama::GetStatus();
+
+ // Check that the value is a possible bitmask.
+ ASSERT_LE(0, status1);
+ ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
+ Yama::STATUS_STRICT_ENFORCING,
+ status1);
+
+ // The status should not just be a random value.
+ int status2 = Yama::GetStatus();
+ EXPECT_EQ(status1, status2);
+
+ // This test is not running sandboxed, there is no reason to not know the
+ // status.
+ EXPECT_NE(0, Yama::STATUS_KNOWN & status1);
+
+ if (status1 & Yama::STATUS_STRICT_ENFORCING) {
+ // If Yama is strictly enforcing, it is also enforcing.
+ EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
+ }
+
+ if (status1 & Yama::STATUS_ENFORCING) {
+ // If Yama is enforcing, Yama is present.
+ EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
+ }
+
+ // Verify that the helper functions work as intended.
+ EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
+ Yama::IsEnforcing());
+ EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
+ Yama::IsPresent());
+
+ fprintf(stdout,
+ "Yama present: %s - enforcing: %s\n",
+ Yama::IsPresent() ? "Y" : "N",
+ Yama::IsEnforcing() ? "Y" : "N");
+}
+
+SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
+ // This call will succeed iff Yama is present.
+ bool restricted = Yama::RestrictPtracersToAncestors();
+ CHECK_EQ(restricted, Yama::IsPresent());
+}
+
+// Attempts to enable or disable Yama restrictions.
+void SetYamaRestrictions(bool enable_restriction) {
+ if (enable_restriction) {
+ Yama::RestrictPtracersToAncestors();
+ } else {
+ Yama::DisableYamaRestrictions();
+ }
+}
+
+TEST(Yama, RestrictPtraceWorks) {
+ if (HasLinux32Bug())
+ return;
+
+ ScopedProcess process1(base::Bind(&SetYamaRestrictions, true));
+ ASSERT_TRUE(process1.WaitForClosureToRun());
+
+ if (Yama::IsEnforcing()) {
+ // A sibling process cannot ptrace process1.
+ ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
+ }
+
+ if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
+ // However, parent can ptrace process1.
+ ASSERT_TRUE(CanPtrace(process1.GetPid()));
+
+ // A sibling can ptrace process2 which disables any Yama protection.
+ ScopedProcess process2(base::Bind(&SetYamaRestrictions, false));
+ ASSERT_TRUE(process2.WaitForClosureToRun());
+ ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
+ }
+}
+
+void DoNothing() {}
+
+SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
+ if (!Yama::IsPresent() || HasLinux32Bug())
+ return;
+
+ CHECK(Yama::DisableYamaRestrictions());
+ ScopedProcess process1(base::Bind(&DoNothing));
+
+ if (Yama::IsEnforcing()) {
+ // Check that process1 is protected by Yama, even though it has
+ // been created from a process that disabled Yama.
+ CHECK(!CanSubProcessPtrace(process1.GetPid()));
+ }
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_channel.cc b/sandbox/linux/syscall_broker/broker_channel.cc
new file mode 100644
index 0000000000..fa0f7615fc
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_channel.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// static
+void BrokerChannel::CreatePair(EndPoint* reader, EndPoint* writer) {
+ DCHECK(reader);
+ DCHECK(writer);
+ int socket_pair[2];
+ // Use SOCK_SEQPACKET, to preserve message boundaries but we also want to be
+ // notified (recvmsg should return and not block) when the connection has
+ // been broken which could mean that the other end has been closed.
+ PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socket_pair));
+
+ reader->reset(socket_pair[0]);
+ PCHECK(0 == shutdown(reader->get(), SHUT_WR));
+
+ writer->reset(socket_pair[1]);
+ PCHECK(0 == shutdown(writer->get(), SHUT_RD));
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_channel.h b/sandbox/linux/syscall_broker/broker_channel.h
new file mode 100644
index 0000000000..2abdba413a
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_channel.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// A small class to create a pipe-like communication channel. It is based on a
+// SOCK_SEQPACKET unix socket, which is connection-based and guaranteed to
+// preserve message boundaries.
+class BrokerChannel {
+ public:
+ typedef base::ScopedFD EndPoint;
+ static void CreatePair(EndPoint* reader, EndPoint* writer);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BrokerChannel);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
diff --git a/sandbox/linux/syscall_broker/broker_client.cc b/sandbox/linux/syscall_broker/broker_client.cc
new file mode 100644
index 0000000000..760cf59b3c
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_client.cc
@@ -0,0 +1,144 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_client.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "build/build_config.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+#include "sandbox/linux/syscall_broker/broker_policy.h"
+
+#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
+#define MSG_CMSG_CLOEXEC 0x40000000
+#endif
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// Make a remote system call over IPC for syscalls that take a path and flags
+// as arguments, currently open() and access().
+// Will return -errno like a real system call.
+// This function needs to be async signal safe.
+int BrokerClient::PathAndFlagsSyscall(IPCCommand syscall_type,
+ const char* pathname,
+ int flags) const {
+ int recvmsg_flags = 0;
+ RAW_CHECK(syscall_type == COMMAND_OPEN || syscall_type == COMMAND_ACCESS);
+ if (!pathname)
+ return -EFAULT;
+
+ // For this "remote system call" to work, we need to handle any flag that
+ // cannot be sent over a Unix socket in a special way.
+ // See the comments around kCurrentProcessOpenFlagsMask.
+ if (syscall_type == COMMAND_OPEN && (flags & kCurrentProcessOpenFlagsMask)) {
+ // This implementation only knows about O_CLOEXEC, someone needs to look at
+ // this code if other flags are added.
+ RAW_CHECK(kCurrentProcessOpenFlagsMask == O_CLOEXEC);
+ recvmsg_flags |= MSG_CMSG_CLOEXEC;
+ flags &= ~O_CLOEXEC;
+ }
+
+ // There is no point in forwarding a request that we know will be denied.
+ // Of course, the real security check needs to be on the other side of the
+ // IPC.
+ if (fast_check_in_client_) {
+ if (syscall_type == COMMAND_OPEN &&
+ !broker_policy_.GetFileNameIfAllowedToOpen(
+ pathname, flags, NULL /* file_to_open */,
+ NULL /* unlink_after_open */)) {
+ return -broker_policy_.denied_errno();
+ }
+ if (syscall_type == COMMAND_ACCESS &&
+ !broker_policy_.GetFileNameIfAllowedToAccess(pathname, flags, NULL)) {
+ return -broker_policy_.denied_errno();
+ }
+ }
+
+ base::Pickle write_pickle;
+ write_pickle.WriteInt(syscall_type);
+ write_pickle.WriteString(pathname);
+ write_pickle.WriteInt(flags);
+ RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+
+ int returned_fd = -1;
+ uint8_t reply_buf[kMaxMessageLength];
+
+ // Send a request (in write_pickle) as well that will include a new
+ // temporary socketpair (created internally by SendRecvMsg()).
+ // Then read the reply on this new socketpair in reply_buf and put an
+ // eventual attached file descriptor in |returned_fd|.
+ ssize_t msg_len = base::UnixDomainSocket::SendRecvMsgWithFlags(
+ ipc_channel_.get(), reply_buf, sizeof(reply_buf), recvmsg_flags,
+ &returned_fd, write_pickle);
+ if (msg_len <= 0) {
+ if (!quiet_failures_for_tests_)
+ RAW_LOG(ERROR, "Could not make request to broker process");
+ return -ENOMEM;
+ }
+
+ base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
+ base::PickleIterator iter(read_pickle);
+ int return_value = -1;
+ // Now deserialize the return value and eventually return the file
+ // descriptor.
+ if (iter.ReadInt(&return_value)) {
+ switch (syscall_type) {
+ case COMMAND_ACCESS:
+ // We should never have a fd to return.
+ RAW_CHECK(returned_fd == -1);
+ return return_value;
+ case COMMAND_OPEN:
+ if (return_value < 0) {
+ RAW_CHECK(returned_fd == -1);
+ return return_value;
+ } else {
+ // We have a real file descriptor to return.
+ RAW_CHECK(returned_fd >= 0);
+ return returned_fd;
+ }
+ default:
+ RAW_LOG(ERROR, "Unsupported command");
+ return -ENOSYS;
+ }
+ } else {
+ RAW_LOG(ERROR, "Could not read pickle");
+ NOTREACHED();
+ return -ENOMEM;
+ }
+}
+
+BrokerClient::BrokerClient(const BrokerPolicy& broker_policy,
+ BrokerChannel::EndPoint ipc_channel,
+ bool fast_check_in_client,
+ bool quiet_failures_for_tests)
+ : broker_policy_(broker_policy),
+ ipc_channel_(ipc_channel.Pass()),
+ fast_check_in_client_(fast_check_in_client),
+ quiet_failures_for_tests_(quiet_failures_for_tests) {
+}
+
+BrokerClient::~BrokerClient() {
+}
+
+int BrokerClient::Access(const char* pathname, int mode) const {
+ return PathAndFlagsSyscall(COMMAND_ACCESS, pathname, mode);
+}
+
+int BrokerClient::Open(const char* pathname, int flags) const {
+ return PathAndFlagsSyscall(COMMAND_OPEN, pathname, flags);
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_client.h b/sandbox/linux/syscall_broker/broker_client.h
new file mode 100644
index 0000000000..2dfef8150c
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_client.h
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_
+
+#include "base/macros.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerPolicy;
+
+// This class can be embedded in a sandboxed process and can be
+// used to perform certain system calls in another, presumably
+// non-sandboxed process (which embeds BrokerHost).
+// A key feature of this class is the ability to use some of its methods in a
+// thread-safe and async-signal safe way. The goal is to be able to use it to
+// replace the open() or access() system calls happening anywhere in a process
+// (as allowed for instance by seccomp-bpf's SIGSYS mechanism).
+class BrokerClient {
+ public:
+ // |policy| needs to match the policy used by BrokerHost. This
+ // allows to predict some of the requests which will be denied
+ // and save an IPC round trip.
+ // |ipc_channel| needs to be a suitable SOCK_SEQPACKET unix socket.
+ // |fast_check_in_client| should be set to true and
+ // |quiet_failures_for_tests| to false unless you are writing tests.
+ BrokerClient(const BrokerPolicy& policy,
+ BrokerChannel::EndPoint ipc_channel,
+ bool fast_check_in_client,
+ bool quiet_failures_for_tests);
+ ~BrokerClient();
+
+ // Can be used in place of access().
+ // X_OK will always return an error in practice since the broker process
+ // doesn't support execute permissions.
+ // It's similar to the access() system call and will return -errno on errors.
+ // This is async signal safe.
+ int Access(const char* pathname, int mode) const;
+ // Can be used in place of open().
+ // The implementation only supports certain white listed flags and will
+ // return -EPERM on other flags.
+ // It's similar to the open() system call and will return -errno on errors.
+ // This is async signal safe.
+ int Open(const char* pathname, int flags) const;
+
+ // Get the file descriptor used for IPC. This is used for tests.
+ int GetIPCDescriptor() const { return ipc_channel_.get(); }
+
+ private:
+ const BrokerPolicy& broker_policy_;
+ const BrokerChannel::EndPoint ipc_channel_;
+ const bool fast_check_in_client_; // Whether to forward a request that we
+ // know will be denied to the broker. (Used
+ // for tests).
+ const bool quiet_failures_for_tests_; // Disable certain error message when
+ // testing for failures.
+
+ int PathAndFlagsSyscall(IPCCommand syscall_type,
+ const char* pathname,
+ int flags) const;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerClient);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_
diff --git a/sandbox/linux/syscall_broker/broker_file_permission.cc b/sandbox/linux/syscall_broker/broker_file_permission.cc
new file mode 100644
index 0000000000..beceda93f5
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_file_permission.cc
@@ -0,0 +1,243 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+
+#include <fcntl.h>
+#include <string.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// Async signal safe
+bool BrokerFilePermission::ValidatePath(const char* path) {
+ if (!path)
+ return false;
+
+ const size_t len = strlen(path);
+ // No empty paths
+ if (len == 0)
+ return false;
+ // Paths must be absolute and not relative
+ if (path[0] != '/')
+ return false;
+ // No trailing / (but "/" is valid)
+ if (len > 1 && path[len - 1] == '/')
+ return false;
+ // No trailing /..
+ if (len >= 3 && path[len - 3] == '/' && path[len - 2] == '.' &&
+ path[len - 1] == '.')
+ return false;
+ // No /../ anywhere
+ for (size_t i = 0; i < len; i++) {
+ if (path[i] == '/' && (len - i) > 3) {
+ if (path[i + 1] == '.' && path[i + 2] == '.' && path[i + 3] == '/') {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Async signal safe
+// Calls std::string::c_str(), strncmp and strlen. All these
+// methods are async signal safe in common standard libs.
+// TODO(leecam): remove dependency on std::string
+bool BrokerFilePermission::MatchPath(const char* requested_filename) const {
+ const char* path = path_.c_str();
+ if ((recursive_ && strncmp(requested_filename, path, strlen(path)) == 0)) {
+ // Note: This prefix match will allow any path under the whitelisted
+ // path, for any number of directory levels. E.g. if the whitelisted
+ // path is /good/ then the following will be permitted by the policy.
+ // /good/file1
+ // /good/folder/file2
+ // /good/folder/folder2/file3
+ // If an attacker could make 'folder' a symlink to ../../ they would have
+ // access to the entire filesystem.
+ // Whitelisting with multiple depths is useful, e.g /proc/ but
+ // the system needs to ensure symlinks can not be created!
+ // That said if an attacker can convert any of the absolute paths
+ // to a symlink they can control any file on the system also.
+ return true;
+ } else if (strcmp(requested_filename, path) == 0) {
+ return true;
+ }
+ return false;
+}
+
+// Async signal safe.
+// External call to std::string::c_str() is
+// called in MatchPath.
+// TODO(leecam): remove dependency on std::string
+bool BrokerFilePermission::CheckAccess(const char* requested_filename,
+ int mode,
+ const char** file_to_access) const {
+ // First, check if |mode| is existence, ability to read or ability
+ // to write. We do not support X_OK.
+ if (mode != F_OK && mode & ~(R_OK | W_OK)) {
+ return false;
+ }
+
+ if (!ValidatePath(requested_filename))
+ return false;
+
+ if (!MatchPath(requested_filename)) {
+ return false;
+ }
+ bool allowed = false;
+ switch (mode) {
+ case F_OK:
+ if (allow_read_ || allow_write_)
+ allowed = true;
+ break;
+ case R_OK:
+ if (allow_read_)
+ allowed = true;
+ break;
+ case W_OK:
+ if (allow_write_)
+ allowed = true;
+ break;
+ case R_OK | W_OK:
+ if (allow_read_ && allow_write_)
+ allowed = true;
+ break;
+ default:
+ return false;
+ }
+
+ if (allowed && file_to_access) {
+ if (!recursive_)
+ *file_to_access = path_.c_str();
+ else
+ *file_to_access = requested_filename;
+ }
+ return allowed;
+}
+
+// Async signal safe.
+// External call to std::string::c_str() is
+// called in MatchPath.
+// TODO(leecam): remove dependency on std::string
+bool BrokerFilePermission::CheckOpen(const char* requested_filename,
+ int flags,
+ const char** file_to_open,
+ bool* unlink_after_open) const {
+ if (!ValidatePath(requested_filename))
+ return false;
+
+ if (!MatchPath(requested_filename)) {
+ return false;
+ }
+
+ // First, check the access mode is valid.
+ const int access_mode = flags & O_ACCMODE;
+ if (access_mode != O_RDONLY && access_mode != O_WRONLY &&
+ access_mode != O_RDWR) {
+ return false;
+ }
+
+ // Check if read is allowed
+ if (!allow_read_ && (access_mode == O_RDONLY || access_mode == O_RDWR)) {
+ return false;
+ }
+
+ // Check if write is allowed
+ if (!allow_write_ && (access_mode == O_WRONLY || access_mode == O_RDWR)) {
+ return false;
+ }
+
+ // Check if file creation is allowed.
+ if (!allow_create_ && (flags & O_CREAT)) {
+ return false;
+ }
+
+ // If O_CREAT is present, ensure O_EXCL
+ if ((flags & O_CREAT) && !(flags & O_EXCL)) {
+ return false;
+ }
+
+ // If this file is to be unlinked, ensure it's created.
+ if (unlink_ && !(flags & O_CREAT)) {
+ return false;
+ }
+
+ // Some flags affect the behavior of the current process. We don't support
+ // them and don't allow them for now.
+ if (flags & kCurrentProcessOpenFlagsMask) {
+ return false;
+ }
+
+ // Now check that all the flags are known to us.
+ const int creation_and_status_flags = flags & ~O_ACCMODE;
+
+ const int known_flags = O_APPEND | O_ASYNC | O_CLOEXEC | O_CREAT | O_DIRECT |
+ O_DIRECTORY | O_EXCL | O_LARGEFILE | O_NOATIME |
+ O_NOCTTY | O_NOFOLLOW | O_NONBLOCK | O_NDELAY |
+ O_SYNC | O_TRUNC;
+
+ const int unknown_flags = ~known_flags;
+ const bool has_unknown_flags = creation_and_status_flags & unknown_flags;
+
+ if (has_unknown_flags)
+ return false;
+
+ if (file_to_open) {
+ if (!recursive_)
+ *file_to_open = path_.c_str();
+ else
+ *file_to_open = requested_filename;
+ }
+ if (unlink_after_open)
+ *unlink_after_open = unlink_;
+
+ return true;
+}
+
+const char* BrokerFilePermission::GetErrorMessageForTests() {
+ static char kInvalidBrokerFileString[] = "Invalid BrokerFilePermission";
+ return kInvalidBrokerFileString;
+}
+
+BrokerFilePermission::BrokerFilePermission(const std::string& path,
+ bool recursive,
+ bool unlink,
+ bool allow_read,
+ bool allow_write,
+ bool allow_create)
+ : path_(path),
+ recursive_(recursive),
+ unlink_(unlink),
+ allow_read_(allow_read),
+ allow_write_(allow_write),
+ allow_create_(allow_create) {
+ // Validate this permission and die if invalid!
+
+ // Must have enough length for a '/'
+ CHECK(path_.length() > 0) << GetErrorMessageForTests();
+ // Whitelisted paths must be absolute.
+ CHECK(path_[0] == '/') << GetErrorMessageForTests();
+
+ // Don't allow unlinking on creation without create permission
+ if (unlink_) {
+ CHECK(allow_create) << GetErrorMessageForTests();
+ }
+ const char last_char = *(path_.rbegin());
+ // Recursive paths must have a trailing slash
+ if (recursive_) {
+ CHECK(last_char == '/') << GetErrorMessageForTests();
+ } else {
+ CHECK(last_char != '/') << GetErrorMessageForTests();
+ }
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox \ No newline at end of file
diff --git a/sandbox/linux/syscall_broker/broker_file_permission.h b/sandbox/linux/syscall_broker/broker_file_permission.h
new file mode 100644
index 0000000000..03300d1d74
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_file_permission.h
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// BrokerFilePermission defines a path for whitelisting.
+// Pick the correct static factory method to create a permission.
+// CheckOpen and CheckAccess are async signal safe.
+// Constuction and Destruction are not async signal safe.
+// |path| is the path to be whitelisted.
+class SANDBOX_EXPORT BrokerFilePermission {
+ public:
+ ~BrokerFilePermission() {}
+ BrokerFilePermission(const BrokerFilePermission&) = default;
+ BrokerFilePermission& operator=(const BrokerFilePermission&) = default;
+
+ static BrokerFilePermission ReadOnly(const std::string& path) {
+ return BrokerFilePermission(path, false, false, true, false, false);
+ }
+
+ static BrokerFilePermission ReadOnlyRecursive(const std::string& path) {
+ return BrokerFilePermission(path, true, false, true, false, false);
+ }
+
+ static BrokerFilePermission WriteOnly(const std::string& path) {
+ return BrokerFilePermission(path, false, false, false, true, false);
+ }
+
+ static BrokerFilePermission ReadWrite(const std::string& path) {
+ return BrokerFilePermission(path, false, false, true, true, false);
+ }
+
+ static BrokerFilePermission ReadWriteCreate(const std::string& path) {
+ return BrokerFilePermission(path, false, false, true, true, true);
+ }
+
+ static BrokerFilePermission ReadWriteCreateUnlink(const std::string& path) {
+ return BrokerFilePermission(path, false, true, true, true, true);
+ }
+
+ static BrokerFilePermission ReadWriteCreateUnlinkRecursive(
+ const std::string& path) {
+ return BrokerFilePermission(path, true, true, true, true, true);
+ }
+
+ // Returns true if |requested_filename| is allowed to be opened
+ // by this permission.
+ // If |file_to_open| is not NULL it is set to point to either
+ // the |requested_filename| in the case of a recursive match,
+ // or a pointer the matched path in the whitelist if an absolute
+ // match.
+ // If not NULL |unlink_after_open| is set to point to true if the
+ // caller should unlink the path after openning.
+ // Async signal safe if |file_to_open| is NULL.
+ bool CheckOpen(const char* requested_filename,
+ int flags,
+ const char** file_to_open,
+ bool* unlink_after_open) const;
+ // Returns true if |requested_filename| is allowed to be accessed
+ // by this permission as per access(2).
+ // If |file_to_open| is not NULL it is set to point to either
+ // the |requested_filename| in the case of a recursive match,
+ // or a pointer to the matched path in the whitelist if an absolute
+ // match.
+ // |mode| is per mode argument of access(2).
+ // Async signal safe if |file_to_access| is NULL
+ bool CheckAccess(const char* requested_filename,
+ int mode,
+ const char** file_to_access) const;
+
+ private:
+ friend class BrokerFilePermissionTester;
+ BrokerFilePermission(const std::string& path,
+ bool recursive,
+ bool unlink,
+ bool allow_read,
+ bool allow_write,
+ bool allow_create);
+
+ // ValidatePath checks |path| and returns true if these conditions are met
+ // * Greater than 0 length
+ // * Is an absolute path
+ // * No trailing slash
+ // * No /../ path traversal
+ static bool ValidatePath(const char* path);
+
+ // MatchPath returns true if |requested_filename| is covered by this instance
+ bool MatchPath(const char* requested_filename) const;
+
+ // Used in by BrokerFilePermissionTester for tests.
+ static const char* GetErrorMessageForTests();
+
+ // These are not const as std::vector requires copy-assignment and this class
+ // is stored in vectors. All methods are marked const so
+ // the compiler will still enforce no changes outside of the constructor.
+ std::string path_;
+ bool recursive_; // Allow everything under this path. |path| must be a dir.
+ bool unlink_; // unlink after opening.
+ bool allow_read_;
+ bool allow_write_;
+ bool allow_create_;
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_ \ No newline at end of file
diff --git a/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc b/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
new file mode 100644
index 0000000000..b58a901cde
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
@@ -0,0 +1,262 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerFilePermissionTester {
+ public:
+ static bool ValidatePath(const char* path) {
+ return BrokerFilePermission::ValidatePath(path);
+ }
+ static const char* GetErrorMessage() {
+ return BrokerFilePermission::GetErrorMessageForTests();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BrokerFilePermissionTester);
+};
+
+namespace {
+
+// Creation tests are DEATH tests as a bad permission causes termination.
+SANDBOX_TEST(BrokerFilePermission, CreateGood) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+SANDBOX_TEST(BrokerFilePermission, CreateGoodRecursive) {
+ const char kPath[] = "/tmp/good/";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+ BrokerFilePermission,
+ CreateBad,
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+ const char kPath[] = "/tmp/bad/";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+ BrokerFilePermission,
+ CreateBadRecursive,
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+ const char kPath[] = "/tmp/bad";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+ BrokerFilePermission,
+ CreateBadNotAbs,
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+ const char kPath[] = "tmp/bad";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+ BrokerFilePermission,
+ CreateBadEmpty,
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+ const char kPath[] = "";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+// CheckPerm tests |path| against |perm| given |access_flags|.
+// If |create| is true then file creation is tested for success.
+void CheckPerm(const BrokerFilePermission& perm,
+ const char* path,
+ int access_flags,
+ bool create) {
+ const char* file_to_open = NULL;
+
+ ASSERT_FALSE(perm.CheckAccess(path, X_OK, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, F_OK, NULL));
+ // check bad perms
+ switch (access_flags) {
+ case O_RDONLY:
+ ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
+ ASSERT_FALSE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
+ ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
+ ASSERT_FALSE(perm.CheckAccess(path, W_OK, NULL));
+ break;
+ case O_WRONLY:
+ ASSERT_FALSE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
+ ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
+ ASSERT_FALSE(perm.CheckAccess(path, R_OK, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
+ break;
+ case O_RDWR:
+ ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
+ break;
+ default:
+ // Bad test case
+ NOTREACHED();
+ }
+
+// O_SYNC can be defined as (__O_SYNC|O_DSYNC)
+#ifdef O_DSYNC
+ const int kSyncFlag = O_SYNC & ~O_DSYNC;
+#else
+ const int kSyncFlag = O_SYNC;
+#endif
+
+ const int kNumberOfBitsInOAccMode = 2;
+ static_assert(O_ACCMODE == ((1 << kNumberOfBitsInOAccMode) - 1),
+ "incorrect number of bits");
+ // check every possible flag and act accordingly.
+ // Skipping AccMode bits as they are present in every case.
+ for (int i = kNumberOfBitsInOAccMode; i < 32; i++) {
+ int flag = 1 << i;
+ switch (flag) {
+ case O_APPEND:
+ case O_ASYNC:
+ case O_DIRECT:
+ case O_DIRECTORY:
+#ifdef O_DSYNC
+ case O_DSYNC:
+#endif
+ case O_EXCL:
+ case O_LARGEFILE:
+ case O_NOATIME:
+ case O_NOCTTY:
+ case O_NOFOLLOW:
+ case O_NONBLOCK:
+#if (O_NONBLOCK != O_NDELAY)
+ case O_NDELAY:
+#endif
+ case kSyncFlag:
+ case O_TRUNC:
+ ASSERT_TRUE(
+ perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
+ break;
+ case O_CLOEXEC:
+ case O_CREAT:
+ default:
+ ASSERT_FALSE(
+ perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
+ }
+ }
+ if (create) {
+ bool unlink;
+ ASSERT_TRUE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
+ &file_to_open, &unlink));
+ ASSERT_FALSE(unlink);
+ } else {
+ ASSERT_FALSE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
+ &file_to_open, NULL));
+ }
+}
+
+TEST(BrokerFilePermission, ReadOnly) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+ CheckPerm(perm, kPath, O_RDONLY, false);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ReadOnlyRecursive) {
+ const char kPath[] = "/tmp/good/";
+ const char kPathFile[] = "/tmp/good/file";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
+ CheckPerm(perm, kPathFile, O_RDONLY, false);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, WriteOnly) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::WriteOnly(kPath);
+ CheckPerm(perm, kPath, O_WRONLY, false);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ReadWrite) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::ReadWrite(kPath);
+ CheckPerm(perm, kPath, O_RDWR, false);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ReadWriteCreate) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::ReadWriteCreate(kPath);
+ CheckPerm(perm, kPath, O_RDWR, true);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+void CheckUnlink(BrokerFilePermission& perm,
+ const char* path,
+ int access_flags) {
+ bool unlink;
+ ASSERT_FALSE(perm.CheckOpen(path, access_flags, NULL, &unlink));
+ ASSERT_FALSE(perm.CheckOpen(path, access_flags | O_CREAT, NULL, &unlink));
+ ASSERT_TRUE(
+ perm.CheckOpen(path, access_flags | O_CREAT | O_EXCL, NULL, &unlink));
+ ASSERT_TRUE(unlink);
+}
+
+TEST(BrokerFilePermission, ReadWriteCreateUnlink) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm =
+ BrokerFilePermission::ReadWriteCreateUnlink(kPath);
+ CheckUnlink(perm, kPath, O_RDWR);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ReadWriteCreateUnlinkRecursive) {
+ const char kPath[] = "/tmp/good/";
+ const char kPathFile[] = "/tmp/good/file";
+ BrokerFilePermission perm =
+ BrokerFilePermission::ReadWriteCreateUnlinkRecursive(kPath);
+ CheckUnlink(perm, kPathFile, O_RDWR);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ValidatePath) {
+ EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/path"));
+ EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/"));
+ EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/..path"));
+
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath(""));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad"));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/"));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad/"));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/.."));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/../bad"));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/../bad"));
+}
+
+} // namespace
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_host.cc b/sandbox/linux/syscall_broker/broker_host.cc
new file mode 100644
index 0000000000..830b98bf93
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_host.cc
@@ -0,0 +1,231 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_host.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+#include "sandbox/linux/syscall_broker/broker_policy.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "third_party/valgrind/valgrind.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+namespace {
+
+bool IsRunningOnValgrind() {
+ return RUNNING_ON_VALGRIND;
+}
+
+// A little open(2) wrapper to handle some oddities for us. In the general case
+// make a direct system call since we want to keep in control of the broker
+// process' system calls profile to be able to loosely sandbox it.
+int sys_open(const char* pathname, int flags) {
+ // Hardcode mode to rw------- when creating files.
+ int mode;
+ if (flags & O_CREAT) {
+ mode = 0600;
+ } else {
+ mode = 0;
+ }
+ if (IsRunningOnValgrind()) {
+ // Valgrind does not support AT_FDCWD, just use libc's open() in this case.
+ return open(pathname, flags, mode);
+ } else {
+ return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
+ }
+}
+
+// Open |requested_filename| with |flags| if allowed by our policy.
+// Write the syscall return value (-errno) to |write_pickle| and append
+// a file descriptor to |opened_files| if relevant.
+void OpenFileForIPC(const BrokerPolicy& policy,
+ const std::string& requested_filename,
+ int flags,
+ base::Pickle* write_pickle,
+ std::vector<int>* opened_files) {
+ DCHECK(write_pickle);
+ DCHECK(opened_files);
+ const char* file_to_open = NULL;
+ bool unlink_after_open = false;
+ const bool safe_to_open_file = policy.GetFileNameIfAllowedToOpen(
+ requested_filename.c_str(), flags, &file_to_open, &unlink_after_open);
+
+ if (safe_to_open_file) {
+ CHECK(file_to_open);
+ int opened_fd = sys_open(file_to_open, flags);
+ if (opened_fd < 0) {
+ write_pickle->WriteInt(-errno);
+ } else {
+ // Success.
+ if (unlink_after_open) {
+ unlink(file_to_open);
+ }
+ opened_files->push_back(opened_fd);
+ write_pickle->WriteInt(0);
+ }
+ } else {
+ write_pickle->WriteInt(-policy.denied_errno());
+ }
+}
+
+// Perform access(2) on |requested_filename| with mode |mode| if allowed by our
+// policy. Write the syscall return value (-errno) to |write_pickle|.
+void AccessFileForIPC(const BrokerPolicy& policy,
+ const std::string& requested_filename,
+ int mode,
+ base::Pickle* write_pickle) {
+ DCHECK(write_pickle);
+ const char* file_to_access = NULL;
+ const bool safe_to_access_file = policy.GetFileNameIfAllowedToAccess(
+ requested_filename.c_str(), mode, &file_to_access);
+
+ if (safe_to_access_file) {
+ CHECK(file_to_access);
+ int access_ret = access(file_to_access, mode);
+ int access_errno = errno;
+ if (!access_ret)
+ write_pickle->WriteInt(0);
+ else
+ write_pickle->WriteInt(-access_errno);
+ } else {
+ write_pickle->WriteInt(-policy.denied_errno());
+ }
+}
+
+// Handle a |command_type| request contained in |iter| and send the reply
+// on |reply_ipc|.
+// Currently COMMAND_OPEN and COMMAND_ACCESS are supported.
+bool HandleRemoteCommand(const BrokerPolicy& policy,
+ IPCCommand command_type,
+ int reply_ipc,
+ base::PickleIterator iter) {
+ // Currently all commands have two arguments: filename and flags.
+ std::string requested_filename;
+ int flags = 0;
+ if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
+ return false;
+
+ base::Pickle write_pickle;
+ std::vector<int> opened_files;
+
+ switch (command_type) {
+ case COMMAND_ACCESS:
+ AccessFileForIPC(policy, requested_filename, flags, &write_pickle);
+ break;
+ case COMMAND_OPEN:
+ OpenFileForIPC(
+ policy, requested_filename, flags, &write_pickle, &opened_files);
+ break;
+ default:
+ LOG(ERROR) << "Invalid IPC command";
+ break;
+ }
+
+ CHECK_LE(write_pickle.size(), kMaxMessageLength);
+ ssize_t sent = base::UnixDomainSocket::SendMsg(
+ reply_ipc, write_pickle.data(), write_pickle.size(), opened_files);
+
+ // Close anything we have opened in this process.
+ for (std::vector<int>::iterator it = opened_files.begin();
+ it != opened_files.end();
+ ++it) {
+ int ret = IGNORE_EINTR(close(*it));
+ DCHECK(!ret) << "Could not close file descriptor";
+ }
+
+ if (sent <= 0) {
+ LOG(ERROR) << "Could not send IPC reply";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+BrokerHost::BrokerHost(const BrokerPolicy& broker_policy,
+ BrokerChannel::EndPoint ipc_channel)
+ : broker_policy_(broker_policy), ipc_channel_(ipc_channel.Pass()) {
+}
+
+BrokerHost::~BrokerHost() {
+}
+
+// Handle a request on the IPC channel ipc_channel_.
+// A request should have a file descriptor attached on which we will reply and
+// that we will then close.
+// A request should start with an int that will be used as the command type.
+BrokerHost::RequestStatus BrokerHost::HandleRequest() const {
+ ScopedVector<base::ScopedFD> fds;
+ char buf[kMaxMessageLength];
+ errno = 0;
+ const ssize_t msg_len = base::UnixDomainSocket::RecvMsg(
+ ipc_channel_.get(), buf, sizeof(buf), &fds);
+
+ if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
+ // EOF from the client, or the client died, we should die.
+ return RequestStatus::LOST_CLIENT;
+ }
+
+ // The client should send exactly one file descriptor, on which we
+ // will write the reply.
+ // TODO(mdempsky): ScopedVector doesn't have 'at()', only 'operator[]'.
+ if (msg_len < 0 || fds.size() != 1 || fds[0]->get() < 0) {
+ PLOG(ERROR) << "Error reading message from the client";
+ return RequestStatus::FAILURE;
+ }
+
+ base::ScopedFD temporary_ipc(fds[0]->Pass());
+
+ base::Pickle pickle(buf, msg_len);
+ base::PickleIterator iter(pickle);
+ int command_type;
+ if (iter.ReadInt(&command_type)) {
+ bool command_handled = false;
+ // Go through all the possible IPC messages.
+ switch (command_type) {
+ case COMMAND_ACCESS:
+ case COMMAND_OPEN:
+ // We reply on the file descriptor sent to us via the IPC channel.
+ command_handled = HandleRemoteCommand(
+ broker_policy_, static_cast<IPCCommand>(command_type),
+ temporary_ipc.get(), iter);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ if (command_handled) {
+ return RequestStatus::SUCCESS;
+ } else {
+ return RequestStatus::FAILURE;
+ }
+
+ NOTREACHED();
+ }
+
+ LOG(ERROR) << "Error parsing IPC request";
+ return RequestStatus::FAILURE;
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_host.h b/sandbox/linux/syscall_broker/broker_host.h
new file mode 100644
index 0000000000..9866507d1c
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_host.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_HOST_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_HOST_H_
+
+#include "base/macros.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerPolicy;
+
+// The BrokerHost class should be embedded in a (presumably not sandboxed)
+// process. It will honor IPC requests from a BrokerClient sent over
+// |ipc_channel| according to |broker_policy|.
+class BrokerHost {
+ public:
+ enum class RequestStatus { LOST_CLIENT = 0, SUCCESS, FAILURE };
+
+ BrokerHost(const BrokerPolicy& broker_policy,
+ BrokerChannel::EndPoint ipc_channel);
+ ~BrokerHost();
+
+ RequestStatus HandleRequest() const;
+
+ private:
+ const BrokerPolicy& broker_policy_;
+ const BrokerChannel::EndPoint ipc_channel_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerHost);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_HOST_H_
diff --git a/sandbox/linux/syscall_broker/broker_policy.cc b/sandbox/linux/syscall_broker/broker_policy.cc
new file mode 100644
index 0000000000..d9f69e3b81
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_policy.cc
@@ -0,0 +1,99 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_policy.h"
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+BrokerPolicy::BrokerPolicy(int denied_errno,
+ const std::vector<BrokerFilePermission>& permissions)
+ : denied_errno_(denied_errno),
+ permissions_(permissions),
+ num_of_permissions_(permissions.size()) {
+ // The spec guarantees vectors store their elements contiguously
+ // so set up a pointer to array of element so it can be used
+ // in async signal safe code instead of vector operations.
+ if (num_of_permissions_ > 0) {
+ permissions_array_ = &permissions_[0];
+ } else {
+ permissions_array_ = NULL;
+ }
+}
+
+BrokerPolicy::~BrokerPolicy() {
+}
+
+// Check if calling access() should be allowed on |requested_filename| with
+// mode |requested_mode|.
+// Note: access() being a system call to check permissions, this can get a bit
+// confusing. We're checking if calling access() should even be allowed with
+// the same policy we would use for open().
+// If |file_to_access| is not NULL, we will return the matching pointer from
+// the whitelist. For paranoia a caller should then use |file_to_access|. See
+// GetFileNameIfAllowedToOpen() for more explanation.
+// return true if calling access() on this file should be allowed, false
+// otherwise.
+// Async signal safe if and only if |file_to_access| is NULL.
+bool BrokerPolicy::GetFileNameIfAllowedToAccess(
+ const char* requested_filename,
+ int requested_mode,
+ const char** file_to_access) const {
+ if (file_to_access && *file_to_access) {
+ // Make sure that callers never pass a non-empty string. In case callers
+ // wrongly forget to check the return value and look at the string
+ // instead, this could catch bugs.
+ RAW_LOG(FATAL, "*file_to_access should be NULL");
+ return false;
+ }
+ for (size_t i = 0; i < num_of_permissions_; i++) {
+ if (permissions_array_[i].CheckAccess(requested_filename, requested_mode,
+ file_to_access)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Check if |requested_filename| can be opened with flags |requested_flags|.
+// If |file_to_open| is not NULL, we will return the matching pointer from the
+// whitelist. For paranoia, a caller should then use |file_to_open| rather
+// than |requested_filename|, so that it never attempts to open an
+// attacker-controlled file name, even if an attacker managed to fool the
+// string comparison mechanism.
+// Return true if opening should be allowed, false otherwise.
+// Async signal safe if and only if |file_to_open| is NULL.
+bool BrokerPolicy::GetFileNameIfAllowedToOpen(const char* requested_filename,
+ int requested_flags,
+ const char** file_to_open,
+ bool* unlink_after_open) const {
+ if (file_to_open && *file_to_open) {
+ // Make sure that callers never pass a non-empty string. In case callers
+ // wrongly forget to check the return value and look at the string
+ // instead, this could catch bugs.
+ RAW_LOG(FATAL, "*file_to_open should be NULL");
+ return false;
+ }
+ for (size_t i = 0; i < num_of_permissions_; i++) {
+ if (permissions_array_[i].CheckOpen(requested_filename, requested_flags,
+ file_to_open, unlink_after_open)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_policy.h b/sandbox/linux/syscall_broker/broker_policy.h
new file mode 100644
index 0000000000..d5146edc06
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_policy.h
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_POLICY_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_POLICY_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+// BrokerPolicy allows to define the security policy enforced by a
+// BrokerHost. The BrokerHost will evaluate requests sent over its
+// IPC channel according to the BrokerPolicy.
+// Some of the methods of this class can be used in an async-signal safe
+// way.
+class BrokerPolicy {
+ public:
+ // |denied_errno| is the error code returned when IPC requests for system
+ // calls such as open() or access() are denied because a file is not in the
+ // whitelist. EACCESS would be a typical value.
+ // |permissions| is a list of BrokerPermission objects that define
+ // what the broker will allow.
+ BrokerPolicy(int denied_errno,
+ const std::vector<BrokerFilePermission>& permissions);
+
+ ~BrokerPolicy();
+
+ // Check if calling access() should be allowed on |requested_filename| with
+ // mode |requested_mode|.
+ // Note: access() being a system call to check permissions, this can get a bit
+ // confusing. We're checking if calling access() should even be allowed with
+ // If |file_to_open| is not NULL, a pointer to the path will be returned.
+ // In the case of a recursive match, this will be the requested_filename,
+ // otherwise it will return the matching pointer from the
+ // whitelist. For paranoia a caller should then use |file_to_access|. See
+ // GetFileNameIfAllowedToOpen() for more explanation.
+ // return true if calling access() on this file should be allowed, false
+ // otherwise.
+ // Async signal safe if and only if |file_to_access| is NULL.
+ bool GetFileNameIfAllowedToAccess(const char* requested_filename,
+ int requested_mode,
+ const char** file_to_access) const;
+
+ // Check if |requested_filename| can be opened with flags |requested_flags|.
+ // If |file_to_open| is not NULL, a pointer to the path will be returned.
+ // In the case of a recursive match, this will be the requested_filename,
+ // otherwise it will return the matching pointer from the
+ // whitelist. For paranoia, a caller should then use |file_to_open| rather
+ // than |requested_filename|, so that it never attempts to open an
+ // attacker-controlled file name, even if an attacker managed to fool the
+ // string comparison mechanism.
+ // |unlink_after_open| if not NULL will be set to point to true if the
+ // policy requests the caller unlink the path after opening.
+ // Return true if opening should be allowed, false otherwise.
+ // Async signal safe if and only if |file_to_open| is NULL.
+ bool GetFileNameIfAllowedToOpen(const char* requested_filename,
+ int requested_flags,
+ const char** file_to_open,
+ bool* unlink_after_open) const;
+ int denied_errno() const { return denied_errno_; }
+
+ private:
+ const int denied_errno_;
+ // The permissions_ vector is used as storage for the BrokerFilePermission
+ // objects but is not referenced outside of the constructor as
+ // vectors are unfriendly in async signal safe code.
+ const std::vector<BrokerFilePermission> permissions_;
+ // permissions_array_ is set up to point to the backing store of
+ // permissions_ and is used in async signal safe methods.
+ const BrokerFilePermission* permissions_array_;
+ const size_t num_of_permissions_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerPolicy);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_POLICY_H_
diff --git a/sandbox/linux/syscall_broker/broker_process.cc b/sandbox/linux/syscall_broker/broker_process.cc
new file mode 100644
index 0000000000..81131cc4e0
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_process.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_process.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_metrics.h"
+#include "build/build_config.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+#include "sandbox/linux/syscall_broker/broker_client.h"
+#include "sandbox/linux/syscall_broker/broker_host.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+BrokerProcess::BrokerProcess(
+ int denied_errno,
+ const std::vector<syscall_broker::BrokerFilePermission>& permissions,
+ bool fast_check_in_client,
+ bool quiet_failures_for_tests)
+ : initialized_(false),
+ fast_check_in_client_(fast_check_in_client),
+ quiet_failures_for_tests_(quiet_failures_for_tests),
+ broker_pid_(-1),
+ policy_(denied_errno, permissions) {
+}
+
+BrokerProcess::~BrokerProcess() {
+ if (initialized_) {
+ if (broker_client_.get()) {
+ // Closing the socket should be enough to notify the child to die,
+ // unless it has been duplicated.
+ CloseChannel();
+ }
+ PCHECK(0 == kill(broker_pid_, SIGKILL));
+ siginfo_t process_info;
+ // Reap the child.
+ int ret = HANDLE_EINTR(waitid(P_PID, broker_pid_, &process_info, WEXITED));
+ PCHECK(0 == ret);
+ }
+}
+
+bool BrokerProcess::Init(
+ const base::Callback<bool(void)>& broker_process_init_callback) {
+ CHECK(!initialized_);
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+#if !defined(THREAD_SANITIZER)
+ DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
+#endif
+ int child_pid = fork();
+ if (child_pid == -1) {
+ return false;
+ }
+ if (child_pid) {
+ // We are the parent and we have just forked our broker process.
+ ipc_reader.reset();
+ broker_pid_ = child_pid;
+ broker_client_.reset(new BrokerClient(policy_, ipc_writer.Pass(),
+ fast_check_in_client_,
+ quiet_failures_for_tests_));
+ initialized_ = true;
+ return true;
+ } else {
+ // We are the broker process. Make sure to close the writer's end so that
+ // we get notified if the client disappears.
+ ipc_writer.reset();
+ CHECK(broker_process_init_callback.Run());
+ BrokerHost broker_host(policy_, ipc_reader.Pass());
+ for (;;) {
+ switch (broker_host.HandleRequest()) {
+ case BrokerHost::RequestStatus::LOST_CLIENT:
+ _exit(1);
+ case BrokerHost::RequestStatus::SUCCESS:
+ case BrokerHost::RequestStatus::FAILURE:
+ continue;
+ }
+ }
+ _exit(1);
+ }
+ NOTREACHED();
+ return false;
+}
+
+void BrokerProcess::CloseChannel() {
+ broker_client_.reset();
+}
+
+int BrokerProcess::Access(const char* pathname, int mode) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Access(pathname, mode);
+}
+
+int BrokerProcess::Open(const char* pathname, int flags) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Open(pathname, flags);
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox.
diff --git a/sandbox/linux/syscall_broker/broker_process.h b/sandbox/linux/syscall_broker/broker_process.h
new file mode 100644
index 0000000000..8a512a0c12
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_process.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/process/process.h"
+#include "sandbox/linux/syscall_broker/broker_policy.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerClient;
+class BrokerFilePermission;
+
+// Create a new "broker" process to which we can send requests via an IPC
+// channel by forking the current process.
+// This is a low level IPC mechanism that is suitable to be called from a
+// signal handler.
+// A process would typically create a broker process before entering
+// sandboxing.
+// 1. BrokerProcess open_broker(read_whitelist, write_whitelist);
+// 2. CHECK(open_broker.Init(NULL));
+// 3. Enable sandbox.
+// 4. Use open_broker.Open() to open files.
+class SANDBOX_EXPORT BrokerProcess {
+ public:
+ // |denied_errno| is the error code returned when methods such as Open()
+ // or Access() are invoked on a file which is not in the whitelist. EACCESS
+ // would be a typical value.
+ // |allowed_r_files| and |allowed_w_files| are white lists of files that can
+ // be opened later via the Open() API, respectively for reading and writing.
+ // A file available read-write should be listed in both.
+ // |fast_check_in_client| and |quiet_failures_for_tests| are reserved for
+ // unit tests, don't use it.
+
+ BrokerProcess(
+ int denied_errno,
+ const std::vector<syscall_broker::BrokerFilePermission>& permissions,
+ bool fast_check_in_client = true,
+ bool quiet_failures_for_tests = false);
+
+ ~BrokerProcess();
+ // Will initialize the broker process. There should be no threads at this
+ // point, since we need to fork().
+ // broker_process_init_callback will be called in the new broker process,
+ // after fork() returns.
+ bool Init(const base::Callback<bool(void)>& broker_process_init_callback);
+
+ // Can be used in place of access(). Will be async signal safe.
+ // X_OK will always return an error in practice since the broker process
+ // doesn't support execute permissions.
+ // It's similar to the access() system call and will return -errno on errors.
+ int Access(const char* pathname, int mode) const;
+ // Can be used in place of open(). Will be async signal safe.
+ // The implementation only supports certain white listed flags and will
+ // return -EPERM on other flags.
+ // It's similar to the open() system call and will return -errno on errors.
+ int Open(const char* pathname, int flags) const;
+
+ int broker_pid() const { return broker_pid_; }
+
+ private:
+ friend class BrokerProcessTestHelper;
+
+ // Close the IPC channel with the other party. This should only be used
+ // by tests an none of the class methods should be used afterwards.
+ void CloseChannel();
+
+ bool initialized_; // Whether we've been through Init() yet.
+ const bool fast_check_in_client_;
+ const bool quiet_failures_for_tests_;
+ pid_t broker_pid_; // The PID of the broker (child).
+ syscall_broker::BrokerPolicy policy_; // The sandboxing policy.
+ scoped_ptr<syscall_broker::BrokerClient> broker_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerProcess);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
diff --git a/sandbox/linux/syscall_broker/broker_process_unittest.cc b/sandbox/linux/syscall_broker/broker_process_unittest.cc
new file mode 100644
index 0000000000..9ad0e719de
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -0,0 +1,656 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_process.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "sandbox/linux/syscall_broker/broker_client.h"
+#include "sandbox/linux/tests/scoped_temporary_file.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerProcessTestHelper {
+ public:
+ static void CloseChannel(BrokerProcess* broker) { broker->CloseChannel(); }
+ // Get the client's IPC descriptor to send IPC requests directly.
+ // TODO(jln): refator tests to get rid of this.
+ static int GetIPCDescriptor(const BrokerProcess* broker) {
+ return broker->broker_client_->GetIPCDescriptor();
+ }
+};
+
+namespace {
+
+bool NoOpCallback() {
+ return true;
+}
+
+} // namespace
+
+TEST(BrokerProcess, CreateAndDestroy) {
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
+
+ scoped_ptr<BrokerProcess> open_broker(new BrokerProcess(EPERM, permissions));
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+ // Destroy the broker and check it has exited properly.
+ open_broker.reset();
+ ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
+}
+
+TEST(BrokerProcess, TestOpenAccessNull) {
+ std::vector<BrokerFilePermission> empty;
+ BrokerProcess open_broker(EPERM, empty);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ int fd = open_broker.Open(NULL, O_RDONLY);
+ ASSERT_EQ(fd, -EFAULT);
+
+ int ret = open_broker.Access(NULL, F_OK);
+ ASSERT_EQ(ret, -EFAULT);
+}
+
+void TestOpenFilePerms(bool fast_check_in_client, int denied_errno) {
+ const char kR_WhiteListed[] = "/proc/DOESNOTEXIST1";
+ // We can't debug the init process, and shouldn't be able to access
+ // its auxv file.
+ const char kR_WhiteListedButDenied[] = "/proc/1/auxv";
+ const char kW_WhiteListed[] = "/proc/DOESNOTEXIST2";
+ const char kRW_WhiteListed[] = "/proc/DOESNOTEXIST3";
+ const char k_NotWhitelisted[] = "/proc/DOESNOTEXIST4";
+
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly(kR_WhiteListed));
+ permissions.push_back(
+ BrokerFilePermission::ReadOnly(kR_WhiteListedButDenied));
+ permissions.push_back(BrokerFilePermission::WriteOnly(kW_WhiteListed));
+ permissions.push_back(BrokerFilePermission::ReadWrite(kRW_WhiteListed));
+
+ BrokerProcess open_broker(denied_errno, permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ int fd = -1;
+ fd = open_broker.Open(kR_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kR_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(kR_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ int ret = -1;
+ ret = open_broker.Access(kR_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kR_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kR_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ // Android sometimes runs tests as root.
+ // This part of the test requires a process that doesn't have
+ // CAP_DAC_OVERRIDE. We check against a root euid as a proxy for that.
+ if (geteuid()) {
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_RDONLY);
+ // The broker process will allow this, but the normal permission system
+ // won't.
+ ASSERT_EQ(fd, -EACCES);
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_WRONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, F_OK);
+ // The normal permission system will let us check that the file exists.
+ ASSERT_EQ(ret, 0);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK);
+ ASSERT_EQ(ret, -EACCES);
+ ret = open_broker.Access(kR_WhiteListedButDenied, W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ }
+
+ fd = open_broker.Open(kW_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(kW_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kW_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kW_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kW_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ fd = open_broker.Open(kRW_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kRW_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kRW_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ fd = open_broker.Open(k_NotWhitelisted, O_RDONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(k_NotWhitelisted, O_WRONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(k_NotWhitelisted, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, F_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ // We have some extra sanity check for clearly wrong values.
+ fd = open_broker.Open(kRW_WhiteListed, O_RDONLY | O_WRONLY | O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+
+ // It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this
+ // is denied.
+ fd = open_broker.Open(kRW_WhiteListed, O_RDWR | O_CREAT);
+ ASSERT_EQ(fd, -denied_errno);
+}
+
+// Run the same thing twice. The second time, we make sure that no security
+// check is performed on the client.
+TEST(BrokerProcess, OpenFilePermsWithClientCheck) {
+ TestOpenFilePerms(true /* fast_check_in_client */, EPERM);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenOpenFilePermsNoClientCheck) {
+ TestOpenFilePerms(false /* fast_check_in_client */, EPERM);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+// Run the same twice again, but with ENOENT instead of EPERM.
+TEST(BrokerProcess, OpenFilePermsWithClientCheckNoEnt) {
+ TestOpenFilePerms(true /* fast_check_in_client */, ENOENT);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenOpenFilePermsNoClientCheckNoEnt) {
+ TestOpenFilePerms(false /* fast_check_in_client */, ENOENT);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+void TestBadPaths(bool fast_check_in_client) {
+ const char kFileCpuInfo[] = "/proc/cpuinfo";
+ const char kNotAbsPath[] = "proc/cpuinfo";
+ const char kDotDotStart[] = "/../proc/cpuinfo";
+ const char kDotDotMiddle[] = "/proc/self/../cpuinfo";
+ const char kDotDotEnd[] = "/proc/..";
+ const char kTrailingSlash[] = "/proc/";
+
+ std::vector<BrokerFilePermission> permissions;
+
+ permissions.push_back(BrokerFilePermission::ReadOnlyRecursive("/proc/"));
+ scoped_ptr<BrokerProcess> open_broker(
+ new BrokerProcess(EPERM, permissions, fast_check_in_client));
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+ // Open cpuinfo via the broker.
+ int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+ ASSERT_GE(cpuinfo_fd, 0);
+
+ int fd = -1;
+ int can_access;
+
+ can_access = open_broker->Access(kNotAbsPath, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kNotAbsPath, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+
+ can_access = open_broker->Access(kDotDotStart, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kDotDotStart, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+
+ can_access = open_broker->Access(kDotDotMiddle, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kDotDotMiddle, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+
+ can_access = open_broker->Access(kDotDotEnd, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kDotDotEnd, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+
+ can_access = open_broker->Access(kTrailingSlash, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kTrailingSlash, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+}
+
+TEST(BrokerProcess, BadPathsClientCheck) {
+ TestBadPaths(true /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, BadPathsNoClientCheck) {
+ TestBadPaths(false /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+void TestOpenCpuinfo(bool fast_check_in_client, bool recursive) {
+ const char kFileCpuInfo[] = "/proc/cpuinfo";
+ const char kDirProc[] = "/proc/";
+
+ std::vector<BrokerFilePermission> permissions;
+ if (recursive)
+ permissions.push_back(BrokerFilePermission::ReadOnlyRecursive(kDirProc));
+ else
+ permissions.push_back(BrokerFilePermission::ReadOnly(kFileCpuInfo));
+
+ scoped_ptr<BrokerProcess> open_broker(
+ new BrokerProcess(EPERM, permissions, fast_check_in_client));
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+
+ int fd = -1;
+ fd = open_broker->Open(kFileCpuInfo, O_RDWR);
+ base::ScopedFD fd_closer(fd);
+ ASSERT_EQ(fd, -EPERM);
+
+ // Check we can read /proc/cpuinfo.
+ int can_access = open_broker->Access(kFileCpuInfo, R_OK);
+ ASSERT_EQ(can_access, 0);
+ can_access = open_broker->Access(kFileCpuInfo, W_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ // Check we can not write /proc/cpuinfo.
+
+ // Open cpuinfo via the broker.
+ int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+ ASSERT_GE(cpuinfo_fd, 0);
+ char buf[3];
+ memset(buf, 0, sizeof(buf));
+ int read_len1 = read(cpuinfo_fd, buf, sizeof(buf));
+ ASSERT_GT(read_len1, 0);
+
+ // Open cpuinfo directly.
+ int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
+ ASSERT_GE(cpuinfo_fd2, 0);
+ char buf2[3];
+ memset(buf2, 1, sizeof(buf2));
+ int read_len2 = read(cpuinfo_fd2, buf2, sizeof(buf2));
+ ASSERT_GT(read_len1, 0);
+
+ // The following is not guaranteed true, but will be in practice.
+ ASSERT_EQ(read_len1, read_len2);
+ // Compare the cpuinfo as returned by the broker with the one we opened
+ // ourselves.
+ ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
+
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+ open_broker.reset();
+ ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
+}
+
+// Run this test 4 times. With and without the check in client
+// and using a recursive path.
+TEST(BrokerProcess, OpenCpuinfoWithClientCheck) {
+ TestOpenCpuinfo(true /* fast_check_in_client */, false /* not recursive */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoNoClientCheck) {
+ TestOpenCpuinfo(false /* fast_check_in_client */, false /* not recursive */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoWithClientCheckRecursive) {
+ TestOpenCpuinfo(true /* fast_check_in_client */, true /* recursive */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoNoClientCheckRecursive) {
+ TestOpenCpuinfo(false /* fast_check_in_client */, true /* recursive */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenFileRW) {
+ ScopedTemporaryFile tempfile;
+ const char* tempfile_name = tempfile.full_file_name();
+
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadWrite(tempfile_name));
+
+ BrokerProcess open_broker(EPERM, permissions);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ // Check we can access that file with read or write.
+ int can_access = open_broker.Access(tempfile_name, R_OK | W_OK);
+ ASSERT_EQ(can_access, 0);
+
+ int tempfile2 = -1;
+ tempfile2 = open_broker.Open(tempfile_name, O_RDWR);
+ ASSERT_GE(tempfile2, 0);
+
+ // Write to the descriptor opened by the broker.
+ char test_text[] = "TESTTESTTEST";
+ ssize_t len = write(tempfile2, test_text, sizeof(test_text));
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+
+ // Read back from the original file descriptor what we wrote through
+ // the descriptor provided by the broker.
+ char buf[1024];
+ len = read(tempfile.fd(), buf, sizeof(buf));
+
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+ ASSERT_EQ(memcmp(test_text, buf, sizeof(test_text)), 0);
+
+ ASSERT_EQ(close(tempfile2), 0);
+}
+
+// SANDBOX_TEST because the process could die with a SIGPIPE
+// and we want this to happen in a subprocess.
+SANDBOX_TEST(BrokerProcess, BrokerDied) {
+ const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
+
+ BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
+ true /* quiet_failures_for_tests */);
+ SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+ const pid_t broker_pid = open_broker.broker_pid();
+ SANDBOX_ASSERT(kill(broker_pid, SIGKILL) == 0);
+
+ // Now we check that the broker has been signaled, but do not reap it.
+ siginfo_t process_info;
+ SANDBOX_ASSERT(HANDLE_EINTR(waitid(
+ P_PID, broker_pid, &process_info, WEXITED | WNOWAIT)) ==
+ 0);
+ SANDBOX_ASSERT(broker_pid == process_info.si_pid);
+ SANDBOX_ASSERT(CLD_KILLED == process_info.si_code);
+ SANDBOX_ASSERT(SIGKILL == process_info.si_status);
+
+ // Check that doing Open with a dead broker won't SIGPIPE us.
+ SANDBOX_ASSERT(open_broker.Open(kCpuInfo, O_RDONLY) == -ENOMEM);
+ SANDBOX_ASSERT(open_broker.Access(kCpuInfo, O_RDONLY) == -ENOMEM);
+}
+
+void TestOpenComplexFlags(bool fast_check_in_client) {
+ const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
+
+ BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ // Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
+ int fd = -1;
+ int ret = 0;
+ fd = open_broker.Open(kCpuInfo, O_RDONLY);
+ ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFL);
+ ASSERT_NE(-1, ret);
+ // The descriptor shouldn't have the O_CLOEXEC attribute, nor O_NONBLOCK.
+ ASSERT_EQ(0, ret & (O_CLOEXEC | O_NONBLOCK));
+ ASSERT_EQ(0, close(fd));
+
+ fd = open_broker.Open(kCpuInfo, O_RDONLY | O_CLOEXEC);
+ ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFD);
+ ASSERT_NE(-1, ret);
+ // Important: use F_GETFD, not F_GETFL. The O_CLOEXEC flag in F_GETFL
+ // is actually not used by the kernel.
+ ASSERT_TRUE(FD_CLOEXEC & ret);
+ ASSERT_EQ(0, close(fd));
+
+ fd = open_broker.Open(kCpuInfo, O_RDONLY | O_NONBLOCK);
+ ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFL);
+ ASSERT_NE(-1, ret);
+ ASSERT_TRUE(O_NONBLOCK & ret);
+ ASSERT_EQ(0, close(fd));
+}
+
+TEST(BrokerProcess, OpenComplexFlagsWithClientCheck) {
+ TestOpenComplexFlags(true /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenComplexFlagsNoClientCheck) {
+ TestOpenComplexFlags(false /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+// We need to allow noise because the broker will log when it receives our
+// bogus IPCs.
+SANDBOX_TEST_ALLOW_NOISE(BrokerProcess, RecvMsgDescriptorLeak) {
+ // Android creates a socket on first use of the LOG call.
+ // We need to ensure this socket is open before we
+ // begin the test.
+ LOG(INFO) << "Ensure Android LOG socket is allocated";
+
+ // Find the four lowest available file descriptors.
+ int available_fds[4];
+ SANDBOX_ASSERT(0 == pipe(available_fds));
+ SANDBOX_ASSERT(0 == pipe(available_fds + 2));
+
+ // Save one FD to send to the broker later, and close the others.
+ base::ScopedFD message_fd(available_fds[0]);
+ for (size_t i = 1; i < arraysize(available_fds); i++) {
+ SANDBOX_ASSERT(0 == IGNORE_EINTR(close(available_fds[i])));
+ }
+
+ // Lower our file descriptor limit to just allow three more file descriptors
+ // to be allocated. (N.B., RLIMIT_NOFILE doesn't limit the number of file
+ // descriptors a process can have: it only limits the highest value that can
+ // be assigned to newly-created descriptors allocated by the process.)
+ const rlim_t fd_limit =
+ 1 +
+ *std::max_element(available_fds,
+ available_fds + arraysize(available_fds));
+
+ // Valgrind doesn't allow changing the hard descriptor limit, so we only
+ // change the soft descriptor limit here.
+ struct rlimit rlim;
+ SANDBOX_ASSERT(0 == getrlimit(RLIMIT_NOFILE, &rlim));
+ SANDBOX_ASSERT(fd_limit <= rlim.rlim_cur);
+ rlim.rlim_cur = fd_limit;
+ SANDBOX_ASSERT(0 == setrlimit(RLIMIT_NOFILE, &rlim));
+
+ static const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
+
+ BrokerProcess open_broker(EPERM, permissions);
+ SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ const int ipc_fd = BrokerProcessTestHelper::GetIPCDescriptor(&open_broker);
+ SANDBOX_ASSERT(ipc_fd >= 0);
+
+ static const char kBogus[] = "not a pickle";
+ std::vector<int> fds;
+ fds.push_back(message_fd.get());
+
+ // The broker process should only have a couple spare file descriptors
+ // available, but for good measure we send it fd_limit bogus IPCs anyway.
+ for (rlim_t i = 0; i < fd_limit; ++i) {
+ SANDBOX_ASSERT(
+ base::UnixDomainSocket::SendMsg(ipc_fd, kBogus, sizeof(kBogus), fds));
+ }
+
+ const int fd = open_broker.Open(kCpuInfo, O_RDONLY);
+ SANDBOX_ASSERT(fd >= 0);
+ SANDBOX_ASSERT(0 == IGNORE_EINTR(close(fd)));
+}
+
+bool CloseFD(int fd) {
+ PCHECK(0 == IGNORE_EINTR(close(fd)));
+ return true;
+}
+
+// Return true if the other end of the |reader| pipe was closed,
+// false if |timeout_in_seconds| was reached or another event
+// or error occured.
+bool WaitForClosedPipeWriter(int reader, int timeout_in_ms) {
+ struct pollfd poll_fd = {reader, POLLIN | POLLRDHUP, 0};
+ const int num_events = HANDLE_EINTR(poll(&poll_fd, 1, timeout_in_ms));
+ if (1 == num_events && poll_fd.revents | POLLHUP)
+ return true;
+ return false;
+}
+
+// Closing the broker client's IPC channel should terminate the broker
+// process.
+TEST(BrokerProcess, BrokerDiesOnClosedChannel) {
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
+
+ // Get the writing end of a pipe into the broker (child) process so
+ // that we can reliably detect when it dies.
+ int lifeline_fds[2];
+ PCHECK(0 == pipe(lifeline_fds));
+
+ BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
+ false /* quiet_failures_for_tests */);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&CloseFD, lifeline_fds[0])));
+ // Make sure the writing end only exists in the broker process.
+ CloseFD(lifeline_fds[1]);
+ base::ScopedFD reader(lifeline_fds[0]);
+
+ const pid_t broker_pid = open_broker.broker_pid();
+
+ // This should cause the broker process to exit.
+ BrokerProcessTestHelper::CloseChannel(&open_broker);
+
+ const int kTimeoutInMilliseconds = 5000;
+ const bool broker_lifeline_closed =
+ WaitForClosedPipeWriter(reader.get(), kTimeoutInMilliseconds);
+ // If the broker exited, its lifeline fd should be closed.
+ ASSERT_TRUE(broker_lifeline_closed);
+ // Now check that the broker has exited, but do not reap it.
+ siginfo_t process_info;
+ ASSERT_EQ(0, HANDLE_EINTR(waitid(P_PID, broker_pid, &process_info,
+ WEXITED | WNOWAIT)));
+ EXPECT_EQ(broker_pid, process_info.si_pid);
+ EXPECT_EQ(CLD_EXITED, process_info.si_code);
+ EXPECT_EQ(1, process_info.si_status);
+}
+
+TEST(BrokerProcess, CreateFile) {
+ std::string temp_str;
+ {
+ ScopedTemporaryFile tmp_file;
+ temp_str = tmp_file.full_file_name();
+ }
+ const char* tempfile_name = temp_str.c_str();
+
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadWriteCreate(tempfile_name));
+
+ BrokerProcess open_broker(EPERM, permissions);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ int fd = -1;
+
+ // Try without O_EXCL
+ fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT);
+ ASSERT_EQ(fd, -EPERM);
+
+ const char kTestText[] = "TESTTESTTEST";
+ // Create a file
+ fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
+ ASSERT_GE(fd, 0);
+ {
+ base::ScopedFD scoped_fd(fd);
+
+ // Confirm fail if file exists
+ int bad_fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
+ ASSERT_EQ(bad_fd, -EEXIST);
+
+ // Write to the descriptor opened by the broker.
+
+ ssize_t len = HANDLE_EINTR(write(fd, kTestText, sizeof(kTestText)));
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
+ }
+
+ int fd_check = open(tempfile_name, O_RDONLY);
+ ASSERT_GE(fd_check, 0);
+ {
+ base::ScopedFD scoped_fd(fd_check);
+ char buf[1024];
+ ssize_t len = HANDLE_EINTR(read(fd_check, buf, sizeof(buf)));
+
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
+ ASSERT_EQ(memcmp(kTestText, buf, sizeof(kTestText)), 0);
+ }
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox