blob: 73fe266d0f59be57fee32fa4649c326342126e6e [file]
// Copyright 2026 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <openssl/asn1.h>
#include <openssl/base.h>
#include <openssl/evp.h>
#include <openssl/mldsa.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <iostream>
using namespace bssl;
// Copied from crypto/x509/x509_test.cc
static UniquePtr<X509_NAME> MakeTestName(std::string_view common_name) {
auto bytes = StringAsBytes(common_name);
UniquePtr<X509_NAME> name(X509_NAME_new());
if (name == nullptr ||
!X509_NAME_add_entry_by_txt(name.get(), "CN", MBSTRING_UTF8, bytes.data(),
bytes.size(), -1, 0)) {
return nullptr;
}
return name;
}
// Copied from crypto/x509/x509_test.cc, with some modifications.
static UniquePtr<X509> MakeTestCert(std::string_view issuer,
std::string_view subject, EVP_PKEY *key,
int64_t reference_time, bool is_ca) {
UniquePtr<X509_NAME> issuer_name = MakeTestName(issuer);
UniquePtr<X509_NAME> subject_name = MakeTestName(subject);
UniquePtr<X509> cert(X509_new());
UniquePtr<ASN1_INTEGER> serial(ASN1_INTEGER_new());
if (issuer_name == nullptr || subject_name == nullptr || cert == nullptr ||
serial == nullptr || //
!X509_set_version(cert.get(), X509_VERSION_3) ||
!ASN1_INTEGER_set_uint64(serial.get(), 42) ||
!X509_set_serialNumber(cert.get(), serial.get()) ||
!X509_set_issuer_name(cert.get(), issuer_name.get()) ||
!X509_set_subject_name(cert.get(), subject_name.get()) ||
!X509_set_pubkey(cert.get(), key) ||
!ASN1_TIME_adj(X509_getm_notBefore(cert.get()), reference_time, -1, 0) ||
!ASN1_TIME_adj(X509_getm_notAfter(cert.get()), reference_time, 1, 0)) {
return nullptr;
}
UniquePtr<BASIC_CONSTRAINTS> bc(BASIC_CONSTRAINTS_new());
if (!bc) {
return nullptr;
}
bc->ca = is_ca ? ASN1_BOOLEAN_TRUE : ASN1_BOOLEAN_FALSE;
if (!X509_add1_ext_i2d(cert.get(), NID_basic_constraints, bc.get(),
/*crit=*/1, /*flags=*/0)) {
return nullptr;
}
return cert;
}
static bool PrintCert(X509 *cert) {
UniquePtr<BIO> bio(BIO_new_fp(stdout, BIO_NOCLOSE));
return PEM_write_bio_X509(bio.get(), cert);
}
// This tool is used to generate mldsa-root.pem, mldsa-intermediate.pem, and
// mldsa-leaf.pem.
int main() {
// Generate keys
UniquePtr<EVP_PKEY> root_key(
EVP_PKEY_generate_from_alg(EVP_pkey_ml_dsa_87()));
UniquePtr<EVP_PKEY> int_key(EVP_PKEY_generate_from_alg(EVP_pkey_ml_dsa_65()));
UniquePtr<EVP_PKEY> leaf_key(
EVP_PKEY_generate_from_alg(EVP_pkey_ml_dsa_44()));
if (!root_key || !int_key || !leaf_key) {
std::cerr << "Failed to generate keys" << std::endl;
return 1;
}
static const int64_t reference_time = 1775458800; // April 6, 2026
// Root CA (self-signed)
UniquePtr<X509> root(
MakeTestCert("Root", "Root", root_key.get(), reference_time, true));
if (!root || !X509_sign(root.get(), root_key.get(), nullptr)) {
std::cerr << "Failed to create root cert" << std::endl;
return 1;
}
// Intermediate CA (signed by Root)
UniquePtr<X509> intermediate(MakeTestCert(
"Root", "Intermediate", int_key.get(), reference_time, true));
if (!intermediate ||
!X509_sign(intermediate.get(), root_key.get(), nullptr)) {
std::cerr << "Failed to create intermediate cert" << std::endl;
return 1;
}
// Leaf (signed by Intermediate)
UniquePtr<X509> leaf(MakeTestCert("Intermediate", "Leaf", leaf_key.get(),
reference_time, false));
if (!leaf || !X509_sign(leaf.get(), int_key.get(), nullptr)) {
std::cerr << "Failed to create leaf cert" << std::endl;
return 1;
}
if (!PrintCert(root.get()) || !PrintCert(intermediate.get()) ||
!PrintCert(leaf.get())) {
std::cerr << "Failed to print certs" << std::endl;
return 1;
}
return 0;
}