blob: 9730d8b8f86c01d34c60ade909e2566cd79b53d9 [file] [log] [blame]
// Copyright (c) 2011 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 "login_manager/nss_util.h"
#include <stdint.h>
#include <stdlib.h>
#include <memory>
#include <string>
#include <utility>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/memory/ptr_util.h>
#include <base/strings/stringprintf.h>
#include <crypto/nss_util.h>
#include <crypto/nss_util_internal.h>
#include <crypto/rsa_private_key.h>
#include <crypto/scoped_nss_types.h>
#include <crypto/signature_creator.h>
#include <crypto/signature_verifier.h>
#include <keyhi.h>
#include <pk11pub.h>
#include <prerror.h>
#include <secmod.h>
#include <secmodt.h>
using crypto::RSAPrivateKey;
using crypto::ScopedPK11Slot;
using crypto::ScopedSECItem;
using crypto::ScopedSECKEYPrivateKey;
using crypto::ScopedSECKEYPublicKey;
namespace {
// This should match the same constant in Chrome tree:
// chrome/browser/chromeos/settings/
const char kOwnerKeyFile[] = "/var/lib/whitelist/owner.key";
// TODO(hidehiko): Move this to scoped_nss_types.h.
struct CERTSubjectPublicKeyInfoDeleter {
void operator()(CERTSubjectPublicKeyInfo* ptr) const {
using ScopedCERTSubjectPublicKeyInfo =
std::unique_ptr<CERTSubjectPublicKeyInfo, CERTSubjectPublicKeyInfoDeleter>;
} // namespace
namespace login_manager {
// NssUtil
NssUtil::NssUtil() = default;
NssUtil::~NssUtil() = default;
// NssUtilImpl
class NssUtilImpl : public NssUtil {
~NssUtilImpl() override;
ScopedPK11Slot OpenUserDB(const base::FilePath& user_homedir) override;
std::unique_ptr<RSAPrivateKey> GetPrivateKeyForUser(
const std::vector<uint8_t>& public_key_der,
PK11SlotInfo* user_slot) override;
std::unique_ptr<RSAPrivateKey> GenerateKeyPairForUser(
PK11SlotInfo* user_slot) override;
base::FilePath GetOwnerKeyFilePath() override;
base::FilePath GetNssdbSubpath() override;
bool CheckPublicKeyBlob(const std::vector<uint8_t>& blob) override;
bool Verify(const std::vector<uint8_t>& signature,
const std::vector<uint8_t>& data,
const std::vector<uint8_t>& public_key) override;
bool Sign(const std::vector<uint8_t>& data,
RSAPrivateKey* key,
std::vector<uint8_t>* out_signature) override;
static const uint16_t kKeySizeInBits;
static const char kNssdbSubpath[];
// Defined here, instead of up above, because we need NssUtilImpl.
// static
std::unique_ptr<NssUtil> NssUtil::Create() {
return std::make_unique<NssUtilImpl>();
// We're generating and using 2048-bit RSA keys.
// static
const uint16_t NssUtilImpl::kKeySizeInBits = 2048;
// static
const char NssUtilImpl::kNssdbSubpath[] = ".pki/nssdb";
NssUtilImpl::NssUtilImpl() {
if (setenv("NSS_SDB_USE_CACHE", "no", 1) == -1)
PLOG(WARNING) << "Can't set NSS_SDB_USE_CACHE=no in the environment!";
NssUtilImpl::~NssUtilImpl() = default;
ScopedPK11Slot NssUtilImpl::OpenUserDB(const base::FilePath& user_homedir) {
// TODO(cmasone): If we ever try to keep the session_manager alive across
// user sessions, we'll need to close these persistent DBs.
base::FilePath db_path(user_homedir.AppendASCII(kNssdbSubpath));
const std::string modspec =
base::StringPrintf("configDir='sql:%s' tokenDescription='%s'",
db_path.value().c_str(), user_homedir.value().c_str());
ScopedPK11Slot db_slot(SECMOD_OpenUserDB(modspec.c_str()));
if (!db_slot.get()) {
LOG(ERROR) << "Error opening persistent database (" << modspec
<< "): " << PR_GetError();
return ScopedPK11Slot();
if (PK11_NeedUserInit(db_slot.get()))
PK11_InitPin(db_slot.get(), nullptr, nullptr);
// If we opened successfully, we will have a non-default private key slot.
if (PK11_IsInternalKeySlot(db_slot.get()))
return ScopedPK11Slot();
return db_slot;
std::unique_ptr<RSAPrivateKey> NssUtilImpl::GetPrivateKeyForUser(
const std::vector<uint8_t>& public_key_der, PK11SlotInfo* user_slot) {
if (public_key_der.size() == 0) {
LOG(ERROR) << "Not checking key because size is 0";
return nullptr;
// First, decode and save the public key.
SECItem key_der;
key_der.type = siBuffer; = const_cast<unsigned char*>(&public_key_der[0]);
key_der.len = public_key_der.size();
ScopedCERTSubjectPublicKeyInfo spki(
if (!spki) {
return nullptr;
ScopedSECKEYPublicKey public_key(SECKEY_ExtractPublicKey(spki.get()));
if (!public_key) {
return nullptr;
// Make sure the key is an RSA key. If not, that's an error
if (SECKEY_GetPublicKeyType(public_key.get()) != rsaKey) {
return nullptr;
ScopedSECItem ck_id(PK11_MakeIDFromPubKey(&(public_key->u.rsa.modulus)));
if (!ck_id) {
return nullptr;
// Search in just the user slot for the key with the given ID.
ScopedSECKEYPrivateKey key(
PK11_FindKeyByKeyID(user_slot, ck_id.get(), nullptr));
if (!key) {
// We didn't find the key.
return nullptr;
return base::WrapUnique(RSAPrivateKey::CreateFromKey(key.get()));
std::unique_ptr<RSAPrivateKey> NssUtilImpl::GenerateKeyPairForUser(
PK11SlotInfo* user_slot) {
PK11RSAGenParams param;
param.keySizeInBits = kKeySizeInBits; = 65537L;
SECKEYPublicKey* public_key_ptr = nullptr;
ScopedSECKEYPrivateKey key(PK11_GenerateKeyPair(
user_slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &param, &public_key_ptr,
PR_TRUE /* permanent */, PR_TRUE /* sensitive */, nullptr));
ScopedSECKEYPublicKey public_key(public_key_ptr);
if (!key)
return nullptr;
return base::WrapUnique(RSAPrivateKey::CreateFromKey(key.get()));
base::FilePath NssUtilImpl::GetOwnerKeyFilePath() {
return base::FilePath(kOwnerKeyFile);
base::FilePath NssUtilImpl::GetNssdbSubpath() {
return base::FilePath(kNssdbSubpath);
bool NssUtilImpl::CheckPublicKeyBlob(const std::vector<uint8_t>& blob) {
SECItem spki_der;
spki_der.type = siBuffer; = const_cast<uint8_t*>(&blob[0]);
spki_der.len = blob.size();
ScopedCERTSubjectPublicKeyInfo spki(
if (!spki)
return false;
ScopedSECKEYPublicKey public_key(SECKEY_ExtractPublicKey(spki.get()));
return static_cast<bool>(public_key);
// This is pretty much just a blind passthrough, so I won't test it
// in the NssUtil unit tests. I'll test it from a class that uses this API.
bool NssUtilImpl::Verify(const std::vector<uint8_t>& signature,
const std::vector<uint8_t>& data,
const std::vector<uint8_t>& public_key) {
crypto::SignatureVerifier verifier;
if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1,, signature.size(),, public_key.size())) {
LOG(ERROR) << "Could not initialize verifier";
return false;
verifier.VerifyUpdate(, data.size());
return verifier.VerifyFinal();
// This is pretty much just a blind passthrough, so I won't test it
// in the NssUtil unit tests. I'll test it from a class that uses this API.
bool NssUtilImpl::Sign(const std::vector<uint8_t>& data,
RSAPrivateKey* key,
std::vector<uint8_t>* out_signature) {
std::unique_ptr<crypto::SignatureCreator> signer(
crypto::SignatureCreator::Create(key, crypto::SignatureCreator::SHA1));
if (!signer->Update(, data.size()))
return false;
return signer->Final(out_signature);
} // namespace login_manager