Have ideas to improve npm?Join in the discussion! »

memespeech

1.0.5 • Public • Published

Memespeech Javascript Library

Introduction

Please see the functional demo at https://www.obsessivefacts.com/memespeech

Memespeech is a text formatting library that allows encoding of encrypted binary data into passages of arbitrary text (Carrier Text), and decoding and decrypting binary data from the same. Memespeech allows people to hide encrypted messages inside other messages and exchange them over communication networks that may be untrustworthy.

A key difference between Memespeech and other formats of encoding binary as text (such as PGP's ASCII Armor) is that the output is human-readable text. Without a decryption key it is prohibitively difficult to know whether there is actually an encrypted message inside a passage of Memespeech text. Because Memespeech uses "Free Speech" as its exchange medium, it is unlikely that the U.S. Government would be be able to ban it (due to the First Amendment of the U.S. Constitution), and Memespeech can be exchanged over networks that only provide for the exchange of text communications.

Memespeech is not an encryption algorithm. Rather, Memespeech is a way of expressing encrypted binary data from an arbitrary cipher. The current library wraps encryption and decryption functionality from a 256-bit AES-CBC cipher, but the specification is flexible to adapt to other ciphers as the need arises.

This document explains usage details for the Memespeech Javascript Library as well as the specification for the Memespeech format.

Table of Contents

  1. WARNING: JAVASCRIPT CRYPTOGRAPHY CONSIDERED HARMFUL
  2. How Memespeech works in 60 seconds
  3. Memespeech Javascript Library Quick-start Guide
  4. Memespeech Format Specification
  5. TODO, Contributing, and Help Wanted!

WARNING: JAVASCRIPT CRYPTOGRAPHY CONSIDERED HARMFUL

This library works in both server-side Node.js, Express, and web browsers, but with extremely limited exceptions you should not use it in a web browser for anything serious. In fact you should probably not offer any in-browser cryptography functionality with any other library either. Please read the cliche-status Javascript Cryptography Considered Harmful essay if you want more explanation.

The one application where it's safe to use this in a web browser is in a browser extension. That's because the extensions are signed by Mozilla or Google and have a clear chain of trust from the developer to the end-user. But you don't actually need to bother building an extension, because the Memespeech Webextension is already super awesome.

If you ignore this and use Memespeech in a web browser for anything other than toy status, then you are potentially exposing your users and the people they communicate with to risk of interception. Providing no security is better than providing a false sense of security so please be kind to your users.

How Memespeech works in 60 seconds

Memespeech lets you hide an encrypted message inside a passage of Carrier Text by tweaking the letter formatting eVeR So sLiGhtLy. So suppose you have this Carrier Text:

Holy shit. My mom came into my room to bring me a plate of chicken nuggets and I
literally screamed at her and hit the plate of chicken nuggets out of her hand.
She started yelling and swearing at me and I slammed the door on her. I'm so
distressed right now I don't know what to do. I didn't mean to do that to my mom
but I'm literally in shock from the results tonight. I feel like I'm going to
explode. Why the fucking fuck is he losing? This can't be happening. I'm having
a fucking breakdown. I don't want to believe the world is so corrupt. I want a
future to believe in. I want Bernie to be president and fix this broken country.
I cannot fucking deal with this right now. It wasn't supposed to be like this, I
thought he was polling well in New York???? This is so fucked.

Now suppose you have a secret message you want to encrypt (let the tendies hit the floor) and password SANIC. Then, run all this through Memespeech's encryption algorithm and you'll end up with something like:

hOLY sHIT. MY mom cAme inTO MY RoOM TO BRIng mE A PLATE OF ChickEn NuggETS aND i
LITerALLy scrEAMEd AT her And hIT THE pLATe of CHICKen NUGGeTs Out Of Her Hand.
ShE staRted YEllING and SWeARInG aT me aNd i slamMed THe dOOR oN hER. i'M SO
DiStReSSeD rIgHt NOw i don't KnOW WHAT To dO. I dIDn't mean To DO tHAT TO my mOm
bUt I'M liTerAlly In SHOck frOM the results ToNIght. i fEeL lIKE i'm GOInG to
exPlodE. WHY The fuCkINg FuCk Is he losINg? thIs cAN't be HaPpeNING. I'm haVIng
a FUckInG brEAkdowN. i dON't WaNt To BeliEve THe WorLd iS So CORRupt. I WAnT a
futuRE to BeLiEve iN. i wANt BERniE to bE PreSIdEnT anD Fix thIS brOken cOUnTRY.
i cANNoT fUCKInG DEaL with ThiS Right Now. IT waSn'T SUPPOseD To Be lIKE thiS, I
THoUGHt hE WAs poLlInG wEll iN new york???? ThIs Is sO fUCkED.

Then if you run the same thing back into Memespeech's decryption algorithm with password SANIC, you'll end up with your let the tendies hit the floor plaintext again.

This works because any bit of encrypted binary data can have two states (0 or 1) and any alphabetical ASCII character can have two states (lowercase or uppercase), and so by changing the capitalization, we can encode the binary into ASCII while keeping it about as readable as any YouTube comments section. The main drawback is you'll need a lot of Carrier Text to encode a small amount of encrypted data (you can encrypt up to 47 characters of secret text into the passage above).

Memespeech Javascript Library Quick-start Guide

Installation

For Node.js

  1. npm install memespeech in your project to install from npm

  2. Add const Memespeech = require('memespeech'); to include it in your app

For browsers

  1. Copy the memespeech.js file somewhere in your project. Do not link to a third party or CDN source that you do not control.

  2. Include the script in your page:

    <script type="text/javascript" src="/PATH/TO/memespeech.js"></script>
  3. Memespeech will now be available as Memespeech in the global namespace.

Encrypting data

First initialize your Memespeech instance:

let ms = new Memespeech()

Next set some Carrier Text into your instance. This is the longer text that will conceal your encrypted message:

// truncated for readability
ms.setCarrierText('Holy shit. My mom came into my room to bring me a plate...');

Now lets say you have a secret message (Plaintext) you want to encrypt, (eg. 'harambe did nothing wrong'). Will this fit into your Carrier Text? You can easily check by calling:

ms.computeAESByteBudget();
// Example output: { byteBudgetStandard: 27, byteBudgetCompact: 47 }

Memespeech offers two modes of encryption, a stronger "standard" mode (uses a 32 byte salt), or a weaker "compact" mode (8 byte salt). Both are strong enough to prevent the use of rainbow attacks, but obviously you should use the strongest mode available! What the example output above tells you is that you can encrypt up to 27 bytes of Plaintext data using the standard mode, and up to 47 in the compact mode.

Keep in mind that for text, one character usually (but not always) equals one byte. If you're using multibyte UTF-8 characters, it can quickly add up. So if you have a Plaintext string and you want to see if it will fit into your Byte Budget, you can convert it to bytes with Memespeech.DataUtils.stringToBytes and check the length, eg:

Memespeech.DataUtils.stringToBytes('bush did 9/11').length; // outputs 13
Memespeech.DataUtils.stringToBytes('ƀüŜɦ Ðĩɖ 9/11').length; // outputs 20

TL;DR:

  • If your Plaintext byte length is less than or equal to byteBudgetStandard, use "standard" mode encryption.

  • If your Plaintext byte length is longer than byteBudgetStandard but less than or equal to byteBudgetCompact, use "compact" mode encryption.

  • If your Plaintext byte length is longer than both byteBudgetStandard and byteBudgetCompact, then you'll need either to choose longer Carrier Text or shorten your Plaintext.

  • And if both byteBudgetStandard and byteBudgetCompact are less than zero, then your Carrier Text is too short to encode anything. Try again.

Now you can encrypt your Plaintext and hide it inside the Carrier Text by calling:

ms.passphraseEncrypt('my secret message', 'p@ssw0rd', 'standard').toString();
// Example output: hOLY sHit. my Mom CAMe iNto my ROoM tO brINg me A pLaTe of...

Decrypting data

Decrypting is a lot simpler because you don't have to worry about whether anything will fit inside of anything else. All you need is the correct password.

let ms = new Memespeech({
    carrierText: 'hOLY sHit. my Mom CAMe iNto my ROoM tO brINg me A pLaTe of...'
});
ms.passphraseDecrypt('p@ssw0rd').toString();
// Example output: my secret message

Creating "decoy" data

Suppose some NSA agents are hot on your trail and you really want to fuck with them. Then you could send a Memespeech message to an associate but not actually encrypt anything into it. They'll spend millions of dollars on AWS GPU clusters trying to crack it for absolutely no reason lol. If this sounds like fun, then Memespeech.generateDecoyText is for you:

let ms = new Memespeech({
    carrierText: 'I won\'t be repeating it. My wife and I sold our wedding...'
});
ms.generateDecoyText();

Assuming your Carrier Text is long enough to hypothetically fit any encrypted data into, then the Decoy will be parseable as Memespeech but impossible to decrypt (because there is just random noise instead of Ciphertext Payload data).

Memespeech Format Specification

The goal of this format specification is to allow developers to implement Memespeech in the languages of their choice. Because Memespeech is not an encryption standard, rather Memespeech wraps common cryptography functionality provided by well-established cipher and hashing algorithms, this document will not go into the details of these underlying encryption standards (beyond implementation-related specifics).

If implemented correctly, the underlying cryptographic algorithms will provide cross-compatibility with existing Memespeech implementations and provide a high level of encryption security to the end-user. However, it is assumed as a prerequisite that a developer is familiar with usage specifics of the AES-CBC block cipher, PBKDF2 key derivation algorithm and SHA-384 hasher, and is able to do so in a manner which safeguards user data against leakage and interception.

Concepts such as memory safety are beyond the scope of this document, but at a bare minimum we urge developers to never persist Cryptographic Key information in memory longer than necessary to perform encryption or decryption operations (and never persist to storage). As a matter of ethics, it is crucial to implement cryptographic functionality carefully and competently in order to protect the security of the end-user, and remember that providing no security is better than providing a false sense of security.

Definitions

  • binary-casing: the process of encoding binary data into text by modifying the capitalization of the text's alphabetical ASCII characters. For example binary 011010111001 binary-cased into "help i am a rock" results in "hELp I aM A RocK".

  • Bit Budget: the number of binary bits that can be encoded into a particular Carrier Text. The bit budget equals the number of alphabetical ASCII characters within the Carrier Text. Each of these characters can be made lowercase or uppercase to represent bit values 0 or 1, respectively.

  • Carrier Text: a passage of text that we can hide ciphertext inside by re-encoding the whole thing as Memespeech

  • Cipher: the block cipher used for encryption. Currently, Memespeech supports the 256-bit AES-CBC cipher.

  • Ciphertext: the encrypted binary data generated by the Cipher.

  • Ciphertext Packet: the leading portion of the Cryptographic Payload that contains encrypted (or randomized) binary data of arbitrary length. The Ciphertext Packet is divided into chunks, each starting with a length byte, followed by a number of bits equal to the length byte value, and terminating with a boolean bit that indicates whether this is the last chunk in the Cihphertext Packet.

  • Cryptographic Key: the key used for encryption and decryption with the Cipher. This is based on the Password, Salt, and Iterations, and taken from the first 32 bytes of the 48-byte PBKDF2 key-derivation algorithm output over the SHA-384 hasher.

  • Cryptographic Payload: the underlying binary data that is encoded into Carrier Text to form Memespeech. The Cryptographic Payload consists of a Ciphertext Packet followed by a Footer Packet.

  • Encoding Mode: a mode that provides options for salt length and decryption integrity verification for the Cipher. In the Standard mode, a 32 byte salt is used and a four byte "signature" is added to the Plaintext bytes prior to encryption. In the Compact mode, an 8 bytes salt is used and no verification of decryption integrity is used.

  • Footer Packet: the portion of the Cryptographic Payload that contains information about the Cipher and Encoding Mode used in the Ciphertext Packet.

  • Initial Vector: a 16 byte value used to initialize the Cipher. This is based on the Password, Salt, and Iterations, and taken from the final 16 bytes of the 48-byte PBKDF2 key-derivation algorithm output over the SHA-384 hasher.

  • Iterations: the number of iterations to apply to the PBKDF2 key-derivation algorithm. This is stored in the Footer Packet to make decryption possible. To reduce the size of the Footer Packet, Memespeech requires an even multiple of 10,000 Iterations (up to a maximum of 150,000).

  • Memespeech: a binary encoding format that conceals a Cryptographic Payload inside Carrier Text by mapping each alphabetical ASCII character of the Carrier Text to a binary bit, lowercasing the character to express binary 0, and uppercasing to express binary 1. A goal of any Memespeech implementation is to maximize the perceived "randomness" of letter capitalization in the output text.

  • Plaintext: bytes of data that will be encrypted, stored in the Cryptographic Payload and encoded as Memespeech.

  • Plaintext Byte Budget: the maximum number of Plaintext bytes for a particular Encoding Mode that can be encrypted and and stored within a Cryptographic Payload while still fitting within the Bit Budget.

  • Password (aka Passphrase): a string for which the byte values are used in tandem with a Salt value to derive a Cryptographic Key by applying a set number of Iterations of the PBKDF2 key-derivation format.

  • Salt: random bytes added to the Password to prevent the use of rainbow table attacks against the cryptographic key. In Encoding Mode - Standard, a 32 byte Salt is used. In Encoding Mode - Compact, an 8 byte Salt is used. The Salt is stored in the Footer Packet to make decryption possible.

Implementation Requirements

A Memespeech implementation must be able to:

  • Consume a Carrier Text string and derive a Bit Budget from it
  • Encrypt a Plaintext string using a Cryptographic Key
  • Structure the encrypted binary data into a Cryptographic Payload
  • Encode the Cryptographic Payload into Carrier Text to form Memespeech
  • Parse and decode a Cryptographic Payload from a Memespeech string
  • Decrypt Ciphertext using a Cryptographic key

The following document will provide specifications for these requirements, and end in a discussion of how, by adding new types of Footer Packets, the Memespeech format could be extended for use with other Ciphers and binary storage formats.

Consume a Carrier Text string and derive a Bit Budget from it

When a Carrier Text string is consumed by your Memespeech implementation, you need calculate your Bit Budget to know how many binary bits can be encoded into it. Recall that Memespeech is formed by taking each bit of binary data in the Cryptographic Payload and encoding it into to each successive alphabetical ASCII character of the Carrier Text. However, since any passage of Carrier Text will typically contain non-alphabetic or non-ASCII characters, your Bit Budget is usually smaller than the string length of the Carrier Text.

You can use a regular expression match to find all alphabetical ASCII characters and take its length to derive the Bit Budget. For example, adapted from the Javascript implementation:

let matches = [...this.carrierText.matchAll(/[A-Za-z]/g)];
 
this.bitBudget = matches.length;
this.carrierTextABCMatches = matches;

Note that it also helps to keep the actual character matches for later reference. Keeping a lookup table of alphabetical characters mapped to their positions within the Carrier Text will aid the process of encoding the Cryptographic Payload into the Carrier Text when the time comes to render the Memespeech text output.

Encrypt a Plaintext string using a Cryptographic Key

Currently the Memespeech specification supports only the 256-bit AES-CBC Cipher, with the open-ended possibility of standardizing more. Password-based key generation is handled by PBKDF2 using a SHA-384 hasher. This documentation will not cover usage details for these existing cryptographic standards, but we will call out a few Memespeech-specific implementation points:

  • Before encrypting Plaintext, find your Plaintext Byte Budgets to make sure there is enough room in the Bit Budget to encode a Cryptographic Payload derived from the expected block output length of your Cipher.

    You don't need to go through the work of encrypting and generating a Cryptographic Payload in order to check if it will fit. You can compute Plaintext Byte Budgets for both the Standard and Compact Encoding Modes to find the maximum number of Plaintext bytes you could encrypt and structure into a Cryptographic Payload while still fitting in the overall Bit Budget.

    Keep in mind that the Cryptographic Payload consists of a Ciphertext Packet and a Footer Packet. The Footer Packet is always 262 bits in Standard Mode and 69 bits in Compact Mode. If you subtract these from your Bit Budget, you will have the maximum number of bits available to encode a Ciphertext Packet in Standard and Compact Encoding Modes, respectively. We will refer to these values as ciphertextBitsAvailableStandard and ciphertextBitsAvailableCompact, expressed in pseudocode:

    ciphertextBitsAvailableStandard = bitBudget - 262
    ciphertextBitsAvailableCompact = bitBudget - 69
    

    Now, knowing that the AES Cipher outputs blocks of 16 bytes, we can compute the number of bytes of Plaintext that could be encrypted into a Ciphertext Packet and split into chunks as described in the definitions section above. The following pseudocode expresses our Plaintext Byte Budgets, with further explanation below:

    pByteBudgetStandard = MATH_FLOOR(ciphertextBitsAvailableStandard/137) * 16 - 5
    pByteBudgetCompact  = MATH_FLOOR(ciphertextBitsAvailableCompact /137) * 16 - 1
    

    (In this pseudocode, MATH_FLOOR is a function that slices off any remainder from the quotient.) Recall that the Ciphertext Packet is divided into chunks, each starting a length byte, followed by the same length in bits of data, and terminating with an "is last" boolean flag bit. While, within the Ciphertext Packet generation algorithm, it is common to use chunks containing more than 128 bits of data (but less than or equal to 255 bits), this is not ideal for the budget computation algorithm because it increases the likelihood of a user completely saturating the Bit Budget and ending up with chunks of either very high or very low length byte values (eg. 11111111), which will end up showing a lot of consecutive capitalized or lowercased letters in the Memespeech text output (which would not look very "random"). So to be safe we compute the Plaintext Byte Budgets based upon division into 137 bit chunks, enclosing a maximum of 128 bits of Ciphertext data within each chunk.

    The output of the MATH_FLOOR function taken on the above quotient yields the number of 16 byte AES-CBC Cipher blocks available for encryption, which we multiply by 16 to almost arrive at the total bytes available in the Plaintext Byte Budget, but in the Standard Encoding Mode, we must subtract 5 bytes: 4 for the "signature" bytes and 1 further to prevent completely maxing out the last AES-CBC block, which would result in an extra padding block being outputted from the Cipher. In Compact Encoding Mode we don't have a "signature", so we simply subtract one byte to prevent maxing out the final AES-CBC block.

    If the byte length (not string length) of your Plaintext string fits within the Plaintext Byte Budget for your desired Encoding Mode, then you can confidently proceed to encryption without worrying that the Cryptographic Payload will be too large to fit in your Bit Budget.

  • Note that in Standard Encoding Mode you must append a 4 byte "signature" to your Plaintext bytes prior to encryption, and these will be verified in a sort of "poor man's integrity check" on decryption.

    Currently the "signature" consists only of the bytes 240, 159, 145, and 140. If these bytes are present upon decryption then it's a safe-enough bet that the correct decryption key was used and they can be optionally stripped out of the byte data when converting the decrypted Plaintext bytes back to a string.

  • Salt values should use cryptographically secure pseudorandom number generation (CSPRNG), and the length is determined by your Encoding Mode

    For Standard Encoding Mode, choose a random 32-byte Salt. For Compact Encoding Mode use an 8-byte salt. The Salt value is stored in the Cryptographic Payload's Footer Packet, and this has a rigid length (based on your Encoding Mode), so no other lengths are currently supported.

  • You must use SHA-384 as the hasher for the PBKDF2 algorithm and select an output length of 48 bytes. The first 32 bytes of the PBKDF2 output is your Cryptographic Key, the remaining 16 bytes is your Initial Vector for the AES-CBC cipher.

    This is required not only for cross-compatibility with existing Memespeech implementations, but also because we use the PBKDF2 output to derive both the Cryptographic Key (32 bytes) and the Initial Vector (16 bytes). In order for this to be cryptographically "safe," it is required to use a hasher that provides at least 48 bytes of output, which SHA-384 does.

  • Iterations count for the PBKDF2 key-derivation algorithm must be an even multiple of 10,000, with a minimum value of 10,000 and a maximum of 150,000.

    The Iterations count is stored in the Footer Packet and compressed into a 4-bit number. So an iteration count of 10,000 is expressed in binary as 0001 and 150,000 is expressed as 1111. No other values will fit or are allowed in the Footer Packet.

Structure the encrypted binary data into a Cryptographic Payload

Once you've encrypted your Plaintext, you'll end up with some binary Ciphertext. It won't be possible to simply binary-case your Ciphertext into the Carrier Text and have any hope of parsing or decrypting it later, because there's no information in the Ciphertext that a parser could use to determine where the Ciphertext actually ends, much less any information about the Salt or Iterations count needed to prime the PBKDF2 key-derivation algorithm for decryption.

The Memespeech Cryptographic Payload solves this problem by encoding the raw Ciphertext data into a well-defined Ciphertext Packet followed by a Footer Packet containing information about the Encoding Mode, Salt and Iterations. The Cryptographic Payload is designed to preserve the perceived "randomness" of the Memespeech text, avoiding the long lines of binary 0's that would be typically present if we relied on binary formats such as Multiprecision Integers.

The Cryptographic Payload starts with the Ciphertext Packet and ends with the Footer Packet. Any other data after that in the Memespeech text is irrelevant to the parser and discarded.

NOTE: Most binary data format specifications focus on bytes. Because Memespeech encodes binary data into text based on the underlying bits, keep in mind that most of time we are using bits as our unit of truth for within the Cryptographic Payload.

Ciphertext Packet
[
    [[...8-bit Length byte (N)...] + [...N bits data...] + [0]]    // chunk 1
  + [[...8-bit Length byte (N)...] + [...N bits data...] + [0]]    // chunk 2
  + ...
  + [[...8-bit Length byte (N)...] + [...N bits data...] + [1]]    // last chunk
                                                            ^
                                                            └──// "is last" flag
]

The Ciphertext Packet simply takes the Ciphertext and randomly divides it into "chunks" where each chunk consists of:

  • An 8-bit length byte (a number from 1 to 255) specifying the number of bits of Ciphertext data in this chunk

  • A slice of the Ciphertext bit data equal to the number of bits specified by the length bit

  • An "is last" boolean termination bit. If this chunk encloses the last slice of Ciphertext bit data (and is thus the end of the Ciphertext Packet) then the "is last" boolean bit is 1. Otherwise it is 0, signalling that the next bit of Ciphertext Packet data corresponds to the beginning of the next chunk, which contains the next slice of Ciphertext data.

Dividing the Ciphertext up into chunks of the appropriate length is up to the implementation and is where the science meets art in this specification. You could technically choose 255 as all your length bytes until you run out of Ciphertext, but then your length bytes will be expressed as 11111111 and binary-cased into all caps in the Memespeech output text. So the trick is choosing divisions that have random-looking length bytes. The existing Javascript implementation is a first attempt, but I do not claim to have arrived at the ideal solution for this.

The only hard requirement is that you don't exceed your Bit Budget when accounting for the fact that your Footer Packet will take up 262 bits in Standard Encoding Mode and 69 bits in Compact Encoding Mode.

Footer Packet

This is where information about the Encoding Mode and Cipher is stored, and is also a place where the Memespeech spec could be extended to support other Ciphers, options, or even arbitrary binary data sources. But for now, the Footer Packet can take one of two forms:

Standard Mode Footer Packet

[0] + [1] + [...256 Salt bits...] + [...4 Iterations bits...] = 262 bits

The Standard Mode Footer Packet starts with bits 0 and 1, followed by 256 bits corresponding to the 32 byte salt, and terminates with 4 bits representing the compressed Iterations count. Recall that Iterations are even multiples of 10,000 up to a maximum of 150,000, so this value can be divided by 10,000 and expressed in a 4 bit value from 0001 (1) to 1111 (15).

Compact Mode Footer Packet

[1] + [...64 Salt bits...] + [...4 Iterations bits...] = 69 bits lol

The Compact Mode Footer Packet starts with bit 1, followed by 64 bits corresponding to the 8 byte salt, and terminates with 4 bits representing the compressed Iterations count.

Encode the Cryptographic Payload into Carrier Text to form Memespeech

Once your Cryptographic Payload is formed, you are ready to binary-case it into your Carrier Text to form Memespeech text. For each binary bit of the Cryptographic data, find the next untouched alphabetical character of Carrier Text and make it lowercase if the bit is 0 and uppercase if the bit is 1. Note that this is easier if you stored a lookup array mapping the successive alphabetical characters of the Carrier Text to their positions in the string at the time that you first parsed the Bit Budget from the Carrier Text.

After all of the bytes of the Cryptographic Payload are binary-cased into the Carrier Text, you will likely be left with some final part of the string that has been left untouched. You should just apply random capitalization to any of these left over characters, and you're done!

Parse and decode a Cryptographic Payload from a Memespeech string

This is simply running the binary-casing in reverse to convert the Memespeech string into binary bits, and then reversing out the Cryptographic Payload from the binary data, discarding any leftover data after termination of the Footer Packet.

More precisely, once you've converted the Memespeech string into binary, the fist bit of your binary corresponds to the beginning of the length byte of the first chunk of the Ciphertext Packet. Starting from there, you can reverse out the Ciphertext Packet as follows:

  • Read 8 bits into an integer N. This is the length byte of the current chunk of the Ciphertext Packet.

  • Read the next N bits into a buffer that builds up your Ciphertext, where N is the length value from the previous step.

  • Read the next bit. If it's 1 then you're done parsing the Ciphertext Packet. If it's 0 then it's time to parse the next chunk; repeat these steps starting at the next bit.

Once you've run through the Ciphertext Packet, then it's time to parse the Footer Packet.

Compact Encoding Mode Footer Packet:

If the next bit after the Ciphertext Packet is 1, then you're in Compact Mode. The following 64 bits will be your 8-byte Salt, and the 4 bits after that are your compressed Iterations count. Read this into a number and multiply by 10,000 to arrive at the true Iterations number (between 10,000 and 150,000).

Standard Encoding Mode Footer Packet:

If the bits immediately following the Ciphertext Packet are 0 and 1, then you're in Standard Mode. The following 256 bits will be your 32-byte Salt, and the 4 bits after that are your compressed Iterations count. Read this into a number and multiply by 10,000 to arrive at the true Iterations number.

Any bits remaining after parsing the Footer Packet are irrelevant and can be discarded.

Decrypt Ciphertext using a Cryptographic key

Using the Ciphertext, Encoding Mode, Salt and Iterations Count parsed from the Cryptographic Payload, you are almost ready to decrypt. The only thing you need is the correct Password. Use the same key-derivation options above from the encryption section (choosing a 48 byte output length from the PBKDF2 algorithm over a SHA-384 hasher, the first 32 bytes are your Cryptographic Key and the final 16 bytes are your Initial Vector for the AES-CBC Cipher).

NOTE: When decrypting Memespeech saved in the Standard Encoding Mode format, the decrypted Plaintext bytes should end with our 4 byte "poor man's signature." Check that these final four bytes equal 240, 159, 145, and 140, and optionally strip them out before converting the Plaintext bytes to a string.

Extending Memespeech with new Footer Packets

Right now Footer Packets beginning with 1 or with 0 followed by 1 are reserved for Compact and Standard Encoding modes, but more format support could be added by creating more flexible Footer Packet formats. The challenge here will be adding new functionality without sacrificing too much perceived "randomness" in the binary-cased Memespeech output. Please suggest improvements if the Memespeech format doesn't quite work for your use-case and we can figure something out!

TODO, Contributing, and Help Wanted!

  • I would like to draft the Memespeech format as an IETF spec but have no idea where to begin with this, even after reading through many of their docs. If someone with more experience working with open standards could help me or provide advice, I would greatly appreciate it!

  • Currently Memespeech only supports two encoding modes, both on an AES-CBC cipher. It would be possible to add more encryption ciphers or the capability of encoding arbitrary binary data by designing new Footer Packet. If you have an idea or something you want to see added, please let me know.

  • If you'd like to help localize these instructions to other languages, it would help more people learn about the format.

  • Please report any issues in Gitlab, or by emailing henriquez@protonmail.com if you don't have (or don't want to create) a Gitlab account.

Install

npm i memespeech

DownloadsWeekly Downloads

8

Version

1.0.5

License

GPL-3.0-or-later

Unpacked Size

136 kB

Total Files

5

Last publish

Collaborators

  • avatar