#define TOR_X509_PRIVATE
#include "lib/tls/x509.h"
#include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/crypt_ops/crypto_nss_mgt.h"
#include "lib/log/util_bug.h"
#include "lib/encoding/time_fmt.h"
#include "lib/string/printf.h"
DISABLE_GCC_WARNING("-Wstrict-prototypes")
#include <pk11pub.h>
#include <cryptohi.h>
#include <cert.h>
#include <keyhi.h>
#include <time.h>
ENABLE_GCC_WARNING("-Wstrict-prototypes")
#define PRTIME_PER_SEC (1000*1000)
static tor_x509_cert_impl_t *tor_x509_cert_decode_internal(
const uint8_t *certificate, int certificate_len);
static tor_x509_cert_impl_t *
tor_tls_create_certificate_internal(crypto_pk_t *rsa,
crypto_pk_t *rsa_sign,
CERTName *subject_dn,
CERTName *issuer_dn,
time_t start_time,
time_t end_time)
{
if (! crypto_pk_key_is_private(rsa_sign)) {
return NULL;
}
const SECKEYPublicKey *subject_key = crypto_pk_get_nss_pubkey(rsa);
const SECKEYPrivateKey *signing_key = crypto_pk_get_nss_privkey(rsa_sign);
SECStatus s;
CERTSubjectPublicKeyInfo *subject_spki = NULL;
CERTCertificateRequest *request = NULL;
CERTValidity *validity = NULL;
CERTCertificate *cert = NULL;
SECItem der = { .data = NULL, .len = 0 };
SECItem signed_der = { .data = NULL, .len = 0 };
CERTCertificate *result_cert = NULL;
validity = CERT_CreateValidity(((PRTime)start_time) * PRTIME_PER_SEC,
((PRTime)end_time) * PRTIME_PER_SEC);
if (BUG(! validity)) {
crypto_nss_log_errors(LOG_WARN, "creating a validity object");
goto err;
}
unsigned long serial_number;
crypto_rand((char*)&serial_number, sizeof(serial_number));
subject_spki = SECKEY_CreateSubjectPublicKeyInfo(subject_key);
if (!subject_spki)
goto err;
request = CERT_CreateCertificateRequest(subject_dn,
subject_spki,
NULL );
if (!request)
goto err;
cert = CERT_CreateCertificate(serial_number,
issuer_dn,
validity,
request);
if (!cert)
goto err;
*cert->version.data = 2;
cert->version.len = 1;
KeyType privkey_type = SECKEY_GetPrivateKeyType(signing_key);
SECOidTag oid_tag = SEC_GetSignatureAlgorithmOidTag(privkey_type,
SEC_OID_SHA256);
if (oid_tag == SEC_OID_UNKNOWN)
goto err;
s = SECOID_SetAlgorithmID(cert->arena, &cert->signature, oid_tag, NULL);
if (s != SECSuccess)
goto err;
void *tmp;
tmp = SEC_ASN1EncodeItem(cert->arena, &der, cert,
SEC_ASN1_GET(CERT_CertificateTemplate));
if (!tmp)
goto err;
#if 0#else
s = SEC_DerSignData(cert->arena,
&signed_der,
der.data, der.len,
(SECKEYPrivateKey *)signing_key, SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION);
#endif
if (s != SECSuccess)
goto err;
result_cert = tor_x509_cert_decode_internal(signed_der.data, signed_der.len);
#if 1
{
tor_assert(result_cert);
SECKEYPublicKey *issuer_pk = (SECKEYPublicKey *)
crypto_pk_get_nss_pubkey(rsa_sign);
SECStatus cert_ok = CERT_VerifySignedDataWithPublicKey(
&result_cert->signatureWrap, issuer_pk, NULL);
tor_assert(cert_ok == SECSuccess);
}
#endif
err:
if (subject_spki)
SECKEY_DestroySubjectPublicKeyInfo(subject_spki);
if (request)
CERT_DestroyCertificateRequest(request);
if (validity)
CERT_DestroyValidity(validity);
if (cert)
CERT_DestroyCertificate(cert);
return result_cert;
}
MOCK_IMPL(tor_x509_cert_impl_t *,
tor_tls_create_certificate,(crypto_pk_t *rsa,
crypto_pk_t *rsa_sign,
const char *cname,
const char *cname_sign,
unsigned int cert_lifetime))
{
tor_assert(rsa);
tor_assert(rsa_sign);
tor_assert(cname);
tor_assert(cname_sign);
char *cname_rfc_1485 = NULL, *cname_sign_rfc_1485 = NULL;
CERTName *subject_dn = NULL, *issuer_dn = NULL;
time_t start_time;
time_t end_time;
CERTCertificate *result = NULL;
tor_asprintf(&cname_rfc_1485, "CN=%s", cname);
tor_asprintf(&cname_sign_rfc_1485, "CN=%s", cname_sign);
subject_dn = CERT_AsciiToName(cname_rfc_1485);
issuer_dn = CERT_AsciiToName(cname_sign_rfc_1485);
if (!subject_dn || !issuer_dn)
goto err;
tor_tls_pick_certificate_lifetime(time(NULL), cert_lifetime,
&start_time, &end_time);
result = tor_tls_create_certificate_internal(rsa,
rsa_sign,
subject_dn,
issuer_dn,
start_time,
end_time);
err:
tor_free(cname_rfc_1485);
tor_free(cname_sign_rfc_1485);
if (subject_dn)
CERT_DestroyName(subject_dn);
if (issuer_dn)
CERT_DestroyName(issuer_dn);
return result;
}
void
tor_x509_cert_get_der(const tor_x509_cert_t *cert,
const uint8_t **encoded_out, size_t *size_out)
{
tor_assert(cert);
tor_assert(cert->cert);
tor_assert(encoded_out);
tor_assert(size_out);
const SECItem *item = &cert->cert->derCert;
*encoded_out = item->data;
*size_out = (size_t)item->len;
}
void
tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert)
{
if (cert)
CERT_DestroyCertificate(cert);
}
tor_x509_cert_impl_t *
tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert)
{
if (cert)
return CERT_DupCertificate(cert);
else
return NULL;
}
static tor_x509_cert_impl_t *
tor_x509_cert_decode_internal(const uint8_t *certificate,
int certificate_len)
{
tor_assert(certificate);
if (certificate_len > INT_MAX)
return NULL;
SECItem der = { .type = siBuffer,
.data = (unsigned char *)certificate,
.len = certificate_len };
CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
tor_assert(certdb);
return CERT_NewTempCertificate(certdb,
&der,
NULL ,
PR_FALSE,
PR_TRUE );
}
tor_x509_cert_t *
tor_x509_cert_decode(const uint8_t *certificate,
size_t certificate_len)
{
CERTCertificate *cert = tor_x509_cert_decode_internal(certificate,
(int)certificate_len);
if (! cert) {
crypto_nss_log_errors(LOG_INFO, "decoding certificate");
return NULL;
}
tor_x509_cert_t *newcert = tor_x509_cert_new(cert);
return newcert;
}
crypto_pk_t *
tor_tls_cert_get_key(tor_x509_cert_t *cert)
{
tor_assert(cert);
CERTSubjectPublicKeyInfo *spki = &cert->cert->subjectPublicKeyInfo;
SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(spki); if (pub == NULL)
return NULL;
if (SECKEY_GetPublicKeyType(pub) != rsaKey) {
SECKEY_DestroyPublicKey(pub);
return NULL;
}
return crypto_pk_new_from_nss_pubkey(pub);
}
int
tor_tls_cert_is_valid(int severity,
const tor_x509_cert_t *cert,
const tor_x509_cert_t *signing_cert,
time_t now,
int check_rsa_1024)
{
int result = 0;
tor_assert(cert);
tor_assert(signing_cert);
SECKEYPublicKey *pk = CERT_ExtractPublicKey(signing_cert->cert);
if (pk == NULL) {
log_fn(severity, LD_CRYPTO,
"Invalid certificate: could not extract issuer key");
goto fail;
}
SECStatus s = CERT_VerifySignedDataWithPublicKey(&cert->cert->signatureWrap,
pk, NULL);
if (s != SECSuccess) {
log_fn(severity, LD_CRYPTO,
"Invalid certificate: could not validate signature.");
goto fail;
}
if (tor_x509_check_cert_lifetime_internal(severity,
cert->cert,
now,
TOR_X509_PAST_SLOP,
TOR_X509_FUTURE_SLOP) < 0)
goto fail;
if (check_rsa_1024) {
if (SECKEY_GetPublicKeyType(pk) != rsaKey ||
SECKEY_PublicKeyStrengthInBits(pk) != 1024) {
log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is not RSA1024.");
goto fail;
}
} else {
unsigned min_bits = (SECKEY_GetPublicKeyType(pk) == ecKey) ? 128: 1024;
if (SECKEY_PublicKeyStrengthInBits(pk) < min_bits) {
log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is too weak.");
goto fail;
}
}
result = 1;
fail:
if (pk)
SECKEY_DestroyPublicKey(pk);
return result;
}
static void
log_cert_lifetime(int severity,
const char *status,
time_t now,
PRTime notBefore,
PRTime notAfter)
{
log_fn(severity, LD_GENERAL,
"Certificate %s. Either their clock is set wrong, or your clock "
"is incorrect.", status);
char nowbuf[ISO_TIME_LEN+1];
char nbbuf[ISO_TIME_LEN+1];
char nabuf[ISO_TIME_LEN+1];
format_iso_time(nowbuf, now);
format_iso_time(nbbuf, notBefore / PRTIME_PER_SEC);
format_iso_time(nabuf, notAfter / PRTIME_PER_SEC);
log_fn(severity, LD_GENERAL,
"(The certificate is valid from %s until %s. Your time is %s.)",
nbbuf, nabuf, nowbuf);
}
int
tor_x509_check_cert_lifetime_internal(int severity,
const tor_x509_cert_impl_t *cert,
time_t now,
int past_tolerance,
int future_tolerance)
{
tor_assert(cert);
PRTime notBefore=0, notAfter=0;
int64_t t;
SECStatus r = CERT_GetCertTimes(cert, ¬Before, ¬After);
if (r != SECSuccess) {
log_fn(severity, LD_CRYPTO,
"Couldn't get validity times from certificate");
return -1;
}
t = ((int64_t)now) + future_tolerance;
t *= PRTIME_PER_SEC;
if (notBefore > t) {
log_cert_lifetime(severity, "not yet valid", now,
notBefore, notAfter);
return -1;
}
t = ((int64_t)now) - past_tolerance;
t *= PRTIME_PER_SEC;
if (notAfter < t) {
log_cert_lifetime(severity, "already expired", now,
notBefore, notAfter);
return -1;
}
return 0;
}
#ifdef TOR_UNIT_TESTS
tor_x509_cert_t *
tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp,
time_t new_expiration_time,
crypto_pk_t *signing_key)
{
tor_assert(inp);
tor_assert(signing_key);
PRTime notBefore=0, notAfter=0;
SECStatus r = CERT_GetCertTimes(inp->cert, ¬Before, ¬After);
if (r != SECSuccess)
return NULL;
time_t start_time = notBefore / PRTIME_PER_SEC;
if (new_expiration_time < start_time) {
start_time = new_expiration_time - 10;
}
crypto_pk_t *subject_key = tor_tls_cert_get_key((tor_x509_cert_t *)inp);
if (!subject_key)
return NULL;
CERTCertificate *newcert;
newcert = tor_tls_create_certificate_internal(subject_key,
signing_key,
&inp->cert->subject,
&inp->cert->issuer,
start_time,
new_expiration_time);
crypto_pk_free(subject_key);
return newcert ? tor_x509_cert_new(newcert) : NULL;
}
#endif