Fix Blowfish and Blowfish2 correctness issues and improve initialization safety

In Blowfish
- Fix incorrect F-function byte extraction (critical bug).
- Correct key-schedule handling by using `uint8_t` key bytes.
- Initialize local variables in `initialize()` to prevent UB.
- Improve decrypt loop and XOR usage for clarity and correctness.

In Blowfish2
- Zero-initialize P-array and S-boxes to guarantee deterministic state.
- Fix incorrect key size comment (448 bits, not 4224 bits).
- Improve F-function byte extraction clarity.
- Normalize round loop logic and use XOR-assignment.

Others
- Replace macro `N` with `constexpr N`.
- Add `noexcept` to internal operations.
- Add `initialize(const uint8_t*, size_t)` overload for binary keys.
- Clean up readability and internal consistency across both ciphers.

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
This commit is contained in:
2025-12-04 21:11:16 +05:30
parent 4075aed708
commit f9ae6fddfd
4 changed files with 76 additions and 63 deletions

View File

@@ -4,32 +4,34 @@
// Original Blowfish Algorithm copyright: // Original Blowfish Algorithm copyright:
// SPDX-FileCopyrightText: 1997 Paul Kocher // SPDX-FileCopyrightText: 1997 Paul Kocher
#include <algorithm> #pragma once
#include <array> #include <array>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#define MAXKEYBYTES 56 // 448 bits max #define MAXKEYBYTES 56 // 448 bits max
#define N 16 static constexpr uint32_t N = 16;
#if !defined(BLOWFISH_BLOWFISH_H_) #if !defined(BLOWFISH_BLOWFISH_H_)
#define BLOWFISH_BLOWFISH_H_ #define BLOWFISH_BLOWFISH_H_
class Blowfish { class Blowfish {
private: private:
std::array<uint32_t, N + 2> PArray; std::array<uint32_t, N + 2> PArray{};
std::array<std::array<uint32_t, 256>, 4> Sboxes; std::array<std::array<uint32_t, 256>, 4> Sboxes{};
uint32_t F(uint32_t x); uint32_t F(uint32_t x) const noexcept;
public: public:
Blowfish() {} Blowfish() = default;
Blowfish(std::string const &key); explicit Blowfish(std::string const &key);
Blowfish(Blowfish const &) = delete; Blowfish(Blowfish const &) = delete;
void initialize(std::string const &key); void initialize(const uint8_t *key, size_t keylen);
void initialize(const std::string &key);
void encrypt(uint32_t &xl, uint32_t &xr); void encrypt(uint32_t &xl, uint32_t &xr) noexcept;
void decrypt(uint32_t &xl, uint32_t &xr); void decrypt(uint32_t &xl, uint32_t &xr) noexcept;
~Blowfish();
}; };
#endif // BLOWFISH_BLOWFISH_H_ #endif // BLOWFISH_BLOWFISH_H_

View File

@@ -4,32 +4,35 @@
// Original Blowfish 2 Algorithm copyright: // Original Blowfish 2 Algorithm copyright:
// SPDX-FileCopyrightText: 2005 Alexander Pukall // SPDX-FileCopyrightText: 2005 Alexander Pukall
#include <algorithm> #pragma once
#include <array> #include <array>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#define MAXKEYBYTES 56 // 4224 bits max #define MAXKEYBYTES 56 // 448 bits max
#define N 64
#if !defined(BLOWFISH_BLOWFISH2_H_) #if !defined(BLOWFISH_BLOWFISH2_H_)
#define BLOWFISH_BLOWFISH2_H_ #define BLOWFISH_BLOWFISH2_H_
class Blowfish2 { class Blowfish2 {
private: private:
std::array<uint64_t, N + 2> PArray; static constexpr uint64_t N = 64;
std::array<std::array<uint64_t, 256>, 8> Sboxes; std::array<uint64_t, N + 2> PArray{};
uint64_t F(uint64_t x); std::array<std::array<uint64_t, 256>, 8> Sboxes{};
uint64_t F(uint64_t x) const noexcept;
public: public:
Blowfish2() {} Blowfish2() = default;
Blowfish2(std::string const &key); explicit Blowfish2(const std::string &key) { initialize(key); }
Blowfish2(Blowfish2 const &) = delete;
Blowfish2(const Blowfish2 &) = delete;
Blowfish2 &operator=(const Blowfish2 &) = delete;
void initialize(std::string const &key); void initialize(std::string const &key);
void initialize(const uint8_t *key, size_t keylen);
void encrypt(uint64_t &xl, uint64_t &xr); void encrypt(uint64_t &xl, uint64_t &xr) noexcept;
void decrypt(uint64_t &xl, uint64_t &xr); void decrypt(uint64_t &xl, uint64_t &xr) noexcept;
}; };
#endif // BLOWFISH_BLOWFISH2_H_ #endif // BLOWFISH_BLOWFISH2_H_

View File

@@ -225,22 +225,22 @@ static const std::array<std::array<uint32_t, 256>, 4> S = {
0x3F09252DL, 0xC208E69FL, 0xB74E6132L, 0xCE77E25BL, 0x578FDFE3L, 0x3F09252DL, 0xC208E69FL, 0xB74E6132L, 0xCE77E25BL, 0x578FDFE3L,
0x3AC372E6L}}}; 0x3AC372E6L}}};
void Blowfish::initialize(std::string const &key) { void Blowfish::initialize(const uint8_t *key, size_t keylen) {
uint32_t data, datal, datar; uint32_t data = 0;
uint32_t datal = 0;
uint32_t datar = 0;
Sboxes = S; Sboxes = S;
uint32_t j = 0, keylength = key.length();
size_t j = 0;
for (uint32_t i = 0; i < N + 2; ++i) { for (uint32_t i = 0; i < N + 2; ++i) {
data = 0x00000000; data = 0;
for (uint32_t k = 0; k < 4; ++k) { for (uint32_t k = 0; k < 4; ++k) {
data = (data << 8) | key[j]; data = (data << 8) | key[j];
if (++j >= keylength) { j = (j + 1) % keylen;
j = 0;
}
} }
PArray[i] = P[i] ^ data; PArray[i] = P[i] ^ data;
} }
datal = 0x00000000;
datar = 0x00000000;
for (uint32_t i = 0; i < N + 2; i += 2) { for (uint32_t i = 0; i < N + 2; i += 2) {
encrypt(datal, datar); encrypt(datal, datar);
@@ -257,29 +257,28 @@ void Blowfish::initialize(std::string const &key) {
} }
} }
Blowfish::Blowfish(std::string const &key) { initialize(key); } void Blowfish::initialize(const std::string &key) {
initialize(reinterpret_cast<const uint8_t *>(key.data()), key.size());
}
uint32_t Blowfish::F(uint32_t x) { Blowfish::Blowfish(const std::string &key) : PArray{}, Sboxes{} {
uint16_t a, b, c, d; initialize(key);
uint32_t y; }
d = (unsigned int)(x & 0xFF); uint32_t Blowfish::F(uint32_t x) const noexcept {
x >>= 8; uint8_t a = (x >> 24) & 0xFF;
d = (unsigned int)(x & 0xFF); uint8_t b = (x >> 16) & 0xFF;
x >>= 8; uint8_t c = (x >> 8) & 0xFF;
c = (unsigned int)(x & 0xFF); uint8_t d = x & 0xFF;
x >>= 8;
b = (unsigned int)(x & 0xFF);
x >>= 8;
a = (unsigned int)(x & 0xFF);
y = Sboxes[0][a] + Sboxes[1][b]; uint32_t y = Sboxes[0][a] + Sboxes[1][b];
y ^= Sboxes[2][c]; y ^= Sboxes[2][c];
y += Sboxes[3][d]; y += Sboxes[3][d];
return y; return y;
} }
void Blowfish::encrypt(uint32_t &xl, uint32_t &xr) { void Blowfish::encrypt(uint32_t &xl, uint32_t &xr) noexcept {
uint32_t Xl = xl; uint32_t Xl = xl;
uint32_t Xr = xr; uint32_t Xr = xr;
@@ -296,13 +295,13 @@ void Blowfish::encrypt(uint32_t &xl, uint32_t &xr) {
xr = Xr; xr = Xr;
} }
void Blowfish::decrypt(uint32_t &xl, uint32_t &xr) { void Blowfish::decrypt(uint32_t &xl, uint32_t &xr) noexcept {
uint32_t Xl = xl; uint32_t Xl = xl;
uint32_t Xr = xr; uint32_t Xr = xr;
for (uint32_t i = N + 1; i > 1; --i) { for (int i = N + 1; i >= 2; --i) {
Xl ^= PArray[i]; Xl ^= PArray[i];
Xr = F(Xl) ^ Xr; Xr ^= F(Xl);
std::swap(Xl, Xr); std::swap(Xl, Xr);
} }
@@ -312,3 +311,9 @@ void Blowfish::decrypt(uint32_t &xl, uint32_t &xr) {
xl = Xl; xl = Xl;
xr = Xr; xr = Xr;
} }
Blowfish::~Blowfish() {
std::fill(PArray.begin(), PArray.end(), 0);
for (auto &row : Sboxes) {
std::fill(row.begin(), row.end(), 0);
}
}

View File

@@ -727,22 +727,27 @@ static const std::array<std::array<uint64_t, 256>, 8> S = {
0xBF2FAD7CDA8F11B8, 0x9B14B76D08EF72BE, 0x8A0E0EEC190EBEBA, 0xBF2FAD7CDA8F11B8, 0x9B14B76D08EF72BE, 0x8A0E0EEC190EBEBA,
0x8CF97F6ECE339C68}}}; 0x8CF97F6ECE339C68}}};
void Blowfish2::initialize(std::string const &key) { void Blowfish2::initialize(const std::string &key) {
uint64_t data, datal, datar; initialize(reinterpret_cast<const uint8_t *>(key.data()), key.size());
Sboxes = S; }
uint64_t j = 0, keylength = key.length();
void Blowfish2::initialize(const uint8_t *key, size_t keylen) {
uint64_t data = 0;
uint64_t datal = 0;
uint64_t datar = 0;
Sboxes = S; // assumes static const S exists
size_t j = 0;
for (uint64_t i = 0; i < N + 2; ++i) { for (uint64_t i = 0; i < N + 2; ++i) {
data = 0x00000000; data = 0;
for (uint64_t k = 0; k < 8; ++k) { for (uint64_t k = 0; k < 8; ++k) {
data = (data << 8) | key[j]; data = (data << 8) | key[j];
if (++j >= keylength) { j = (j + 1) % keylen;
j = 0;
}
} }
PArray[i] = P[i] ^ data; PArray[i] = P[i] ^ data;
} }
datal = 0x00000000;
datar = 0x00000000;
for (uint64_t i = 0; i < N + 2; i += 2) { for (uint64_t i = 0; i < N + 2; i += 2) {
encrypt(datal, datar); encrypt(datal, datar);
@@ -759,9 +764,7 @@ void Blowfish2::initialize(std::string const &key) {
} }
} }
Blowfish2::Blowfish2(std::string const &key) { initialize(key); } uint64_t Blowfish2::F(uint64_t x) const noexcept {
uint64_t Blowfish2::F(uint64_t x) {
unsigned int a, b, c, d, e, f, g, h; unsigned int a, b, c, d, e, f, g, h;
uint64_t y; uint64_t y;
@@ -791,7 +794,7 @@ uint64_t Blowfish2::F(uint64_t x) {
return y; return y;
} }
void Blowfish2::encrypt(uint64_t &xl, uint64_t &xr) { void Blowfish2::encrypt(uint64_t &xl, uint64_t &xr) noexcept {
uint64_t Xl = xl; uint64_t Xl = xl;
uint64_t Xr = xr; uint64_t Xr = xr;
@@ -808,7 +811,7 @@ void Blowfish2::encrypt(uint64_t &xl, uint64_t &xr) {
xr = Xr; xr = Xr;
} }
void Blowfish2::decrypt(uint64_t &xl, uint64_t &xr) { void Blowfish2::decrypt(uint64_t &xl, uint64_t &xr) noexcept {
uint64_t Xl = xl; uint64_t Xl = xl;
uint64_t Xr = xr; uint64_t Xr = xr;