Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
402 changes: 255 additions & 147 deletions bun.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ PODS:
- glog
- hermes-engine
- NitroModules
- OpenSSL-Universal
- OpenSSL-Universal (= 3.3.3001)
- RCT-Folly (= 2024.10.14.00)
- RCTRequired
- RCTTypeSafety
Expand Down Expand Up @@ -1976,7 +1976,7 @@ SPEC CHECKSUMS:
hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11
NitroModules: a93d85b07601390249d7816b59d95afc6317f09d
OpenSSL-Universal: 6082b0bf950e5636fe0d78def171184e2b3899c2
QuickCrypto: 3ae297b5c14685bb03a8b2c6a1daef8093feba76
QuickCrypto: 77dcfb0671e4997d53b89a7d36b42b11d0f8c2c8
RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17
RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83
RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716
Expand Down
41 changes: 41 additions & 0 deletions example/src/tests/hash/hash_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
createHash,
getHashes,
type Encoding,
keccak256,
} from 'react-native-quick-crypto';
import { expect } from 'chai';
import { test } from '../util';
Expand All @@ -33,6 +34,46 @@ test(SUITE, 'createHash with null algorithm', () => {
}).to.throw(/Algorithm must be a non-empty string/);
});

test(SUITE, 'check openssl version', () => {
expect(() => {
// Create a hash to trigger OpenSSL initialization
const hash = createHash('sha256');

// Get OpenSSL version directly from the hash object
const version = hash.getOpenSSLVersion();
console.log('OpenSSL Version:', version);
}).to.not.throw();
});

test(SUITE, 'keccak256 function using provider-aware API', () => {
// Test with a simple string
const result1 = keccak256('test');
expect(result1.toString('hex')).to.equal(
'9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658',
);

// Test with empty string
const result2 = keccak256('');
expect(result2.toString('hex')).to.equal(
'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470',
);

// Test with Buffer
const result3 = keccak256(Buffer.from('hello world'));
expect(result3.toString('hex')).to.equal(
'47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad',
);

// Verify the result is 32 bytes (256 bits)
expect(result1.length).to.equal(32);
expect(result2.length).to.equal(32);
expect(result3.length).to.equal(32);

// Test that it's different from SHA3-256 (they should be different)
const sha3Hash = createHash('SHA3-256').update('test').digest();
expect(result1.toString('hex')).to.not.equal(sha3Hash.toString('hex'));
});

// test hashing
const a0 = createHash('md5').update('Test123').digest('latin1');
const a1 = createHash('sha1').update('Test123').digest('hex');
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-quick-crypto/QuickCrypto.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,6 @@ Pod::Spec.new do |s|

s.dependency "React-jsi"
s.dependency "React-callinvoker"
s.dependency "OpenSSL-Universal"
s.dependency "OpenSSL-Universal", "3.3.3001"
install_modules_dependencies(s)
end
41 changes: 41 additions & 0 deletions packages/react-native-quick-crypto/cpp/hash/HybridHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <memory>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/opensslv.h>
#include <optional>
#include <string>
#include <vector>
Expand Down Expand Up @@ -148,4 +149,44 @@ void HybridHash::setParams() {
}
}

std::string HybridHash::getOpenSSLVersion() {
return OpenSSL_version(OPENSSL_VERSION);
}

std::shared_ptr<ArrayBuffer> HybridHash::keccak256(const std::shared_ptr<ArrayBuffer>& data) {
// 1. Obtain the Keccak-256 message-digest implementation from any loaded provider.
const EVP_MD* md = EVP_MD_fetch(nullptr, "KECCAK-256", nullptr);
if (!md) {
throw std::runtime_error("KECCAK-256 digest not available in the current OpenSSL build (provider not loaded?)");
}

// 2. Create and initialise a digest context.
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
if (!ctx) {
throw std::runtime_error("Failed to allocate EVP_MD_CTX");
}
auto ctx_guard = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(ctx, &EVP_MD_CTX_free);

if (EVP_DigestInit_ex(ctx, md, nullptr) != 1) {
throw std::runtime_error("Failed to initialise KECCAK-256 digest");
}

// 3. Feed the data.
if (EVP_DigestUpdate(ctx, data->data(), data->size()) != 1) {
throw std::runtime_error("Failed to update KECCAK-256 digest");
}

// 4. Finalise and collect the output.
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int out_len = 0;
if (EVP_DigestFinal_ex(ctx, hash, &out_len) != 1) {
throw std::runtime_error("Failed to finalise KECCAK-256 digest");
}

// 5. Move the result into a managed ArrayBuffer.
unsigned char* out_buf = new unsigned char[out_len];
std::memcpy(out_buf, hash, out_len);
return std::make_shared<NativeArrayBuffer>(out_buf, out_len, [out_buf]() { delete[] out_buf; });
}

} // namespace margelo::nitro::crypto
2 changes: 2 additions & 0 deletions packages/react-native-quick-crypto/cpp/hash/HybridHash.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class HybridHash : public HybridHashSpec {
std::shared_ptr<ArrayBuffer> digest(const std::optional<std::string>& encoding = std::nullopt) override;
std::shared_ptr<margelo::nitro::crypto::HybridHashSpec> copy(const std::optional<double> outputLength) override;
std::vector<std::string> getSupportedHashAlgorithms() override;
std::string getOpenSSLVersion() override;
std::shared_ptr<ArrayBuffer> keccak256(const std::shared_ptr<ArrayBuffer>& data) override;

private:
// Methods
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions packages/react-native-quick-crypto/src/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@ class HashUtils {
public static getSupportedHashAlgorithms(): string[] {
return this.native.getSupportedHashAlgorithms();
}
public static keccak256(data: BinaryLike): Buffer {
const nativeDigest = this.native.keccak256(
binaryLikeToArrayBuffer(data, 'utf8'),
);
return Buffer.from(nativeDigest);
}
}

export function getHashes() {
return HashUtils.getSupportedHashAlgorithms();
}

export function keccak256(data: BinaryLike): Buffer {
return HashUtils.keccak256(data);
}

interface HashOptions extends TransformOptions {
/**
* For XOF hash functions such as `shake256`, the
Expand Down Expand Up @@ -157,6 +167,27 @@ class Hash extends Stream.Transform {
return hash;
}

/**
* Returns the OpenSSL version string
* @since v1.0.0
*/
getOpenSSLVersion(): string {
return this.native.getOpenSSLVersion();
}

/**
* Computes KECCAK-256 hash of the provided data using OpenSSL's provider-aware API
* @since v1.0.0
* @param data The data to hash
* @returns Buffer containing the KECCAK-256 hash
*/
keccak256(data: BinaryLike): Buffer {
const nativeDigest = this.native.keccak256(
binaryLikeToArrayBuffer(data, 'utf8'),
);
return Buffer.from(nativeDigest);
}

// stream interface
_transform(
chunk: BinaryLike,
Expand Down Expand Up @@ -205,4 +236,5 @@ export function createHash(algorithm: string, options?: HashOptions): Hash {
export const hashExports = {
createHash,
getHashes,
keccak256,
};
2 changes: 2 additions & 0 deletions packages/react-native-quick-crypto/src/specs/hash.nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ export interface Hash extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
digest(encoding?: string): ArrayBuffer;
copy(outputLength?: number): Hash;
getSupportedHashAlgorithms(): string[];
getOpenSSLVersion(): string;
keccak256(data: ArrayBuffer): ArrayBuffer;
}
Loading