/src/mozilla-central/media/mtransport/dtlsidentity.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | } |
26 | 0 | |
27 | 0 | uint8_t random_name[16]; |
28 | 0 |
|
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 | 0 | |
34 | 0 | std::string name; |
35 | 0 | char chunk[3]; |
36 | 0 | for (unsigned char r_name : random_name) { |
37 | 0 | SprintfLiteral(chunk, "%.2x", r_name); |
38 | 0 | name += chunk; |
39 | 0 | } |
40 | 0 |
|
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 | 0 | } |
46 | 0 | |
47 | 0 | 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 | 0 | } |
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 | 0 |
|
58 | 0 | SECKEYPublicKey *pubkey; |
59 | 0 | UniqueSECKEYPrivateKey private_key( |
60 | 0 | PK11_GenerateKeyPair(slot.get(), |
61 | 0 | 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 | 0 |
|
68 | 0 | UniqueCERTSubjectPublicKeyInfo spki( |
69 | 0 | SECKEY_CreateSubjectPublicKeyInfo(public_key.get())); |
70 | 0 | if (!spki) { |
71 | 0 | return nullptr; |
72 | 0 | } |
73 | 0 | |
74 | 0 | UniqueCERTCertificateRequest certreq( |
75 | 0 | CERT_CreateCertificateRequest(subject_name.get(), spki.get(), nullptr)); |
76 | 0 | if (!certreq) { |
77 | 0 | return nullptr; |
78 | 0 | } |
79 | 0 | |
80 | 0 | // From 1 day before todayto 30 days after. |
81 | 0 | // This is a sort of arbitrary range designed to be valid |
82 | 0 | // now with some slack in case the other side expects |
83 | 0 | // some before expiry. |
84 | 0 | // |
85 | 0 | // Note: explicit casts necessary to avoid |
86 | 0 | // warning C4307: '*' : integral constant overflow |
87 | 0 | static const PRTime oneDay = PRTime(PR_USEC_PER_SEC) |
88 | 0 | * PRTime(60) // sec |
89 | 0 | * PRTime(60) // min |
90 | 0 | * PRTime(24); // hours |
91 | 0 | PRTime now = PR_Now(); |
92 | 0 | PRTime notBefore = now - oneDay; |
93 | 0 | PRTime notAfter = now + (PRTime(30) * oneDay); |
94 | 0 |
|
95 | 0 | UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter)); |
96 | 0 | if (!validity) { |
97 | 0 | return nullptr; |
98 | 0 | } |
99 | 0 | |
100 | 0 | unsigned long serial; |
101 | 0 | // Note: This serial in principle could collide, but it's unlikely |
102 | 0 | rv = PK11_GenerateRandomOnSlot(slot.get(), |
103 | 0 | reinterpret_cast<unsigned char *>(&serial), |
104 | 0 | sizeof(serial)); |
105 | 0 | if (rv != SECSuccess) { |
106 | 0 | return nullptr; |
107 | 0 | } |
108 | 0 | |
109 | 0 | UniqueCERTCertificate certificate( |
110 | 0 | CERT_CreateCertificate(serial, subject_name.get(), validity.get(), |
111 | 0 | certreq.get())); |
112 | 0 | if (!certificate) { |
113 | 0 | return nullptr; |
114 | 0 | } |
115 | 0 | |
116 | 0 | PLArenaPool *arena = certificate->arena; |
117 | 0 |
|
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 | 0 | |
123 | 0 | // Set version to X509v3. |
124 | 0 | *(certificate->version.data) = SEC_CERTIFICATE_VERSION_3; |
125 | 0 | certificate->version.len = 1; |
126 | 0 |
|
127 | 0 | SECItem innerDER; |
128 | 0 | innerDER.len = 0; |
129 | 0 | innerDER.data = nullptr; |
130 | 0 |
|
131 | 0 | if (!SEC_ASN1EncodeItem(arena, &innerDER, certificate.get(), |
132 | 0 | SEC_ASN1_GET(CERT_CertificateTemplate))) { |
133 | 0 | return nullptr; |
134 | 0 | } |
135 | 0 | |
136 | 0 | SECItem *signedCert = PORT_ArenaZNew(arena, SECItem); |
137 | 0 | if (!signedCert) { |
138 | 0 | return nullptr; |
139 | 0 | } |
140 | 0 | |
141 | 0 | rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len, |
142 | 0 | private_key.get(), |
143 | 0 | SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); |
144 | 0 | if (rv != SECSuccess) { |
145 | 0 | return nullptr; |
146 | 0 | } |
147 | 0 | certificate->derCert = *signedCert; |
148 | 0 |
|
149 | 0 | RefPtr<DtlsIdentity> identity = new DtlsIdentity(std::move(private_key), |
150 | 0 | std::move(certificate), |
151 | 0 | ssl_kea_ecdh); |
152 | 0 | return identity.forget(); |
153 | 0 | } |
154 | | |
155 | | const std::string DtlsIdentity::DEFAULT_HASH_ALGORITHM = "sha-256"; |
156 | | |
157 | | nsresult DtlsIdentity::ComputeFingerprint(const std::string algorithm, |
158 | | uint8_t *digest, |
159 | | size_t size, |
160 | 0 | size_t *digest_length) const { |
161 | 0 | const UniqueCERTCertificate& c = cert(); |
162 | 0 | MOZ_ASSERT(c); |
163 | 0 |
|
164 | 0 | return ComputeFingerprint(c, algorithm, digest, size, digest_length); |
165 | 0 | } |
166 | | |
167 | | nsresult DtlsIdentity::ComputeFingerprint(const UniqueCERTCertificate& cert, |
168 | | const std::string algorithm, |
169 | | uint8_t *digest, |
170 | | size_t size, |
171 | 0 | size_t *digest_length) { |
172 | 0 | MOZ_ASSERT(cert); |
173 | 0 |
|
174 | 0 | HASH_HashType ht; |
175 | 0 |
|
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 | 0 | } else { |
187 | 0 | return NS_ERROR_INVALID_ARG; |
188 | 0 | } |
189 | 0 | |
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 | 0 | } |
195 | 0 | |
196 | 0 | MOZ_ASSERT(ho->length >= 20); // Double check |
197 | 0 |
|
198 | 0 | if (size < ho->length) { |
199 | 0 | return NS_ERROR_INVALID_ARG; |
200 | 0 | } |
201 | 0 | |
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 | 0 | } |
208 | 0 | |
209 | 0 | *digest_length = ho->length; |
210 | 0 |
|
211 | 0 | return NS_OK; |
212 | 0 | } |
213 | | |
214 | | } // close namespace |