Skip to content

Commit

Permalink
Merge pull request #20 from WallaceIT/feature/kernel-hasher
Browse files Browse the repository at this point in the history
sha256: add optional support for kernel crypto API
  • Loading branch information
embetrix authored Feb 6, 2025
2 parents d2d1288 + 8ace8f8 commit a99fd82
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 33 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ jobs:
- uses: actions/checkout@v4

- name: Install dependencies
run: sudo apt-get update && sudo apt-get --no-install-recommends install -y bmap-tools libssl-dev libtinyxml2-dev libarchive-dev tar bzip2 gzip lz4 lzop xz-utils zstd python3 wget cppcheck
run: sudo apt-get update && sudo apt-get --no-install-recommends install -y bmap-tools libssl-dev libtinyxml2-dev libarchive-dev libkcapi-dev tar bzip2 gzip lz4 lzop xz-utils zstd python3 wget cppcheck

- name: Cppcheck
run: cppcheck --enable=all --suppress=missingIncludeSystem *.cpp

- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: cmake -B ${{github.workspace}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
run: cmake -B ${{github.workspace}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DUSE_KERNEL_CRYPTO_API=ON

- name: Build
# Build your program with the given configuration
Expand Down
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ else()
message(FATAL_ERROR "libarchive not found")
endif()

option(USE_KERNEL_CRYPTO_API "Use the kernel crypto API to perform the hashing")
if(USE_KERNEL_CRYPTO_API)
list(APPEND CRYPTO_LIBRARIES kcapi)
add_compile_definitions(USE_KERNEL_CRYPTO_API)
endif()

if (NOT DEFINED DEFAULT_READ_BLK_SIZE)
set(DEFAULT_READ_BLK_SIZE 16384)
endif()
Expand All @@ -47,7 +53,7 @@ add_executable(bmap-writer bmap-writer.cpp sha256.cpp)
target_compile_options(bmap-writer PUBLIC -Wformat -Wformat-security -Wconversion -Wsign-conversion -pedantic -Werror)

# Link the libraries
target_link_libraries(bmap-writer ${TINYXML2_LIBRARIES} ${LibArchive_LIBRARIES})
target_link_libraries(bmap-writer ${TINYXML2_LIBRARIES} ${LibArchive_LIBRARIES} ${CRYPTO_LIBRARIES})

# Specify the install rules
install(TARGETS bmap-writer DESTINATION bin)
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Unlike the Yocto BMAP tool, `bmap-writer` is C++ based does not require Python a
- Handles all compression filters that are supported by `libarchive`, decompressing the data on-the-fly during the writing process.
- Ensures data integrity by verifying checksums for each block.
- Writes only the necessary blocks, reducing the overall write time and wear on storage devices.
- Can use the Linux kernel crypto API to leverage hardware-accelerate hashing.

## How It Works

Expand All @@ -28,6 +29,7 @@ Unlike the Yocto BMAP tool, `bmap-writer` is C++ based does not require Python a
- CMake
- Libarchive
- TinyXML-2
- libkcapi (optional)

## Build and Installation

Expand All @@ -37,7 +39,7 @@ Unlike the Yocto BMAP tool, `bmap-writer` is C++ based does not require Python a

```sh
sudo apt-get update
sudo apt-get install -y libarchive-dev libtinyxml2-dev
sudo apt-get install -y libarchive-dev libtinyxml2-dev libkcapi-dev
```

## Build
Expand All @@ -47,6 +49,15 @@ cmake .
make
```

### Enable support for the Linux kernel crypto API

To enable support for the Linux kernel crypto API, which is disabled by default, the `USE_KERNEL_CRYPTO_API` option
shall be set to `ON`:

```sh
cmake -DUSE_KERNEL_CRYPTO_API=ON .
```

## Test

```sh
Expand Down
8 changes: 8 additions & 0 deletions bmap-writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ int checkBmap(const std::string &filename, const std::string& checksum) {
} else {
SHA256Ctx sha256Ctx = {};

if (sha256Init(sha256Ctx) != 0) {
throw std::string("Failed to initalize hasher");
}

while (std::getline(file, line)) {
std::size_t found = line.find(checksum);
// The actual checksum of the BMAP file shall be replaced with a set of '0'
Expand Down Expand Up @@ -364,6 +368,10 @@ int BmapWriteImage(int fd, const bmap_t &bmap, const std::string &device, bool n
size_t readSize = 0;
SHA256Ctx verifySha256Ctx = {};

if (sha256Init(verifySha256Ctx) != 0) {
throw std::string("Failed to initalize hasher");
}

while (readSize < writtenSize) {
size_t bufferSize = (maxBufferSize > 0) ? maxBufferSize : writtenSize;
if (bufferSize > (writtenSize - readSize)) {
Expand Down
113 changes: 85 additions & 28 deletions sha256.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@

#include <sstream>
#include <iomanip>
#include <iostream>
#include <cstring>
#include <cstdint>

#ifdef USE_KERNEL_CRYPTO_API
#include <kcapi.h>
#endif

#include "sha256.h"

// Utility: Right rotate
Expand Down Expand Up @@ -82,44 +87,96 @@ void sha256Transform(SHA256Ctx& context) {
context.h[7] += h;
}

void sha256Update(SHA256Ctx& context, const std::string& data) {
const uint8_t* input = reinterpret_cast<const uint8_t*>(data.data());
size_t length = data.size();

while (length--) {
context.dataBlock[context.dataBlockIndex++] = *input++;
context.bitLength += 8;

if (context.dataBlockIndex == 64) {
sha256Transform(context);
context.dataBlockIndex = 0;
int sha256Init(SHA256Ctx& context) {
if (!context.initialized) {
#ifdef USE_KERNEL_CRYPTO_API
const char *hashname = "sha256";
static bool warned = false;
int ret;

ret = kcapi_md_init(&context.handle, hashname, 0);
if ((ret != 0) && !warned) {
std::cerr << "Failed to init kernel crypto API: " << ret << std::endl;
std::cerr << "Falling back to software hashing" << std::endl;
warned = true;
}
#endif
context.initialized = true;
}
}

std::string sha256Finalize(SHA256Ctx& context) {
context.dataBlock[context.dataBlockIndex++] = 0x80;
return 0;
}

if (context.dataBlockIndex > 56) {
while (context.dataBlockIndex < 64) {
context.dataBlock[context.dataBlockIndex++] = 0x00;
int sha256Update(SHA256Ctx& context, const std::string& data) {
const uint8_t* input = reinterpret_cast<const uint8_t*>(data.data());
size_t length = data.size();
int ret = -1;

if (context.initialized) {
#ifdef USE_KERNEL_CRYPTO_API
if (context.handle != nullptr) {
if (kcapi_md_update(context.handle, input, length) == 0) {
ret = 0;
}
} else
#endif
{
while (length--) {
context.dataBlock[context.dataBlockIndex++] = *input++;
context.bitLength += 8;

if (context.dataBlockIndex == 64) {
sha256Transform(context);
context.dataBlockIndex = 0;
}
}
}
sha256Transform(context);
context.dataBlockIndex = 0;
}

while (context.dataBlockIndex < 56) {
context.dataBlock[context.dataBlockIndex++] = 0x00;
}

uint64_t bitLengthBigEndian = __builtin_bswap64(context.bitLength);
std::memcpy(&context.dataBlock[56], &bitLengthBigEndian, sizeof(bitLengthBigEndian));
sha256Transform(context);
return ret;
}

std::string sha256Finalize(SHA256Ctx& context) {
std::ostringstream output;
output << std::hex << std::setfill('0');
for (uint32_t value : context.h) {
output << std::setw(8) << value;

if (context.initialized) {
#ifdef USE_KERNEL_CRYPTO_API
if (context.handle != nullptr) {
std::array<uint8_t, 64> buf;
ssize_t ret = kcapi_md_final(context.handle, buf.data(), buf.size());
kcapi_md_destroy(context.handle);
context.handle = nullptr;

for (auto i = 0; i < ret; i++) {
output << std::setw(2) << static_cast<uint32_t>(buf.data()[i]);
}
} else
#endif
{
context.dataBlock[context.dataBlockIndex++] = 0x80;

if (context.dataBlockIndex > 56) {
while (context.dataBlockIndex < 64) {
context.dataBlock[context.dataBlockIndex++] = 0x00;
}
sha256Transform(context);
context.dataBlockIndex = 0;
}

while (context.dataBlockIndex < 56) {
context.dataBlock[context.dataBlockIndex++] = 0x00;
}

uint64_t bitLengthBigEndian = __builtin_bswap64(context.bitLength);
std::memcpy(&context.dataBlock[56], &bitLengthBigEndian, sizeof(bitLengthBigEndian));
sha256Transform(context);

for (uint32_t value : context.h) {
output << std::setw(8) << value;
}
}
}

return output.str();
}
11 changes: 10 additions & 1 deletion sha256.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
#include <string>
#include <array>

#ifdef USE_KERNEL_CRYPTO_API
#include <kcapi.h>
#endif

// SHA256 constants
constexpr uint32_t k[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
Expand All @@ -46,10 +50,15 @@ struct SHA256Ctx {
uint64_t bitLength = 0;
std::array<uint8_t, 64> dataBlock{};
size_t dataBlockIndex = 0;
#ifdef USE_KERNEL_CRYPTO_API
struct kcapi_handle *handle = nullptr;
#endif
bool initialized = false;
};

// Public functions
void sha256Update(SHA256Ctx& context, const std::string& data);
int sha256Init(SHA256Ctx& context);
int sha256Update(SHA256Ctx& context, const std::string& data);
std::string sha256Finalize(SHA256Ctx& context);

#endif // SHA256_H

0 comments on commit a99fd82

Please sign in to comment.