mirror of
https://github.com/avinal/blowfish.git
synced 2026-01-10 23:08:33 +05:30
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:
@@ -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_
|
||||||
|
|||||||
@@ -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_
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user