Add poly1305 MAC support
authorPaul Yang <yang.yang@baishancloud.com>
Mon, 22 Oct 2018 06:54:24 +0000 (14:54 +0800)
committerPaul Yang <yang.yang@baishancloud.com>
Mon, 5 Nov 2018 05:07:07 +0000 (13:07 +0800)
This is based on the latest EVP MAC interface introduced in PR #7393.

Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/7459)

13 files changed:
crypto/err/openssl.txt
crypto/evp/c_allm.c
crypto/evp/evp_err.c
crypto/evp/pkey_mac.c
crypto/include/internal/evp_int.h
crypto/poly1305/build.info
crypto/poly1305/poly1305_meth.c [new file with mode: 0644]
crypto/poly1305/poly1305_pmeth.c [deleted file]
doc/man3/EVP_MAC.pod
doc/man7/EVP_MAC_POLY1305.pod [new file with mode: 0644]
include/openssl/evp.h
include/openssl/evperr.h
test/recipes/30-test_evp_data/evpmac.txt

index 6c52881e53705d6449d41adf62b2982c528f00ec..b5a441a24bd3c4a2ad69a99d4656f8474b272da8 100644 (file)
@@ -810,6 +810,7 @@ EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN:164:PKCS5_v2_PBKDF2_keyivgen
 EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN:180:PKCS5_v2_scrypt_keyivgen
 EVP_F_PKEY_MAC_INIT:214:pkey_mac_init
 EVP_F_PKEY_SET_TYPE:158:pkey_set_type
+EVP_F_POLY1305_CTRL:215:poly1305_ctrl
 EVP_F_RC2_MAGIC_TO_METH:109:rc2_magic_to_meth
 EVP_F_RC5_CTRL:125:rc5_ctrl
 EVP_F_S390X_AES_GCM_CTRL:201:s390x_aes_gcm_ctrl
index 2b9d442d6b93fec75b6df478ff46f2850f0a9f4f..ba8acc7884fd823f78871e6c6b382ef42fcaafcc 100644 (file)
@@ -20,4 +20,7 @@ void openssl_add_all_macs_int(void)
 #ifndef OPENSSL_NO_SIPHASH
     EVP_add_mac(&siphash_meth);
 #endif
+#ifndef OPENSSL_NO_POLY1305
+    EVP_add_mac(&poly1305_meth);
+#endif
 }
index 05d956548f6f5045ade58fcc120706d462dc0a0f..4ef0cf54912a7748a0e12a59ef9abb3d2fcb48ef 100644 (file)
@@ -153,6 +153,7 @@ static const ERR_STRING_DATA EVP_str_functs[] = {
      "PKCS5_v2_scrypt_keyivgen"},
     {ERR_PACK(ERR_LIB_EVP, EVP_F_PKEY_MAC_INIT, 0), "pkey_mac_init"},
     {ERR_PACK(ERR_LIB_EVP, EVP_F_PKEY_SET_TYPE, 0), "pkey_set_type"},
+    {ERR_PACK(ERR_LIB_EVP, EVP_F_POLY1305_CTRL, 0), "poly1305_ctrl"},
     {ERR_PACK(ERR_LIB_EVP, EVP_F_RC2_MAGIC_TO_METH, 0), "rc2_magic_to_meth"},
     {ERR_PACK(ERR_LIB_EVP, EVP_F_RC5_CTRL, 0), "rc5_ctrl"},
     {ERR_PACK(ERR_LIB_EVP, EVP_F_S390X_AES_GCM_CTRL, 0), "s390x_aes_gcm_ctrl"},
index d8c0e897edb96e28c46af17ebe4664c54dd14188..d9e55ec07c032d56bb9efe48fd44c64960c1e6b0 100644 (file)
@@ -425,3 +425,36 @@ const EVP_PKEY_METHOD siphash_pkey_meth = {
     pkey_mac_ctrl,
     pkey_mac_ctrl_str
 };
+
+const EVP_PKEY_METHOD poly1305_pkey_meth = {
+    EVP_PKEY_POLY1305,
+    EVP_PKEY_FLAG_SIGCTX_CUSTOM,
+    pkey_mac_init,
+    pkey_mac_copy,
+    pkey_mac_cleanup,
+
+    0, 0,
+
+    0,
+    pkey_mac_keygen,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    pkey_mac_signctx_init,
+    pkey_mac_signctx,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    pkey_mac_ctrl,
+    pkey_mac_ctrl_str
+};
index 98adf1fd2f59f089b05826f6e5db280d1c061a22..85d3487adc7f3407b9a73e4808f043196fad639c 100644 (file)
@@ -132,6 +132,7 @@ extern const EVP_MAC cmac_meth;
 extern const EVP_MAC gmac_meth;
 extern const EVP_MAC hmac_meth;
 extern const EVP_MAC siphash_meth;
+extern const EVP_MAC poly1305_meth;
 
 /*
  * This function is internal for now, but can be made external when needed.
index 631b32b8e099ace19dd614ce2d99903278f127e5..363d62e6ebde276e9366020a59ed2d3d2558c592 100644 (file)
@@ -1,7 +1,7 @@
 LIBS=../../libcrypto
 SOURCE[../../libcrypto]=\
-        poly1305_pmeth.c \
         poly1305_ameth.c \
+        poly1305_meth.c \
         poly1305.c {- $target{poly1305_asm_src} -}
 
 GENERATE[poly1305-sparcv9.S]=asm/poly1305-sparcv9.pl $(PERLASM_SCHEME)
diff --git a/crypto/poly1305/poly1305_meth.c b/crypto/poly1305/poly1305_meth.c
new file mode 100644 (file)
index 0000000..dfee56d
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+#include <openssl/evp.h>
+#include "internal/evp_int.h"
+#include "internal/poly1305.h"
+#include "internal/cryptlib.h"
+#include "poly1305_local.h"
+
+/* typedef EVP_MAC_IMPL */
+struct evp_mac_impl_st {
+    POLY1305 *ctx;               /* poly1305 context */
+};
+
+static EVP_MAC_IMPL *poly1305_new(void)
+{
+    EVP_MAC_IMPL *ctx;
+
+    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL
+            || (ctx->ctx = OPENSSL_zalloc(sizeof(POLY1305))) == NULL) {
+        OPENSSL_free(ctx);
+        return 0;
+    }
+    return ctx;
+}
+
+static void poly1305_free(EVP_MAC_IMPL *ctx)
+{
+    if (ctx != NULL) {
+        OPENSSL_free(ctx->ctx);
+        OPENSSL_free(ctx);
+    }
+}
+
+static int poly1305_copy(EVP_MAC_IMPL *dst, EVP_MAC_IMPL *src)
+{
+    *dst->ctx = *src->ctx;
+
+    return 1;
+}
+
+static size_t poly1305_size(EVP_MAC_IMPL *ctx)
+{
+    return POLY1305_DIGEST_SIZE;
+}
+
+static int poly1305_init(EVP_MAC_IMPL *ctx)
+{
+    /* initialize the context in MAC_ctrl function */
+    return 1;
+}
+
+static int poly1305_update(EVP_MAC_IMPL *ctx, const unsigned char *data,
+                       size_t datalen)
+{
+    POLY1305 *poly_ctx = ctx->ctx;
+
+    /* poly1305 has nothing to return in its update function */
+    Poly1305_Update(poly_ctx, data, datalen);
+    return 1;
+}
+
+static int poly1305_final(EVP_MAC_IMPL *ctx, unsigned char *out)
+{
+    POLY1305 *poly_ctx = ctx->ctx;
+
+    Poly1305_Final(poly_ctx, out);
+    return 1;
+}
+
+static int poly1305_ctrl(EVP_MAC_IMPL *ctx, int cmd, va_list args)
+{
+    POLY1305 *poly_ctx = ctx->ctx;
+    unsigned char *key;
+    size_t keylen;
+
+    switch (cmd) {
+    case EVP_MAC_CTRL_SET_KEY:
+        key = va_arg(args, unsigned char *);
+        keylen = va_arg(args, size_t);
+
+        if (keylen != POLY1305_KEY_SIZE) {
+            EVPerr(EVP_F_POLY1305_CTRL, EVP_R_INVALID_KEY_LENGTH);
+            return 0;
+        }
+        Poly1305_Init(poly_ctx, key);
+        return 1;
+    default:
+        return -2;
+    }
+    return 1;
+}
+
+static int poly1305_ctrl_int(EVP_MAC_IMPL *ctx, int cmd, ...)
+{
+    int rv;
+    va_list args;
+
+    va_start(args, cmd);
+    rv = poly1305_ctrl(ctx, cmd, args);
+    va_end(args);
+
+    return rv;
+}
+
+static int poly1305_ctrl_str_cb(void *ctx, int cmd, void *buf, size_t buflen)
+{
+    return poly1305_ctrl_int(ctx, cmd, buf, buflen);
+}
+
+static int poly1305_ctrl_str(EVP_MAC_IMPL *ctx,
+                             const char *type, const char *value)
+{
+    if (value == NULL)
+        return 0;
+    if (strcmp(type, "key") == 0)
+        return EVP_str2ctrl(poly1305_ctrl_str_cb, ctx, EVP_MAC_CTRL_SET_KEY,
+                            value);
+    if (strcmp(type, "hexkey") == 0)
+        return EVP_hex2ctrl(poly1305_ctrl_str_cb, ctx, EVP_MAC_CTRL_SET_KEY,
+                            value);
+    return -2;
+}
+
+const EVP_MAC poly1305_meth = {
+    EVP_MAC_POLY1305,
+    poly1305_new,
+    poly1305_copy,
+    poly1305_free,
+    poly1305_size,
+    poly1305_init,
+    poly1305_update,
+    poly1305_final,
+    poly1305_ctrl,
+    poly1305_ctrl_str
+};
diff --git a/crypto/poly1305/poly1305_pmeth.c b/crypto/poly1305/poly1305_pmeth.c
deleted file mode 100644 (file)
index 3bc24c9..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright 2007-2018 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the OpenSSL license (the "License").  You may not use
- * this file except in compliance with the License.  You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-
-#include <stdio.h>
-#include "internal/cryptlib.h"
-#include <openssl/x509.h>
-#include <openssl/x509v3.h>
-#include <openssl/evp.h>
-#include <openssl/err.h>
-#include "internal/poly1305.h"
-#include "poly1305_local.h"
-#include "internal/evp_int.h"
-
-/* POLY1305 pkey context structure */
-
-typedef struct {
-    ASN1_OCTET_STRING ktmp;     /* Temp storage for key */
-    POLY1305 ctx;
-} POLY1305_PKEY_CTX;
-
-static int pkey_poly1305_init(EVP_PKEY_CTX *ctx)
-{
-    POLY1305_PKEY_CTX *pctx;
-
-    if ((pctx = OPENSSL_zalloc(sizeof(*pctx))) == NULL) {
-        CRYPTOerr(CRYPTO_F_PKEY_POLY1305_INIT, ERR_R_MALLOC_FAILURE);
-        return 0;
-    }
-    pctx->ktmp.type = V_ASN1_OCTET_STRING;
-
-    EVP_PKEY_CTX_set_data(ctx, pctx);
-    EVP_PKEY_CTX_set0_keygen_info(ctx, NULL, 0);
-    return 1;
-}
-
-static void pkey_poly1305_cleanup(EVP_PKEY_CTX *ctx)
-{
-    POLY1305_PKEY_CTX *pctx = EVP_PKEY_CTX_get_data(ctx);
-
-    if (pctx != NULL) {
-        OPENSSL_clear_free(pctx->ktmp.data, pctx->ktmp.length);
-        OPENSSL_clear_free(pctx, sizeof(*pctx));
-        EVP_PKEY_CTX_set_data(ctx, NULL);
-    }
-}
-
-static int pkey_poly1305_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
-{
-    POLY1305_PKEY_CTX *sctx, *dctx;
-
-    /* allocate memory for dst->data and a new POLY1305_CTX in dst->data->ctx */
-    if (!pkey_poly1305_init(dst))
-        return 0;
-    sctx = EVP_PKEY_CTX_get_data(src);
-    dctx = EVP_PKEY_CTX_get_data(dst);
-    if (ASN1_STRING_get0_data(&sctx->ktmp) != NULL &&
-        !ASN1_STRING_copy(&dctx->ktmp, &sctx->ktmp)) {
-        /* cleanup and free the POLY1305_PKEY_CTX in dst->data */
-        pkey_poly1305_cleanup(dst);
-        return 0;
-    }
-    memcpy(&dctx->ctx, &sctx->ctx, sizeof(POLY1305));
-    return 1;
-}
-
-static int pkey_poly1305_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
-{
-    ASN1_OCTET_STRING *key;
-    POLY1305_PKEY_CTX *pctx = EVP_PKEY_CTX_get_data(ctx);
-
-    if (ASN1_STRING_get0_data(&pctx->ktmp) == NULL)
-        return 0;
-    key = ASN1_OCTET_STRING_dup(&pctx->ktmp);
-    if (key == NULL)
-        return 0;
-    return EVP_PKEY_assign_POLY1305(pkey, key);
-}
-
-static int int_update(EVP_MD_CTX *ctx, const void *data, size_t count)
-{
-    POLY1305_PKEY_CTX *pctx = EVP_PKEY_CTX_get_data(EVP_MD_CTX_pkey_ctx(ctx));
-
-    Poly1305_Update(&pctx->ctx, data, count);
-    return 1;
-}
-
-static int poly1305_signctx_init(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx)
-{
-    POLY1305_PKEY_CTX *pctx = ctx->data;
-    ASN1_OCTET_STRING *key = (ASN1_OCTET_STRING *)ctx->pkey->pkey.ptr;
-
-    if (key->length != POLY1305_KEY_SIZE)
-        return 0;
-    EVP_MD_CTX_set_flags(mctx, EVP_MD_CTX_FLAG_NO_INIT);
-    EVP_MD_CTX_set_update_fn(mctx, int_update);
-    Poly1305_Init(&pctx->ctx, key->data);
-    return 1;
-}
-static int poly1305_signctx(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
-                            EVP_MD_CTX *mctx)
-{
-    POLY1305_PKEY_CTX *pctx = ctx->data;
-
-    *siglen = POLY1305_DIGEST_SIZE;
-    if (sig != NULL)
-        Poly1305_Final(&pctx->ctx, sig);
-    return 1;
-}
-
-static int pkey_poly1305_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
-{
-    POLY1305_PKEY_CTX *pctx = EVP_PKEY_CTX_get_data(ctx);
-    const unsigned char *key;
-    size_t len;
-
-    switch (type) {
-
-    case EVP_PKEY_CTRL_MD:
-        /* ignore */
-        break;
-
-    case EVP_PKEY_CTRL_SET_MAC_KEY:
-    case EVP_PKEY_CTRL_DIGESTINIT:
-        if (type == EVP_PKEY_CTRL_SET_MAC_KEY) {
-            /* user explicitly setting the key */
-            key = p2;
-            len = p1;
-        } else {
-            /* user indirectly setting the key via EVP_DigestSignInit */
-            key = EVP_PKEY_get0_poly1305(EVP_PKEY_CTX_get0_pkey(ctx), &len);
-        }
-        if (key == NULL || len != POLY1305_KEY_SIZE ||
-            !ASN1_OCTET_STRING_set(&pctx->ktmp, key, len))
-            return 0;
-        Poly1305_Init(&pctx->ctx, ASN1_STRING_get0_data(&pctx->ktmp));
-        break;
-
-    default:
-        return -2;
-
-    }
-    return 1;
-}
-
-static int pkey_poly1305_ctrl_str(EVP_PKEY_CTX *ctx,
-                                  const char *type, const char *value)
-{
-    if (value == NULL)
-        return 0;
-    if (strcmp(type, "key") == 0)
-        return EVP_PKEY_CTX_str2ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY, value);
-    if (strcmp(type, "hexkey") == 0)
-        return EVP_PKEY_CTX_hex2ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY, value);
-    return -2;
-}
-
-const EVP_PKEY_METHOD poly1305_pkey_meth = {
-    EVP_PKEY_POLY1305,
-    EVP_PKEY_FLAG_SIGCTX_CUSTOM, /* we don't deal with a separate MD */
-    pkey_poly1305_init,
-    pkey_poly1305_copy,
-    pkey_poly1305_cleanup,
-
-    0, 0,
-
-    0,
-    pkey_poly1305_keygen,
-
-    0, 0,
-
-    0, 0,
-
-    0, 0,
-
-    poly1305_signctx_init,
-    poly1305_signctx,
-
-    0, 0,
-
-    0, 0,
-
-    0, 0,
-
-    0, 0,
-
-    pkey_poly1305_ctrl,
-    pkey_poly1305_ctrl_str
-};
index 473d6c9e6b06986e1c54b53016d8255c7351e4cd..cc0d543162e7411a11caf0145ca4d4bf06959e3a 100644 (file)
@@ -335,7 +335,8 @@ F<./foo>)
 L<EVP_MAC_CMAC(7)>,
 L<EVP_MAC_GMAC(7)>,
 L<EVP_MAC_HMAC(7)>,
-L<EVP_MAC_SIPHASH(7)>
+L<EVP_MAC_SIPHASH(7)>,
+L<EVP_MAC_POLY1305(7)>
 
 =head1 COPYRIGHT
 
diff --git a/doc/man7/EVP_MAC_POLY1305.pod b/doc/man7/EVP_MAC_POLY1305.pod
new file mode 100644 (file)
index 0000000..d25e1d5
--- /dev/null
@@ -0,0 +1,55 @@
+=pod
+
+=head1 NAME
+
+EVP_MAC_POLY1305 - The Poly1305 EVP_MAC implementation
+
+=head1 DESCRIPTION
+
+Support for computing Poly1305 MACs through the B<EVP_MAC> API.
+
+=head2 Numeric identity
+
+B<EVP_MAC_POLY1305> is the numeric identity for this implementation,
+and can be used in functions like EVP_MAC_CTX_new_id() and
+EVP_get_macbynid().
+
+=head2 Supported controls
+
+The supported controls are:
+
+=over 4
+
+=item B<EVP_MAC_CTRL_SET_KEY>
+
+EVP_MAC_ctrl_str() takes two type strings for this control:
+
+=over 4
+
+=item "key"
+
+The value string is used as is.
+
+=item "hexkey"
+
+The value string is expected to be a hexadecimal number, which will be
+decoded before passing on as control value.
+
+=back
+
+=back
+
+=head1 SEE ALSO
+
+L<EVP_MAC_ctrl(3)>, L<EVP_MAC(3)/CONTROLS>
+
+=head1 COPYRIGHT
+
+Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the OpenSSL license (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
index 79845aa1e27b14f14f7a28e8858d43f785bd6a9b..6661e2ed291062fefe7682c925c6f302d60ba204 100644 (file)
@@ -991,6 +991,7 @@ void EVP_MD_do_all_sorted(void (*fn)
 # define EVP_MAC_GMAC           NID_gmac
 # define EVP_MAC_HMAC           NID_hmac
 # define EVP_MAC_SIPHASH        NID_siphash
+# define EVP_MAC_POLY1305       NID_poly1305
 
 EVP_MAC_CTX *EVP_MAC_CTX_new(const EVP_MAC *mac);
 EVP_MAC_CTX *EVP_MAC_CTX_new_id(int nid);
index 17b8187f71feeefbdc0defa480bc2e725f588e45..b5064fdffe42de1b97011df554b1371def3b6085 100644 (file)
@@ -120,6 +120,7 @@ int ERR_load_EVP_strings(void);
 # define EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN                   180
 # define EVP_F_PKEY_MAC_INIT                              214
 # define EVP_F_PKEY_SET_TYPE                              158
+# define EVP_F_POLY1305_CTRL                              215
 # define EVP_F_RC2_MAGIC_TO_METH                          109
 # define EVP_F_RC5_CTRL                                   125
 # define EVP_F_S390X_AES_GCM_CTRL                         201
index 82a350763f1f41c371c0d2a8caa4c4c172cb280c..640e1a125dda165261001c93a29d553ffa0cbac1 100644 (file)
@@ -710,3 +710,23 @@ Input = e33594d7505e43b900000000000000003394d7505e4379cd010000000000000000000000
 Key = 0100000000000000040000000000000000000000000000000000000000000000
 Output = 13000000000000000000000000000000
 
+# Here are 4 duplicated cases for Poly1305 by EVP_PKEY
+MAC = Poly1305 by EVP_PKEY
+Key = 0000000000000000000000000000000000000000000000000000000000000000
+Input = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+Output = 00000000000000000000000000000000
+
+MAC = Poly1305 by EVP_PKEY
+Key = 0000000000000000000000000000000036e5f6b5c5e06070f0efca96227a863e
+Input = 416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e20224945544620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c2073746174656d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207768696368206172652061646472657373656420746f
+Output = 36e5f6b5c5e06070f0efca96227a863e
+
+MAC = Poly1305 by EVP_PKEY
+Key = 36e5f6b5c5e06070f0efca96227a863e00000000000000000000000000000000
+Input = 416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e20224945544620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c2073746174656d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207768696368206172652061646472657373656420746f
+Output = f3477e7cd95417af89a6b8794c310cf0
+
+MAC = Poly1305 by EVP_PKEY
+Key = 1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0
+Input = 2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e642067696d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e6420746865206d6f6d65207261746873206f757467726162652e
+Output = 4541669a7eaaee61e708dc7cbcc5eb62