| // Copyright 2014 The BoringSSL Authors | |
| // | |
| // 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. | |
| // This implementation was taken from the public domain, neon2 version in | |
| // SUPERCOP by D. J. Bernstein and Peter Schwabe. | |
| #include <openssl/poly1305.h> | |
| #include <assert.h> | |
| #include <string.h> | |
| #include "../internal.h" | |
| #include "internal.h" | |
| BSSL_NAMESPACE_BEGIN | |
| #if defined(OPENSSL_POLY1305_NEON) | |
| #define addmulmod openssl_poly1305_neon2_addmulmod | |
| #define blocks openssl_poly1305_neon2_blocks | |
| static void freeze(fe1305x2 *r) { | |
| int i; | |
| uint32_t x0 = r->v[0]; | |
| uint32_t x1 = r->v[2]; | |
| uint32_t x2 = r->v[4]; | |
| uint32_t x3 = r->v[6]; | |
| uint32_t x4 = r->v[8]; | |
| uint32_t y0; | |
| uint32_t y1; | |
| uint32_t y2; | |
| uint32_t y3; | |
| uint32_t y4; | |
| uint32_t swap; | |
| for (i = 0; i < 3; ++i) { | |
| x1 += x0 >> 26; | |
| x0 &= 0x3ffffff; | |
| x2 += x1 >> 26; | |
| x1 &= 0x3ffffff; | |
| x3 += x2 >> 26; | |
| x2 &= 0x3ffffff; | |
| x4 += x3 >> 26; | |
| x3 &= 0x3ffffff; | |
| x0 += 5 * (x4 >> 26); | |
| x4 &= 0x3ffffff; | |
| } | |
| y0 = x0 + 5; | |
| y1 = x1 + (y0 >> 26); | |
| y0 &= 0x3ffffff; | |
| y2 = x2 + (y1 >> 26); | |
| y1 &= 0x3ffffff; | |
| y3 = x3 + (y2 >> 26); | |
| y2 &= 0x3ffffff; | |
| y4 = x4 + (y3 >> 26); | |
| y3 &= 0x3ffffff; | |
| swap = -(y4 >> 26); | |
| y4 &= 0x3ffffff; | |
| y0 ^= x0; | |
| y1 ^= x1; | |
| y2 ^= x2; | |
| y3 ^= x3; | |
| y4 ^= x4; | |
| y0 &= swap; | |
| y1 &= swap; | |
| y2 &= swap; | |
| y3 &= swap; | |
| y4 &= swap; | |
| y0 ^= x0; | |
| y1 ^= x1; | |
| y2 ^= x2; | |
| y3 ^= x3; | |
| y4 ^= x4; | |
| r->v[0] = y0; | |
| r->v[2] = y1; | |
| r->v[4] = y2; | |
| r->v[6] = y3; | |
| r->v[8] = y4; | |
| } | |
| static void fe1305x2_tobytearray(uint8_t r[16], fe1305x2 *x) { | |
| uint32_t x0 = x->v[0]; | |
| uint32_t x1 = x->v[2]; | |
| uint32_t x2 = x->v[4]; | |
| uint32_t x3 = x->v[6]; | |
| uint32_t x4 = x->v[8]; | |
| x1 += x0 >> 26; | |
| x0 &= 0x3ffffff; | |
| x2 += x1 >> 26; | |
| x1 &= 0x3ffffff; | |
| x3 += x2 >> 26; | |
| x2 &= 0x3ffffff; | |
| x4 += x3 >> 26; | |
| x3 &= 0x3ffffff; | |
| CRYPTO_store_u32_le(r, x0 + (x1 << 26)); | |
| CRYPTO_store_u32_le(r + 4, (x1 >> 6) + (x2 << 20)); | |
| CRYPTO_store_u32_le(r + 8, (x2 >> 12) + (x3 << 14)); | |
| CRYPTO_store_u32_le(r + 12, (x3 >> 18) + (x4 << 8)); | |
| } | |
| static void fe1305x2_frombytearray(fe1305x2 *r, const uint8_t *x, size_t xlen) { | |
| size_t i; | |
| uint8_t t[17]; | |
| for (i = 0; (i < 16) && (i < xlen); i++) { | |
| t[i] = x[i]; | |
| } | |
| xlen -= i; | |
| x += i; | |
| t[i++] = 1; | |
| for (; i < 17; i++) { | |
| t[i] = 0; | |
| } | |
| r->v[0] = 0x3ffffff & CRYPTO_load_u32_le(t); | |
| r->v[2] = 0x3ffffff & (CRYPTO_load_u32_le(t + 3) >> 2); | |
| r->v[4] = 0x3ffffff & (CRYPTO_load_u32_le(t + 6) >> 4); | |
| r->v[6] = 0x3ffffff & (CRYPTO_load_u32_le(t + 9) >> 6); | |
| r->v[8] = CRYPTO_load_u32_le(t + 13); | |
| if (xlen) { | |
| for (i = 0; (i < 16) && (i < xlen); i++) { | |
| t[i] = x[i]; | |
| } | |
| t[i++] = 1; | |
| for (; i < 17; i++) { | |
| t[i] = 0; | |
| } | |
| r->v[1] = 0x3ffffff & CRYPTO_load_u32_le(t); | |
| r->v[3] = 0x3ffffff & (CRYPTO_load_u32_le(t + 3) >> 2); | |
| r->v[5] = 0x3ffffff & (CRYPTO_load_u32_le(t + 6) >> 4); | |
| r->v[7] = 0x3ffffff & (CRYPTO_load_u32_le(t + 9) >> 6); | |
| r->v[9] = CRYPTO_load_u32_le(t + 13); | |
| } else { | |
| r->v[1] = r->v[3] = r->v[5] = r->v[7] = r->v[9] = 0; | |
| } | |
| } | |
| static const fe1305x2 zero = {0}; | |
| struct poly1305_state_st { | |
| fe1305x2 r, h, c, precomp[2]; | |
| uint8_t buf[32]; | |
| size_t buf_used; | |
| uint8_t key[16]; | |
| }; | |
| static_assert( | |
| sizeof(struct poly1305_state_st) + 63 <= sizeof(poly1305_state), | |
| "poly1305_state isn't large enough to hold aligned poly1305_state_st."); | |
| static poly1305_state_st *poly1305_aligned_state(poly1305_state *state) { | |
| return reinterpret_cast<poly1305_state_st *>(align_pointer(state, 64)); | |
| } | |
| void CRYPTO_poly1305_init_neon(poly1305_state *state, const uint8_t key[32]) { | |
| poly1305_state_st *st = poly1305_aligned_state(state); | |
| fe1305x2 *const r = &st->r; | |
| fe1305x2 *const h = &st->h; | |
| fe1305x2 *const precomp = st->precomp; | |
| r->v[1] = r->v[0] = 0x3ffffff & CRYPTO_load_u32_le(key); | |
| r->v[3] = r->v[2] = 0x3ffff03 & (CRYPTO_load_u32_le(key + 3) >> 2); | |
| r->v[5] = r->v[4] = 0x3ffc0ff & (CRYPTO_load_u32_le(key + 6) >> 4); | |
| r->v[7] = r->v[6] = 0x3f03fff & (CRYPTO_load_u32_le(key + 9) >> 6); | |
| r->v[9] = r->v[8] = 0x00fffff & (CRYPTO_load_u32_le(key + 12) >> 8); | |
| for (size_t j = 0; j < 10; j++) { | |
| h->v[j] = 0; // XXX: should fast-forward a bit | |
| } | |
| addmulmod(precomp, r, r, &zero); // precompute r^2 | |
| addmulmod(precomp + 1, precomp, precomp, &zero); // precompute r^4 | |
| OPENSSL_memcpy(st->key, key + 16, 16); | |
| st->buf_used = 0; | |
| } | |
| void CRYPTO_poly1305_update_neon(poly1305_state *state, const uint8_t *in, | |
| size_t in_len) { | |
| poly1305_state_st *st = poly1305_aligned_state(state); | |
| fe1305x2 *const h = &st->h; | |
| fe1305x2 *const c = &st->c; | |
| fe1305x2 *const precomp = st->precomp; | |
| if (st->buf_used) { | |
| size_t todo = 32 - st->buf_used; | |
| if (todo > in_len) { | |
| todo = in_len; | |
| } | |
| for (size_t i = 0; i < todo; i++) { | |
| st->buf[st->buf_used + i] = in[i]; | |
| } | |
| st->buf_used += todo; | |
| in_len -= todo; | |
| in += todo; | |
| if (st->buf_used == sizeof(st->buf) && in_len) { | |
| addmulmod(h, h, precomp, &zero); | |
| fe1305x2_frombytearray(c, st->buf, sizeof(st->buf)); | |
| for (size_t i = 0; i < 10; i++) { | |
| h->v[i] += c->v[i]; | |
| } | |
| st->buf_used = 0; | |
| } | |
| } | |
| while (in_len > 32) { | |
| size_t tlen = 1048576; | |
| if (in_len < tlen) { | |
| tlen = in_len; | |
| } | |
| tlen -= blocks(h, precomp, in, tlen); | |
| in_len -= tlen; | |
| in += tlen; | |
| } | |
| if (in_len) { | |
| for (size_t i = 0; i < in_len; i++) { | |
| st->buf[i] = in[i]; | |
| } | |
| st->buf_used = in_len; | |
| } | |
| } | |
| void CRYPTO_poly1305_finish_neon(poly1305_state *state, uint8_t mac[16]) { | |
| poly1305_state_st *st = poly1305_aligned_state(state); | |
| fe1305x2 *const r = &st->r; | |
| fe1305x2 *const h = &st->h; | |
| fe1305x2 *const c = &st->c; | |
| fe1305x2 *const precomp = st->precomp; | |
| addmulmod(h, h, precomp, &zero); | |
| if (st->buf_used > 16) { | |
| fe1305x2_frombytearray(c, st->buf, st->buf_used); | |
| precomp->v[1] = r->v[1]; | |
| precomp->v[3] = r->v[3]; | |
| precomp->v[5] = r->v[5]; | |
| precomp->v[7] = r->v[7]; | |
| precomp->v[9] = r->v[9]; | |
| addmulmod(h, h, precomp, c); | |
| } else if (st->buf_used > 0) { | |
| fe1305x2_frombytearray(c, st->buf, st->buf_used); | |
| r->v[1] = 1; | |
| r->v[3] = 0; | |
| r->v[5] = 0; | |
| r->v[7] = 0; | |
| r->v[9] = 0; | |
| addmulmod(h, h, r, c); | |
| } | |
| h->v[0] += h->v[1]; | |
| h->v[2] += h->v[3]; | |
| h->v[4] += h->v[5]; | |
| h->v[6] += h->v[7]; | |
| h->v[8] += h->v[9]; | |
| freeze(h); | |
| fe1305x2_frombytearray(c, st->key, 16); | |
| c->v[8] ^= (1 << 24); | |
| h->v[0] += c->v[0]; | |
| h->v[2] += c->v[2]; | |
| h->v[4] += c->v[4]; | |
| h->v[6] += c->v[6]; | |
| h->v[8] += c->v[8]; | |
| fe1305x2_tobytearray(mac, h); | |
| } | |
| #endif // OPENSSL_POLY1305_NEON | |
| BSSL_NAMESPACE_END |