Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "dtlsidentity.h"
8 :
9 : #include "cert.h"
10 : #include "cryptohi.h"
11 : #include "keyhi.h"
12 : #include "nsError.h"
13 : #include "pk11pub.h"
14 : #include "sechash.h"
15 : #include "ssl.h"
16 :
17 : #include "mozilla/Sprintf.h"
18 :
19 : namespace mozilla {
20 :
21 0 : RefPtr<DtlsIdentity> DtlsIdentity::Generate() {
22 0 : UniquePK11SlotInfo slot(PK11_GetInternalSlot());
23 0 : if (!slot) {
24 0 : return nullptr;
25 : }
26 :
27 : uint8_t random_name[16];
28 :
29 0 : SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), random_name,
30 0 : sizeof(random_name));
31 0 : if (rv != SECSuccess)
32 0 : return nullptr;
33 :
34 0 : std::string name;
35 : char chunk[3];
36 0 : for (unsigned char r_name : random_name) {
37 0 : SprintfLiteral(chunk, "%.2x", r_name);
38 0 : name += chunk;
39 : }
40 :
41 0 : std::string subject_name_string = "CN=" + name;
42 0 : UniqueCERTName subject_name(CERT_AsciiToName(subject_name_string.c_str()));
43 0 : if (!subject_name) {
44 0 : return nullptr;
45 : }
46 :
47 : unsigned char paramBuf[12]; // OIDs are small
48 0 : SECItem ecdsaParams = { siBuffer, paramBuf, sizeof(paramBuf) };
49 0 : SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
50 0 : if (!oidData || (oidData->oid.len > (sizeof(paramBuf) - 2))) {
51 0 : return nullptr;
52 : }
53 0 : ecdsaParams.data[0] = SEC_ASN1_OBJECT_ID;
54 0 : ecdsaParams.data[1] = oidData->oid.len;
55 0 : memcpy(ecdsaParams.data + 2, oidData->oid.data, oidData->oid.len);
56 0 : ecdsaParams.len = oidData->oid.len + 2;
57 :
58 : SECKEYPublicKey *pubkey;
59 : UniqueSECKEYPrivateKey private_key(
60 : PK11_GenerateKeyPair(slot.get(),
61 : CKM_EC_KEY_PAIR_GEN, &ecdsaParams, &pubkey,
62 0 : PR_FALSE, PR_TRUE, nullptr));
63 0 : if (private_key == nullptr)
64 0 : return nullptr;
65 0 : UniqueSECKEYPublicKey public_key(pubkey);
66 0 : pubkey = nullptr;
67 :
68 : UniqueCERTSubjectPublicKeyInfo spki(
69 0 : SECKEY_CreateSubjectPublicKeyInfo(public_key.get()));
70 0 : if (!spki) {
71 0 : return nullptr;
72 : }
73 :
74 : UniqueCERTCertificateRequest certreq(
75 0 : CERT_CreateCertificateRequest(subject_name.get(), spki.get(), nullptr));
76 0 : if (!certreq) {
77 0 : return nullptr;
78 : }
79 :
80 : // From 1 day before todayto 30 days after.
81 : // This is a sort of arbitrary range designed to be valid
82 : // now with some slack in case the other side expects
83 : // some before expiry.
84 : //
85 : // Note: explicit casts necessary to avoid
86 : // warning C4307: '*' : integral constant overflow
87 : static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
88 : * PRTime(60) // sec
89 : * PRTime(60) // min
90 : * PRTime(24); // hours
91 0 : PRTime now = PR_Now();
92 0 : PRTime notBefore = now - oneDay;
93 0 : PRTime notAfter = now + (PRTime(30) * oneDay);
94 :
95 0 : UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
96 0 : if (!validity) {
97 0 : return nullptr;
98 : }
99 :
100 : unsigned long serial;
101 : // Note: This serial in principle could collide, but it's unlikely
102 0 : rv = PK11_GenerateRandomOnSlot(slot.get(),
103 : reinterpret_cast<unsigned char *>(&serial),
104 0 : sizeof(serial));
105 0 : if (rv != SECSuccess) {
106 0 : return nullptr;
107 : }
108 :
109 : UniqueCERTCertificate certificate(
110 : CERT_CreateCertificate(serial, subject_name.get(), validity.get(),
111 0 : certreq.get()));
112 0 : if (!certificate) {
113 0 : return nullptr;
114 : }
115 :
116 0 : PLArenaPool *arena = certificate->arena;
117 :
118 0 : rv = SECOID_SetAlgorithmID(arena, &certificate->signature,
119 0 : SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, nullptr);
120 0 : if (rv != SECSuccess)
121 0 : return nullptr;
122 :
123 : // Set version to X509v3.
124 0 : *(certificate->version.data) = SEC_CERTIFICATE_VERSION_3;
125 0 : certificate->version.len = 1;
126 :
127 : SECItem innerDER;
128 0 : innerDER.len = 0;
129 0 : innerDER.data = nullptr;
130 :
131 0 : if (!SEC_ASN1EncodeItem(arena, &innerDER, certificate.get(),
132 : SEC_ASN1_GET(CERT_CertificateTemplate))) {
133 0 : return nullptr;
134 : }
135 :
136 0 : SECItem *signedCert = PORT_ArenaZNew(arena, SECItem);
137 0 : if (!signedCert) {
138 0 : return nullptr;
139 : }
140 :
141 0 : rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len,
142 : private_key.get(),
143 0 : SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
144 0 : if (rv != SECSuccess) {
145 0 : return nullptr;
146 : }
147 0 : certificate->derCert = *signedCert;
148 :
149 0 : RefPtr<DtlsIdentity> identity = new DtlsIdentity(Move(private_key),
150 0 : Move(certificate),
151 0 : ssl_kea_ecdh);
152 0 : return identity.forget();
153 : }
154 :
155 3 : const std::string DtlsIdentity::DEFAULT_HASH_ALGORITHM = "sha-256";
156 :
157 0 : nsresult DtlsIdentity::ComputeFingerprint(const std::string algorithm,
158 : uint8_t *digest,
159 : size_t size,
160 : size_t *digest_length) const {
161 0 : const UniqueCERTCertificate& c = cert();
162 0 : MOZ_ASSERT(c);
163 :
164 0 : return ComputeFingerprint(c, algorithm, digest, size, digest_length);
165 : }
166 :
167 0 : nsresult DtlsIdentity::ComputeFingerprint(const UniqueCERTCertificate& cert,
168 : const std::string algorithm,
169 : uint8_t *digest,
170 : size_t size,
171 : size_t *digest_length) {
172 0 : MOZ_ASSERT(cert);
173 :
174 : HASH_HashType ht;
175 :
176 0 : if (algorithm == "sha-1") {
177 0 : ht = HASH_AlgSHA1;
178 0 : } else if (algorithm == "sha-224") {
179 0 : ht = HASH_AlgSHA224;
180 0 : } else if (algorithm == "sha-256") {
181 0 : ht = HASH_AlgSHA256;
182 0 : } else if (algorithm == "sha-384") {
183 0 : ht = HASH_AlgSHA384;
184 0 : } else if (algorithm == "sha-512") {
185 0 : ht = HASH_AlgSHA512;
186 : } else {
187 0 : return NS_ERROR_INVALID_ARG;
188 : }
189 :
190 0 : const SECHashObject *ho = HASH_GetHashObject(ht);
191 0 : MOZ_ASSERT(ho);
192 0 : if (!ho) {
193 0 : return NS_ERROR_INVALID_ARG;
194 : }
195 :
196 0 : MOZ_ASSERT(ho->length >= 20); // Double check
197 :
198 0 : if (size < ho->length) {
199 0 : return NS_ERROR_INVALID_ARG;
200 : }
201 :
202 0 : SECStatus rv = HASH_HashBuf(ho->type, digest,
203 0 : cert->derCert.data,
204 0 : cert->derCert.len);
205 0 : if (rv != SECSuccess) {
206 0 : return NS_ERROR_FAILURE;
207 : }
208 :
209 0 : *digest_length = ho->length;
210 :
211 0 : return NS_OK;
212 : }
213 :
214 9 : } // close namespace
|