summaryrefslogtreecommitdiff
path: root/keystore2/src/crypto/include/certificate_utils.h
blob: cad82b61eadff1d5f1f015c3d4cbc883161282db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
 * 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_ALGOR);
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,
        SignatureFailed,
        TimeError,
    };

  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 X509 name encoded 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,                                                   //
         std::optional<std::reference_wrapper<const std::vector<uint8_t>>> serial,   //
         std::optional<std::reference_wrapper<const std::vector<uint8_t>>> subject,  //
         const int64_t activeDateTimeMilliSeconds,                                   //
         const int64_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,
};

/**
 * Takes an int64_t representing UNIX epoch time in milliseconds and turns it into a UTCTime
 * or GeneralizedTime string depending on whether the year is in the interval [1950 .. 2050).
 * Note: The string returned in the array buffer is NUL terminated and of length 13 (UTCTime)
 * or 15 (GeneralizedTime).
 * @param timeMillis
 * @return UTCTime or GeneralizedTime string.
 */
std::optional<std::array<char, 16>> toTimeString(int64_t timeMillis);

/**
 * 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