1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2010-2020. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 
21 #include "bn.h"
22 
23 
get_bn_from_mpint(ErlNifEnv * env,ERL_NIF_TERM term,BIGNUM ** bnp)24 int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp)
25 {
26     BIGNUM *ret;
27     ErlNifBinary bin;
28     int sz;
29 
30     if (!enif_inspect_binary(env, term, &bin))
31         goto err;
32     if (bin.size > INT_MAX - 4)
33         goto err;
34 
35     if (bin.size < 4)
36         goto err;
37     sz = (int)bin.size - 4;
38     if (get_int32(bin.data) != sz)
39         goto err;
40 
41     if ((ret = BN_bin2bn(bin.data+4, sz, NULL)) == NULL)
42         goto err;
43 
44     *bnp = ret;
45     return 1;
46 
47  err:
48     return 0;
49 }
50 
get_bn_from_bin(ErlNifEnv * env,ERL_NIF_TERM term,BIGNUM ** bnp)51 int get_bn_from_bin(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp)
52 {
53     return get_bn_from_bin_sz(env, term, bnp, NULL);
54 }
55 
get_bn_from_bin_sz(ErlNifEnv * env,ERL_NIF_TERM term,BIGNUM ** bnp,size_t * binsize)56 int get_bn_from_bin_sz(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp, size_t* binsize)
57 {
58     BIGNUM *ret;
59     ErlNifBinary bin;
60 
61     if (!enif_inspect_binary(env, term, &bin))
62         goto err;
63     if (bin.size > INT_MAX)
64         goto err;
65 
66     if ((ret = BN_bin2bn(bin.data, (int)bin.size, NULL)) == NULL)
67         goto err;
68 
69     if (binsize != NULL)
70         *binsize = bin.size;
71     *bnp = ret;
72     return 1;
73 
74  err:
75     return 0;
76 }
77 
bin_from_bn(ErlNifEnv * env,const BIGNUM * bn)78 ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn)
79 {
80     int bn_len;
81     unsigned char *bin_ptr;
82     ERL_NIF_TERM term;
83 
84     /* Copy the bignum into an erlang binary. */
85     if ((bn_len = BN_num_bytes(bn)) < 0)
86         goto err;
87     if ((bin_ptr = enif_make_new_binary(env, (size_t)bn_len, &term)) == NULL)
88         goto err;
89 
90     if (BN_bn2bin(bn, bin_ptr) < 0)
91         goto err;
92 
93     return term;
94 
95  err:
96     return atom_error;
97 }
98 
mod_exp_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])99 ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
100 {/* (Base,Exponent,Modulo,bin_hdr) */
101     BIGNUM *bn_base = NULL, *bn_exponent = NULL, *bn_modulo = NULL, *bn_result = NULL;
102     BN_CTX *bn_ctx = NULL;
103     unsigned char* ptr;
104     int dlen;
105     unsigned bin_hdr; /* return type: 0=plain binary, 4: mpint */
106     unsigned extra_byte;
107     ERL_NIF_TERM ret;
108 
109     if (!get_bn_from_bin(env, argv[0], &bn_base))
110         goto bad_arg;
111     if (!get_bn_from_bin(env, argv[1], &bn_exponent))
112         goto bad_arg;
113     if (!get_bn_from_bin(env, argv[2], &bn_modulo))
114         goto bad_arg;
115     if (!enif_get_uint(env, argv[3], &bin_hdr))
116         goto bad_arg;
117     if (bin_hdr != 0 && bin_hdr != 4)
118         goto bad_arg;
119 
120     if ((bn_result = BN_new()) == NULL)
121         goto err;
122     if ((bn_ctx = BN_CTX_new()) == NULL)
123         goto err;
124 
125     if (!BN_mod_exp(bn_result, bn_base, bn_exponent, bn_modulo, bn_ctx))
126         goto err;
127 
128     dlen = BN_num_bytes(bn_result);
129     if (dlen < 0 || dlen > INT_MAX / 8)
130         goto bad_arg;
131     extra_byte = bin_hdr && BN_is_bit_set(bn_result, dlen * 8 - 1);
132 
133     if ((ptr = enif_make_new_binary(env, bin_hdr + extra_byte + (unsigned int)dlen, &ret)) == NULL)
134         goto err;
135 
136     if (bin_hdr) {
137         put_uint32(ptr, extra_byte + (unsigned int)dlen);
138         ptr[4] = 0; /* extra zeroed byte to ensure a positive mpint */
139         ptr += bin_hdr + extra_byte;
140     }
141 
142     BN_bn2bin(bn_result, ptr);
143     goto done;
144 
145  bad_arg:
146  err:
147     ret = enif_make_badarg(env);
148 
149  done:
150     if (bn_base)
151         BN_free(bn_base);
152     if (bn_exponent)
153         BN_free(bn_exponent);
154     if (bn_modulo)
155         BN_free(bn_modulo);
156     if (bn_result)
157         BN_free(bn_result);
158     if (bn_ctx)
159         BN_CTX_free(bn_ctx);
160     return ret;
161 }
162 
163 #ifdef HAVE_EC
bn2term(ErlNifEnv * env,size_t size,const BIGNUM * bn)164 ERL_NIF_TERM bn2term(ErlNifEnv* env, size_t size, const BIGNUM *bn)
165 {
166     int dlen;
167     unsigned char* ptr;
168     ERL_NIF_TERM ret;
169 
170     if (bn == NULL)
171         return atom_undefined;
172 
173     dlen = BN_num_bytes(bn);
174     if (dlen < 0)
175         goto err;
176     if (dlen > (int)size)
177         goto err;
178     if ((ptr = enif_make_new_binary(env, size, &ret)) == NULL)
179         goto err;
180 
181 #ifdef HAS_BN_bn2binpad
182     BN_bn2binpad(bn, ptr, (int) size);
183 #else
184     /* First, maybe pad with zeroes */
185     memset(ptr, 0, (size-dlen) );
186     BN_bn2bin(bn, ptr + (size-dlen));
187 #endif
188 
189     return ret;
190 
191  err:
192     return enif_make_badarg(env);
193 }
194 #endif
195