2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

sha1: Use implementation from openssl if available.

Implementation of SHA1 in OpenSSL library is much faster and optimized
for all available CPU architectures and instruction sets.  OVS should
use it instead of internal implementation if possible.

Depending on compiler options OpenSSL's version finishes our sha1
unit tests from 3 to 12 times faster.  Performance of OpenSSL's
version is constant, but OVS's implementation highly depends on
compiler.  Interestingly, default build with  '-g -O2' works faster
than optimized '-march=native -Ofast'.

Tests with ovsdb-server on big databases shows ~5-10% improvement of
the time needed for database compaction (sha1 is only a part of this
operation), depending on compiler options.

We still need internal implementation, because OpenSSL can be not
available on some platforms.  Tests enhanced to check both versions
of API.

Reviewed-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
Ilya Maximets 2022-05-07 00:55:21 +02:00
parent 7b3a4c2e86
commit e8f557df33
5 changed files with 193 additions and 63 deletions

View File

@ -31,12 +31,131 @@
#include <config.h>
#include "sha1.h"
#ifdef HAVE_OPENSSL
#include <openssl/err.h>
#include <openssl/evp.h>
#endif
#include <ctype.h>
#include <string.h>
#include "compiler.h"
#include "openvswitch/vlog.h"
#include "util.h"
/* a bit faster & bigger, if defined */
VLOG_DEFINE_THIS_MODULE(sha1);
#ifdef HAVE_OPENSSL
static void
log_openssl_err(const char *func)
{
char buf[1024];
ERR_error_string_n(ERR_get_error(), buf, 1024);
VLOG_FATAL("%s failed: %s", func, buf);
}
#endif
/*
* Initialize the SHA digest.
* context: The SHA context to initialize
*/
void
sha1_init(struct sha1_ctx *sha_info)
{
#ifdef HAVE_OPENSSL
sha_info->ctx = EVP_MD_CTX_create();
if (!EVP_DigestInit_ex(sha_info->ctx, EVP_sha1(), NULL)) {
log_openssl_err("EVP_DigestInit_ex");
}
#else
ovs_sha1_init(sha_info);
#endif
}
/*
* Update the SHA digest.
* context: The SHA1 context to update.
* input: The buffer to add to the SHA digest.
* inputLen: The length of the input buffer.
*/
void
sha1_update(struct sha1_ctx *ctx, const void *buffer_, uint32_t count)
{
#ifdef HAVE_OPENSSL
if (!EVP_DigestUpdate(ctx->ctx, buffer_, count)) {
log_openssl_err("EVP_DigestUpdate");
}
#else
ovs_sha1_update(ctx, buffer_, count);
#endif
}
/*
* Finish computing the SHA digest.
* digest: the output buffer in which to store the digest.
* context: The context to finalize.
*/
void
sha1_final(struct sha1_ctx *ctx, uint8_t digest[SHA1_DIGEST_SIZE])
{
#ifdef HAVE_OPENSSL
unsigned int len;
if (!EVP_DigestFinal_ex(ctx->ctx, digest, &len)) {
log_openssl_err("EVP_DigestFinal_ex");
}
ovs_assert(len == SHA1_DIGEST_SIZE);
EVP_MD_CTX_destroy(ctx->ctx);
#else
ovs_sha1_final(ctx, digest);
#endif
}
/* Computes the hash of 'n' bytes in 'data' into 'digest'. */
void
sha1_bytes(const void *data, uint32_t n, uint8_t digest[SHA1_DIGEST_SIZE])
{
struct sha1_ctx ctx;
sha1_init(&ctx);
sha1_update(&ctx, data, n);
sha1_final(&ctx, digest);
}
void
sha1_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE],
char hex[SHA1_HEX_DIGEST_LEN + 1])
{
int i;
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
*hex++ = "0123456789abcdef"[digest[i] >> 4];
*hex++ = "0123456789abcdef"[digest[i] & 15];
}
*hex = '\0';
}
bool
sha1_from_hex(uint8_t digest[SHA1_DIGEST_SIZE], const char *hex)
{
int i;
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
bool ok;
digest[i] = hexits_value(hex, 2, &ok);
if (!ok) {
return false;
}
hex += 2;
}
return true;
}
/* Generic implementation for the case where OpenSSL is not available. */
/* A bit faster & bigger, if defined */
#define UNROLL_LOOPS
/* SHA f()-functions */
@ -178,7 +297,7 @@ maybe_byte_reverse(uint32_t *buffer OVS_UNUSED, int count OVS_UNUSED)
* context: The SHA context to initialize
*/
void
sha1_init(struct sha1_ctx *sha_info)
ovs_sha1_init(struct sha1_ctx *sha_info)
{
sha_info->digest[0] = 0x67452301L;
sha_info->digest[1] = 0xefcdab89L;
@ -197,7 +316,7 @@ sha1_init(struct sha1_ctx *sha_info)
* inputLen: The length of the input buffer.
*/
void
sha1_update(struct sha1_ctx *ctx, const void *buffer_, uint32_t count)
ovs_sha1_update(struct sha1_ctx *ctx, const void *buffer_, uint32_t count)
{
const uint8_t *buffer = buffer_;
unsigned int i;
@ -240,7 +359,7 @@ sha1_update(struct sha1_ctx *ctx, const void *buffer_, uint32_t count)
* context: The context to finalize.
*/
void
sha1_final(struct sha1_ctx *ctx, uint8_t digest[SHA1_DIGEST_SIZE])
ovs_sha1_final(struct sha1_ctx *ctx, uint8_t digest[SHA1_DIGEST_SIZE])
{
int count, i, j;
uint32_t lo_bit_count, hi_bit_count, k;
@ -274,42 +393,11 @@ sha1_final(struct sha1_ctx *ctx, uint8_t digest[SHA1_DIGEST_SIZE])
/* Computes the hash of 'n' bytes in 'data' into 'digest'. */
void
sha1_bytes(const void *data, uint32_t n, uint8_t digest[SHA1_DIGEST_SIZE])
ovs_sha1_bytes(const void *data, uint32_t n, uint8_t digest[SHA1_DIGEST_SIZE])
{
struct sha1_ctx ctx;
sha1_init(&ctx);
sha1_update(&ctx, data, n);
sha1_final(&ctx, digest);
ovs_sha1_init(&ctx);
ovs_sha1_update(&ctx, data, n);
ovs_sha1_final(&ctx, digest);
}
void
sha1_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE],
char hex[SHA1_HEX_DIGEST_LEN + 1])
{
int i;
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
*hex++ = "0123456789abcdef"[digest[i] >> 4];
*hex++ = "0123456789abcdef"[digest[i] & 15];
}
*hex = '\0';
}
bool
sha1_from_hex(uint8_t digest[SHA1_DIGEST_SIZE], const char *hex)
{
int i;
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
bool ok;
digest[i] = hexits_value(hex, 2, &ok);
if (!ok) {
return false;
}
hex += 2;
}
return true;
}

View File

@ -33,15 +33,26 @@
#include <stddef.h>
#include <stdint.h>
#ifdef HAVE_OPENSSL
#include <openssl/evp.h>
#endif
#define SHA1_DIGEST_SIZE 20 /* Size of the SHA1 digest. */
#define SHA1_HEX_DIGEST_LEN 40 /* Length of SHA1 digest as hex in ASCII. */
/* SHA1 context structure. */
struct sha1_ctx {
uint32_t digest[5]; /* Message digest. */
uint32_t count_lo, count_hi; /* 64-bit bit counts. */
uint32_t data[16]; /* SHA data buffer */
int local; /* Unprocessed amount in data. */
union {
#ifdef HAVE_OPENSSL
EVP_MD_CTX *ctx; /* OpenSSL context. */
#endif
struct {
uint32_t digest[5]; /* Message digest. */
uint32_t count_lo, count_hi; /* 64-bit bit counts. */
uint32_t data[16]; /* SHA data buffer */
int local; /* Unprocessed amount in data. */
};
};
};
void sha1_init(struct sha1_ctx *);
@ -63,4 +74,12 @@ void sha1_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE],
char hex[SHA1_HEX_DIGEST_LEN + 1]);
bool sha1_from_hex(uint8_t digest[SHA1_DIGEST_SIZE], const char *hex);
/* Generic implementation for the case where OpenSSL is not available.
* This API should not be used directly. Exposed for unit testing. */
void ovs_sha1_init(struct sha1_ctx *);
void ovs_sha1_update(struct sha1_ctx *, const void *, uint32_t size);
void ovs_sha1_final(struct sha1_ctx *, uint8_t digest[SHA1_DIGEST_SIZE]);
void ovs_sha1_bytes(const void *, uint32_t size,
uint8_t digest[SHA1_DIGEST_SIZE]);
#endif /* sha1.h */

View File

@ -400,6 +400,7 @@ parse_body(struct ovsdb_log *file, off_t offset, unsigned long int length,
chunk = MIN(length, sizeof input);
if (fread(input, 1, chunk, file->stream) != chunk) {
sha1_final(&ctx, sha1);
json_parser_abort(parser);
return ovsdb_io_error(ferror(file->stream) ? errno : EOF,
"%s: error reading %lu bytes "

View File

@ -54,7 +54,7 @@ AT_CLEANUP
AT_SETUP([SHA-1])
AT_KEYWORDS([sha1])
AT_CHECK([ovstest test-sha1], [0], [..........
AT_CHECK([ovstest test-sha1], [0], [....................
])
AT_CLEANUP

View File

@ -32,6 +32,14 @@ struct test_vector {
const uint8_t output[20];
};
struct test_api {
void (*sha1_init)(struct sha1_ctx *);
void (*sha1_update)(struct sha1_ctx *, const void *, uint32_t size);
void (*sha1_final)(struct sha1_ctx *, uint8_t digest[SHA1_DIGEST_SIZE]);
void (*sha1_bytes)(const void *, uint32_t size,
uint8_t digest[SHA1_DIGEST_SIZE]);
};
static const struct test_vector vectors[] = {
/* FIPS 180-1. */
{
@ -92,13 +100,13 @@ static const struct test_vector vectors[] = {
};
static void
test_one(const struct test_vector *vec)
test_one(const struct test_api *api, const struct test_vector *vec)
{
uint8_t md[SHA1_DIGEST_SIZE];
int i;
/* All at once. */
sha1_bytes(vec->data, vec->size, md);
api->sha1_bytes(vec->data, vec->size, md);
assert(!memcmp(md, vec->output, SHA1_DIGEST_SIZE));
/* In two pieces. */
@ -107,10 +115,10 @@ test_one(const struct test_vector *vec)
int n1 = vec->size - n0;
struct sha1_ctx sha1;
sha1_init(&sha1);
sha1_update(&sha1, vec->data, n0);
sha1_update(&sha1, vec->data + n0, n1);
sha1_final(&sha1, md);
api->sha1_init(&sha1);
api->sha1_update(&sha1, vec->data, n0);
api->sha1_update(&sha1, vec->data + n0, n1);
api->sha1_final(&sha1, md);
assert(!memcmp(md, vec->output, SHA1_DIGEST_SIZE));
}
@ -119,7 +127,7 @@ test_one(const struct test_vector *vec)
}
static void
test_big_vector(void)
test_big_vector(const struct test_api *api)
{
enum { SIZE = 1000000 };
struct test_vector vec = {
@ -133,12 +141,12 @@ test_big_vector(void)
for (i = 0; i < SIZE; i++) {
vec.data[i] = 'a';
}
test_one(&vec);
test_one(api, &vec);
free(vec.data);
}
static void
test_huge_vector(void)
test_huge_vector(const struct test_api *api)
{
enum { SIZE = 1000000000 };
struct test_vector vec = {
@ -159,13 +167,13 @@ test_huge_vector(void)
vec.data[i] = 'a';
}
sha1_init(&sha1);
api->sha1_init(&sha1);
for (sz = 0; sz < SIZE; sz += chunk) {
int n = sz + chunk < SIZE ? chunk : SIZE - sz;
sha1_update(&sha1, vec.data, n);
api->sha1_update(&sha1, vec.data, n);
}
sha1_final(&sha1, md);
api->sha1_final(&sha1, md);
ovs_assert(!memcmp(md, vec.output, SHA1_DIGEST_SIZE));
free(vec.data);
@ -176,15 +184,29 @@ test_huge_vector(void)
static void
test_shar1_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
int i;
struct test_api api[] = {
{
.sha1_init = sha1_init,
.sha1_update = sha1_update,
.sha1_final = sha1_final,
.sha1_bytes = sha1_bytes,
},
{
.sha1_init = ovs_sha1_init,
.sha1_update = ovs_sha1_update,
.sha1_final = ovs_sha1_final,
.sha1_bytes = ovs_sha1_bytes,
},
};
for (i = 0; i < ARRAY_SIZE(vectors); i++) {
test_one(&vectors[i]);
for (int i = 0; i < ARRAY_SIZE(api); i++) {
for (int j = 0; j < ARRAY_SIZE(vectors); j++) {
test_one(&api[i], &vectors[j]);
}
test_big_vector(&api[i]);
test_huge_vector(&api[i]);
}
test_big_vector();
test_huge_vector();
putchar('\n');
}