blob: eb7a5ade2d5aaf9027be3b7ac130dd7eeb85f4db [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 <string>
#include <utility>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/memory/scoped_ptr.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::ScopedSECKEYPublicKey;
using crypto::ScopedSECKEYPrivateKey;
namespace {
// This should match the same constant in Chrome tree:
// chrome/browser/chromeos/settings/owner_key_util.cc
const char kOwnerKeyFile[] = "/var/lib/whitelist/owner.key";
} // namespace
namespace login_manager {
///////////////////////////////////////////////////////////////////////////
// NssUtil
NssUtil::NssUtil() {
}
NssUtil::~NssUtil() {
}
///////////////////////////////////////////////////////////////////////////
// NssUtilImpl
class NssUtilImpl : public NssUtil {
public:
NssUtilImpl();
virtual ~NssUtilImpl();
ScopedPK11Slot OpenUserDB(const base::FilePath& user_homedir) override;
RSAPrivateKey* GetPrivateKeyForUser(
const std::vector<uint8_t>& public_key_der,
PK11SlotInfo* user_slot) override;
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 uint8_t* algorithm,
int algorithm_len,
const uint8_t* signature,
int signature_len,
const uint8_t* data,
int data_len,
const uint8_t* public_key,
int public_key_len) override;
bool Sign(const uint8_t* data,
int data_len,
std::vector<uint8_t>* OUT_signature,
RSAPrivateKey* key) override;
private:
static const uint16_t kKeySizeInBits;
static const char kNssdbSubpath[];
DISALLOW_COPY_AND_ASSIGN(NssUtilImpl);
};
// Defined here, instead of up above, because we need NssUtilImpl.
// static
NssUtil* NssUtil::Create() {
return new NssUtilImpl;
}
// static
void NssUtil::BlobFromBuffer(const std::string& buf,
std::vector<uint8_t>* out) {
out->resize(buf.length());
if (out->size() == 0)
return;
memcpy(&((*out)[0]), buf.c_str(), out->size());
}
// 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!";
crypto::EnsureNSSInit();
}
NssUtilImpl::~NssUtilImpl() {
}
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(), NULL, NULL);
// If we opened successfully, we will have a non-default private key slot.
if (PK11_IsInternalKeySlot(db_slot.get()))
return ScopedPK11Slot();
return ScopedPK11Slot(db_slot.get());
}
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 NULL;
}
// First, decode and save the public key.
SECItem key_der;
key_der.type = siBuffer;
key_der.data = const_cast<unsigned char*>(&public_key_der[0]);
key_der.len = public_key_der.size();
CERTSubjectPublicKeyInfo* spki =
SECKEY_DecodeDERSubjectPublicKeyInfo(&key_der);
if (!spki) {
NOTREACHED();
return NULL;
}
ScopedSECKEYPublicKey public_key(SECKEY_ExtractPublicKey(spki));
SECKEY_DestroySubjectPublicKeyInfo(spki);
if (!public_key.get()) {
NOTREACHED();
return NULL;
}
// Make sure the key is an RSA key. If not, that's an error
if (SECKEY_GetPublicKeyType(public_key.get()) != rsaKey) {
NOTREACHED();
return NULL;
}
ScopedSECItem ck_id(PK11_MakeIDFromPubKey(&(public_key->u.rsa.modulus)));
if (!ck_id.get()) {
NOTREACHED();
return NULL;
}
// Search in just the user slot for the key with the given ID.
ScopedSECKEYPrivateKey key(PK11_FindKeyByKeyID(user_slot, ck_id.get(), NULL));
if (key.get())
return RSAPrivateKey::CreateFromKey(key.get());
// We didn't find the key.
return NULL;
}
RSAPrivateKey* NssUtilImpl::GenerateKeyPairForUser(PK11SlotInfo* user_slot) {
PK11RSAGenParams param;
param.keySizeInBits = kKeySizeInBits;
param.pe = 65537L;
SECKEYPublicKey* public_key_ptr = NULL;
ScopedSECKEYPrivateKey key(PK11_GenerateKeyPair(user_slot,
CKM_RSA_PKCS_KEY_PAIR_GEN,
&param,
&public_key_ptr,
PR_TRUE /* permanent */,
PR_TRUE /* sensitive */,
NULL));
ScopedSECKEYPublicKey public_key(public_key_ptr);
if (!key.get())
return NULL;
return 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) {
CERTSubjectPublicKeyInfo* spki = NULL;
SECItem spki_der;
spki_der.type = siBuffer;
spki_der.data = const_cast<uint8_t*>(&blob[0]);
spki_der.len = blob.size();
spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der);
if (!spki)
return false;
ScopedSECKEYPublicKey public_key(SECKEY_ExtractPublicKey(spki));
SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki.
if (!public_key.get())
return false;
return true;
}
// 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 uint8_t* algorithm,
int algorithm_len,
const uint8_t* signature,
int signature_len,
const uint8_t* data,
int data_len,
const uint8_t* public_key,
int public_key_len) {
crypto::SignatureVerifier verifier_;
if (!verifier_.VerifyInit(algorithm,
algorithm_len,
signature,
signature_len,
public_key,
public_key_len)) {
LOG(ERROR) << "Could not initialize verifier";
return false;
}
verifier_.VerifyUpdate(data, data_len);
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 uint8_t* data,
int data_len,
std::vector<uint8_t>* OUT_signature,
RSAPrivateKey* key) {
scoped_ptr<crypto::SignatureCreator> signer(
crypto::SignatureCreator::Create(key, crypto::SignatureCreator::SHA1));
if (!signer->Update(data, data_len))
return false;
return signer->Final(OUT_signature);
}
} // namespace login_manager