Commit 9b2f1269 authored by jan.koester's avatar jan.koester
Browse files

test

parent d6c7bb58
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line

list(APPEND netplussrc
    exception.cpp
    connection.cpp
    ssl.cpp
    quick.cpp
    crypto/sha.cpp
    crypto/base64.cpp
    crypto/des.cpp
    crypto/aes.cpp
@@ -12,6 +9,12 @@ list(APPEND netplussrc
    crypto/x509.cpp
    crypto/ecc_u256.cpp
    crypto/ecc_p256.cpp
    crypto/hkdf.cpp
    crypto/rsa_pss_sha256.cpp
    exception.cpp
    connection.cpp
    ssl.cpp
    quick.cpp
)

if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows")
@@ -57,6 +60,9 @@ set(headers
    crypto/x509.h
    crypto/ecc_u256.h
    crypto/ecc_p256.h
    crypto/hkdf.h
    crypto/rsa_pss_sha256.h
    crypto/sha.h
)

add_library(netplus SHARED ${netplussrc} ${headers})
+176 −1
Original line number Diff line number Diff line
@@ -382,5 +382,180 @@ std::vector<uint8_t> aes::decryptCBC(const std::vector<uint8_t>& ciphertext,
    return out;
}

} // namespace netplus
// ---------- helpers ----------
static inline void xor_block_inplace(uint8_t out[16], const uint8_t b[16]) {
    for (int i=0;i<16;i++) out[i] ^= b[i];
}

static inline void store_be64(uint8_t out[8], uint64_t x) {
    for (int i=0;i<8;i++) out[i] = (uint8_t)(x >> (56 - 8*i));
}

// multiply in GF(2^128) with poly 0xE1
static void gcm_mul(uint8_t X[16], const uint8_t H[16]) {
    uint8_t Z[16] = {0};
    uint8_t V[16];
    std::memcpy(V, H, 16);

    for (int i = 0; i < 128; i++) {
        int xi = (X[i/8] >> (7 - (i%8))) & 1;
        if (xi) xor_block_inplace(Z, V);

        // shift V right by 1
        uint8_t lsb = V[15] & 1;
        for (int j = 15; j > 0; j--) {
            V[j] = (uint8_t)((V[j] >> 1) | ((V[j-1] & 1) << 7));
        }
        V[0] >>= 1;

        if (lsb) V[0] ^= 0xE1;
    }
    std::memcpy(X, Z, 16);
}

static void ghash_update(uint8_t Y[16], const uint8_t H[16], const uint8_t* data, size_t len) {
    while (len >= 16) {
        for (int i=0;i<16;i++) Y[i] ^= data[i];
        gcm_mul(Y, H);
        data += 16;
        len -= 16;
    }
    if (len > 0) {
        uint8_t last[16] = {0};
        std::memcpy(last, data, len);
        for (int i=0;i<16;i++) Y[i] ^= last[i];
        gcm_mul(Y, H);
    }
}

static void ghash(uint8_t out[16], const uint8_t H[16],
                  const uint8_t* aad, size_t aad_len,
                  const uint8_t* ct, size_t ct_len)
{
    uint8_t Y[16] = {0};

    ghash_update(Y, H, aad, aad_len);
    ghash_update(Y, H, ct, ct_len);

    uint8_t lenblk[16];
    store_be64(lenblk,     (uint64_t)aad_len * 8);
    store_be64(lenblk + 8, (uint64_t)ct_len  * 8);

    for (int i=0;i<16;i++) Y[i] ^= lenblk[i];
    gcm_mul(Y, H);

    std::memcpy(out, Y, 16);
}

static inline void inc32(uint8_t ctr[16]) {
    uint32_t x = (uint32_t(ctr[12]) << 24) | (uint32_t(ctr[13]) << 16) |
                 (uint32_t(ctr[14]) << 8)  | (uint32_t(ctr[15]));
    x++;
    ctr[12] = (uint8_t)(x >> 24);
    ctr[13] = (uint8_t)(x >> 16);
    ctr[14] = (uint8_t)(x >> 8);
    ctr[15] = (uint8_t)(x);
}

static inline uint8_t ct_memeq_mask(const uint8_t* a, const uint8_t* b, size_t n) {
    uint8_t diff = 0;
    for (size_t i=0;i<n;i++) diff |= (a[i] ^ b[i]);
    return diff; // 0 if equal
}

// encrypt one AES block using existing aes::encrypt()
static inline void aes_encrypt_block(netplus::aes& A, const uint8_t in[16], uint8_t out[16]) {
    block128 pt(in, in + 16);
    block128 ct = A.encrypt(pt);
    std::memcpy(out, ct.data(), 16);
}

// ---------- MEMBER GCM IMPLEMENTATION ----------
bool aes::aes_gcm_encrypt(
    const uint8_t iv12[12],
    const uint8_t* aad, size_t aad_len,
    const uint8_t* pt, size_t pt_len,
    uint8_t* ct,
    uint8_t tag16[16]
){
    // H = AES_k(0^128)
    uint8_t H[16] = {0};
    aes_encrypt_block(*this, H, H);

    // J0 = IV || 0x00000001 (TLS uses 96-bit IV)
    uint8_t J0[16] = {0};
    std::memcpy(J0, iv12, 12);
    J0[15] = 1;

    // CTR keystream
    uint8_t ctr[16];
    std::memcpy(ctr, J0, 16);

    size_t off = 0;
    while (off < pt_len) {
        inc32(ctr);
        uint8_t S[16];
        aes_encrypt_block(*this, ctr, S);

        size_t take = (pt_len - off < 16) ? (pt_len - off) : 16;
        for (size_t i=0;i<take;i++) ct[off+i] = pt[off+i] ^ S[i];
        off += take;
    }

    // GHASH
    uint8_t Sgh[16];
    ghash(Sgh, H, aad, aad_len, ct, pt_len);

    // Tag = AES_k(J0) XOR GHASH
    uint8_t EJ0[16];
    aes_encrypt_block(*this, J0, EJ0);
    for (int i=0;i<16;i++) tag16[i] = EJ0[i] ^ Sgh[i];

    return true;
}

bool aes::aes_gcm_decrypt(
    const uint8_t iv12[12],
    const uint8_t* aad, size_t aad_len,
    const uint8_t* ct, size_t ct_len,
    const uint8_t tag16[16],
    uint8_t* pt
){
    uint8_t H[16] = {0};
    aes_encrypt_block(*this, H, H);

    uint8_t J0[16] = {0};
    std::memcpy(J0, iv12, 12);
    J0[15] = 1;

    // expected tag
    uint8_t Sgh[16];
    ghash(Sgh, H, aad, aad_len, ct, ct_len);

    uint8_t EJ0[16];
    aes_encrypt_block(*this, J0, EJ0);

    uint8_t expTag[16];
    for (int i=0;i<16;i++) expTag[i] = EJ0[i] ^ Sgh[i];

    if (ct_memeq_mask(expTag, tag16, 16) != 0) return false;

    // CTR decrypt
    uint8_t ctr[16];
    std::memcpy(ctr, J0, 16);

    size_t off = 0;
    while (off < ct_len) {
        inc32(ctr);
        uint8_t S[16];
        aes_encrypt_block(*this, ctr, S);

        size_t take = (ct_len - off < 16) ? (ct_len - off) : 16;
        for (size_t i=0;i<take;i++) pt[off+i] = ct[off+i] ^ S[i];
        off += take;
    }

    return true;
}

};
+17 −0
Original line number Diff line number Diff line
@@ -89,6 +89,23 @@ public:

    std::vector<uint8_t> decryptCBC(const std::vector<uint8_t>& ciphertext,
                                    const std::vector<uint8_t>& iv);

    bool aes_gcm_encrypt(
        const uint8_t iv12[12],
        const uint8_t* aad, size_t aad_len,
        const uint8_t* pt, size_t pt_len,
        uint8_t* ct,
        uint8_t tag16[16]
    );

    bool aes_gcm_decrypt(
        const uint8_t iv12[12],
        const uint8_t* aad, size_t aad_len,
        const uint8_t* ct, size_t ct_len,
        const uint8_t tag16[16],
        uint8_t* pt
    );

};

} // namespace netplus

src/crypto/hkdf.cpp

0 → 100644
+107 −0
Original line number Diff line number Diff line
#include "hkdf.h"
#include <stdexcept>
#include <cstring>

namespace netplus {

HKDF_SHA256::HKDF_SHA256(Sha256Func sha256, HmacFunc hmac)
    : _sha256(std::move(sha256)), _hmac(std::move(hmac))
{
    if (!_sha256 || !_hmac)
        throw std::runtime_error("HKDF_SHA256: callbacks missing");
}

std::vector<uint8_t> HKDF_SHA256::extract(const std::vector<uint8_t>& salt,
                                          const std::vector<uint8_t>& ikm) const
{
    // RFC 5869: PRK = HMAC(salt, IKM)
    return _hmac(salt, ikm);
}

std::vector<uint8_t> HKDF_SHA256::expand(const std::vector<uint8_t>& prk,
                                         const std::vector<uint8_t>& info,
                                         size_t outLen) const
{
    // RFC 5869 Expand
    if (outLen == 0) return {};

    const size_t hashLen = 32;
    if (outLen > 255 * hashLen)
        throw std::runtime_error("HKDF expand: outLen too large");

    std::vector<uint8_t> okm;
    okm.reserve(outLen);

    std::vector<uint8_t> t;
    uint8_t counter = 1;

    while (okm.size() < outLen) {
        std::vector<uint8_t> in;
        in.reserve(t.size() + info.size() + 1);

        in.insert(in.end(), t.begin(), t.end());
        in.insert(in.end(), info.begin(), info.end());
        in.push_back(counter);

        t = _hmac(prk, in); // 32 bytes

        size_t need = std::min(t.size(), outLen - okm.size());
        okm.insert(okm.end(), t.begin(), t.begin() + need);

        counter++;
        if (counter == 0)
            throw std::runtime_error("HKDF expand: counter overflow");
    }

    return okm;
}

std::vector<uint8_t> HKDF_SHA256::expand_label(const std::vector<uint8_t>& secret,
                                               const std::string& label,
                                               const std::vector<uint8_t>& context,
                                               size_t outLen) const
{
    // TLS 1.3 HKDF-Expand-Label:
    //
    // struct {
    //   uint16 length = outLen;
    //   opaque label<7..255> = "tls13 " + label;
    //   opaque context<0..255> = context;
    // } HkdfLabel;
    //
    // return HKDF-Expand(secret, HkdfLabel, outLen)

    std::string fullLabel = "tls13 " + label;
    if (fullLabel.size() > 255)
        throw std::runtime_error("HKDF expand_label: label too long");
    if (context.size() > 255)
        throw std::runtime_error("HKDF expand_label: context too long");
    if (outLen > 0xFFFF)
        throw std::runtime_error("HKDF expand_label: outLen too big");

    std::vector<uint8_t> info;
    info.reserve(2 + 1 + fullLabel.size() + 1 + context.size());

    // length
    info.push_back(uint8_t((outLen >> 8) & 0xFF));
    info.push_back(uint8_t(outLen & 0xFF));

    // label
    info.push_back(uint8_t(fullLabel.size()));
    info.insert(info.end(), fullLabel.begin(), fullLabel.end());

    // context
    info.push_back(uint8_t(context.size()));
    info.insert(info.end(), context.begin(), context.end());

    return expand(secret, info, outLen);
}

std::vector<uint8_t> HKDF_SHA256::hash_empty() const
{
    std::vector<uint8_t> empty;
    return _sha256(empty);
}

} // namespace netplus

src/crypto/hkdf.h

0 → 100644
+45 −0
Original line number Diff line number Diff line
#pragma once
#include <vector>
#include <string>
#include <functional>
#include <cstdint>

namespace netplus {

class HKDF_SHA256 {
public:
    // Callback types:
    // sha256(data) -> 32 bytes
    using Sha256Func = std::function<std::vector<uint8_t>(const std::vector<uint8_t>&)>;

    // hmac(key,data) -> 32 bytes
    using HmacFunc   = std::function<std::vector<uint8_t>(const std::vector<uint8_t>&,
                                                          const std::vector<uint8_t>&)>;

    HKDF_SHA256(Sha256Func sha256, HmacFunc hmac);

    // HKDF-Extract(salt, IKM) -> PRK
    std::vector<uint8_t> extract(const std::vector<uint8_t>& salt,
                                 const std::vector<uint8_t>& ikm) const;

    // HKDF-Expand(PRK, info, L) -> OKM
    std::vector<uint8_t> expand(const std::vector<uint8_t>& prk,
                                const std::vector<uint8_t>& info,
                                size_t outLen) const;

    // TLS 1.3 HKDF-Expand-Label
    // HKDF-Expand-Label(secret, label, context, outLen)
    std::vector<uint8_t> expand_label(const std::vector<uint8_t>& secret,
                                      const std::string& label,
                                      const std::vector<uint8_t>& context,
                                      size_t outLen) const;

    // Utility: SHA256 over empty string (used in TLS 1.3 "derived")
    std::vector<uint8_t> hash_empty() const;

private:
    Sha256Func _sha256;
    HmacFunc   _hmac;
};

} // namespace netplus
Loading