Jump to content

Using Hex as Temporary Encryption


Ranger Fox

Recommended Posts

When I played the first available location-based cartridge, I tried guessing the answers. This worked up to the point where I had to guess the buoy number in the harbor. "There has to be a better way," I wondered. Sure enough, I found a way to read the answers right out of a compiled cartridge. Since then, I have been able to solve virtually every question a cartridge asked by referencing the compiled code.

 

Since then, many user developers have been aware of this hack. Some have asked about ways around this or some sort of encryption. So, today, I tried to look into it.

 

The Interest in Encrypting Data

The use case is simple enough. A user enters text (a string) into an input. That string is compared against a value. If it's equal, the user entered the right answer. This value is almost always entered into the builder right there as text comparison. The line looks like this in the actual lua source (the builder's output):

if   Wherigo.NoCaseEquals(var,"correct answer") then

The problem with this is that it can be read easily from a compiled cartridge file:

var	Wherigo	NoCaseEquals   correct answer

I can easily read the answer from the compiled output. Such an act will make people think twice before using inputs as a way to create a Wherigo experience.

 

This is where this article comes in. I've only been on this stuff since Wherigo was released. Groundspeak is the best to ask about solving this problem and I believe we will see at some time an integrated, codeless fix to this problem. But, until then, there is still something passionate user developers can do to prevent easy hacks. That is what I will cover here. Just keep in mind I don't profess to have a correct answer or to be an expert on the subject. Keep in mind this will require some work and knowledge on your part to implement. But if you need it, here's one approach you can use.

 

Encrypting Text - A Use Case

So we need to encrypt text so the answer can't easily be found by opening the source. Before we decide how we will do it, we should know where. Open your cartridge's lua source file and you'll see the following type of code. I'm using "var" as the variable name.

if   Wherigo.NoCaseEquals(var,"correct answer") then

As you can see, we're comparing the user's answer directly to the correct answer.

 

We need to change this "correct answer" string to something encrypted. We also need to encrypt the variable we're comparing to. This way, we can compare only the encrypted values and not have our "correct answer" seen anywhere in the source, much less the compiled code. Now, we know where to do it.

 

MD5 Hashing

One popular way to encrypt data is to use an MD5 hashing algorithm. Looking at this site will tell you about MD5 in Lua. To make this story short, I tried including the MD5 dll as a reference in my test code, then tried to include the required core.dll, but didn't meet with much success. This would be the ideal solution, if it worked.

 

I did, however, learn something.

 

Strings and Hex

While not encryption in its own right, converting a string into something else still makes it really difficult to hack. We can't use ROT13 (geocache hint encryption) because it is widely known and easy to decrypt. However, we can at least convert the string to something else. Two options would be hexadecimal and octal, take your pick. I'll choose hex for this article.

 

How secure is this? This is still string substitution, so it's poor. However, you can still add your own style to it by making it more complicated. But, at the least, it's something to deter some people from looking at the source. And, hopefully, this will get you thinking about other ways to inject a little encryption.

 

Let's continue with the hex example. Say like the correct answer is "Wherigo". With this conversion, it would be "7768657269676f". That looks a lot better than just having the answer verbatim--anything would be better.

 

What's Involved -- Implementing it in the Source

First off, you will be changing some code in the lua source file. The builder looks at this code, so there's a good likelihood it will overwrite your changes if you save the lua source in the builder after you edit and save the source. This should be done as the last step. After that, I can open the cartridge in the builder, compile it, and play with it in the emulator. This does work.

 

First off, you need the function that will do the conversion for you. This is the function:

function concathex (k)
 --k = string.lower(k)
 return (string.gsub(k, ".", function (c)
	   return string.format("%02x", string.byte(c))
	 end))
end

Put this inside the author functions section (search for "-- #Author Functions Go Here# --" and insert the code between that line and the "-- #End Author Functions# --" line). Putting the function here guarantees the builder will never mess with it. That's a good thing.

 

The function name is "concathex". I'll reference it by name later in this article. It IS case sensitive by design. If you want case insensitivity, uncomment (remove the "--" from) the "k = string.lower(k)" line.

 

Next, find your string comparisons in your code. That's easy to do. Start from the top and do a search for "Wherigo.NoCaseEquals(" to find every one. You'll see your plain text answer right there. Go to this web page and enter your plain text answer in the string box. It will convert it to hex. Don't forget about your casing! The string "Wherigo" will convert to "77 68 65 72 69 67 6F". Remove the spaces, convert it to lower case, and replace your correct answer string with this hex string.

 

Finally, we need to convert the player's answer to hex or it'll never match--unless the user knows hex. If you're only doing one if/then comparison to determine a correct answer, you can change the code right there to look like the following:

if   Wherigo.NoCaseEquals(concathex(var),"7768657269676f") then

If you're doing more comparisons, it makes sense to look elsewhere in the code. Generally, you should see something like "var = input" (keep in mind "var" is the name of my variable) a few lines above in the code. Change this to:

var = concathex(var)

If you do this, you won't have to mess with any of your if/then comparison code later in the function.

 

Conclusion

Encrypting the correct answers is a really good thing to do. However, as of the date this article was written, no one knows of a way to do so within the current Wherigo code base. Converting a correct answer to something else--even if it's just a little more secure or ambiguous--is better than leaving it as-is. I have covered one such way to do so using hexadecimal conversion. Using the same techniques, you can come up with your own way to use conversion to encrypt data. (Or someone could figure out how to reference the MD5 libraries in a Wherigo cartridge.)

 

I have attached the source of a test cartridge I created to show this in action.

 

Any comments or ideas for other ways to take this?

Hex_Test.zip

Link to comment

Wow Ranger Fox, Where do you get the time to do all this!

 

Well I'm sure you are more proficient than I am. This is interesting and does give me ideas. It is a good general solution, although I may try different or multiple encryption so that people with say your knowledge, can't hack it either (assuming they can't hack the code another way). Wish I knew code better.

 

Thanks again for an easy to follow post.

 

Blauweogen

(Don)

Link to comment

Ranger,

 

Thanks for the excellent article. I'd considered something like this a few times, but don't know enough about Builder or LUA to be able to manage it just yet. I'll definitely be giving this a shot when I get my first "real" cartridge up and running.

 

Would it be possible to enter the code in the Events section of an Input in builder? I can see in the Inputs section in builder than it has "Bold = Script Exists" in the description window. If you can enter the code here for every input, wouldn't that make it "normal" in builder, so you don't have to worry about Builder stomping on the LUA source code after editing it?

Link to comment

I took a look at the Vigenere cypher link you posted. To use Vigenere, you would have to pick a cipher key equal to that of the text you wish to encrypt. For simplicity, you could include a long string of text that will be shortened to match the length of whatever needs to be encrypted; one key for all encryptions. This would work, but your key would still be in plain text, which will be visible to any enterprising individual, which is also one of the problems with the hex approach.

 

But let's look at it the other way around. The way you're encrypting players' answers to match your already-encrypted answers could be the thing that is obscured. You may know the encrypted answer, but unless you know how text is encrypted, it does you little good. So instead of finding an unbreakable algorithm, just don't tell people how you encrypted the text.

 

=============================================================

 

To illustrate this point, let's have a little challenge. I have attached a compiled cartridge to this post. I did my own type of wacky encryption. It begins by converting the text into hex, then does a little math on it such that you usually get up to six digit numbers regardless of text length. It uses a combination of multiplication, addition, division, and exponentiation. By far, there is not a one for one correlation between input and output--that is, more than one type of input can get you that output.

 

For this challenge, move the character to the field and talk to the emcee character. The emcee will give you four choices, three of which will be to answer a different question. The fourth option, "Encrypt Something", I included to illustrate what any string looks like when encrypted. It's your ray of light and hope. Look at the compiled code all you want; you can see the three encrypted answers: 449913, 452877, and 474086.

 

The questions have real, justifiable answers.

 

As I wrote, the purpose of this is to illustrate that as long as your method of encryption is not known, it's almost impossible to decrypt the plain text encrypted answers. In all honesty, I do not expect anyone to complete this challenge. I'll reveal the answers and post the source code when everyone gives up, but not for a few weeks.

 

I do ask one thing while you are playing with this cartridge. Ask yourself if you are fine with creating experiences only playable in a specific area. I have enjoyed seeing what others come up with and looking at the area through satellite view on the emulator. I learn something about the location with each cartridge I play. Wouldn't Wherigo be less enjoyable if you couldn't even play a cartridge unless it was in your area? Why not allow people to finish the cartridge, but have the completion flag set by a simple check against the player's altitude (which shows as zero in the emulator)? Such a simple thing would take it from being about the "numbers" to being about the experience. Writing about encryption is a difficult decision for me because I'm concerned about missing out on those experiences. There are some good uses for encryption, of course, but not all the time. Just keep that in mind, please.

Ranger_Fox_Encryption.zip

Link to comment

I do ask one thing while you are playing with this cartridge. Ask yourself if you are fine with creating experiences only playable in a specific area. I have enjoyed seeing what others come up with and looking at the area through satellite view on the emulator. I learn something about the location with each cartridge I play. Wouldn't Wherigo be less enjoyable if you couldn't even play a cartridge unless it was in your area?

 

Ranger,

 

I understand your concern and I agree with it. I'm sure I'll be playing plenty of carts that I can't physically get to in order to play. But, the opposite side of that coin should also be considered... I can see people having 30 carts listed in their profile, from all over the world, even though they've never left their home town. I'd be willing to be that someone figuring out how to work around the altitude zero issue eventually.

 

When designing carts, I'd be happy to allow someone to go through everything except one question, which I would encrypt. If they get it right, I know they're on location and they get a completion code, badge, congratulatory email, etc. That "final completion" should be something that someone can ONLY get by running the actual cartridge in the right place. Encryption of some kind allows us to keep that sense of achievement. It's an important tool. Honestly, I think encrypted answers should be available as an option in the builder and it wouldn't surprise me to see it in a future version. Until then, I appreciate your work on it. It's good to know there are options available should I need them.

Link to comment

 

I do ask one thing while you are playing with this cartridge. Ask yourself if you are fine with creating experiences only playable in a specific area. I have enjoyed seeing what others come up with and looking at the area through satellite view on the emulator. I learn something about the location with each cartridge I play. Wouldn't Wherigo be less enjoyable if you couldn't even play a cartridge unless it was in your area? Why not allow people to finish the cartridge, but have the completion flag set by a simple check against the player's altitude (which shows as zero in the emulator)? Such a simple thing would take it from being about the "numbers" to being about the experience. Writing about encryption is a difficult decision for me because I'm concerned about missing out on those experiences. There are some good uses for encryption, of course, but not all the time. Just keep that in mind, please.

 

I agree too. I like being able to play from home since I can't go around the country yet. I think the "experience" whether in area or play anywhere is what it is all about.

I also have an issue with my last cartridge "Port Orange Clue Hunt" where I made two buttons. One that was for out of area players (emulator, most likely) and one for local. It gives non-local players some additional things like a zone/object being visable since the areas are not on the map and you need to look at something. Playing it for real, you don't want to give this away, they have to find it. I also included some events happening, since you don't get to experience the area for real.

 

I was wondering what anyone thought of this approach? (the buttons I mean)

 

The problem was I couldn't mark it play anywhere AND start at a location (unless I'm missing something).

 

Of course it is up to the player to not get the answers from the code if they want to experience it fully. I am wanting to make contests in the future where encryption is a must though.

 

Wherigo is very exciting and I think it will be even more so as it develops. I wish I had a Pocket PC, have to wait a while on that.

Thanks everyone for your knowledge and willingness to help us noobs. Oh, I can't wait to check out your cart Ranger Fox.

 

Blauweogen

Link to comment

I was wondering what anyone thought of this approach? (the buttons I mean)

I thought that was a novel approach and well done. Something like that could become popular.

 

Thanks everyone for your knowledge and willingness to help us noobs. Oh, I can't wait to check out your cart Ranger Fox.

Thank you for the compliment. While you can check out the test cartridges I make, I'm not planning to create a real cartridge just yet. If or when I do, it will be location-specific and involve a combination of virtual (Wherigo) and real (geocaching) elements. For instance, finding a suitcase in the woods and taking some information you need with you or making a rubbing of a design and later using it to orient your way.

 

Perhaps when I have more free time than I have at the moment. Some days I'm even strained to keep my one-a-day geocaching streak alive; it's around 340 now.

Link to comment

Hmm, well, I obviously am not an encryption expert, I really thought maybe I could figure out your method though, and get the answers, specially since you gave me some hints. Somehow I think it would be a very long time if I ever did! Very interesting. much harder than hex. I'm not sure if an expert would (in any reasonable amount of time) figure it out either. Unfortunately, I'm still trying to get the time to understand the hex example. I don't know enough to make changes to it or to attempt what you have done with this latest example. I guess I'll have to study Lua more.

 

Well done!

 

Blauweogen

Edited by blauweogen
Link to comment

With the version of the builder I was using, the only thing that changes is the function call.

 

This:

var = concathex(var)

 

will become this:

var = "concathex(var)"

 

It'll leave the rest of the code alone.

 

When you're ready to compile the cartridge for the last time, just go into the lua file and do a global find and replace. Even the current 2/11 version of the builder does that.

Link to comment

Sorry Ranger Fox,

I mean I don't understand how to do math and other funtions in lua. I am very interested in the example cart you made using the math to convert text to a 6 digit string. I have no idea how to create a similar set up. Really quit a noob with any coding and I don't know what any of it means. for example %02x etc. in the hex example. I can only make a guess. I wish I had a grasp of it and work has kept me too busy. I assume I could use a similar math method only changed to be unique, if I knew where to begin. Your examples are great, it's just my limited knowledge holding me back.

 

Thanks for all your input. I'm going to try to work on my lua knowledge a bit.

 

Blauweogen

Link to comment

Sorry Ranger Fox,

I mean I don't understand how to do math and other funtions in lua. I am very interested in the example cart you made using the math to convert text to a 6 digit string. I have no idea how to create a similar set up. Really quit a noob with any coding and I don't know what any of it means. for example %02x etc. in the hex example. I can only make a guess. I wish I had a grasp of it and work has kept me too busy. I assume I could use a similar math method only changed to be unique, if I knew where to begin. Your examples are great, it's just my limited knowledge holding me back.

 

Thanks for all your input. I'm going to try to work on my lua knowledge a bit.

 

Blauweogen

 

The odd string.format("%02x", string.byte©) part I saw in one other lua example and that's where I found it.

 

Here are some other resources you might be interested in using:

String manipulation

string.format and other examples (a'la %02x)

more about string.format

 

For the format mask I used in my example, %x is hex and I'm guessing %02x refers to two hex digits. Yes, I'm still learning, but the fastest way for me to soak stuff up is by example.

 

 

Would it not be easier if the player had a built in decryption function and the builder automatically encrypted all the variable & constant assignments and inserted the correct function calls to decrypt them when accessing? No author built functions involved? The algorithm could be pretty strong, and not easily reversed.

 

Uh, you missed a key piece of information here. This thread deals with a way to add some encryption temporarily to cartridges--until Groundspeak builds encryption into the player and compiler. Once that happens, and I assume it will due to the valid demand, this thread will be moot. Your line of thinking is correct and it's the desired outcome. But, for now, we can at least do encryption ourselves.

Edited by Ranger Fox
Link to comment

Uh, you missed a key piece of information here. This thread deals with a way to add some encryption temporarily to cartridges--until Groundspeak builds encryption into the player and compiler. Once that happens, and I assume it will due to the valid demand, this thread will be moot. Your line of thinking is correct and it's the desired outcome. But, for now, we can at least do encryption ourselves.

 

I just would like to thank you for this thread...

I am trying to play a little bit with your idea and see if I came out with something to add.

 

Kazuma, geocaching-italia.com

Link to comment

I'll agree that at this stage of this game that it might be more beneficial to keep encryption to a minimum. After all, we want people to enjoy our creations. But, if you do want to hide a few things, there are some really easy ways to hide things and some excellent ideas have been made here. Here are some of mine.

 

-Assign a new variable with the answer. When comparing user input, just check it against the variable instead of a value. So now they'll have to figure out where the variable is assigned...but if you make the variable name something that seems like the right answer, that just might confuse the cartridge hacker.

 

-Then on top of that, you can also use a numerical input. Again, compare the answer to a pre-defined variable. But rather than simply assigning the the number to a variable, do a few "increment variable by 110" in one place and "by 6" in another. And if you think they will hack that, add a few "increment by" operations that will never get called.

 

I'm sure there are plenty of ways to add confustion without having to edit the lua file manually.

Link to comment

Am I right in thinking that numeric variables can only go up to 100? If so, that's a bit restrictive. It would be more useful if it could go up to 4 digits so that at least we could ask the user for dates. There don't seem to be any built in functions for strings, so you're stuck whichever way you want it. I've tried adding those hex routines, but every time I bring the modified file back into the Builder, it crashes and trashes the file.

Link to comment

Am I right in thinking that numeric variables can only go up to 100? If so, that's a bit restrictive. It would be more useful if it could go up to 4 digits so that at least we could ask the user for dates. There don't seem to be any built in functions for strings, so you're stuck whichever way you want it. I've tried adding those hex routines, but every time I bring the modified file back into the Builder, it crashes and trashes the file.

 

As far as a I know, you're right about numeric values. However, you can edit the code and include something different. I'm not sure what the builder would do to the code, though.

 

As for your second question about the builder crashing, it shouldn't. I haven't yet tried this on a newer version of the builder, though. I know the builder won't like to edit the file after you implemented this change, but it should be fine with compiling it. Are you inserting the code correctly? A syntax error might have the same effect.

Link to comment

As for your second question about the builder crashing, it shouldn't. I haven't yet tried this on a newer version of the builder, though. I know the builder won't like to edit the file after you implemented this change, but it should be fine with compiling it. Are you inserting the code correctly? A syntax error might have the same effect.

 

I confirm that Ranger's example could be the case. The builder crashes when opening the modified file after i added the hex function. I forgot a parenthesis.... once it was fixed, the file has been correctly opened, modified and works great.

 

I've written a very simple and rough java code (i prefer java since i use linux) now that does the "job": I mean that opens the lua source code, add the hex funcions (without errors) and looks for all the places where the input strings are used and do the changes. It also changes the unencrypted answer with the hex one.

I'm lazy of doing that manually every time... :blink:

The output file can be compiled with the builder.

 

If anyone is interested, I can work a little bit on that code for making it more "usable" (now it is a simple command line code).

 

Regards,

Kazuma

Link to comment

If anyone is interested, I can work a little bit on that code for making it more "usable" (now it is a simple command line code).

 

Thank you for contributing that. If you'd like, I can make a .Net port of that.

 

Well, I must thank you since without your input this wouldn't be possible.

At this point I'll try to add some GUI and a few options, and I'll post here the link.

 

Unfortunately I don't have that much free time... I'll do my best though. Promise.

 

regards,

Kazuma

Link to comment
MD5 Hashing

One popular way to encrypt data is to use an MD5 hashing algorithm. Looking at this site will tell you about MD5 in Lua. To make this story short, I tried including the MD5 dll as a reference in my test code, then tried to include the required core.dll, but didn't meet with much success. This would be the ideal solution, if it worked.

Did you try it directly, without external libs?

You may check this: http://www.equi4.com/md5/md5calc.lua

Otherwise, you could try to port this javascript implementation to LUA: http://www.webtoolkit.info/javascript-md5.html

 

Regards

Mika

Link to comment

Okay, tried this a few times and now need a little help:

edited the LUA file in notepad making the necessary changes and converting the answers to HEX, save the file and opened it in the builder.

I then tried to run the file in emulator and it worked fine.

On saving the LUA file once more it cut the text in two discarding the end half, needless to say it wouldnt convert the remaining half to a cartridge file.

Am I doing something simple wrong?

 

The builder is the latest version.

thanks

Link to comment

Okay, got that thanks.

Once the changed LUA file is opened in the emulator, just to check it, then closed and answer ,no, to the offer to save changes when closing down the builder the file is trashed and unable to publish a cartidge, either to PC or to the Wherigo site.

Do you need to edit the LUA file and then directly upload it before playing on the emulator?

Link to comment

At this point I'll try to add some GUI and a few options, and I'll post here the link.

 

Unfortunately I don't have that much free time... I'll do my best though. Promise.

 

I know that this is late since I think Jeremy and the team are working hard on a release that will solve encryptions and other "protecting" features.

 

In the meantime I have set a very simple GUI for the piece of software that opens a .lua file, adds the encryption code (i have also added a 'reverse' functionality inside the code, just to mix it a little more) and the code for a very simple emulator check.

 

The sequence of operations would be:

1) open the .lua file

2) select the lines where to set the encryption

3) process

4) save.

 

Even if the code would create a backup file of the original .lua, I would strongly suggest to keep a copy. I have written this code in a few hours and it is absolutely not 100% reliable and there is almost no error checking... I thought that spending to much time on that was really useless since someone else is working more seriously on that.

 

Here is the link for the .jar file: WannaBeLuaProtector.jar

 

You need Java 6 Runtime that can be downloaded from java.sun.com.

To run it: java -jar WannaBeLuaProtector.jar

 

Source code is available on request (but i must warn you... it is very, very, very, very poorly written... I didn't have time to write a reasonable code).

 

regards,

Kazuma, geocaching-italia

Link to comment

 

Do you need to edit the LUA file and then directly upload it before playing on the emulator?

 

You can compile the edited file using the Builder just do: tools -> compile a cartridge. You can then run the compiled cartridge in the emulator.

Do not open the lua file in the builder though as this option will be greyed out.

 

Also, I found that I had to set my pc to US regional settings to prevent the file being knobbled...

Link to comment

Yeah I can see exactly where you are going wrong.

Fore example, your function:

 

function zinputchurch:OnGetInput(input)

Church = concvathex(Church)

-- #GroupDescription=camp question --

-- #Comment=camp question Comment --

if Wherigo.NoCaseEquals(concathex(Church),"3537") then

 

should be:

 

function zinputchurch:OnGetInput(input)

Church = concathex(input)

-- #GroupDescription=camp question --

-- #Comment=camp question Comment --

if Wherigo.NoCaseEquals(Church,"3537") then

 

 

Also, it makes life easier if you use the same variable thoughout; thus:

 

function zinputchurch:OnGetInput(input)

var= concathex(input)

-- #GroupDescription=camp question --

-- #Comment=camp question Comment --

if Wherigo.NoCaseEquals(var,"3537") then

 

That way you can do a search and replace for all instances of

var= "concathex(input)" with var= concathex(input)

when the builder messes it up again.

 

 

I just noticed that your 'Church' one above has a typo in the function name (a stray v) as well which wouldn't help matters!

Edited by Delta68
Link to comment

It looks like subtracting 64 or 96 from the value of each character in the string would probably be sufficient to obfuscate strings, since the .GWC file seems to contain a large number of bytes under 32. Lua is 8-bit clean, so it's no problem to include arbitrary binary values in strings. Then a string in the cartridge file would be replaced by a call to a function called deobfuscate() which would just walk along the string and add 64 or 96 back (modulo 256 - or is it Unicode?).

 

This could be specified in the Builder via an "obfuscate" check box per string or global for the cartridge. Or it could just be made the default, come to think of it.

 

However, I don't think that any claims should be made for this. Wherigo will be used for all kinds of things which we can't imagine right now, and one day it will be really worth someone's while to deobfuscate the strings. At that point it would be better for everyone if no claims had been made!

Edited by sTeamTraen
Link to comment

Hey there.

 

Just a small question! (Maybe I'm to stupid, but it doesn't work! :blink: )

 

I tried the concathex function, and it worked fine on the pre-compiled cartridge provided in the first post! Now I just wanted to "pimp" the decryption, but there was no option to get it working! Even the original lua-file doesn't work!

 

What is going on? (After I published the catridge to my machine, the line var = concathex(var) is changed into var = "concathex(var)" and as result, var seems to have concathex(var) as value!

 

What can I do? I want to get this stuff work!

 

Hope for a soon answer!

 

Best regards!

Dr.A.Colian

Link to comment

A cross-platform solution for string hashing would be greatly appreciated. It just makes it that extra bit harder to just look at the code at pick up the requested answers.

 

Btw, another 'trick' I've used in my latest cartridge to avoid Wherigo hacking (which is, unfortunately, very common here in the Netherlands): I've named one zone 'Geocache', with some text describing where the cache ought to be hidden. In the field, however, there's only an empty petling holder. When someone sends me a message or logs a DNF saying the cache is gone, I know exactly what has happened. :lol:

 

Obviously, the zone isn't actually used in the Wherigo. At no point it becomes active or visible.

 

The real cache zone has a non-descriptive name, and it's changed to 'Geocache' at runtime (by a hopefully non-clear method).

Edited by Vooruit!
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...