1 /* -*- Mode: C; tab-width: 8 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "cmmf.h"
7 #include "cmmfi.h"
8 #include "sechash.h"
9 #include "genname.h"
10 #include "pk11func.h"
11 #include "cert.h"
12 #include "secitem.h"
13 #include "secmod.h"
14 #include "keyhi.h"
15 
16 static int
cmmf_create_witness_and_challenge(PLArenaPool * poolp,CMMFChallenge * challenge,long inRandom,SECItem * senderDER,SECKEYPublicKey * inPubKey,void * passwdArg)17 cmmf_create_witness_and_challenge(PLArenaPool *poolp,
18                                   CMMFChallenge *challenge,
19                                   long inRandom,
20                                   SECItem *senderDER,
21                                   SECKEYPublicKey *inPubKey,
22                                   void *passwdArg)
23 {
24     SECItem *encodedRandNum;
25     SECItem encodedRandStr = { siBuffer, NULL, 0 };
26     SECItem *dummy;
27     unsigned char *randHash, *senderHash, *encChal = NULL;
28     unsigned modulusLen = 0;
29     SECStatus rv = SECFailure;
30     CMMFRand randStr = { { siBuffer, NULL, 0 }, { siBuffer, NULL, 0 } };
31     PK11SlotInfo *slot;
32     PK11SymKey *symKey = NULL;
33     CERTSubjectPublicKeyInfo *spki = NULL;
34 
35     encodedRandNum = SEC_ASN1EncodeInteger(poolp, &challenge->randomNumber,
36                                            inRandom);
37     if (!encodedRandNum) {
38         goto loser;
39     }
40     randHash = PORT_ArenaNewArray(poolp, unsigned char, SHA1_LENGTH);
41     senderHash = PORT_ArenaNewArray(poolp, unsigned char, SHA1_LENGTH);
42     if (randHash == NULL) {
43         goto loser;
44     }
45     rv = PK11_HashBuf(SEC_OID_SHA1, randHash, encodedRandNum->data,
46                       (PRUint32)encodedRandNum->len);
47     if (rv != SECSuccess) {
48         goto loser;
49     }
50     rv = PK11_HashBuf(SEC_OID_SHA1, senderHash, senderDER->data,
51                       (PRUint32)senderDER->len);
52     if (rv != SECSuccess) {
53         goto loser;
54     }
55     challenge->witness.data = randHash;
56     challenge->witness.len = SHA1_LENGTH;
57 
58     randStr.integer = *encodedRandNum;
59     randStr.senderHash.data = senderHash;
60     randStr.senderHash.len = SHA1_LENGTH;
61     dummy = SEC_ASN1EncodeItem(NULL, &encodedRandStr, &randStr,
62                                CMMFRandTemplate);
63     if (dummy != &encodedRandStr) {
64         rv = SECFailure;
65         goto loser;
66     }
67     /* XXXX Now I have to encrypt encodedRandStr and stash it away. */
68     modulusLen = SECKEY_PublicKeyStrength(inPubKey);
69     encChal = PORT_ArenaNewArray(poolp, unsigned char, modulusLen);
70     if (encChal == NULL) {
71         rv = SECFailure;
72         goto loser;
73     }
74     slot = PK11_GetBestSlotWithAttributes(CKM_RSA_PKCS, CKF_WRAP, 0, passwdArg);
75     if (slot == NULL) {
76         rv = SECFailure;
77         goto loser;
78     }
79     (void)PK11_ImportPublicKey(slot, inPubKey, PR_FALSE);
80     /* In order to properly encrypt the data, we import as a symmetric
81      * key, and then wrap that key.  That in essence encrypts the data.
82      * This is the method recommended in the PK11 world in order
83      * to prevent threading issues as well as breaking any other semantics
84      * the PK11 libraries depend on.
85      */
86     symKey = PK11_ImportSymKey(slot, CKM_RSA_PKCS, PK11_OriginGenerated,
87                                CKA_VALUE, &encodedRandStr, passwdArg);
88     if (symKey == NULL) {
89         rv = SECFailure;
90         goto loser;
91     }
92     challenge->challenge.data = encChal;
93     challenge->challenge.len = modulusLen;
94     rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, inPubKey, symKey,
95                             &challenge->challenge);
96     PK11_FreeSlot(slot);
97     if (rv != SECSuccess) {
98         goto loser;
99     }
100     rv = SECITEM_CopyItem(poolp, &challenge->senderDER, senderDER);
101     crmf_get_public_value(inPubKey, &challenge->key);
102 /* Fall through */
103 loser:
104     if (spki != NULL) {
105         SECKEY_DestroySubjectPublicKeyInfo(spki);
106     }
107     if (encodedRandStr.data != NULL) {
108         PORT_Free(encodedRandStr.data);
109     }
110     if (encodedRandNum != NULL) {
111         SECITEM_FreeItem(encodedRandNum, PR_TRUE);
112     }
113     if (symKey != NULL) {
114         PK11_FreeSymKey(symKey);
115     }
116     return rv;
117 }
118 
119 static SECStatus
cmmf_create_first_challenge(CMMFPOPODecKeyChallContent * challContent,long inRandom,SECItem * senderDER,SECKEYPublicKey * inPubKey,void * passwdArg)120 cmmf_create_first_challenge(CMMFPOPODecKeyChallContent *challContent,
121                             long inRandom,
122                             SECItem *senderDER,
123                             SECKEYPublicKey *inPubKey,
124                             void *passwdArg)
125 {
126     SECOidData *oidData;
127     CMMFChallenge *challenge;
128     SECAlgorithmID *algId;
129     PLArenaPool *poolp;
130     SECStatus rv;
131 
132     oidData = SECOID_FindOIDByTag(SEC_OID_SHA1);
133     if (oidData == NULL) {
134         return SECFailure;
135     }
136     poolp = challContent->poolp;
137     challenge = PORT_ArenaZNew(poolp, CMMFChallenge);
138     if (challenge == NULL) {
139         return SECFailure;
140     }
141     algId = challenge->owf = PORT_ArenaZNew(poolp, SECAlgorithmID);
142     if (algId == NULL) {
143         return SECFailure;
144     }
145     rv = SECITEM_CopyItem(poolp, &algId->algorithm, &oidData->oid);
146     if (rv != SECSuccess) {
147         return SECFailure;
148     }
149     rv = cmmf_create_witness_and_challenge(poolp, challenge, inRandom,
150                                            senderDER, inPubKey, passwdArg);
151     challContent->challenges[0] = (rv == SECSuccess) ? challenge : NULL;
152     challContent->numChallenges++;
153     return rv;
154 }
155 
156 CMMFPOPODecKeyChallContent *
CMMF_CreatePOPODecKeyChallContent(void)157 CMMF_CreatePOPODecKeyChallContent(void)
158 {
159     PLArenaPool *poolp;
160     CMMFPOPODecKeyChallContent *challContent;
161 
162     poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE);
163     if (poolp == NULL) {
164         return NULL;
165     }
166     challContent = PORT_ArenaZNew(poolp, CMMFPOPODecKeyChallContent);
167     if (challContent == NULL) {
168         PORT_FreeArena(poolp, PR_FALSE);
169         return NULL;
170     }
171     challContent->poolp = poolp;
172     return challContent;
173 }
174 
175 SECStatus
CMMF_POPODecKeyChallContentSetNextChallenge(CMMFPOPODecKeyChallContent * inDecKeyChall,long inRandom,CERTGeneralName * inSender,SECKEYPublicKey * inPubKey,void * passwdArg)176 CMMF_POPODecKeyChallContentSetNextChallenge(CMMFPOPODecKeyChallContent *inDecKeyChall,
177                                             long inRandom,
178                                             CERTGeneralName *inSender,
179                                             SECKEYPublicKey *inPubKey,
180                                             void *passwdArg)
181 {
182     CMMFChallenge *curChallenge;
183     PLArenaPool *genNamePool = NULL, *poolp;
184     SECStatus rv;
185     SECItem *genNameDER;
186     void *mark;
187 
188     PORT_Assert(inDecKeyChall != NULL &&
189                 inSender != NULL &&
190                 inPubKey != NULL);
191 
192     if (inDecKeyChall == NULL ||
193         inSender == NULL || inPubKey == NULL) {
194         return SECFailure;
195     }
196     poolp = inDecKeyChall->poolp;
197     mark = PORT_ArenaMark(poolp);
198 
199     genNamePool = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE);
200     genNameDER = CERT_EncodeGeneralName(inSender, NULL, genNamePool);
201     if (genNameDER == NULL) {
202         rv = SECFailure;
203         goto loser;
204     }
205     if (inDecKeyChall->challenges == NULL) {
206         inDecKeyChall->challenges =
207             PORT_ArenaZNewArray(poolp, CMMFChallenge *, (CMMF_MAX_CHALLENGES + 1));
208         inDecKeyChall->numAllocated = CMMF_MAX_CHALLENGES;
209     }
210 
211     if (inDecKeyChall->numChallenges >= inDecKeyChall->numAllocated) {
212         rv = SECFailure;
213         goto loser;
214     }
215 
216     if (inDecKeyChall->numChallenges == 0) {
217         rv = cmmf_create_first_challenge(inDecKeyChall, inRandom,
218                                          genNameDER, inPubKey, passwdArg);
219     } else {
220         curChallenge = PORT_ArenaZNew(poolp, CMMFChallenge);
221         if (curChallenge == NULL) {
222             rv = SECFailure;
223             goto loser;
224         }
225         rv = cmmf_create_witness_and_challenge(poolp, curChallenge, inRandom,
226                                                genNameDER, inPubKey,
227                                                passwdArg);
228         if (rv == SECSuccess) {
229             inDecKeyChall->challenges[inDecKeyChall->numChallenges] =
230                 curChallenge;
231             inDecKeyChall->numChallenges++;
232         }
233     }
234     if (rv != SECSuccess) {
235         goto loser;
236     }
237     PORT_ArenaUnmark(poolp, mark);
238     PORT_FreeArena(genNamePool, PR_FALSE);
239     return SECSuccess;
240 
241 loser:
242     PORT_ArenaRelease(poolp, mark);
243     if (genNamePool != NULL) {
244         PORT_FreeArena(genNamePool, PR_FALSE);
245     }
246     PORT_Assert(rv != SECSuccess);
247     return rv;
248 }
249 
250 SECStatus
CMMF_DestroyPOPODecKeyRespContent(CMMFPOPODecKeyRespContent * inDecKeyResp)251 CMMF_DestroyPOPODecKeyRespContent(CMMFPOPODecKeyRespContent *inDecKeyResp)
252 {
253     PORT_Assert(inDecKeyResp != NULL);
254     if (inDecKeyResp != NULL && inDecKeyResp->poolp != NULL) {
255         PORT_FreeArena(inDecKeyResp->poolp, PR_FALSE);
256     }
257     return SECSuccess;
258 }
259 
260 int
CMMF_POPODecKeyRespContentGetNumResponses(CMMFPOPODecKeyRespContent * inRespCont)261 CMMF_POPODecKeyRespContentGetNumResponses(CMMFPOPODecKeyRespContent *inRespCont)
262 {
263     int numResponses = 0;
264 
265     PORT_Assert(inRespCont != NULL);
266     if (inRespCont == NULL) {
267         return 0;
268     }
269 
270     while (inRespCont->responses[numResponses] != NULL) {
271         numResponses++;
272     }
273     return numResponses;
274 }
275 
276 SECStatus
CMMF_POPODecKeyRespContentGetResponse(CMMFPOPODecKeyRespContent * inRespCont,int inIndex,long * inDest)277 CMMF_POPODecKeyRespContentGetResponse(CMMFPOPODecKeyRespContent *inRespCont,
278                                       int inIndex,
279                                       long *inDest)
280 {
281     PORT_Assert(inRespCont != NULL);
282 
283     if (inRespCont == NULL || inIndex < 0 ||
284         inIndex >= CMMF_POPODecKeyRespContentGetNumResponses(inRespCont)) {
285         return SECFailure;
286     }
287     *inDest = DER_GetInteger(inRespCont->responses[inIndex]);
288     return (*inDest == -1) ? SECFailure : SECSuccess;
289 }
290