| // Copyright 2018 The Chromium OS 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 "oobe_config/usb_utils.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include <base/files/file_util.h> |
| #include <base/strings/string_util.h> |
| #include <brillo/process/process.h> |
| #include <crypto/scoped_openssl_types.h> |
| #include <openssl/evp.h> |
| #include <openssl/pem.h> |
| |
| using base::FilePath; |
| using crypto::ScopedEVP_PKEY; |
| using std::string; |
| using std::vector; |
| |
| namespace oobe_config { |
| |
| const char kStatefulDir[] = "/mnt/stateful_partition/"; |
| const char kUnencryptedOobeConfigDir[] = "unencrypted/oobe_auto_config/"; |
| const char kConfigFile[] = "config.json"; |
| const char kDomainFile[] = "enrollment_domain"; |
| const char kKeyFile[] = "validation_key.pub"; |
| const char kDevDiskById[] = "/dev/disk/by-id/"; |
| const char kUsbDevicePathSigFile[] = "usb_device_path.sig"; |
| const char kStoreDir[] = "/var/lib/oobe_config_restore/"; |
| const char kOobeConfigRestoreUser[] = "oobe_config_restore"; |
| |
| bool Sign(const FilePath& priv_key, |
| const string& src_content, |
| const FilePath& dst) { |
| if (src_content.empty()) { |
| LOG(ERROR) << "Input content string cannot be empty!"; |
| return false; |
| } |
| // Reading the private key. |
| base::ScopedFILE pkf(base::OpenFile(priv_key, "r")); |
| if (!pkf) { |
| PLOG(ERROR) << "Failed to open the private key file " << priv_key.value(); |
| return false; |
| } |
| crypto::ScopedEVP_PKEY private_key( |
| PEM_read_PrivateKey(pkf.get(), nullptr, nullptr, nullptr)); |
| if (!private_key) { |
| PLOG(ERROR) << "Failed to read the PEM private key file " |
| << priv_key.value(); |
| return false; |
| } |
| |
| // Creating the context. |
| crypto::ScopedEVP_MD_CTX mdctx(EVP_MD_CTX_new()); |
| if (!mdctx) { |
| LOG(ERROR) << "Failed to create a EVP_MD context."; |
| return false; |
| } |
| |
| // Signing the |src_content|. |
| size_t signature_len = 0; |
| if (EVP_DigestSignInit(mdctx.get(), nullptr, EVP_sha256(), nullptr, |
| private_key.get()) != 1 || |
| EVP_DigestSignUpdate(mdctx.get(), src_content.c_str(), |
| src_content.length()) != 1 || |
| EVP_DigestSignFinal(mdctx.get(), nullptr, &signature_len) != 1) { |
| LOG(ERROR) << "Failed to sign the content."; |
| } |
| // Now we know the size of the signature, let's get it. |
| crypto::ScopedOpenSSLBytes signature(reinterpret_cast<unsigned char*>( |
| OPENSSL_malloc(sizeof(unsigned char) * signature_len))); |
| if (!signature) { |
| PLOG(ERROR) << "Failed to malloc " << signature_len |
| << " bytes for the signature."; |
| return false; |
| } |
| if (EVP_DigestSignFinal(mdctx.get(), signature.get(), &signature_len) != 1) { |
| LOG(ERROR) << "Getting the signature failed; however, technically this" |
| << " should not happen!"; |
| return false; |
| } |
| if (!base::WriteFile(dst, reinterpret_cast<char*>(signature.get()), |
| signature_len)) { |
| PLOG(ERROR) << "Failed to write the signature to file " << dst.value(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool Sign(const FilePath& priv_key, const FilePath& src, const FilePath& dst) { |
| // Reading the source file. |
| string src_content; |
| if (!base::ReadFileToString(src, &src_content)) { |
| PLOG(ERROR) << "Failed to read the source file " << src.value(); |
| return false; |
| } |
| |
| return Sign(priv_key, src_content, dst); |
| } |
| |
| bool ReadPublicKey(const FilePath& pub_key_file, ScopedEVP_PKEY* pub_key) { |
| base::ScopedFILE pkf(base::OpenFile(pub_key_file, "r")); |
| if (!pkf) { |
| PLOG(ERROR) << "Failed to open the public key file " |
| << pub_key_file.value(); |
| return false; |
| } |
| pub_key->reset(PEM_read_PUBKEY(pkf.get(), nullptr, nullptr, nullptr)); |
| if (!(*pub_key)) { |
| LOG(ERROR) << "Failed to read the PEM public key file " |
| << pub_key_file.value(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool VerifySignature(const string& message, |
| const string& signature, |
| const ScopedEVP_PKEY& pub_key) { |
| crypto::ScopedEVP_MD_CTX mdctx(EVP_MD_CTX_new()); |
| if (!mdctx) { |
| LOG(ERROR) << "Failed to create a EVP_MD context."; |
| return false; |
| } |
| if (EVP_DigestVerifyInit(mdctx.get(), nullptr, EVP_sha256(), nullptr, |
| pub_key.get()) != 1 || |
| EVP_DigestVerifyUpdate(mdctx.get(), message.c_str(), message.length()) != |
| 1 || |
| EVP_DigestVerifyFinal( |
| mdctx.get(), reinterpret_cast<const unsigned char*>(signature.data()), |
| signature.size()) != 1) { |
| LOG(ERROR) << "Failed to verify the signature."; |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace oobe_config |