| // Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. | |
| // Copyright (c) 2012, Intel Corporation. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| // Originally written by Shay Gueron (1, 2), and Vlad Krasnov (1) | |
| // (1) Intel Corporation, Israel Development Center, Haifa, Israel | |
| // (2) University of Haifa, Israel | |
| #include "rsaz_exp.h" | |
| #if defined(RSAZ_ENABLED) | |
| #include <openssl/mem.h> | |
| #include <assert.h> | |
| #include "internal.h" | |
| #include "../../internal.h" | |
| using namespace bssl; | |
| // rsaz_one is 1 in RSAZ's representation. | |
| alignas(64) static const BN_ULONG rsaz_one[40] = { | |
| 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |
| // rsaz_two80 is 2^80 in RSAZ's representation. Note RSAZ uses base 2^29, so this is | |
| // 2^(29*2 + 22) = 2^80, not 2^(64*2 + 22). | |
| alignas(64) static const BN_ULONG rsaz_two80[40] = { | |
| 0, 0, 1 << 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |
| void bssl::RSAZ_1024_mod_exp_avx2(BN_ULONG result_norm[16], | |
| const BN_ULONG base_norm[16], | |
| const BN_ULONG exponent[16], | |
| const BN_ULONG m_norm[16], | |
| const BN_ULONG RR[16], BN_ULONG k0, | |
| BN_ULONG storage[MOD_EXP_CTIME_STORAGE_LEN]) { | |
| static_assert(MOD_EXP_CTIME_ALIGN % 64 == 0, | |
| "MOD_EXP_CTIME_ALIGN is too small"); | |
| assert((uintptr_t)storage % 64 == 0); | |
| BN_ULONG *a_inv, *m, *result, *table_s = storage + 40 * 3, *R2 = table_s; | |
| // Note `R2` aliases `table_s`. | |
| if (((((uintptr_t)storage & 4095) + 320) >> 12) != 0) { | |
| result = storage; | |
| a_inv = storage + 40; | |
| m = storage + 40 * 2; // should not cross page | |
| } else { | |
| m = storage; // should not cross page | |
| result = storage + 40; | |
| a_inv = storage + 40 * 2; | |
| } | |
| rsaz_1024_norm2red_avx2(m, m_norm); | |
| rsaz_1024_norm2red_avx2(a_inv, base_norm); | |
| rsaz_1024_norm2red_avx2(R2, RR); | |
| // Convert `R2` from the usual radix, giving R = 2^1024, to RSAZ's radix, | |
| // giving R = 2^(36*29) = 2^1044. | |
| rsaz_1024_mul_avx2(R2, R2, R2, m, k0); | |
| // R2 = 2^2048 * 2^2048 / 2^1044 = 2^3052 | |
| rsaz_1024_mul_avx2(R2, R2, rsaz_two80, m, k0); | |
| // R2 = 2^3052 * 2^80 / 2^1044 = 2^2088 = (2^1044)² | |
| // table[0] = 1 | |
| // table[1] = a_inv¹ | |
| rsaz_1024_mul_avx2(result, R2, rsaz_one, m, k0); | |
| rsaz_1024_mul_avx2(a_inv, a_inv, R2, m, k0); | |
| rsaz_1024_scatter5_avx2(table_s, result, 0); | |
| rsaz_1024_scatter5_avx2(table_s, a_inv, 1); | |
| // table[2] = a_inv² | |
| rsaz_1024_sqr_avx2(result, a_inv, m, k0, 1); | |
| rsaz_1024_scatter5_avx2(table_s, result, 2); | |
| // table[4] = a_inv⁴ | |
| rsaz_1024_sqr_avx2(result, result, m, k0, 1); | |
| rsaz_1024_scatter5_avx2(table_s, result, 4); | |
| // table[8] = a_inv⁸ | |
| rsaz_1024_sqr_avx2(result, result, m, k0, 1); | |
| rsaz_1024_scatter5_avx2(table_s, result, 8); | |
| // table[16] = a_inv^16 | |
| rsaz_1024_sqr_avx2(result, result, m, k0, 1); | |
| rsaz_1024_scatter5_avx2(table_s, result, 16); | |
| for (int i = 3; i < 32; i += 2) { | |
| // table[i] = table[i-1] * a_inv = a_invⁱ | |
| rsaz_1024_gather5_avx2(result, table_s, i - 1); | |
| rsaz_1024_mul_avx2(result, result, a_inv, m, k0); | |
| rsaz_1024_scatter5_avx2(table_s, result, i); | |
| for (int j = 2 * i; j < 32; j *= 2) { | |
| // table[j] = table[j/2]^2 = a_inv^j | |
| rsaz_1024_sqr_avx2(result, result, m, k0, 1); | |
| rsaz_1024_scatter5_avx2(table_s, result, j); | |
| } | |
| } | |
| // Load the first window. | |
| const uint8_t *p_str = (const uint8_t *)exponent; | |
| int wvalue = p_str[127] >> 3; | |
| rsaz_1024_gather5_avx2(result, table_s, wvalue); | |
| int index = 1014; | |
| while (index > -1) { // Loop for the remaining 127 windows. | |
| rsaz_1024_sqr_avx2(result, result, m, k0, 5); | |
| uint16_t wvalue_16; | |
| memcpy(&wvalue_16, &p_str[index / 8], sizeof(wvalue_16)); | |
| wvalue = wvalue_16; | |
| wvalue = (wvalue >> (index % 8)) & 31; | |
| index -= 5; | |
| rsaz_1024_gather5_avx2(a_inv, table_s, wvalue); // Borrow `a_inv`. | |
| rsaz_1024_mul_avx2(result, result, a_inv, m, k0); | |
| } | |
| // Square four times. | |
| rsaz_1024_sqr_avx2(result, result, m, k0, 4); | |
| wvalue = p_str[0] & 15; | |
| rsaz_1024_gather5_avx2(a_inv, table_s, wvalue); // Borrow `a_inv`. | |
| rsaz_1024_mul_avx2(result, result, a_inv, m, k0); | |
| // Convert from Montgomery. | |
| rsaz_1024_mul_avx2(result, result, rsaz_one, m, k0); | |
| rsaz_1024_red2norm_avx2(result_norm, result); | |
| BN_ULONG scratch[16]; | |
| bn_reduce_once_in_place(result_norm, /*carry=*/0, m_norm, scratch, 16); | |
| OPENSSL_cleanse(storage, MOD_EXP_CTIME_STORAGE_LEN * sizeof(BN_ULONG)); | |
| } | |
| #endif // RSAZ_ENABLED |