diff --git a/libi2pd/ChaCha20.cpp b/libi2pd/ChaCha20.cpp new file mode 100644 index 00000000..e43f1514 --- /dev/null +++ b/libi2pd/ChaCha20.cpp @@ -0,0 +1,147 @@ +#include "ChaCha20.h" + +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +namespace i2p +{ +namespace crypto +{ +namespace chacha +{ +constexpr int rounds = 20; +constexpr std::size_t blocksize = 64; + +void u32t8le(uint32_t v, uint8_t * p) +{ + p[0] = v & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + +uint32_t u8t32le(const uint8_t * p) +{ + uint32_t value = p[3]; + + value = (value << 8) | p[2]; + value = (value << 8) | p[1]; + value = (value << 8) | p[0]; + + return value; +} + +uint32_t rotl32(uint32_t x, int n) +{ + return x << n | (x >> (-n & 31)); +} + +void quarterround(uint32_t *x, int a, int b, int c, int d) +{ + x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); + x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); + x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); + x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7); +} + + struct State_t + { + State_t() {}; + State_t(State_t &&) = delete; + + State_t & operator += (const State_t & other) + { + for(int i = 0; i < 16; i++) + data[i] += other.data[i]; + return *this; + } + + void Copy(const State_t & other) + { + memcpy(data, other.data, sizeof(uint32_t) * 16); + } + uint32_t data[16]; + }; + + struct Block_t + { + Block_t() {}; + Block_t(Block_t &&) = delete; + + uint8_t data[blocksize]; + + void operator << (const State_t & st) + { + int i; + for (i = 0; i < 16; i++) + u32t8le(st.data[i], data + (i << 2)); + } + }; + +void block(const State_t &input, Block_t & block, int rounds) +{ + int i; + State_t x; + x.Copy(input); + + for (i = rounds; i > 0; i -= 2) + { + quarterround(x.data, 0, 4, 8, 12); + quarterround(x.data, 1, 5, 9, 13); + quarterround(x.data, 2, 6, 10, 14); + quarterround(x.data, 3, 7, 11, 15); + quarterround(x.data, 0, 5, 10, 15); + quarterround(x.data, 1, 6, 11, 12); + quarterround(x.data, 2, 7, 8, 13); + quarterround(x.data, 3, 4, 9, 14); + } + x += input; + block << x; + +} +} // namespace chacha + + + + + +void chacha20(uint8_t * buf, size_t sz, const uint8_t * nonce, const uint8_t * key, uint32_t counter) +{ + chacha::State_t state; + chacha::Block_t block; + size_t i, j; + + state.data[0] = 0x61707865; + state.data[1] = 0x3320646e; + state.data[2] = 0x79622d32; + state.data[3] = 0x6b206574; + + for (i = 0; i < 8; i++) + state.data[4 + i] = chacha::u8t32le(key + i * 4); + + + state.data[12] = counter; + + for (i = 0; i < 3; i++) + state.data[13 + i] = chacha::u8t32le(nonce + i * 4); + + + for (i = 0; i < sz; i += chacha::blocksize) + { + chacha::block(state, block, chacha::rounds); + state.data[12]++; + for (j = i; j < i + chacha::blocksize; j++) + { + if (j >= sz) break; + buf[j] ^= block.data[j - i]; + } + } + +} + +} +} \ No newline at end of file diff --git a/libi2pd/ChaCha20.h b/libi2pd/ChaCha20.h new file mode 100644 index 00000000..c88325d5 --- /dev/null +++ b/libi2pd/ChaCha20.h @@ -0,0 +1,26 @@ +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +#ifndef LIBI2PD_CHACHA20_H +#define LIBI2PD_CHACHA20_H +#include +#include + +namespace i2p +{ +namespace crypto +{ + const std::size_t CHACHA20_KEY_BYTES = 32; + const std::size_t CHACHA20_NOUNCE_BYTES = 12; + + /** encrypt buf in place with chacha20 */ + void chacha20(uint8_t * buf, size_t sz, const uint8_t * nonce, const uint8_t * key, uint32_t counter=1); + +} +} + +#endif diff --git a/libi2pd/Poly1305.cpp b/libi2pd/Poly1305.cpp new file mode 100644 index 00000000..355555c6 --- /dev/null +++ b/libi2pd/Poly1305.cpp @@ -0,0 +1,303 @@ +#include "Poly1305.h" +#include "CPU.h" +#include +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +namespace i2p +{ +namespace crypto +{ +#if 0 +#ifdef __AVX2__ + struct Poly1305_AVX2 + { + Poly1305_AVX2(const uint32_t *& k) + { + __asm__ + ( + "VMOVNTDQA %[key0], %%ymm0 \n" + "VMOVNTDQA 32%[key0], %%ymm1 \n" + : + : + [key0]"m"(k) + ); + }; + + ~Poly1305_AVX2() + { + // clear out registers + __asm__ + ( + "VZEROALL\n" + ); + } + + void Update(const uint8_t * buf, size_t sz) + { + + } + + void Finish(uint32_t *& out) + { + + } + + + + size_t leftover; + + }; +#endif +#endif + namespace poly1305 + { + + struct LongBlock + { + unsigned long data[17]; + operator unsigned long * () + { + return data; + } + }; + + struct Block + { + unsigned char data[17]; + + operator uint8_t * () + { + return data; + } + + Block & operator += (const Block & other) + { + unsigned short u; + unsigned int i; + for(u = 0, i = 0; i < 17; i++) + { + u += (unsigned short) data[i] + (unsigned short) other.data[i]; + data[i] = (unsigned char) u & 0xff; + u >>= 8; + } + return *this; + } + + Block & operator %=(const LongBlock & other) + { + unsigned long u; + unsigned int i; + u = 0; + for (i = 0; i < 16; i++) { + u += other.data[i]; + data[i] = (unsigned char)u & 0xff; + u >>= 8; + } + u += other.data[16]; + data[16] = (unsigned char)u & 0x03; + u >>= 2; + u += (u << 2); + for (i = 0; i < 16; i++) { + u += data[i]; + data[i] = (unsigned char)u & 0xff; + u >>= 8; + } + data[16] += (unsigned char)u; + return *this; + } + + Block & operator = (const Block & other) + { + memcpy(data, other.data, sizeof(data)); + return *this; + } + + Block & operator ~ () + { + static const Block minusp = { + 0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfc + }; + Block orig; + unsigned char neg; + unsigned int i; + orig = *this; + *this += minusp; + neg = -(data[16] >> 7); + for(i = 0; i < 17; i++) + data[i] ^= neg & (orig.data[i] ^ data[i]); + + return *this; + } + + void PutKey(const uint8_t * key) + { + data[0] = key[0] & 0xff; + data[1] = key[1] & 0xff; + data[2] = key[2] & 0xff; + data[3] = key[3] & 0x0f; + data[4] = key[4] & 0xfc; + data[5] = key[5] & 0xff; + data[6] = key[6] & 0xff; + data[7] = key[7] & 0x0f; + data[8] = key[8] & 0xfc; + data[9] = key[9] & 0xff; + data[10] = key[10] & 0xff; + data[11] = key[11] & 0x0f; + data[12] = key[12] & 0xfc; + data[13] = key[13] & 0xff; + data[14] = key[14] & 0xff; + data[15] = key[15] & 0x0f; + data[16] = 0; + } + + void Put(const uint8_t * d, uint8_t last=0) + { + memcpy(data, d, 17); + data[16] = last; + } + }; + + struct Buffer + { + uint8_t data[POLY1305_BLOCK_BYTES]; + + operator uint8_t * () + { + return data; + } + }; + } + + struct Poly1305 + { + + Poly1305(const uint8_t * key) : m_Leftover(0), m_H{0}, m_Final(0) + { + m_R.PutKey(key); + m_Pad.Put(key + 16); + } + + void Update(const uint8_t * buf, size_t sz) + { + // process leftover + if(m_Leftover) + { + size_t want = POLY1305_BLOCK_BYTES - m_Leftover; + if(want > sz) want = sz; + memcpy(m_Buffer + m_Leftover, buf, want); + sz -= want; + buf += want; + m_Leftover += want; + if(m_Leftover < POLY1305_BLOCK_BYTES) return; + Blocks(m_Buffer, POLY1305_BLOCK_BYTES); + m_Leftover = 0; + } + // process blocks + if(sz >= POLY1305_BLOCK_BYTES) + { + size_t want = (sz & ~(POLY1305_BLOCK_BYTES - 1)); + Blocks(buf, want); + buf += want; + sz -= want; + } + // leftover + if(sz) + { + memcpy(m_Buffer+m_Leftover, buf, sz); + m_Leftover += sz; + } + } + + void Blocks(const uint8_t * buf, size_t sz) + { + const unsigned char hi = m_Final ^ 1; + while (sz >= POLY1305_BLOCK_BYTES) { + + unsigned long u; + + unsigned int i, j; + m_Msg.Put(buf, hi); + /* h += m */ + m_H += m_Msg; + + /* h *= r */ + for (i = 0; i < 17; i++) { + u = 0; + for (j = 0; j <= i ; j++) { + u += (unsigned short)m_H.data[j] * m_R.data[i - j]; + } + for (j = i + 1; j < 17; j++) { + unsigned long v = (unsigned short)m_H.data[j] * m_R.data[i + 17 - j]; + v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */ + u += v; + } + m_HR[i] = u; + } + /* (partial) h %= p */ + m_H %= m_HR; + buf += POLY1305_BLOCK_BYTES; + sz -= POLY1305_BLOCK_BYTES; + } + } + + void Finish(uint32_t *& out) + { + // process leftovers + if(m_Leftover) + { + size_t idx = m_Leftover; + m_Buffer[idx++] = 1; + for(; idx < POLY1305_BLOCK_BYTES; idx++) + m_Buffer[idx] = 0; + m_Final = 1; + Blocks(m_Buffer, POLY1305_BLOCK_BYTES); + } + + // freeze H + ~m_H; + // add pad + m_H += m_Pad; + // copy digest + memcpy(out, m_H, 16); + } + + size_t m_Leftover; + poly1305::Buffer m_Buffer; + poly1305::Block m_H; + poly1305::Block m_R; + poly1305::Block m_Pad; + poly1305::Block m_Msg; + poly1305::LongBlock m_HR; + uint8_t m_Final; + + }; + + void Poly1305HMAC(uint32_t * out, const uint32_t * key, const uint8_t * buf, std::size_t sz) + { + #if 0 + #ifdef __AVX2__ + if(i2p::cpu::avx2) + { + Poly1305_AVX2 p(key); + p.Update(buf, sz); + p.Finish(out); + } + else + #endif + #endif + { + const uint8_t * k = (const uint8_t *) key; + Poly1305 p(k); + p.Update(buf, sz); + p.Finish(out); + } + } +} +} diff --git a/libi2pd/Poly1305.h b/libi2pd/Poly1305.h new file mode 100644 index 00000000..2c62c5aa --- /dev/null +++ b/libi2pd/Poly1305.h @@ -0,0 +1,28 @@ +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +#ifndef LIBI2PD_POLY1305_H +#define LIBI2PD_POLY1305_H +#include +#include + +namespace i2p +{ +namespace crypto +{ + const std::size_t POLY1305_DIGEST_BYTES = 16; + const std::size_t POLY1305_DIGEST_DWORDS = 4; + const std::size_t POLY1305_KEY_BYTES = 32; + const std::size_t POLY1305_KEY_DWORDS = 8; + const std::size_t POLY1305_BLOCK_BYTES = 16; + + void Poly1305HMAC(uint32_t * out, const uint32_t * key, const uint8_t * buf, std::size_t sz); + +} +} + +#endif diff --git a/libi2pd/Siphash.h b/libi2pd/Siphash.h new file mode 100644 index 00000000..3e74c6e9 --- /dev/null +++ b/libi2pd/Siphash.h @@ -0,0 +1,152 @@ +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +#ifndef SIPHASH_H +#define SIPHASH_H + +#include + +namespace i2p +{ +namespace crypto +{ + namespace siphash + { + constexpr int crounds = 2; + constexpr int drounds = 4; + + uint64_t rotl(const uint64_t & x, int b) + { + uint64_t ret = x << b; + ret |= x >> (64 - b); + return ret; + } + + void u32to8le(const uint32_t & v, uint8_t * p) + { + p[0] = (uint8_t) v; + p[1] = (uint8_t) (v >> 8); + p[2] = (uint8_t) (v >> 16); + p[3] = (uint8_t) (v >> 24); + } + + void u64to8le(const uint64_t & v, uint8_t * p) + { + p[0] = v & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; + p[4] = (v >> 32) & 0xff; + p[5] = (v >> 40) & 0xff; + p[6] = (v >> 48) & 0xff; + p[7] = (v >> 56) & 0xff; + } + + uint64_t u8to64le(const uint8_t * p) + { + uint64_t i = 0; + int idx = 0; + while(idx < 8) + { + i |= ((uint64_t) p[idx]) << (idx * 8); + ++idx; + } + return i; + } + + void round(uint64_t & _v0, uint64_t & _v1, uint64_t & _v2, uint64_t & _v3) + { + _v0 += _v1; + _v1 = rotl(_v1, 13); + _v1 ^= _v0; + _v0 = rotl(_v0, 32); + _v2 += _v3; + _v3 = rotl(_v3, 16); + _v3 ^= _v2; + _v0 += _v3; + _v3 = rotl(_v3, 21); + _v3 ^= _v0; + _v2 += _v1; + _v1 = rotl(_v1, 17); + _v1 ^= _v2; + _v2 = rotl(_v2, 32); + } + } + + /** hashsz must be 8 or 16 */ + template + void Siphash(uint8_t * h, const uint8_t * buf, std::size_t bufsz, const uint8_t * key) + { + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + const uint64_t k0 = siphash::u8to64le(key); + const uint64_t k1 = siphash::u8to64le(key + 8); + uint64_t msg; + int i; + const uint8_t * end = buf + bufsz - (bufsz % sizeof(uint64_t)); + auto left = bufsz & 7; + uint64_t b = ((uint64_t)bufsz) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; + + if(hashsz == 16) v1 ^= 0xee; + + while(buf != end) + { + msg = siphash::u8to64le(buf); + v3 ^= msg; + for(i = 0; i < siphash::crounds; ++i) + siphash::round(v0, v1, v2, v3); + + v0 ^= msg; + buf += 8; + } + + while(left) + { + --left; + b |= ((uint64_t)(buf[left])) << (left * 8); + } + + v3 ^= b; + + for(i = 0; i < siphash::crounds; ++i) + siphash::round(v0, v1, v2, v3); + + v0 ^= b; + + + if(hashsz == 16) + v2 ^= 0xee; + else + v2 ^= 0xff; + + for(i = 0; i < siphash::drounds; ++i) + siphash::round(v0, v1, v2, v3); + + b = v0 ^ v1 ^ v2 ^ v3; + + siphash::u64to8le(b, h); + + if(hashsz == 8) return; + + v1 ^= 0xdd; + + for (i = 0; i < siphash::drounds; ++i) + siphash::round(v0, v1, v2, v3); + + b = v0 ^ v1 ^ v2 ^ v3; + siphash::u64to8le(b, h + 8); + } +} +} + +#endif \ No newline at end of file