diff --git a/crypto/hash/hash.go b/crypto/hash/hash.go index 355c8cafd..f5b7e1d55 100644 --- a/crypto/hash/hash.go +++ b/crypto/hash/hash.go @@ -20,12 +20,18 @@ package hash import ( "crypto/sha256" + "encoding/base64" keccak "github.com/nebulasio/go-nebulas/crypto/sha3" "golang.org/x/crypto/ripemd160" "golang.org/x/crypto/sha3" ) +// const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + +var bcEncoding = base64.NewEncoding(alphabet) + // Sha256 returns the SHA-256 digest of the data. func Sha256(args ...[]byte) []byte { hasher := sha256.New() @@ -61,3 +67,29 @@ func Ripemd160(args ...[]byte) []byte { } return hasher.Sum(nil) } + +// Base64Encode encode to base64 +func Base64Encode(src []byte) []byte { + n := bcEncoding.EncodedLen(len(src)) + dst := make([]byte, n) + bcEncoding.Encode(dst, src) + // for dst[n-1] == '=' { + // n-- + // } + return dst[:n] +} + +// Base64Decode decode base64 +func Base64Decode(src []byte) ([]byte, error) { + numOfEquals := 4 - (len(src) % 4) + for i := 0; i < numOfEquals; i++ { + src = append(src, '=') + } + + dst := make([]byte, bcEncoding.DecodedLen(len(src))) + n, err := bcEncoding.Decode(dst, src) + if err != nil { + return nil, err + } + return dst[:n], nil +} diff --git a/nebtestkit/cases/contract/contract.lib.crypto.test.js b/nebtestkit/cases/contract/contract.lib.crypto.test.js index 187859903..6c170b1c0 100644 --- a/nebtestkit/cases/contract/contract.lib.crypto.test.js +++ b/nebtestkit/cases/contract/contract.lib.crypto.test.js @@ -382,8 +382,7 @@ var caseGroup = { status: 1, result: "\"n1F8QbdnhqpPXDPFT2c9a581tpia8iuF7o2\"" } - } - , + }, { "name": "0-5. test recoverAddress invalid alg", "testInput": { @@ -402,6 +401,44 @@ var caseGroup = { status: 1, result: "null" } + }, + { + "name": "0-6. test md5", + "testInput": { + value: "0", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000, + contract: { + function: "testMd5", + args: "[\"Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.\"]" + } + }, + "testExpect": { + canExcuteTx: true, + toBalanceChange: "0", + status: 1, + result: "\"9954125a33a380c3117269cff93f76a7\"" + } + }, + { + "name": "0-7. test base64", + "testInput": { + value: "0", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000, + contract: { + function: "testBase64", + args: "[\"https://y.qq.com/portal/player_radio.html#id=99\"]" + } + }, + "testExpect": { + canExcuteTx: true, + toBalanceChange: "0", + status: 1, + result: "\"aHR0cHM6Ly95LnFxLmNvbS9wb3J0YWwvcGxheWVyX3JhZGlvLmh0bWwjaWQ9OTk=\"" + } } ] }; diff --git a/nf/nvm/cfuncs.go b/nf/nvm/cfuncs.go index 8fc789def..826f0cae1 100644 --- a/nf/nvm/cfuncs.go +++ b/nf/nvm/cfuncs.go @@ -47,6 +47,8 @@ char *Sha256Func(const char *data, size_t *gasCnt); char *Sha3256Func(const char *data, size_t *gasCnt); char *Ripemd160Func(const char *data, size_t *gasCnt); char *RecoverAddressFunc(int alg, const char *data, const char *sign, size_t *gasCnt); +char *Md5Func(const char *data, size_t *gasCnt); +char *Base64Func(const char *data, size_t *gasCnt); // The gateway functions. void V8Log_cgo(int level, const char *msg) { @@ -100,6 +102,12 @@ char *Ripemd160Func_cgo(const char *data, size_t *gasCnt) { char *RecoverAddressFunc_cgo(int alg, const char *data, const char *sign, size_t *gasCnt) { return RecoverAddressFunc(alg, data, sign, gasCnt); } +char *Md5Func_cgo(const char *data, size_t *gasCnt) { + return Md5Func(data, gasCnt); +} +char *Base64Func_cgo(const char *data, size_t *gasCnt) { + return Base64Func(data, gasCnt); +} */ import "C" diff --git a/nf/nvm/crypto.go b/nf/nvm/crypto.go index 808533f57..d8818cc03 100644 --- a/nf/nvm/crypto.go +++ b/nf/nvm/crypto.go @@ -21,6 +21,8 @@ package nvm import "C" import ( + "crypto/md5" + "github.com/nebulasio/go-nebulas/core" "github.com/nebulasio/go-nebulas/crypto/hash" "github.com/nebulasio/go-nebulas/crypto/keystore" @@ -100,3 +102,23 @@ func RecoverAddressFunc(alg int, data, sign *C.char, gasCnt *C.size_t) *C.char { return C.CString(addr.String()) } + +// Md5Func .. +//export Md5Func +func Md5Func(data *C.char, gasCnt *C.size_t) *C.char { + s := C.GoString(data) + *gasCnt = C.size_t(len(s) * 100) + + r := md5.Sum([]byte(s)) + return C.CString(byteutils.Hex(r[:])) +} + +// Base64Func .. +//export Base64Func +func Base64Func(data *C.char, gasCnt *C.size_t) *C.char { + s := C.GoString(data) + *gasCnt = C.size_t(1000 + len(s)) + + r := hash.Base64Encode([]byte(s)) + return C.CString(string(r)) +} diff --git a/nf/nvm/engine_v8.go b/nf/nvm/engine_v8.go index 2f98e9d9b..4983ea13d 100644 --- a/nf/nvm/engine_v8.go +++ b/nf/nvm/engine_v8.go @@ -44,6 +44,8 @@ char *Sha256Func_cgo(const char *data, size_t *gasCnt); char *Sha3256Func_cgo(const char *data, size_t *gasCnt); char *Ripemd160Func_cgo(const char *data, size_t *gasCnt); char *RecoverAddressFunc_cgo(int alg, const char *data, const char *sign, size_t *gasCnt); +char *Md5Func_cgo(const char *data, size_t *gasCnt); +char *Base64Func_cgo(const char *data, size_t *gasCnt); void EventTriggerFunc_cgo(void *handler, const char *topic, const char *data, size_t *gasCnt); @@ -132,7 +134,9 @@ func InitV8Engine() { C.InitializeCrypto((C.Sha256Func)(unsafe.Pointer(C.Sha256Func_cgo)), (C.Sha3256Func)(unsafe.Pointer(C.Sha3256Func_cgo)), (C.Ripemd160Func)(unsafe.Pointer(C.Ripemd160Func_cgo)), - (C.RecoverAddressFunc)(unsafe.Pointer(C.RecoverAddressFunc_cgo))) + (C.RecoverAddressFunc)(unsafe.Pointer(C.RecoverAddressFunc_cgo)), + (C.Md5Func)(unsafe.Pointer(C.Md5Func_cgo)), + (C.Base64Func)(unsafe.Pointer(C.Base64Func_cgo))) } // DisposeV8Engine dispose the v8 engine. diff --git a/nf/nvm/native-lib/libnebulasv8.dylib b/nf/nvm/native-lib/libnebulasv8.dylib index 5102484b8..cc744b2c8 100755 Binary files a/nf/nvm/native-lib/libnebulasv8.dylib and b/nf/nvm/native-lib/libnebulasv8.dylib differ diff --git a/nf/nvm/test/contract_crypto.js b/nf/nvm/test/contract_crypto.js index e6e2508e2..d9e04cca8 100644 --- a/nf/nvm/test/contract_crypto.js +++ b/nf/nvm/test/contract_crypto.js @@ -26,6 +26,14 @@ Contract.prototype = { testRecoverAddress: function(alg, hash, sign) { return crypto.recoverAddress(alg, hash, sign); + }, + + testMd5: function(data) { + return crypto.md5(data); + }, + + testBase64: function(data) { + return crypto.base64(data); } }; diff --git a/nf/nvm/test/test_crypto.js b/nf/nvm/test/test_crypto.js index d4cf4674e..1b56fe93a 100644 --- a/nf/nvm/test/test_crypto.js +++ b/nf/nvm/test/test_crypto.js @@ -33,6 +33,8 @@ eq(crypto.sha256(input), "a32d6d686968192663b9c9e21e6a3ba1ba9b2e288470c2f98b7902 eq(crypto.sha3256(input), "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b"); eq(crypto.ripemd160(input), "4236aa9974eb7b9ddb0f7a7ed06d4bf3d9c0e386"); eq(crypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101"), "n1F8QbdnhqpPXDPFT2c9a581tpia8iuF7o2"); +eq(crypto.md5(input), "9954125a33a380c3117269cff93f76a7"); +eq(crypto.base64(input), "TmVidWxhcyBpcyBhIG5leHQgZ2VuZXJhdGlvbiBwdWJsaWMgYmxvY2tjaGFpbiwgYWltaW5nIGZvciBhIGNvbnRpbnVvdXNseSBpbXByb3ZpbmcgZWNvc3lzdGVtLg=="); // alg is not a safe integer try { diff --git a/nf/nvm/v8/engine.h b/nf/nvm/v8/engine.h index 88a6fddfe..97a1a1efa 100644 --- a/nf/nvm/v8/engine.h +++ b/nf/nvm/v8/engine.h @@ -81,11 +81,15 @@ typedef char *(*Sha3256Func)(const char *data, size_t *counterVal); typedef char *(*Ripemd160Func)(const char *data, size_t *counterVal); typedef char *(*RecoverAddressFunc)(int alg, const char *data, const char *sign, size_t *counterVal); +typedef char *(*Md5Func)(const char *data, size_t *counterVal); +typedef char *(*Base64Func)(const char *data, size_t *counterVal); EXPORT void InitializeCrypto(Sha256Func sha256, Sha3256Func sha3256, Ripemd160Func ripemd160, - RecoverAddressFunc recoverAddress); + RecoverAddressFunc recoverAddress, + Md5Func md5, + Base64Func base64); // version EXPORT char *GetV8Version(); diff --git a/nf/nvm/v8/lib/crypto.cc b/nf/nvm/v8/lib/crypto.cc index 18a80b46a..fd5686f8b 100644 --- a/nf/nvm/v8/lib/crypto.cc +++ b/nf/nvm/v8/lib/crypto.cc @@ -24,20 +24,25 @@ static Sha256Func sSha256 = NULL; static Sha3256Func sSha3256 = NULL; static Ripemd160Func sRipemd160 = NULL; static RecoverAddressFunc sRecoverAddress = NULL; +static Md5Func sMd5 = NULL; +static Base64Func sBase64 = NULL; void InitializeCrypto(Sha256Func sha256, Sha3256Func sha3256, Ripemd160Func ripemd160, - RecoverAddressFunc recoverAddress) { + RecoverAddressFunc recoverAddress, + Md5Func md5, + Base64Func base64) { sSha256 = sha256; sSha3256 = sha3256; sRipemd160 = ripemd160; sRecoverAddress = recoverAddress; + sMd5 = md5; + sBase64 = base64; } void NewCryptoInstance(Isolate *isolate, Local context) { Local cryptoTpl = ObjectTemplate::New(isolate); -// cryptoTpl->SetInternalFieldCount(1); cryptoTpl->Set(String::NewFromUtf8(isolate, "sha256"), FunctionTemplate::New(isolate, Sha256Callback), @@ -58,9 +63,18 @@ void NewCryptoInstance(Isolate *isolate, Local context) { FunctionTemplate::New(isolate, RecoverAddressCallback), static_cast(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly)); + + cryptoTpl->Set(String::NewFromUtf8(isolate, "md5"), + FunctionTemplate::New(isolate, Md5Callback), + static_cast(PropertyAttribute::DontDelete | + PropertyAttribute::ReadOnly)); + + cryptoTpl->Set(String::NewFromUtf8(isolate, "base64"), + FunctionTemplate::New(isolate, Base64Callback), + static_cast(PropertyAttribute::DontDelete | + PropertyAttribute::ReadOnly)); Local instance = cryptoTpl->NewInstance(context).ToLocalChecked(); -// instance->SetInternalField(0, External::New(isolate, handler)); context->Global()->DefineOwnProperty( context, String::NewFromUtf8(isolate, "_native_crypto"), instance, @@ -203,3 +217,63 @@ void RecoverAddressCallback(const FunctionCallbackInfo &info) { // record storage usage. IncrCounter(isolate, isolate->GetCurrentContext(), cnt); } + +// Md5Callback +void Md5Callback(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + + if (info.Length() != 1) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "md5() requires only 1 argument")); + return; + } + + Local data = info[0]; + if (!data->IsString()) { + isolate->ThrowException(String::NewFromUtf8(isolate, "md5() requires a string argument")); + return; + } + + size_t cnt = 0; + + char *value = sMd5(*String::Utf8Value(data->ToString()), &cnt); + if (value == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(String::NewFromUtf8(isolate, value)); + free(value); + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} + +// Base64Callback +void Base64Callback(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + + if (info.Length() != 1) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "base64() requires only 1 argument")); + return; + } + + Local data = info[0]; + if (!data->IsString()) { + isolate->ThrowException(String::NewFromUtf8(isolate, "base64() requires a string argument")); + return; + } + + size_t cnt = 0; + + char *value = sBase64(*String::Utf8Value(data->ToString()), &cnt); + if (value == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(String::NewFromUtf8(isolate, value)); + free(value); + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} \ No newline at end of file diff --git a/nf/nvm/v8/lib/crypto.h b/nf/nvm/v8/lib/crypto.h index ce09b1f82..d58826003 100644 --- a/nf/nvm/v8/lib/crypto.h +++ b/nf/nvm/v8/lib/crypto.h @@ -29,5 +29,7 @@ void Sha256Callback(const FunctionCallbackInfo &info); void Sha3256Callback(const FunctionCallbackInfo &info); void Ripemd160Callback(const FunctionCallbackInfo &info); void RecoverAddressCallback(const FunctionCallbackInfo &info); +void Md5Callback(const FunctionCallbackInfo &info); +void Base64Callback(const FunctionCallbackInfo &info); #endif //_NEBULAS_NF_NVM_V8_LIB_CRYPTO_H_ \ No newline at end of file diff --git a/nf/nvm/v8/libjs/1.0.5/crypto.js b/nf/nvm/v8/libjs/1.0.5/crypto.js index 84dcfde50..d91994e88 100644 --- a/nf/nvm/v8/libjs/1.0.5/crypto.js +++ b/nf/nvm/v8/libjs/1.0.5/crypto.js @@ -74,6 +74,24 @@ Crypto.prototype = { // sign: cipher hex string by private key, 130 chars return this.nativeCrypto.recoverAddress(alg, hash, sign); }, + + // case sensitive + md5: function(data) { + if (typeof data !== "string") { + throw new Error("input must be string"); + } + // any string + return this.nativeCrypto.md5(data); + }, + + // case sensitive + base64: function(data) { + if (typeof data !== "string") { + throw new Error("input must be string"); + } + // any string + return this.nativeCrypto.base64(data); + } }; module.exports = new Crypto(); \ No newline at end of file