Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
Nodemcu Firmware
Commits
a1122968
Commit
a1122968
authored
Sep 17, 2016
by
Johny Mattsson
Committed by
Marcel Stör
Sep 16, 2016
Browse files
Added crypto.new_hmac() feature. (#1499)
Docs for crypto module refactored for easier maintenance.
parent
19f80abb
Changes
4
Hide whitespace changes
Inline
Side-by-side
app/crypto/digests.c
View file @
a1122968
...
@@ -156,31 +156,22 @@ int ICACHE_FLASH_ATTR crypto_fhash (const digest_mech_info_t *mi,
...
@@ -156,31 +156,22 @@ int ICACHE_FLASH_ATTR crypto_fhash (const digest_mech_info_t *mi,
}
}
int
ICACHE_FLASH_ATTR
crypto_hmac
(
const
digest_mech_info_t
*
mi
,
void
crypto_hmac_begin
(
void
*
ctx
,
const
digest_mech_info_t
*
mi
,
const
char
*
data
,
size_t
data_len
,
const
char
*
key
,
size_t
key_len
,
uint8_t
*
k_opad
)
const
char
*
key
,
size_t
key_len
,
uint8_t
*
digest
)
{
{
if
(
!
mi
)
return
EINVAL
;
void
*
ctx
=
(
void
*
)
os_malloc
(
mi
->
ctx_size
);
if
(
!
ctx
)
return
ENOMEM
;
// If key too long, it needs to be hashed before use
// If key too long, it needs to be hashed before use
char
tmp
[
mi
->
digest_size
];
if
(
key_len
>
mi
->
block_size
)
if
(
key_len
>
mi
->
block_size
)
{
{
mi
->
create
(
ctx
);
mi
->
update
(
ctx
,
key
,
key_len
);
mi
->
update
(
ctx
,
key
,
key_len
);
mi
->
finalize
(
digest
,
ctx
);
mi
->
finalize
(
tmp
,
ctx
);
key
=
digest
;
key
=
tmp
;
key_len
=
mi
->
digest_size
;
key_len
=
mi
->
digest_size
;
mi
->
create
(
ctx
);
// refresh
}
}
const
size_t
bs
=
mi
->
block_size
;
const
size_t
bs
=
mi
->
block_size
;
uint8_t
k_ipad
[
bs
];
uint8_t
k_ipad
[
bs
];
uint8_t
k_opad
[
bs
];
os_memset
(
k_ipad
,
0x36
,
bs
);
os_memset
(
k_ipad
,
0x36
,
bs
);
os_memset
(
k_opad
,
0x5c
,
bs
);
os_memset
(
k_opad
,
0x5c
,
bs
);
...
@@ -191,16 +182,42 @@ int ICACHE_FLASH_ATTR crypto_hmac (const digest_mech_info_t *mi,
...
@@ -191,16 +182,42 @@ int ICACHE_FLASH_ATTR crypto_hmac (const digest_mech_info_t *mi,
k_opad
[
i
]
^=
key
[
i
];
k_opad
[
i
]
^=
key
[
i
];
}
}
mi
->
create
(
ctx
);
mi
->
update
(
ctx
,
k_ipad
,
bs
);
mi
->
update
(
ctx
,
k_ipad
,
bs
);
mi
->
update
(
ctx
,
data
,
data_len
);
}
void
crypto_hmac_finalize
(
void
*
ctx
,
const
digest_mech_info_t
*
mi
,
const
uint8_t
*
k_opad
,
uint8_t
*
digest
)
{
mi
->
finalize
(
digest
,
ctx
);
mi
->
finalize
(
digest
,
ctx
);
mi
->
create
(
ctx
);
mi
->
create
(
ctx
);
mi
->
update
(
ctx
,
k_opad
,
bs
);
mi
->
update
(
ctx
,
k_opad
,
mi
->
block_size
);
mi
->
update
(
ctx
,
digest
,
mi
->
digest_size
);
mi
->
update
(
ctx
,
digest
,
mi
->
digest_size
);
mi
->
finalize
(
digest
,
ctx
);
mi
->
finalize
(
digest
,
ctx
);
}
os_free
(
ctx
);
int
crypto_hmac
(
const
digest_mech_info_t
*
mi
,
const
char
*
data
,
size_t
data_len
,
const
char
*
key
,
size_t
key_len
,
uint8_t
*
digest
)
{
if
(
!
mi
)
return
EINVAL
;
struct
{
uint8_t
ctx
[
mi
->
ctx_size
];
uint8_t
k_opad
[
mi
->
block_size
];
}
*
tmp
=
os_malloc
(
sizeof
(
*
tmp
));
if
(
!
tmp
)
return
ENOMEM
;
mi
->
create
(
tmp
->
ctx
);
crypto_hmac_begin
(
tmp
->
ctx
,
mi
,
key
,
key_len
,
tmp
->
k_opad
);
mi
->
update
(
tmp
->
ctx
,
data
,
data_len
);
crypto_hmac_finalize
(
tmp
->
ctx
,
mi
,
tmp
->
k_opad
,
digest
);
os_free
(
tmp
);
return
0
;
return
0
;
}
}
app/crypto/digests.h
View file @
a1122968
...
@@ -66,7 +66,35 @@ int crypto_hash (const digest_mech_info_t *mi, const char *data, size_t data_len
...
@@ -66,7 +66,35 @@ int crypto_hash (const digest_mech_info_t *mi, const char *data, size_t data_len
int
crypto_fhash
(
const
digest_mech_info_t
*
mi
,
read_fn
read
,
int
readarg
,
uint8_t
*
digest
);
int
crypto_fhash
(
const
digest_mech_info_t
*
mi
,
read_fn
read
,
int
readarg
,
uint8_t
*
digest
);
/**
/**
* Generate a HMAC signature.
* Commence calculating a HMAC signature.
*
* Once this fuction returns successfully, the @c ctx may be updated with
* data in multiple passes uses @c mi->update(), before being finalized via
* @c crypto_hmac_finalize().
*
* @param ctx A created context for the given mech @c mi.
* @param mi A mech from @c crypto_digest_mech().
* @param key The key bytes to use.
* @param key_len Number of bytes the @c key comprises.
* @param k_opad Buffer of size @c mi->block_size bytes to store the second
* round of key material in.
* Must be passed to @c crypto_hmac_finalize().
*/
void
crypto_hmac_begin
(
void
*
ctx
,
const
digest_mech_info_t
*
mi
,
const
char
*
key
,
size_t
key_len
,
uint8_t
*
k_opad
);
/**
* Finalizes calculating a HMAC signature.
* @param ctx The created context used with @c crypto_hmac_begin().
* @param mi The mech used with @c crypto_hmac_begin().
* @param k_opad Second round of keying material, obtained
* from @c crypto_hmac_begin().
* @param digest Output buffer, must be at least @c mi->digest_size in size.
*/
void
crypto_hmac_finalize
(
void
*
ctx
,
const
digest_mech_info_t
*
mi
,
const
uint8_t
*
k_opad
,
uint8_t
*
digest
);
/**
* Generate a HMAC signature in one pass.
* Implemented in terms of @c crypto_hmac_begin() / @c crypto_hmac_end().
* @param mi A mech from @c crypto_digest_mech(). A null pointer @c mi
* @param mi A mech from @c crypto_digest_mech(). A null pointer @c mi
* is harmless, but will of course result in an error return.
* is harmless, but will of course result in an error return.
* @param data The data to generate a signature for.
* @param data The data to generate a signature for.
...
...
app/modules/crypto.c
View file @
a1122968
...
@@ -9,11 +9,18 @@
...
@@ -9,11 +9,18 @@
#include "vfs.h"
#include "vfs.h"
#include "../crypto/digests.h"
#include "../crypto/digests.h"
#include "../crypto/mech.h"
#include "../crypto/mech.h"
#include "lmem.h"
#include "user_interface.h"
#include "user_interface.h"
#include "rom.h"
#include "rom.h"
typedef
struct
{
const
digest_mech_info_t
*
mech_info
;
void
*
ctx
;
uint8_t
*
k_opad
;
}
digest_user_datum_t
;
/**
/**
* hash = crypto.sha1(input)
* hash = crypto.sha1(input)
*
*
...
@@ -150,11 +157,6 @@ static int crypto_lhash (lua_State *L)
...
@@ -150,11 +157,6 @@ static int crypto_lhash (lua_State *L)
return
1
;
return
1
;
}
}
typedef
struct
digest_user_datum_t
{
const
digest_mech_info_t
*
mech_info
;
void
*
ctx
;
}
digest_user_datum_t
;
/* General Usage for extensible hash functions:
/* General Usage for extensible hash functions:
* sha = crypto.new_hash("MD5")
* sha = crypto.new_hash("MD5")
* sha.update("Data")
* sha.update("Data")
...
@@ -162,19 +164,30 @@ typedef struct digest_user_datum_t {
...
@@ -162,19 +164,30 @@ typedef struct digest_user_datum_t {
* strdigest = crypto.toHex(sha.finalize())
* strdigest = crypto.toHex(sha.finalize())
*/
*/
/* crypto.new_hash("MECHTYPE") */
#define WANT_HASH 0
static
int
crypto_new_hash
(
lua_State
*
L
)
#define WANT_HMAC 1
static
int
crypto_new_hash_hmac
(
lua_State
*
L
,
int
what
)
{
{
const
digest_mech_info_t
*
mi
=
crypto_digest_mech
(
luaL_checkstring
(
L
,
1
));
const
digest_mech_info_t
*
mi
=
crypto_digest_mech
(
luaL_checkstring
(
L
,
1
));
if
(
!
mi
)
if
(
!
mi
)
return
bad_mech
(
L
);
return
bad_mech
(
L
);
void
*
ctx
=
os_malloc
(
mi
->
ctx_size
);
size_t
len
=
0
;
if
(
ctx
==
NULL
)
const
char
*
key
=
0
;
return
bad_mem
(
L
);
uint8_t
*
k_opad
=
0
;
if
(
what
==
WANT_HMAC
)
{
key
=
luaL_checklstring
(
L
,
2
,
&
len
);
k_opad
=
luaM_malloc
(
L
,
mi
->
block_size
);
}
void
*
ctx
=
luaM_malloc
(
L
,
mi
->
ctx_size
);
mi
->
create
(
ctx
);
mi
->
create
(
ctx
);
if
(
what
==
WANT_HMAC
)
crypto_hmac_begin
(
ctx
,
mi
,
key
,
len
,
k_opad
);
// create a userdataum with specific metatable
// create a userdataum with specific metatable
digest_user_datum_t
*
dudat
=
(
digest_user_datum_t
*
)
lua_newuserdata
(
L
,
sizeof
(
digest_user_datum_t
));
digest_user_datum_t
*
dudat
=
(
digest_user_datum_t
*
)
lua_newuserdata
(
L
,
sizeof
(
digest_user_datum_t
));
luaL_getmetatable
(
L
,
"crypto.hash"
);
luaL_getmetatable
(
L
,
"crypto.hash"
);
...
@@ -183,10 +196,24 @@ static int crypto_new_hash (lua_State *L)
...
@@ -183,10 +196,24 @@ static int crypto_new_hash (lua_State *L)
// Set pointers to the mechanics and CTX
// Set pointers to the mechanics and CTX
dudat
->
mech_info
=
mi
;
dudat
->
mech_info
=
mi
;
dudat
->
ctx
=
ctx
;
dudat
->
ctx
=
ctx
;
dudat
->
k_opad
=
k_opad
;
return
1
;
// Pass userdata object back
return
1
;
// Pass userdata object back
}
}
/* crypto.new_hash("MECHTYPE") */
static
int
crypto_new_hash
(
lua_State
*
L
)
{
return
crypto_new_hash_hmac
(
L
,
WANT_HASH
);
}
/* crypto.new_hmac("MECHTYPE", "KEY") */
static
int
crypto_new_hmac
(
lua_State
*
L
)
{
return
crypto_new_hash_hmac
(
L
,
WANT_HMAC
);
}
/* Called as object, params:
/* Called as object, params:
1 - userdata "this"
1 - userdata "this"
2 - new string to add to the hash state */
2 - new string to add to the hash state */
...
@@ -197,7 +224,6 @@ static int crypto_hash_update (lua_State *L)
...
@@ -197,7 +224,6 @@ static int crypto_hash_update (lua_State *L)
size_t
sl
;
size_t
sl
;
dudat
=
(
digest_user_datum_t
*
)
luaL_checkudata
(
L
,
1
,
"crypto.hash"
);
dudat
=
(
digest_user_datum_t
*
)
luaL_checkudata
(
L
,
1
,
"crypto.hash"
);
luaL_argcheck
(
L
,
dudat
,
1
,
"crypto.hash expected"
);
const
digest_mech_info_t
*
mi
=
dudat
->
mech_info
;
const
digest_mech_info_t
*
mi
=
dudat
->
mech_info
;
...
@@ -217,12 +243,14 @@ static int crypto_hash_finalize (lua_State *L)
...
@@ -217,12 +243,14 @@ static int crypto_hash_finalize (lua_State *L)
size_t
sl
;
size_t
sl
;
dudat
=
(
digest_user_datum_t
*
)
luaL_checkudata
(
L
,
1
,
"crypto.hash"
);
dudat
=
(
digest_user_datum_t
*
)
luaL_checkudata
(
L
,
1
,
"crypto.hash"
);
luaL_argcheck
(
L
,
dudat
,
1
,
"crypto.hash expected"
);
const
digest_mech_info_t
*
mi
=
dudat
->
mech_info
;
const
digest_mech_info_t
*
mi
=
dudat
->
mech_info
;
uint8_t
digest
[
mi
->
digest_size
];
// Allocate as local
uint8_t
digest
[
mi
->
digest_size
];
// Allocate as local
mi
->
finalize
(
digest
,
dudat
->
ctx
);
if
(
dudat
->
k_opad
)
crypto_hmac_finalize
(
dudat
->
ctx
,
mi
,
dudat
->
k_opad
,
digest
);
else
mi
->
finalize
(
digest
,
dudat
->
ctx
);
lua_pushlstring
(
L
,
digest
,
sizeof
(
digest
));
lua_pushlstring
(
L
,
digest
,
sizeof
(
digest
));
return
1
;
return
1
;
...
@@ -235,9 +263,11 @@ static int crypto_hash_gcdelete (lua_State *L)
...
@@ -235,9 +263,11 @@ static int crypto_hash_gcdelete (lua_State *L)
digest_user_datum_t
*
dudat
;
digest_user_datum_t
*
dudat
;
dudat
=
(
digest_user_datum_t
*
)
luaL_checkudata
(
L
,
1
,
"crypto.hash"
);
dudat
=
(
digest_user_datum_t
*
)
luaL_checkudata
(
L
,
1
,
"crypto.hash"
);
luaL_argcheck
(
L
,
dudat
,
1
,
"crypto.hash expected"
);
os_free
(
dudat
->
ctx
);
// luaM_free() uses type info to obtain original size, so have to delve
// one level deeper and explicitly pass the size due to void*
luaM_realloc_
(
L
,
dudat
->
ctx
,
dudat
->
mech_info
->
ctx_size
,
0
);
luaM_free
(
L
,
dudat
->
k_opad
);
return
0
;
return
0
;
}
}
...
@@ -386,6 +416,7 @@ static const LUA_REG_TYPE crypto_map[] = {
...
@@ -386,6 +416,7 @@ static const LUA_REG_TYPE crypto_map[] = {
{
LSTRKEY
(
"fhash"
),
LFUNCVAL
(
crypto_flhash
)
},
{
LSTRKEY
(
"fhash"
),
LFUNCVAL
(
crypto_flhash
)
},
{
LSTRKEY
(
"new_hash"
),
LFUNCVAL
(
crypto_new_hash
)
},
{
LSTRKEY
(
"new_hash"
),
LFUNCVAL
(
crypto_new_hash
)
},
{
LSTRKEY
(
"hmac"
),
LFUNCVAL
(
crypto_lhmac
)
},
{
LSTRKEY
(
"hmac"
),
LFUNCVAL
(
crypto_lhmac
)
},
{
LSTRKEY
(
"new_hmac"
),
LFUNCVAL
(
crypto_new_hmac
)
},
{
LSTRKEY
(
"encrypt"
),
LFUNCVAL
(
lcrypto_encrypt
)
},
{
LSTRKEY
(
"encrypt"
),
LFUNCVAL
(
lcrypto_encrypt
)
},
{
LSTRKEY
(
"decrypt"
),
LFUNCVAL
(
lcrypto_decrypt
)
},
{
LSTRKEY
(
"decrypt"
),
LFUNCVAL
(
lcrypto_decrypt
)
},
{
LNILKEY
,
LNILVAL
}
{
LNILKEY
,
LNILVAL
}
...
...
docs/en/modules/crypto.md
View file @
a1122968
...
@@ -5,6 +5,16 @@
...
@@ -5,6 +5,16 @@
The crypto modules provides various functions for working with cryptographic algorithms.
The crypto modules provides various functions for working with cryptographic algorithms.
The following encryption/decryption algorithms/modes are supported:
-
`"AES-ECB"`
for 128-bit AES in ECB mode (NOT recommended)
-
`"AES-CBC"`
for 128-bit AES in CBC mode
The following hash algorithms are supported:
-
MD2 (not available by default, has to be explicitly enabled in
`app/include/user_config.h`
)
-
MD5
-
SHA1
-
SHA256, SHA384, SHA512 (unless disabled in
`app/include/user_config.h`
)
## crypto.encrypt()
## crypto.encrypt()
Encrypts Lua strings.
Encrypts Lua strings.
...
@@ -13,9 +23,7 @@ Encrypts Lua strings.
...
@@ -13,9 +23,7 @@ Encrypts Lua strings.
`crypto.encrypt(algo, key, plain [, iv])`
`crypto.encrypt(algo, key, plain [, iv])`
#### Parameters
#### Parameters
-
`algo`
the name of the encryption algorithm to use, one of
-
`algo`
the name of a supported encryption algorithm to use
-
`"AES-ECB"`
for 128-bit AES in ECB mode
-
`"AES-CBC"`
for 128-bit AES in CBC mode
-
`key`
the encryption key as a string; for AES encryption this
*MUST*
be 16 bytes long
-
`key`
the encryption key as a string; for AES encryption this
*MUST*
be 16 bytes long
-
`plain`
the string to encrypt; it will be automatically zero-padded to a 16-byte boundary if necessary
-
`plain`
the string to encrypt; it will be automatically zero-padded to a 16-byte boundary if necessary
-
`iv`
the initilization vector, if using AES-CBC; defaults to all-zero if not given
-
`iv`
the initilization vector, if using AES-CBC; defaults to all-zero if not given
...
@@ -40,9 +48,7 @@ Decrypts previously encrypted data.
...
@@ -40,9 +48,7 @@ Decrypts previously encrypted data.
`crypto.decrypt(algo, key, cipher [, iv])`
`crypto.decrypt(algo, key, cipher [, iv])`
#### Parameters
#### Parameters
-
`algo`
the name of the encryption algorithm to use, one of
-
`algo`
the name of a supported encryption algorithm to use
-
`"AES-ECB"`
for 128-bit AES in ECB mode
-
`"AES-CBC"`
for 128-bit AES in CBC mode
-
`key`
the encryption key as a string; for AES encryption this
*MUST*
be 16 bytes long
-
`key`
the encryption key as a string; for AES encryption this
*MUST*
be 16 bytes long
-
`cipher`
the cipher text to decrypt (as obtained from
`crypto.encrypt()`
)
-
`cipher`
the cipher text to decrypt (as obtained from
`crypto.encrypt()`
)
-
`iv`
the initilization vector, if using AES-CBC; defaults to all-zero if not given
-
`iv`
the initilization vector, if using AES-CBC; defaults to all-zero if not given
...
@@ -75,13 +81,6 @@ Compute a cryptographic hash of a a file.
...
@@ -75,13 +81,6 @@ Compute a cryptographic hash of a a file.
-
`algo`
the hash algorithm to use, case insensitive string
-
`algo`
the hash algorithm to use, case insensitive string
-
`filename`
the path to the file to hash
-
`filename`
the path to the file to hash
Supported hash algorithms are:
-
MD2 (not available by default, has to be explicitly enabled in
`app/include/user_config.h`
)
-
MD5
-
SHA1
-
SHA256, SHA384, SHA512 (unless disabled in
`app/include/user_config.h`
)
#### Returns
#### Returns
A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use
[
`crypto.toHex()`
](
#cryptotohex
)
.
A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use
[
`crypto.toHex()`
](
#cryptotohex
)
.
...
@@ -101,13 +100,6 @@ Compute a cryptographic hash of a Lua string.
...
@@ -101,13 +100,6 @@ Compute a cryptographic hash of a Lua string.
`algo`
the hash algorithm to use, case insensitive string
`algo`
the hash algorithm to use, case insensitive string
`str`
string to hash contents of
`str`
string to hash contents of
Supported hash algorithms are:
-
MD2 (not available by default, has to be explicitly enabled in
`app/include/user_config.h`
)
-
MD5
-
SHA1
-
SHA256, SHA384, SHA512 (unless disabled in
`app/include/user_config.h`
)
#### Returns
#### Returns
A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use
[
`crypto.toHex()`
](
#cryptotohex
)
.
A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use
[
`crypto.toHex()`
](
#cryptotohex
)
.
...
@@ -126,13 +118,6 @@ Create a digest/hash object that can have any number of strings added to it. Obj
...
@@ -126,13 +118,6 @@ Create a digest/hash object that can have any number of strings added to it. Obj
#### Parameters
#### Parameters
`algo`
the hash algorithm to use, case insensitive string
`algo`
the hash algorithm to use, case insensitive string
Supported hash algorithms are:
-
MD2 (not available by default, has to be explicitly enabled in
`app/include/user_config.h`
)
-
MD5
-
SHA1
-
SHA256, SHA384, SHA512 (unless disabled in
`app/include/user_config.h`
)
#### Returns
#### Returns
Userdata object with
`update`
and
`finalize`
functions available.
Userdata object with
`update`
and
`finalize`
functions available.
...
@@ -157,13 +142,6 @@ Compute a [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication
...
@@ -157,13 +142,6 @@ Compute a [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication
-
`str`
data to calculate the hash for
-
`str`
data to calculate the hash for
-
`key`
key to use for signing, may be a binary string
-
`key`
key to use for signing, may be a binary string
Supported hash algorithms are:
-
MD2 (not available by default, has to be explicitly enabled in
`app/include/user_config.h`
)
-
MD5
-
SHA1
-
SHA256, SHA384, SHA512 (unless disabled in
`app/include/user_config.h`
)
#### Returns
#### Returns
A binary string containing the HMAC signature. Use
[
`crypto.toHex()`
](
#cryptotohex
)
to obtain the textual version.
A binary string containing the HMAC signature. Use
[
`crypto.toHex()`
](
#cryptotohex
)
to obtain the textual version.
...
@@ -172,6 +150,30 @@ A binary string containing the HMAC signature. Use [`crypto.toHex()`](#cryptotoh
...
@@ -172,6 +150,30 @@ A binary string containing the HMAC signature. Use [`crypto.toHex()`](#cryptotoh
print
(
crypto
.
toHex
(
crypto
.
hmac
(
"sha1"
,
"abc"
,
"mysecret"
)))
print
(
crypto
.
toHex
(
crypto
.
hmac
(
"sha1"
,
"abc"
,
"mysecret"
)))
```
```
## crypto.new_hmac()
Create a hmac object that can have any number of strings added to it. Object has
`update`
and
`finalize`
functions.
#### Syntax
`hmacobj = crypto.new_hmac(algo, key)`
#### Parameters
-
`algo`
the hash algorithm to use, case insensitive string
-
`key`
the key to use (may be a binary string)
#### Returns
Userdata object with
`update`
and
`finalize`
functions available.
#### Example
```
lua
hmacobj
=
crypto
.
new_hmac
(
"SHA1"
,
"s3kr3t"
)
hmacobj
:
update
(
"FirstString"
))
hmacobj
:
update
(
"SecondString"
))
digest
=
hmacobj
:
finalize
()
print
(
crypto
.
toHex
(
digest
))
```
## crypto.mask()
## crypto.mask()
Applies an XOR mask to a Lua string. Note that this is not a proper cryptographic mechanism, but some protocols may use it nevertheless.
Applies an XOR mask to a Lua string. Note that this is not a proper cryptographic mechanism, but some protocols may use it nevertheless.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment