diff options
author | Christian Heimes <christian@python.org> | 2019-09-12 12:42:07 (GMT) |
---|---|---|
committer | Stéphane Wirtel <stephane@wirtel.be> | 2019-09-12 12:42:07 (GMT) |
commit | 5a4f82f457049b5b07b6fba4ca42bc1ecf597976 (patch) | |
tree | 923484cd7a3d3460dbcedca8d3102bfda074c2b6 /Modules | |
parent | a488879cbaf4b8b52699cadccf73bb4c271bcb29 (diff) | |
download | cpython-5a4f82f457049b5b07b6fba4ca42bc1ecf597976.zip cpython-5a4f82f457049b5b07b6fba4ca42bc1ecf597976.tar.gz cpython-5a4f82f457049b5b07b6fba4ca42bc1ecf597976.tar.bz2 |
bpo-38132: Simplify _hashopenssl code (GH-16023)
Signed-off-by: Christian Heimes <christian@python.org>
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_hashopenssl.c | 283 | ||||
-rw-r--r-- | Modules/clinic/_hashopenssl.c.h | 224 |
2 files changed, 385 insertions, 122 deletions
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 5e76853..798317f 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -30,7 +30,6 @@ typedef struct { PyObject_HEAD - PyObject *name; /* name of this hash algorithm */ EVP_MD_CTX *ctx; /* OpenSSL message digest context */ PyThread_type_lock lock; /* OpenSSL context lock */ } EVPobject; @@ -38,18 +37,6 @@ typedef struct { static PyTypeObject EVPtype; - -#define DEFINE_CONSTS_FOR_NEW(Name) \ - static PyObject *CONST_ ## Name ## _name_obj = NULL; \ - static EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p = NULL; - -DEFINE_CONSTS_FOR_NEW(md5) -DEFINE_CONSTS_FOR_NEW(sha1) -DEFINE_CONSTS_FOR_NEW(sha224) -DEFINE_CONSTS_FOR_NEW(sha256) -DEFINE_CONSTS_FOR_NEW(sha384) -DEFINE_CONSTS_FOR_NEW(sha512) - #include "clinic/_hashopenssl.c.h" /*[clinic input] module _hashlib @@ -90,16 +77,13 @@ _setException(PyObject *exc) /* LCOV_EXCL_STOP */ static EVPobject * -newEVPobject(PyObject *name) +newEVPobject(void) { EVPobject *retval = (EVPobject *)PyObject_New(EVPobject, &EVPtype); if (retval == NULL) { return NULL; } - /* save the name for .name to return */ - Py_INCREF(name); - retval->name = name; retval->lock = NULL; retval->ctx = EVP_MD_CTX_new(); @@ -139,7 +123,6 @@ EVP_dealloc(EVPobject *self) if (self->lock != NULL) PyThread_free_lock(self->lock); EVP_MD_CTX_free(self->ctx); - Py_XDECREF(self->name); PyObject_Del(self); } @@ -167,7 +150,7 @@ EVP_copy_impl(EVPobject *self) { EVPobject *newobj; - if ( (newobj = newEVPobject(self->name))==NULL) + if ( (newobj = newEVPobject())==NULL) return NULL; if (!locked_EVP_MD_CTX_copy(newobj->ctx, self)) { @@ -307,10 +290,20 @@ EVP_get_digest_size(EVPobject *self, void *closure) return PyLong_FromLong(size); } -static PyMemberDef EVP_members[] = { - {"name", T_OBJECT, offsetof(EVPobject, name), READONLY, PyDoc_STR("algorithm name.")}, - {NULL} /* Sentinel */ -}; +static PyObject * +EVP_get_name(EVPobject *self, void *closure) +{ + const char *name = EVP_MD_name(EVP_MD_CTX_md(self->ctx)); + PyObject *name_obj, *name_lower; + + name_obj = PyUnicode_FromString(name); + if (!name_obj) { + return NULL; + } + name_lower = PyObject_CallMethod(name_obj, "lower", NULL); + Py_DECREF(name_obj); + return name_lower; +} static PyGetSetDef EVP_getseters[] = { {"digest_size", @@ -321,6 +314,10 @@ static PyGetSetDef EVP_getseters[] = { (getter)EVP_get_block_size, NULL, NULL, NULL}, + {"name", + (getter)EVP_get_name, NULL, + NULL, + PyDoc_STR("algorithm name.")}, {NULL} /* Sentinel */ }; @@ -328,7 +325,14 @@ static PyGetSetDef EVP_getseters[] = { static PyObject * EVP_repr(EVPobject *self) { - return PyUnicode_FromFormat("<%U HASH object @ %p>", self->name, self); + PyObject *name_obj, *repr; + name_obj = EVP_get_name(self, NULL); + if (!name_obj) { + return NULL; + } + repr = PyUnicode_FromFormat("<%U HASH object @ %p>", name_obj, self); + Py_DECREF(name_obj); + return repr; } PyDoc_STRVAR(hashtype_doc, @@ -379,7 +383,7 @@ static PyTypeObject EVPtype = { 0, /*tp_iter*/ 0, /*tp_iternext*/ EVP_methods, /* tp_methods */ - EVP_members, /* tp_members */ + NULL, /* tp_members */ EVP_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ @@ -389,28 +393,23 @@ static PyTypeObject EVPtype = { }; static PyObject * -EVPnew(PyObject *name_obj, - const EVP_MD *digest, const EVP_MD_CTX *initial_ctx, +EVPnew(const EVP_MD *digest, const unsigned char *cp, Py_ssize_t len) { EVPobject *self; - if (!digest && !initial_ctx) { + if (!digest) { PyErr_SetString(PyExc_ValueError, "unsupported hash type"); return NULL; } - if ((self = newEVPobject(name_obj)) == NULL) + if ((self = newEVPobject()) == NULL) return NULL; - if (initial_ctx) { - EVP_MD_CTX_copy(self->ctx, initial_ctx); - } else { - if (!EVP_DigestInit(self->ctx, digest)) { - _setException(PyExc_ValueError); - Py_DECREF(self); - return NULL; - } + if (!EVP_DigestInit(self->ctx, digest)) { + _setException(PyExc_ValueError); + Py_DECREF(self); + return NULL; } if (cp && len) { @@ -462,13 +461,132 @@ EVP_new_impl(PyObject *module, PyObject *name_obj, PyObject *data_obj) digest = EVP_get_digestbyname(name); - ret_obj = EVPnew(name_obj, digest, NULL, (unsigned char*)view.buf, view.len); + ret_obj = EVPnew(digest, (unsigned char*)view.buf, view.len); if (data_obj) PyBuffer_Release(&view); return ret_obj; } +static PyObject* +EVP_fast_new(PyObject *module, PyObject *data_obj, const EVP_MD *digest) +{ + Py_buffer view = { 0 }; + PyObject *ret_obj; + + if (data_obj) + GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); + + ret_obj = EVPnew(digest, (unsigned char*)view.buf, view.len); + + if (data_obj) + PyBuffer_Release(&view); + + return ret_obj; +} + +/*[clinic input] +_hashlib.openssl_md5 + + string as data_obj: object(py_default="b''") = NULL + +Returns a md5 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_md5_impl(PyObject *module, PyObject *data_obj) +/*[clinic end generated code: output=6caae75b73e22c3f input=52010d3869e1b1a7]*/ +{ + return EVP_fast_new(module, data_obj, EVP_md5()); +} + + +/*[clinic input] +_hashlib.openssl_sha1 + + string as data_obj: object(py_default="b''") = NULL + +Returns a sha1 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha1_impl(PyObject *module, PyObject *data_obj) +/*[clinic end generated code: output=07606d8f75153e61 input=16807d30e4aa8ae9]*/ +{ + return EVP_fast_new(module, data_obj, EVP_sha1()); +} + + +/*[clinic input] +_hashlib.openssl_sha224 + + string as data_obj: object(py_default="b''") = NULL + +Returns a sha224 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha224_impl(PyObject *module, PyObject *data_obj) +/*[clinic end generated code: output=55e848761bcef0c9 input=5dbc2f1d84eb459b]*/ +{ + return EVP_fast_new(module, data_obj, EVP_sha224()); +} + + +/*[clinic input] +_hashlib.openssl_sha256 + + string as data_obj: object(py_default="b''") = NULL + +Returns a sha256 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha256_impl(PyObject *module, PyObject *data_obj) +/*[clinic end generated code: output=05851d7cce34ac65 input=a68a5d21cda5a80f]*/ +{ + return EVP_fast_new(module, data_obj, EVP_sha256()); +} + + +/*[clinic input] +_hashlib.openssl_sha384 + + string as data_obj: object(py_default="b''") = NULL + +Returns a sha384 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha384_impl(PyObject *module, PyObject *data_obj) +/*[clinic end generated code: output=5101a4704a932c2f input=6bdfa006622b64ea]*/ +{ + return EVP_fast_new(module, data_obj, EVP_sha384()); +} + + +/*[clinic input] +_hashlib.openssl_sha512 + + string as data_obj: object(py_default="b''") = NULL + +Returns a sha512 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj) +/*[clinic end generated code: output=20c8e63ee560a5cb input=ece50182ad4b76a6]*/ +{ + return EVP_fast_new(module, data_obj, EVP_sha512()); +} + + /*[clinic input] _hashlib.pbkdf2_hmac as pbkdf2_hmac @@ -800,76 +918,6 @@ generate_hash_name_list(void) return state.set; } - -/* - * This macro generates constructor function definitions for specific - * hash algorithms. These constructors are much faster than calling - * the generic one passing it a python string and are noticeably - * faster than calling a python new() wrapper. That is important for - * code that wants to make hashes of a bunch of small strings. - * The first call will lazy-initialize, which reports an exception - * if initialization fails. - */ -#define GEN_CONSTRUCTOR(NAME) \ - static PyObject * \ - EVP_new_ ## NAME (PyObject *self, PyObject *const *args, Py_ssize_t nargs) \ - { \ - PyObject *data_obj = NULL; \ - Py_buffer view = { 0 }; \ - PyObject *ret_obj; \ - \ - if (!_PyArg_ParseStack(args, nargs, "|O:" #NAME , &data_obj)) { \ - return NULL; \ - } \ - \ - if (CONST_new_ ## NAME ## _ctx_p == NULL) { \ - EVP_MD_CTX *ctx_p = EVP_MD_CTX_new(); \ - if (!EVP_get_digestbyname(#NAME) || \ - !EVP_DigestInit(ctx_p, EVP_get_digestbyname(#NAME))) { \ - _setException(PyExc_ValueError); \ - EVP_MD_CTX_free(ctx_p); \ - return NULL; \ - } \ - CONST_new_ ## NAME ## _ctx_p = ctx_p; \ - } \ - \ - if (data_obj) \ - GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); \ - \ - ret_obj = EVPnew( \ - CONST_ ## NAME ## _name_obj, \ - NULL, \ - CONST_new_ ## NAME ## _ctx_p, \ - (unsigned char*)view.buf, \ - view.len); \ - \ - if (data_obj) \ - PyBuffer_Release(&view); \ - return ret_obj; \ - } - -/* a PyMethodDef structure for the constructor */ -#define CONSTRUCTOR_METH_DEF(NAME) \ - {"openssl_" #NAME, (PyCFunction)(void(*)(void))EVP_new_ ## NAME, METH_FASTCALL, \ - PyDoc_STR("Returns a " #NAME \ - " hash object; optionally initialized with a string") \ - } - -/* used in the init function to setup a constructor: initialize OpenSSL - constructor constants if they haven't been initialized already. */ -#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ - if (CONST_ ## NAME ## _name_obj == NULL) { \ - CONST_ ## NAME ## _name_obj = PyUnicode_FromString(#NAME); \ - } \ -} while (0); - -GEN_CONSTRUCTOR(md5) -GEN_CONSTRUCTOR(sha1) -GEN_CONSTRUCTOR(sha224) -GEN_CONSTRUCTOR(sha256) -GEN_CONSTRUCTOR(sha384) -GEN_CONSTRUCTOR(sha512) - /* List of functions exported by this module */ static struct PyMethodDef EVP_functions[] = { @@ -877,12 +925,12 @@ static struct PyMethodDef EVP_functions[] = { PBKDF2_HMAC_METHODDEF _HASHLIB_SCRYPT_METHODDEF _HASHLIB_HMAC_DIGEST_METHODDEF - CONSTRUCTOR_METH_DEF(md5), - CONSTRUCTOR_METH_DEF(sha1), - CONSTRUCTOR_METH_DEF(sha224), - CONSTRUCTOR_METH_DEF(sha256), - CONSTRUCTOR_METH_DEF(sha384), - CONSTRUCTOR_METH_DEF(sha512), + _HASHLIB_OPENSSL_MD5_METHODDEF + _HASHLIB_OPENSSL_SHA1_METHODDEF + _HASHLIB_OPENSSL_SHA224_METHODDEF + _HASHLIB_OPENSSL_SHA256_METHODDEF + _HASHLIB_OPENSSL_SHA384_METHODDEF + _HASHLIB_OPENSSL_SHA512_METHODDEF {NULL, NULL} /* Sentinel */ }; @@ -939,12 +987,5 @@ PyInit__hashlib(void) Py_INCREF((PyObject *)&EVPtype); PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); - /* these constants are used by the convenience constructors */ - INIT_CONSTRUCTOR_CONSTANTS(md5); - INIT_CONSTRUCTOR_CONSTANTS(sha1); - INIT_CONSTRUCTOR_CONSTANTS(sha224); - INIT_CONSTRUCTOR_CONSTANTS(sha256); - INIT_CONSTRUCTOR_CONSTANTS(sha384); - INIT_CONSTRUCTOR_CONSTANTS(sha512); return m; } diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h index 854d925..9aaea47 100644 --- a/Modules/clinic/_hashopenssl.c.h +++ b/Modules/clinic/_hashopenssl.c.h @@ -109,6 +109,228 @@ exit: return return_value; } +PyDoc_STRVAR(_hashlib_openssl_md5__doc__, +"openssl_md5($module, /, string=b\'\')\n" +"--\n" +"\n" +"Returns a md5 hash object; optionally initialized with a string"); + +#define _HASHLIB_OPENSSL_MD5_METHODDEF \ + {"openssl_md5", (PyCFunction)(void(*)(void))_hashlib_openssl_md5, METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_md5__doc__}, + +static PyObject * +_hashlib_openssl_md5_impl(PyObject *module, PyObject *data_obj); + +static PyObject * +_hashlib_openssl_md5(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"string", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "openssl_md5", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *data_obj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + data_obj = args[0]; +skip_optional_pos: + return_value = _hashlib_openssl_md5_impl(module, data_obj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_hashlib_openssl_sha1__doc__, +"openssl_sha1($module, /, string=b\'\')\n" +"--\n" +"\n" +"Returns a sha1 hash object; optionally initialized with a string"); + +#define _HASHLIB_OPENSSL_SHA1_METHODDEF \ + {"openssl_sha1", (PyCFunction)(void(*)(void))_hashlib_openssl_sha1, METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_sha1__doc__}, + +static PyObject * +_hashlib_openssl_sha1_impl(PyObject *module, PyObject *data_obj); + +static PyObject * +_hashlib_openssl_sha1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"string", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "openssl_sha1", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *data_obj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + data_obj = args[0]; +skip_optional_pos: + return_value = _hashlib_openssl_sha1_impl(module, data_obj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_hashlib_openssl_sha224__doc__, +"openssl_sha224($module, /, string=b\'\')\n" +"--\n" +"\n" +"Returns a sha224 hash object; optionally initialized with a string"); + +#define _HASHLIB_OPENSSL_SHA224_METHODDEF \ + {"openssl_sha224", (PyCFunction)(void(*)(void))_hashlib_openssl_sha224, METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_sha224__doc__}, + +static PyObject * +_hashlib_openssl_sha224_impl(PyObject *module, PyObject *data_obj); + +static PyObject * +_hashlib_openssl_sha224(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"string", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "openssl_sha224", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *data_obj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + data_obj = args[0]; +skip_optional_pos: + return_value = _hashlib_openssl_sha224_impl(module, data_obj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_hashlib_openssl_sha256__doc__, +"openssl_sha256($module, /, string=b\'\')\n" +"--\n" +"\n" +"Returns a sha256 hash object; optionally initialized with a string"); + +#define _HASHLIB_OPENSSL_SHA256_METHODDEF \ + {"openssl_sha256", (PyCFunction)(void(*)(void))_hashlib_openssl_sha256, METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_sha256__doc__}, + +static PyObject * +_hashlib_openssl_sha256_impl(PyObject *module, PyObject *data_obj); + +static PyObject * +_hashlib_openssl_sha256(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"string", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "openssl_sha256", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *data_obj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + data_obj = args[0]; +skip_optional_pos: + return_value = _hashlib_openssl_sha256_impl(module, data_obj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_hashlib_openssl_sha384__doc__, +"openssl_sha384($module, /, string=b\'\')\n" +"--\n" +"\n" +"Returns a sha384 hash object; optionally initialized with a string"); + +#define _HASHLIB_OPENSSL_SHA384_METHODDEF \ + {"openssl_sha384", (PyCFunction)(void(*)(void))_hashlib_openssl_sha384, METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_sha384__doc__}, + +static PyObject * +_hashlib_openssl_sha384_impl(PyObject *module, PyObject *data_obj); + +static PyObject * +_hashlib_openssl_sha384(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"string", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "openssl_sha384", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *data_obj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + data_obj = args[0]; +skip_optional_pos: + return_value = _hashlib_openssl_sha384_impl(module, data_obj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_hashlib_openssl_sha512__doc__, +"openssl_sha512($module, /, string=b\'\')\n" +"--\n" +"\n" +"Returns a sha512 hash object; optionally initialized with a string"); + +#define _HASHLIB_OPENSSL_SHA512_METHODDEF \ + {"openssl_sha512", (PyCFunction)(void(*)(void))_hashlib_openssl_sha512, METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_sha512__doc__}, + +static PyObject * +_hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj); + +static PyObject * +_hashlib_openssl_sha512(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"string", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "openssl_sha512", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *data_obj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + data_obj = args[0]; +skip_optional_pos: + return_value = _hashlib_openssl_sha512_impl(module, data_obj); + +exit: + return return_value; +} + PyDoc_STRVAR(pbkdf2_hmac__doc__, "pbkdf2_hmac($module, /, hash_name, password, salt, iterations,\n" " dklen=None)\n" @@ -401,4 +623,4 @@ exit: #ifndef _HASHLIB_SCRYPT_METHODDEF #define _HASHLIB_SCRYPT_METHODDEF #endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */ -/*[clinic end generated code: output=cfe686cb2fa042e1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=38c2637f67e9bb79 input=a9049054013a1b77]*/ |