1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 #include "CTVerifyResult.h"
9 #include "PSMIPCCommon.h"
10 
11 namespace mozilla {
12 namespace psm {
13 
WrapPrivateKeyInfoWithEmptyPassword(SECKEYPrivateKey * pk)14 SECItem* WrapPrivateKeyInfoWithEmptyPassword(
15     SECKEYPrivateKey* pk) /* encrypt this private key */
16 {
17   if (!pk) {
18     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
19     return nullptr;
20   }
21 
22   UniquePK11SlotInfo slot(PK11_GetInternalSlot());
23   if (!slot) {
24     return nullptr;
25   }
26 
27   // For private keys, NSS cannot export anything other than RSA, but we need EC
28   // also. So, we use the private key encryption function to serialize instead,
29   // using a hard-coded dummy password; this is not intended to provide any
30   // additional security, it just works around a limitation in NSS.
31   SECItem dummyPassword = {siBuffer, nullptr, 0};
32   UniqueSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo(
33       slot.get(), SEC_OID_AES_128_CBC, &dummyPassword, pk, 1, nullptr));
34 
35   if (!epki) {
36     return nullptr;
37   }
38 
39   return SEC_ASN1EncodeItem(
40       nullptr, nullptr, epki.get(),
41       NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false));
42 }
43 
UnwrapPrivateKeyInfoWithEmptyPassword(SECItem * derPKI,const UniqueCERTCertificate & aCert,SECKEYPrivateKey ** privk)44 SECStatus UnwrapPrivateKeyInfoWithEmptyPassword(
45     SECItem* derPKI, const UniqueCERTCertificate& aCert,
46     SECKEYPrivateKey** privk) {
47   if (!derPKI || !aCert || !privk) {
48     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
49     return SECFailure;
50   }
51 
52   UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(aCert.get()));
53   // This is a pointer to data inside publicKey
54   SECItem* publicValue = nullptr;
55   switch (publicKey->keyType) {
56     case dsaKey:
57       publicValue = &publicKey->u.dsa.publicValue;
58       break;
59     case dhKey:
60       publicValue = &publicKey->u.dh.publicValue;
61       break;
62     case rsaKey:
63       publicValue = &publicKey->u.rsa.modulus;
64       break;
65     case ecKey:
66       publicValue = &publicKey->u.ec.publicValue;
67       break;
68     default:
69       MOZ_ASSERT(false);
70       PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
71       return SECFailure;
72   }
73 
74   UniquePK11SlotInfo slot(PK11_GetInternalSlot());
75   if (!slot) {
76     return SECFailure;
77   }
78 
79   UniquePLArenaPool temparena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
80   if (!temparena) {
81     return SECFailure;
82   }
83 
84   SECKEYEncryptedPrivateKeyInfo* epki =
85       PORT_ArenaZNew(temparena.get(), SECKEYEncryptedPrivateKeyInfo);
86   if (!epki) {
87     return SECFailure;
88   }
89 
90   SECStatus rv = SEC_ASN1DecodeItem(
91       temparena.get(), epki,
92       NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false), derPKI);
93   if (rv != SECSuccess) {
94     // If SEC_ASN1DecodeItem fails, we cannot assume anything about the
95     // validity of the data in epki. The best we can do is free the arena
96     // and return.
97     return rv;
98   }
99 
100   // See comment in WrapPrivateKeyInfoWithEmptyPassword about this
101   // dummy password stuff.
102   SECItem dummyPassword = {siBuffer, nullptr, 0};
103   return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
104       slot.get(), epki, &dummyPassword, nullptr, publicValue, false, false,
105       publicKey->keyType, KU_ALL, privk, nullptr);
106 }
107 
SerializeClientCertAndKey(const UniqueCERTCertificate & aCert,const UniqueSECKEYPrivateKey & aKey,ByteArray & aOutSerializedCert,ByteArray & aOutSerializedKey)108 void SerializeClientCertAndKey(const UniqueCERTCertificate& aCert,
109                                const UniqueSECKEYPrivateKey& aKey,
110                                ByteArray& aOutSerializedCert,
111                                ByteArray& aOutSerializedKey) {
112   if (!aCert || !aKey) {
113     return;
114   }
115 
116   UniqueSECItem derPki(WrapPrivateKeyInfoWithEmptyPassword(aKey.get()));
117   if (!derPki) {
118     return;
119   }
120 
121   aOutSerializedCert.data().AppendElements(aCert->derCert.data,
122                                            aCert->derCert.len);
123   aOutSerializedKey.data().AppendElements(derPki->data, derPki->len);
124 }
125 
DeserializeClientCertAndKey(const ByteArray & aSerializedCert,const ByteArray & aSerializedKey,UniqueCERTCertificate & aOutCert,UniqueSECKEYPrivateKey & aOutKey)126 void DeserializeClientCertAndKey(const ByteArray& aSerializedCert,
127                                  const ByteArray& aSerializedKey,
128                                  UniqueCERTCertificate& aOutCert,
129                                  UniqueSECKEYPrivateKey& aOutKey) {
130   if (aSerializedCert.data().IsEmpty() || aSerializedKey.data().IsEmpty()) {
131     return;
132   }
133 
134   SECItem item = {siBuffer,
135                   const_cast<uint8_t*>(aSerializedCert.data().Elements()),
136                   static_cast<unsigned int>(aSerializedCert.data().Length())};
137 
138   UniqueCERTCertificate cert(CERT_NewTempCertificate(
139       CERT_GetDefaultCertDB(), &item, nullptr, false, true));
140 
141   if (!cert) {
142     return;
143   }
144 
145   SECItem derPKI = {siBuffer,
146                     const_cast<uint8_t*>(aSerializedKey.data().Elements()),
147                     static_cast<unsigned int>(aSerializedKey.data().Length())};
148 
149   SECKEYPrivateKey* privateKey;
150   if (UnwrapPrivateKeyInfoWithEmptyPassword(&derPKI, cert, &privateKey) !=
151       SECSuccess) {
152     MOZ_ASSERT(false);
153     return;
154   }
155 
156   aOutCert = std::move(cert);
157   aOutKey.reset(privateKey);
158 }
159 
160 }  // namespace psm
161 }  // namespace mozilla
162