blob: 9d41eb8cd4c6e39ac2eca00e4b1de2ce5c682a93 [file] [log] [blame]
/*
* Copyright 2020, The Android Open Source Project
*
* 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
*
* http://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.
*/
#pragma once
#include <openssl/err.h>
#include <openssl/x509.h>
#include <stdint.h>
#include <memory>
#include <optional>
#include <variant>
namespace keystore {
// We use boringssl error codes. Error codes that we add are folded into LIB_USER.
// The CertificateUtilsInternallErrorCodes enum should not be used by callers, instead use the
// BoringSslError constant definitions below for error codes.
using BoringSslError = unsigned long;
#define DEFINE_OPENSSL_OBJECT_POINTER(name) using name##_Ptr = bssl::UniquePtr<name>
DEFINE_OPENSSL_OBJECT_POINTER(ASN1_BIT_STRING);
DEFINE_OPENSSL_OBJECT_POINTER(ASN1_STRING);
DEFINE_OPENSSL_OBJECT_POINTER(ASN1_INTEGER);
DEFINE_OPENSSL_OBJECT_POINTER(ASN1_OCTET_STRING);
DEFINE_OPENSSL_OBJECT_POINTER(ASN1_TIME);
DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY);
DEFINE_OPENSSL_OBJECT_POINTER(X509);
DEFINE_OPENSSL_OBJECT_POINTER(X509_EXTENSION);
DEFINE_OPENSSL_OBJECT_POINTER(X509_NAME);
DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY_CTX);
class CertUtilsError {
public:
enum Error {
Ok = 0,
BoringSsl,
Encoding,
MemoryAllocation,
InvalidArgument,
UnexpectedNullPointer,
};
private:
Error e_;
public:
constexpr CertUtilsError(Error e) : e_(e) {}
explicit constexpr operator bool() const { return e_ != Ok; }
};
struct KeyUsageExtension {
bool isSigningKey;
bool isEncryptionKey;
bool isCertificationKey;
};
struct BasicConstraintsExtension {
bool isCa;
std::optional<int> pathLength;
};
/**
* This function allocates and prepares an X509 certificate structure with all of the information
* given. Next steps would be to set an Issuer with `setIssuer` and sign it with either
* `signCert` or `signCertWith`.
* @param evp_pkey The public key that the certificate is issued for.
* @param serial The certificate serial number.
* @param subject The subject common name.
* @param activeDateTimeMilliSeconds The not before date in epoch milliseconds.
* @param usageExpireDateTimeMilliSeconds The not after date in epoch milliseconds.
* @param addSubjectKeyIdEx If true, adds the subject key id extension.
* @param keyUsageEx If given adds, the key usage extension with the given flags.
* @param basicConstraints If given, adds the basic constraints extension with the given data.
* @return CertUtilsError::Ok on success.
*/
std::variant<CertUtilsError, X509_Ptr>
makeCert(const EVP_PKEY* evp_pkey, //
const uint32_t serial, //
const char subject[], //
const uint64_t activeDateTimeMilliSeconds, //
const uint64_t usageExpireDateTimeMilliSeconds, //
bool addSubjectKeyIdEx, //
std::optional<KeyUsageExtension> keyUsageEx, //
std::optional<BasicConstraintsExtension> basicConstraints); //
/**
* Takes the subject name from `signingCert` and sets it as issuer name in `cert`.
* if `addAuthKeyExt` is true it also generates the digest of the signing certificates's public key
* and sets it as authority key id extension in `cert`.
* For self signed certificates pass the same pointer to both `cert` and `signingCert`.
*
* @param cert
* @param signingCert
* @param addAuthKeyExt
* @return CertUtilsError::Ok on success.
*/
CertUtilsError setIssuer(X509* cert, const X509* signingCert, bool addAuthKeyExt);
/**
* Takes a certificate, and private signing_key.
* Signs the certificate with the latter.
*/
CertUtilsError signCert(X509* certificate, EVP_PKEY* signing_key);
enum class Digest {
SHA1,
SHA224,
SHA256,
SHA384,
SHA512,
};
enum class Algo {
ECDSA,
RSA,
};
enum class Padding {
Ignored,
PKCS1_5,
PSS,
};
/**
* Sets the signature specifier of the certificate and the signature according to the parameters
* c. Then it signs the certificate with the `sign` callback.
* IMPORTANT: The parameters `algo`, `padding`, and `digest` do not control the actual signing
* algorithm. The caller is responsible to provide a callback that actually performs the signature
* as described by this triplet.
* The `padding` argument is ignored if `algo` is Algo::EC.
* The `digest` field controls the message digest used, and, in case of RSA with PSS padding,
* also the MGF1 digest.
*
* @param certificate X509 certificate structure to be signed.
* @param sign Callback function used to digest and sign the DER encoded to-be-signed certificate.
* @param algo Algorithm specifier used to encode the signing algorithm id of the X509 certificate.
* @param padding Padding specifier used to encode the signing algorithm id of the X509 certificate.
* @param digest Digest specifier used to encode the signing algorithm id of the X509 certificate.
* @return CertUtilsError::Ok on success.
*/
CertUtilsError signCertWith(X509* certificate,
std::function<std::vector<uint8_t>(const uint8_t*, size_t)> sign,
Algo algo, Padding padding, Digest digest);
/**
* Generates the DER representation of the given signed X509 certificate structure.
* @param certificate
* @return std::vector<uint8_t> with the DER encoded certificate on success. An error code
* otherwise.
*/
std::variant<CertUtilsError, std::vector<uint8_t>> encodeCert(X509* certificate);
} // namespace keystore