From 728af0306505f1ff91364ac2175fb6bf5da90ec3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
Date: Thu, 5 Feb 2015 09:41:12 +0200
Subject: [PATCH] engines/e_padlock: implement sha1/sha224/sha256 acceleration

Limited support for VIA C7 that works only when EVP_MD_CTX_FLAG_ONESHOT
is used appropriately (as done by EVP_Digest, and my previous HMAC patch).

Full support for VIA Nano including partial transformation and 64-bit mode.

Benchmarks from VIA Nano 1.6GHz, done with including the previous HMAC and
apps/speed patches done. From single run, error margin of about 100-200k.

No padlock

type         16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
sha1         20057.60k    51514.05k    99721.39k   130167.81k   142811.14k
sha256        7757.72k    16907.18k    28937.05k    35181.23k    37568.51k
hmac(sha1)    8582.53k    27644.69k    70402.30k   114602.67k   140167.85k

With the patch

sha1         37713.77k   114562.71k   259637.33k   379907.41k   438818.13k
sha256       34262.86k   103233.75k   232476.07k   338386.60k   389860.01k
hmac(sha1)    8424.70k    31475.11k   104036.10k   245559.30k   406667.26k
---
 engines/e_padlock.c | 663 ++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 614 insertions(+), 49 deletions(-)

diff --git a/engines/e_padlock.c b/engines/e_padlock.c
index 94406cb..5e99114 100644
--- a/engines/e_padlock.c
+++ b/engines/e_padlock.c
@@ -3,6 +3,9 @@
  * Written by Michal Ludvig <michal@logix.cz>
  *            http://www.logix.cz/michal
  *
+ * SHA support by Timo Teras <timo.teras@iki.fi>. Portions based on
+ * code originally written by Michal Ludvig.
+ *
  * Big thanks to Andy Polyakov for a help with optimization,
  * assembler fixes, port to MS Windows and a lot of other
  * valuable work on this engine!
@@ -63,7 +66,9 @@
  */
 
 #include <stdio.h>
+#include <stdint.h>
 #include <string.h>
+#include <netinet/in.h>
 
 #include <openssl/opensslconf.h>
 #include <openssl/crypto.h>
@@ -73,11 +78,32 @@
 #ifndef OPENSSL_NO_AES
 # include <openssl/aes.h>
 #endif
+#ifndef OPENSSL_NO_SHA
+# include <openssl/sha.h>
+#endif
 #include <openssl/rand.h>
 #include <openssl/err.h>
 
 #ifndef OPENSSL_NO_HW
-# ifndef OPENSSL_NO_HW_PADLOCK
+#  ifndef OPENSSL_NO_HW_PADLOCK
+
+/* PadLock RNG is disabled by default */
+#    define PADLOCK_NO_RNG	1
+
+/* No ASM routines for SHA in MSC yet */
+#    ifdef _MSC_VER
+#      define OPENSSL_NO_SHA
+#    endif
+
+/* 64-bit mode does not need software SHA1 as fallback, we can
+ * do all operations with padlock */
+#    if defined(__x86_64__) || defined(__x86_64)
+#      define PADLOCK_NEED_FALLBACK_SHA 0
+#    else
+#      define PADLOCK_NEED_FALLBACK_SHA 1
+#    endif
+
+#    define PADLOCK_MAX_FINALIZING_LENGTH	0x1FFFFFFE
 
 /* Attempt to have a single source for both 0.9.7 and 0.9.8 :-) */
 #  if (OPENSSL_VERSION_NUMBER >= 0x00908000L)
@@ -151,60 +177,42 @@ void ENGINE_load_padlock(void)
 static int padlock_available(void);
 static int padlock_init(ENGINE *e);
 
+#  ifndef PADLOCK_NO_RNG
 /* RNG Stuff */
 static RAND_METHOD padlock_rand;
-
-/* Cipher Stuff */
-#   ifndef OPENSSL_NO_AES
-static int padlock_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
-                           const int **nids, int nid);
 #   endif
 
 /* Engine names */
 static const char *padlock_id = "padlock";
 static char padlock_name[100];
 
+static int padlock_bind_helper(ENGINE *e);
+
 /* Available features */
-static int padlock_use_ace = 0; /* Advanced Cryptography Engine */
-static int padlock_use_rng = 0; /* Random Number Generator */
+enum padlock_flags {
+    PADLOCK_RNG  = 0x01,
+    PADLOCK_ACE  = 0x02,
+    PADLOCK_ACE2 = 0x04,
+    PADLOCK_PHE  = 0x08,
+    PADLOCK_PMM  = 0x10,
+    PADLOCK_NANO = 0x20,
+};
+enum padlock_flags padlock_flags;
+ 
+#define PADLOCK_HAVE_RNG  (padlock_flags & PADLOCK_RNG)
+#define PADLOCK_HAVE_ACE  (padlock_flags & (PADLOCK_ACE|PADLOCK_ACE2))
+#define PADLOCK_HAVE_ACE1 (padlock_flags & PADLOCK_ACE)
+#define PADLOCK_HAVE_ACE2 (padlock_flags & PADLOCK_ACE2)
+#define PADLOCK_HAVE_PHE  (padlock_flags & PADLOCK_PHE)
+#define PADLOCK_HAVE_PMM  (padlock_flags & PADLOCK_PMM)
+#define PADLOCK_HAVE_NANO (padlock_flags & PADLOCK_NANO)
+
 #   ifndef OPENSSL_NO_AES
 static int padlock_aes_align_required = 1;
 #   endif
 
 /* ===== Engine "management" functions ===== */
 
-/* Prepare the ENGINE structure for registration */
-static int padlock_bind_helper(ENGINE *e)
-{
-    /* Check available features */
-    padlock_available();
-
-#   if 1                        /* disable RNG for now, see commentary in
-                                 * vicinity of RNG code */
-    padlock_use_rng = 0;
-#   endif
-
-    /* Generate a nice engine name with available features */
-    BIO_snprintf(padlock_name, sizeof(padlock_name),
-                 "VIA PadLock (%s, %s)",
-                 padlock_use_rng ? "RNG" : "no-RNG",
-                 padlock_use_ace ? "ACE" : "no-ACE");
-
-    /* Register everything or return with an error */
-    if (!ENGINE_set_id(e, padlock_id) ||
-        !ENGINE_set_name(e, padlock_name) ||
-        !ENGINE_set_init_function(e, padlock_init) ||
-#   ifndef OPENSSL_NO_AES
-        (padlock_use_ace && !ENGINE_set_ciphers(e, padlock_ciphers)) ||
-#   endif
-        (padlock_use_rng && !ENGINE_set_RAND(e, &padlock_rand))) {
-        return 0;
-    }
-
-    /* Everything looks good */
-    return 1;
-}
-
 #   ifdef OPENSSL_NO_DYNAMIC_ENGINE
 
 /* Constructor */
@@ -229,7 +237,7 @@ static ENGINE *ENGINE_padlock(void)
 /* Check availability of the engine */
 static int padlock_init(ENGINE *e)
 {
-    return (padlock_use_rng || padlock_use_ace);
+    return padlock_flags;
 }
 
 /*
@@ -377,10 +385,20 @@ static int padlock_available(void)
                   "=d"(edx)::"ecx");
 
     /* Fill up some flags */
-    padlock_use_ace = ((edx & (0x3 << 6)) == (0x3 << 6));
-    padlock_use_rng = ((edx & (0x3 << 2)) == (0x3 << 2));
-
-    return padlock_use_ace + padlock_use_rng;
+    padlock_flags |= ((edx & (0x3<<3)) ? PADLOCK_RNG : 0);
+    padlock_flags |= ((edx & (0x3<<7)) ? PADLOCK_ACE : 0);
+    padlock_flags |= ((edx & (0x3<<9)) ? PADLOCK_ACE2 : 0);
+    padlock_flags |= ((edx & (0x3<<11)) ? PADLOCK_PHE : 0);
+    padlock_flags |= ((edx & (0x3<<13)) ? PADLOCK_PMM : 0);
+
+    /* Check for VIA Nano CPU */
+    eax = 0x00000001;
+    asm volatile ("pushl %%ebx; cpuid; popl %%ebx"
+                  : "+a"(eax) : : "ecx", "edx");
+    if ((eax | 0x000F) == 0x06FF)
+        padlock_flags |= PADLOCK_NANO;
+
+    return padlock_flags;
 }
 
 /*
@@ -473,10 +491,14 @@ padlock_available(void)
 		: "+a"(eax), "=d"(edx) : : "rbx", "rcx");
 
 	/* Fill up some flags */
-	padlock_use_ace = ((edx & (0x3<<6)) == (0x3<<6));
-	padlock_use_rng = ((edx & (0x3<<2)) == (0x3<<2));
-
-	return padlock_use_ace + padlock_use_rng;
+	padlock_flags |= ((edx & (0x3<<3)) ? PADLOCK_RNG : 0);
+	padlock_flags |= ((edx & (0x3<<7)) ? PADLOCK_ACE : 0);
+	padlock_flags |= ((edx & (0x3<<9)) ? PADLOCK_ACE2 : 0);
+	padlock_flags |= ((edx & (0x3<<11)) ? PADLOCK_PHE : 0);
+	padlock_flags |= ((edx & (0x3<<13)) ? PADLOCK_PMM : 0);
+	padlock_flags |= PADLOCK_NANO;
+
+	return padlock_flags;
 }
 
 /* Force key reload from memory to the CPU microcode.
@@ -1293,6 +1315,496 @@ padlock_aes_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out_arg,
 
 #   endif                       /* OPENSSL_NO_AES */
 
+#ifndef OPENSSL_NO_SHA
+
+static inline void
+padlock_copy_bswap(void *dst, void *src, size_t count)
+{
+	uint32_t *udst = dst, *usrc = src;
+	int i = 0;
+
+	for (i = 0; i < count; i++)
+		udst[i] = htonl(usrc[i]);
+}
+
+static unsigned long padlock_sha_prepare_padding(
+		EVP_MD_CTX *ctx,
+		unsigned char *padding,
+		unsigned char *data, size_t data_len,
+		uint64_t total)
+{
+	unsigned int padding_len;
+
+	padding_len = data_len < 56 ? SHA_CBLOCK : 2 * SHA_CBLOCK;
+	if (data_len)
+		memcpy(padding, data, data_len);
+
+	memset(padding + data_len, 0, padding_len - data_len);
+	padding[data_len] = 0x80;
+	*(uint32_t *)(padding + padding_len - 8) = htonl(total >> 32);
+	*(uint32_t *)(padding + padding_len - 4) = htonl(total & 0xffffffff);
+
+	return data_len < 56 ? 1 : 2;
+}
+
+#define PADLOCK_SHA_ALIGN(dd)	(uint32_t*)(((uintptr_t)(dd) + 15) & ~15)
+#define PADLOCK_SHA_HWCTX	(128+16)
+
+static void
+padlock_sha1(void *hwctx, const void *buf, unsigned long total, unsigned long now)
+{
+	unsigned long pos = total - now;
+
+	asm volatile ("xsha1"
+			: "+S"(buf), "+D"(hwctx), "+a"(pos), "+c"(total)
+			: : "memory");
+}
+
+static void
+padlock_sha1_partial(void *hwctx, const void *buf, unsigned long blocks)
+{
+	asm volatile ("xsha1"
+			: "+S"(buf), "+D"(hwctx), "+c"(blocks)
+			: "a"(-1L) : "memory");
+}
+
+static int padlock_sha1_init(EVP_MD_CTX *ctx)
+{
+	return SHA1_Init(ctx->md_data);
+}
+
+#if PADLOCK_NEED_FALLBACK_SHA
+
+static int padlock_sha1_update_eden(EVP_MD_CTX *ctx, const void *data,
+				    size_t len)
+{
+	unsigned char hwctx[PADLOCK_SHA_HWCTX];
+	uint32_t *aligned = PADLOCK_SHA_ALIGN(hwctx);
+	SHA_CTX *c = ctx->md_data;
+	uint_fast64_t total;
+	const unsigned char *p = data;
+	unsigned long l = 0;
+
+	/* Calculate total length (Nl,Nh) is length in bits */
+	total = (((uint_fast64_t) c->Nh) << 29) + (c->Nl >> 3);
+	total += len;
+
+	if ((ctx->flags & EVP_MD_CTX_FLAG_ONESHOT) &&
+	    (total <= PADLOCK_MAX_FINALIZING_LENGTH)) {
+		if (c->num != 0) {
+			l = (len < SHA_CBLOCK - c->num) ? len : (SHA_CBLOCK - c->num);
+			if (!SHA1_Update(c, data, l))
+				return 0;
+			p += l;
+			if (c->num != 0) {
+				p = (unsigned char *) c->data;
+				len = c->num;
+				l = 0;
+			}
+		}
+		memcpy(aligned, &c->h0, 5 * sizeof(SHA_LONG));
+		padlock_sha1(aligned, p, total, len - l);
+		memcpy(&c->h0, aligned, 5 * sizeof(SHA_LONG));
+		c->num = -1;
+		return 1;
+	}
+
+	return SHA1_Update(c, data, len);
+}
+#endif
+
+static int padlock_sha1_update(EVP_MD_CTX *ctx, const void *data,
+			       size_t len)
+{
+	unsigned char hwctx[PADLOCK_SHA_HWCTX];
+	uint32_t *aligned = PADLOCK_SHA_ALIGN(hwctx);
+	SHA_CTX *c = ctx->md_data;
+	uint_fast64_t total;
+	unsigned char *p;
+	unsigned long n;
+
+	/* Calculate total length (Nl,Nh) is length in bits */
+	total = (((uint_fast64_t) c->Nh) << 29) + (c->Nl >> 3);
+	total += len;
+	c->Nh = total >> 29;
+	c->Nl = (total << 3) & 0xffffffffUL;
+
+	memcpy(aligned, &c->h0, 5 * sizeof(SHA_LONG));
+
+	/* Check partial data */
+	n = c->num;
+	if (n) {
+		p = (unsigned char *) c->data;
+		if (len >= SHA_CBLOCK || len+n >= SHA_CBLOCK) {
+			memcpy(p+n, data, SHA_CBLOCK-n);
+			padlock_sha1_partial(aligned, p, 1);
+			n      = SHA_CBLOCK - n;
+			data  += n;
+			len   -= n;
+			c->num = 0;
+			memset(p, 0, SHA_CBLOCK);
+		} else {
+			memcpy(p+n, data, len);
+			c->num += (unsigned int)len;
+			return 1;
+		}
+	}
+
+	/* Can we finalize straight away? */
+	if ((ctx->flags & EVP_MD_CTX_FLAG_ONESHOT) &&
+	    (total <= PADLOCK_MAX_FINALIZING_LENGTH)) {
+		padlock_sha1(aligned, data, total, len);
+		memcpy(&c->h0, aligned, 5 * sizeof(SHA_LONG));
+		c->num = -1;
+		return 1;
+	}
+
+	/* Use nonfinalizing update */
+	n = len / SHA_CBLOCK;
+	if (n != 0) {
+		padlock_sha1_partial(aligned, data, n);
+		data += n * SHA_CBLOCK;
+		len  -= n * SHA_CBLOCK;
+	}
+	memcpy(&c->h0, aligned, 5 * sizeof(SHA_LONG));
+
+	/* Buffer remaining bytes */
+	if (len) {
+		memcpy(c->data, data, len);
+		c->num = len;
+	}
+
+	return 1;
+}
+
+static int padlock_sha1_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+	unsigned char hwctx[PADLOCK_SHA_HWCTX];
+	uint32_t *aligned = PADLOCK_SHA_ALIGN(hwctx);
+	uint64_t total;
+	SHA_CTX *c = ctx->md_data;
+
+	if (c->num == -1) {
+		padlock_copy_bswap(md, &c->h0, 5);
+		c->num = 0;
+		return 1;
+	}
+
+	total = (((uint_fast64_t) c->Nh) << 29) + (c->Nl >> 3);
+#if PADLOCK_NEED_FALLBACK_SHA
+	if ((!PADLOCK_HAVE_NANO) && (total > PADLOCK_MAX_FINALIZING_LENGTH))
+		return SHA1_Final(md, c);
+#endif
+
+	memcpy(aligned, &c->h0, 5 * sizeof(SHA_LONG));
+	if (total > PADLOCK_MAX_FINALIZING_LENGTH) {
+		unsigned char padding[2 * SHA_CBLOCK];
+		unsigned long n;
+
+		n = padlock_sha_prepare_padding(ctx, padding,
+			(unsigned char *) c->data, c->num, total << 3);
+		padlock_sha1_partial(aligned, padding, n);
+	} else {
+		padlock_sha1(aligned, c->data, total, c->num);
+	}
+	padlock_copy_bswap(md, aligned, 5);
+	c->num = 0;
+
+	return 1;
+}
+
+static EVP_MD padlock_sha1_md = {
+	NID_sha1,
+	NID_sha1WithRSAEncryption,
+	SHA_DIGEST_LENGTH,
+	EVP_MD_FLAG_PKEY_METHOD_SIGNATURE,
+	padlock_sha1_init,
+	padlock_sha1_update,
+	padlock_sha1_final,
+	NULL,
+	NULL,
+	EVP_PKEY_RSA_method,
+	SHA_CBLOCK,
+	sizeof(SHA_CTX),
+};
+
+static EVP_MD padlock_dss1_md = {
+	NID_dsa,
+	NID_dsaWithSHA1,
+	SHA_DIGEST_LENGTH,
+	0,
+	padlock_sha1_init,
+	padlock_sha1_update,
+	padlock_sha1_final,
+	NULL,
+	NULL,
+	EVP_PKEY_DSA_method,
+	SHA_CBLOCK,
+	sizeof(SHA_CTX),
+};
+
+
+#if !defined(OPENSSL_NO_SHA256)
+
+static void
+padlock_sha256(void *hwctx, const void *buf, unsigned long total, unsigned long now)
+{
+	unsigned long pos = total - now;
+
+	asm volatile ("xsha256"
+			: "+S"(buf), "+D"(hwctx), "+a"(pos), "+c"(total)
+			: : "memory");
+}
+
+static void
+padlock_sha256_partial(void *hwctx, const void *buf, unsigned long blocks)
+{
+	asm volatile ("xsha256"
+			: "+S"(buf), "+D"(hwctx), "+c"(blocks)
+			: "a"(-1L) : "memory");
+}
+
+#if PADLOCK_NEED_FALLBACK_SHA
+
+static int padlock_sha256_update_eden(EVP_MD_CTX *ctx, const void *data,
+				      size_t len)
+{
+	unsigned char hwctx[PADLOCK_SHA_HWCTX];
+	uint32_t *aligned = PADLOCK_SHA_ALIGN(hwctx);
+	SHA256_CTX *c = ctx->md_data;
+	uint_fast64_t total;
+	const unsigned char *p = data;
+	unsigned int l = 0;
+
+	/* Calculate total length (Nl,Nh) is length in bits */
+	total = (((uint_fast64_t) c->Nh) << 29) + (c->Nl >> 3);
+	total += len;
+
+	if ((ctx->flags & EVP_MD_CTX_FLAG_ONESHOT) &&
+	    (total <= PADLOCK_MAX_FINALIZING_LENGTH)) {
+		if (c->num != 0) {
+			l = (len < SHA256_CBLOCK - c->num) ? len : (SHA256_CBLOCK - c->num);
+			if (!SHA256_Update(c, data, l))
+				return 0;
+			p += l;
+			if (c->num != 0) {
+				p = (unsigned char *) c->data;
+				len = c->num;
+				l = 0;
+			}
+		}
+		memcpy(aligned, c->h, sizeof(c->h));
+		padlock_sha256(aligned, p, total, len - l);
+		memcpy(c->h, aligned, sizeof(c->h));
+		c->num = -1;
+		return 1;
+	}
+
+	return SHA256_Update(c, data, len);
+}
+
+#endif
+
+static int padlock_sha256_update(EVP_MD_CTX *ctx, const void *data,
+				 size_t len)
+{
+	unsigned char hwctx[PADLOCK_SHA_HWCTX];
+	uint32_t *aligned = PADLOCK_SHA_ALIGN(hwctx);
+	SHA256_CTX *c = ctx->md_data;
+	uint_fast64_t total;
+	unsigned char *p;
+	unsigned long n;
+
+	/* Calculate total length (Nl,Nh) is length in bits */
+	total = (((uint_fast64_t) c->Nh) << 29) + (c->Nl >> 3);
+	total += len;
+	c->Nh = total >> 29;
+	c->Nl = (total << 3) & 0xffffffffUL;
+
+	memcpy(aligned, c->h, sizeof(c->h));
+
+	/* Check partial data */
+	n = c->num;
+	if (n) {
+		p = (unsigned char *) c->data;
+		if (len >= SHA256_CBLOCK || len+n >= SHA256_CBLOCK) {
+			memcpy(p+n, data, SHA256_CBLOCK-n);
+			padlock_sha256_partial(aligned, p, 1);
+			n      = SHA256_CBLOCK - n;
+			data  += n;
+			len   -= n;
+			c->num = 0;
+			memset(p, 0, SHA256_CBLOCK);
+		} else {
+			memcpy(p+n, data, len);
+			c->num += (unsigned int)len;
+			return 1;
+		}
+	}
+
+	/* Can we finalize straight away? */
+	if ((ctx->flags & EVP_MD_CTX_FLAG_ONESHOT) &&
+	    (total <= PADLOCK_MAX_FINALIZING_LENGTH)) {
+		padlock_sha256(aligned, data, total, len);
+		memcpy(c->h, aligned, sizeof(c->h));
+		c->num = -1;
+		return 1;
+	}
+
+	/* Use nonfinalizing update */
+	n = len / SHA256_CBLOCK;
+	if (n != 0) {
+		padlock_sha256_partial(aligned, data, n);
+		data += n * SHA256_CBLOCK;
+		len  -= n * SHA256_CBLOCK;
+	}
+	memcpy(c->h, aligned, sizeof(c->h));
+
+	/* Buffer remaining bytes */
+	if (len) {
+		memcpy(c->data, data, len);
+		c->num = len;
+	}
+
+	return 1;
+}
+
+static int padlock_sha256_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+	unsigned char hwctx[PADLOCK_SHA_HWCTX];
+	uint32_t *aligned = PADLOCK_SHA_ALIGN(hwctx);
+	uint64_t total;
+	SHA256_CTX *c = ctx->md_data;
+
+	if (c->num == -1) {
+		padlock_copy_bswap(md, c->h, sizeof(c->h)/sizeof(c->h[0]));
+		c->num = 0;
+		return 1;
+	}
+
+	total = (((uint_fast64_t) c->Nh) << 29) + (c->Nl >> 3);
+#if PADLOCK_NEED_FALLBACK_SHA
+	if ((!PADLOCK_HAVE_NANO) && (total > PADLOCK_MAX_FINALIZING_LENGTH))
+		return SHA256_Final(md, c);
+#endif
+
+	memcpy(aligned, c->h, sizeof(c->h));
+	if (total > PADLOCK_MAX_FINALIZING_LENGTH) {
+		unsigned char padding[2 * SHA_CBLOCK];
+		unsigned long n;
+
+		n = padlock_sha_prepare_padding(ctx, padding,
+			(unsigned char *) c->data, c->num, total << 3);
+		padlock_sha256_partial(aligned, padding, n);
+	} else {
+		padlock_sha256(aligned, c->data, total, c->num);
+	}
+	padlock_copy_bswap(md, aligned, sizeof(c->h)/sizeof(c->h[0]));
+	c->num = 0;
+	return 1;
+}
+
+#if !defined(OPENSSL_NO_SHA224)
+
+static int padlock_sha224_init(EVP_MD_CTX *ctx)
+{
+	return SHA224_Init(ctx->md_data);
+}
+
+static EVP_MD padlock_sha224_md = {
+	NID_sha224,
+	NID_sha224WithRSAEncryption,
+	SHA224_DIGEST_LENGTH,
+	0,
+	padlock_sha224_init,
+	padlock_sha256_update,
+	padlock_sha256_final,
+	NULL,
+	NULL,
+	EVP_PKEY_RSA_method,
+	SHA_CBLOCK,
+	sizeof(SHA256_CTX),
+};
+#endif /* !OPENSSL_NO_SHA224 */
+
+static int padlock_sha256_init(EVP_MD_CTX *ctx)
+{
+	return SHA256_Init(ctx->md_data);
+}
+
+static EVP_MD padlock_sha256_md = {
+	NID_sha256,
+	NID_sha256WithRSAEncryption,
+	SHA256_DIGEST_LENGTH,
+	0,
+	padlock_sha256_init,
+	padlock_sha256_update,
+	padlock_sha256_final,
+	NULL,
+	NULL,
+	EVP_PKEY_RSA_method,
+	SHA_CBLOCK,
+	sizeof(SHA256_CTX),
+};
+#endif /* !OPENSSL_NO_SHA256 */
+
+static int padlock_digest_nids[] = {
+#if !defined(OPENSSL_NO_SHA)
+	NID_sha1,
+	NID_dsa,
+#endif
+#if !defined(OPENSSL_NO_SHA256)
+#if !defined(OPENSSL_NO_SHA224)
+	NID_sha224,
+#endif
+	NID_sha256,
+#endif
+};
+
+static int padlock_digest_nids_num = sizeof(padlock_digest_nids)/sizeof(padlock_digest_nids[0]);
+
+static int
+padlock_digests (ENGINE *e, const EVP_MD **digest, const int **nids, int nid)
+{
+	/* No specific digest => return a list of supported nids ... */
+	if (!digest) {
+		*nids = padlock_digest_nids;
+		return padlock_digest_nids_num;
+	}
+
+	/* ... or the requested "digest" otherwise */
+	switch (nid) {
+#if !defined(OPENSSL_NO_SHA)
+	  case NID_sha1:
+	    *digest = &padlock_sha1_md;
+	    break;
+	  case NID_dsa:
+	    *digest = &padlock_dss1_md;
+	    break;
+#endif
+#if !defined(OPENSSL_NO_SHA256)
+#if !defined(OPENSSL_NO_SHA224)
+	  case NID_sha224:
+	    *digest = &padlock_sha224_md;
+	    break;
+#endif	/* OPENSSL_NO_SHA224 */
+	  case NID_sha256:
+	    *digest = &padlock_sha256_md;
+	    break;
+#endif	/* OPENSSL_NO_SHA256 */
+	  default:
+	    /* Sorry, we don't support this NID */
+	    *digest = NULL;
+	    return 0;
+	}
+
+	return 1;
+}
+
+#endif /* OPENSSL_NO_SHA */
+
+#ifndef PADLOCK_NO_RNG
+
 /* ===== Random Number Generator ===== */
 /*
  * This code is not engaged. The reason is that it does not comply
@@ -1356,6 +1868,59 @@ static RAND_METHOD padlock_rand = {
     padlock_rand_bytes,         /* pseudorand */
     padlock_rand_status,        /* rand status */
 };
+#endif /* PADLOCK_NO_RNG */
+
+/* Prepare the ENGINE structure for registration */
+static int
+padlock_bind_helper(ENGINE *e)
+{
+	/* Check available features */
+	padlock_available();
+
+	/* Generate a nice engine name with available features */
+	BIO_snprintf(padlock_name, sizeof(padlock_name),
+		"VIA PadLock: %s%s%s%s%s%s",
+		padlock_flags ? "" : "not supported",
+		PADLOCK_HAVE_RNG ? "RNG " : "",
+		PADLOCK_HAVE_ACE ? (PADLOCK_HAVE_ACE2 ? "ACE2 " : "ACE ") : "",
+		PADLOCK_HAVE_PHE ? "PHE " : "",
+		PADLOCK_HAVE_PMM ? "PMM " : "",
+		PADLOCK_HAVE_NANO ? "NANO " : ""
+		);
+
+#if PADLOCK_NEED_FALLBACK_SHA && !defined(OPENSSL_NO_SHA)
+	if (!PADLOCK_HAVE_NANO) {
+		padlock_sha1_md.update = padlock_sha1_update_eden;
+		padlock_dss1_md.update = padlock_sha1_update_eden;
+#if !defined(OPENSSL_NO_SHA256)
+#if !defined(OPENSSL_NO_SHA224)
+		padlock_sha224_md.update = padlock_sha256_update_eden;
+#endif
+		padlock_sha256_md.update = padlock_sha256_update_eden;
+#endif
+	}
+#endif  
+
+	/* Register everything or return with an error */
+	if (!ENGINE_set_id(e, padlock_id) ||
+	    !ENGINE_set_name(e, padlock_name) ||
+	    !ENGINE_set_init_function(e, padlock_init)
+#ifndef OPENSSL_NO_AES
+	    || (PADLOCK_HAVE_ACE && !ENGINE_set_ciphers (e, padlock_ciphers))
+#endif
+#ifndef OPENSSL_NO_SHA
+	    || (PADLOCK_HAVE_PHE && !ENGINE_set_digests (e, padlock_digests))
+#endif
+#ifndef PADLOCK_NO_RNG
+	    || (PADLOCK_HAVE_RNG && !ENGINE_set_RAND (e, &padlock_rand))
+#endif
+	    ) {
+		return 0;
+	}
+
+	/* Everything looks good */
+	return 1;
+}
 
 #  else                         /* !COMPILE_HW_PADLOCK */
 #   ifndef OPENSSL_NO_DYNAMIC_ENGINE
-- 
2.2.2

