Skip to content

Commit

Permalink
sha256: add optional support for kernel crypto API
Browse files Browse the repository at this point in the history
Add the possibility to enable support for the Linux kernel crypto API,
which allows to perform the SHA256 operations exploiting the
facilities exposed by the kernel, including hardware accelerators.

Signed-off-by: Francesco Valla <[email protected]>
  • Loading branch information
WallaceIT committed Feb 5, 2025
1 parent 7d91820 commit 8ace8f8
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 8ace8f8

Please sign in to comment.