1 // SPDX-License-Identifier: BSD-2-Clause
2 /* LibTomCrypt, modular cryptographic library -- Tom St Denis
3  *
4  * LibTomCrypt is a library that provides various cryptographic
5  * algorithms in a highly modular and flexible manner.
6  *
7  * The library is free for all purposes without any express
8  * guarantee it works.
9  */
10 #include "tomcrypt_private.h"
11 
12 /**
13   @file pkcs_1_oaep_decode.c
14   OAEP Padding for PKCS #1, Tom St Denis
15 */
16 
17 #ifdef LTC_PKCS_1
18 
19 /**
20    PKCS #1 v2.00 OAEP decode
21    @param msg              The encoded data to decode
22    @param msglen           The length of the encoded data (octets)
23    @param lparam           The session or system data (can be NULL)
24    @param lparamlen        The length of the lparam
25    @param modulus_bitlen   The bit length of the RSA modulus
26    @param hash_idx         The index of the hash desired
27    @param out              [out] Destination of decoding
28    @param outlen           [in/out] The max size and resulting size of the decoding
29    @param res              [out] Result of decoding, 1==valid, 0==invalid
30    @return CRYPT_OK if successful
31 */
pkcs_1_oaep_decode(const unsigned char * msg,unsigned long msglen,const unsigned char * lparam,unsigned long lparamlen,unsigned long modulus_bitlen,int hash_idx,unsigned char * out,unsigned long * outlen,int * res)32 int pkcs_1_oaep_decode(const unsigned char *msg,    unsigned long msglen,
33                        const unsigned char *lparam, unsigned long lparamlen,
34                              unsigned long modulus_bitlen, int hash_idx,
35                              unsigned char *out,    unsigned long *outlen,
36                              int           *res)
37 {
38    unsigned char *DB, *seed, *mask;
39    unsigned long hLen, x, y, modulus_len;
40    int           err, ret;
41 
42    LTC_ARGCHK(msg    != NULL);
43    LTC_ARGCHK(out    != NULL);
44    LTC_ARGCHK(outlen != NULL);
45    LTC_ARGCHK(res    != NULL);
46 
47    /* default to invalid packet */
48    *res = 0;
49 
50    /* test valid hash */
51    if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
52       return err;
53    }
54    hLen        = hash_descriptor[hash_idx]->hashsize;
55    modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
56 
57    /* test hash/message size */
58    if ((2*hLen >= (modulus_len - 2)) || (msglen != modulus_len)) {
59       return CRYPT_PK_INVALID_SIZE;
60    }
61 
62    /* allocate ram for DB/mask/salt of size modulus_len */
63    DB   = XMALLOC(modulus_len);
64    mask = XMALLOC(modulus_len);
65    seed = XMALLOC(hLen);
66    if (DB == NULL || mask == NULL || seed == NULL) {
67       if (DB != NULL) {
68          XFREE(DB);
69       }
70       if (mask != NULL) {
71          XFREE(mask);
72       }
73       if (seed != NULL) {
74          XFREE(seed);
75       }
76       return CRYPT_MEM;
77    }
78 
79    /* ok so it's now in the form
80 
81       0x00  || maskedseed || maskedDB
82 
83        1    ||   hLen     ||  modulus_len - hLen - 1
84 
85     */
86 
87    ret = CRYPT_OK;
88 
89    /* must have leading 0x00 byte */
90    if (msg[0] != 0x00) {
91       ret = CRYPT_INVALID_PACKET;
92    }
93 
94    /* now read the masked seed */
95    x = 1;
96    XMEMCPY(seed, msg + x, hLen);
97    x += hLen;
98 
99    /* now read the masked DB */
100    XMEMCPY(DB, msg + x, modulus_len - hLen - 1);
101    x += modulus_len - hLen - 1;
102 
103    /* compute MGF1 of maskedDB (hLen) */
104    if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) {
105       goto LBL_ERR;
106    }
107 
108    /* XOR against seed */
109    for (y = 0; y < hLen; y++) {
110       seed[y] ^= mask[y];
111    }
112 
113    /* compute MGF1 of seed (k - hlen - 1) */
114    if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) {
115       goto LBL_ERR;
116    }
117 
118    /* xor against DB */
119    for (y = 0; y < (modulus_len - hLen - 1); y++) {
120        DB[y] ^= mask[y];
121    }
122 
123    /* now DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */
124 
125    /* compute lhash and store it in seed [reuse temps!] */
126    x = modulus_len;
127    if (lparam != NULL) {
128       if ((err = hash_memory(hash_idx, lparam, lparamlen, seed, &x)) != CRYPT_OK) {
129          goto LBL_ERR;
130       }
131    } else {
132       /* can't pass hash_memory a NULL so use DB with zero length */
133       if ((err = hash_memory(hash_idx, DB, 0, seed, &x)) != CRYPT_OK) {
134          goto LBL_ERR;
135       }
136    }
137 
138    /* compare the lhash'es */
139    if (XMEM_NEQ(seed, DB, hLen) != 0) {
140       ret = CRYPT_INVALID_PACKET;
141    }
142 
143    /* now zeroes before a 0x01 */
144    for (x = hLen; x < (modulus_len - hLen - 1) && DB[x] == 0x00; x++) {
145       /* step... */
146    }
147 
148    /* error if wasn't 0x01 */
149    if (x == (modulus_len - hLen - 1) || DB[x] != 0x01) {
150       ret = CRYPT_INVALID_PACKET;
151    }
152 
153    /* rest is the message (and skip 0x01) */
154    if ((modulus_len - hLen - 1 - ++x) > *outlen) {
155       ret = CRYPT_INVALID_PACKET;
156    }
157 
158    if (ret == CRYPT_OK) {
159       /* copy message */
160       *outlen = modulus_len - hLen - 1 - x;
161       XMEMCPY(out, DB + x, modulus_len - hLen - 1 - x);
162 
163       /* valid packet */
164       *res = 1;
165    }
166    err = ret;
167 
168 LBL_ERR:
169 #ifdef LTC_CLEAN_STACK
170    zeromem(DB,   modulus_len);
171    zeromem(seed, hLen);
172    zeromem(mask, modulus_len);
173 #endif
174 
175    XFREE(seed);
176    XFREE(mask);
177    XFREE(DB);
178 
179    return err;
180 }
181 
182 #endif /* LTC_PKCS_1 */
183 
184 /* ref:         $Format:%D$ */
185 /* git commit:  $Format:%H$ */
186 /* commit time: $Format:%ai$ */
187