1

After almost 4 days of work, I've finally gotten Libsodium crypto_aead_xchacha20poly1305_ietf_encrypt to work and produce the same result in JavaScript and PHP.

But I'm confused.

The PHPDoc describes the parameters as:

* @param string $plaintext Message to be encrypted
* @param string $assocData Authenticated Associated Data (unencrypted)
* @param string $nonce Number to be used only Once
* @param string $key Encryption key
* @return string

While JSDoc asks for these parameters:

/**
 * @param {string | Uint8Array} message
 * @param {string | Uint8Array} additional_data
 * @param {string | Uint8Array} secret_nonce
 * @param {Uint8Array} public_nonce
 * @param {Uint8Array} key
 * @param {uint8array} outputFormat
 * @returns {Uint8Array}
 */

My questions:

  1. It seems like a nonce is a type of salt of a specific size that can only be used once because otherwise replay attacks can happen. Can you help me understand this further? Wikipedia and other sites got too complicated.
  2. To a layman, how would you describe AEAD and how to use the "Authenticated Associated Data (unencrypted)" parameter?
  3. In the PHP function (which I copied from https://paragonie.com/b/kIqqEWlp3VUOpRD7), does using the nonce not just as a nonce but also as the "Authenticated Associated Data" reduce the security compared to some other approach where the sender would add some value here and communicate offline to the receiver what the receiver should expect this value to be?
  4. Why would the JavaScript version accept different parameters? JS asks for a secret_nonce separate from a public_nonce, and in order for the results to match PHP's function, I need to supply identical values for secret_nonce and public_nonce. So what's the point?

P.S. Here are the functions:

JavaScript:

/**
 * @param {string} message
 * @param {Uint8Array} key
 * @returns {Uint8Array}
 */
function encryptAndPrependNonce(message, key) {
    let nonce = sodium.randombytes_buf(nonceBytes);
    var encrypted = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(message, nonce, nonce, nonce, key);
    var nonce_and_ciphertext = concatTypedArray(Uint8Array, nonce, encrypted);
    return nonce_and_ciphertext;
}

PHP:

/**
 * @link https://paragonie.com/b/kIqqEWlp3VUOpRD7 (from the `simpleEncrypt` function)
 * @param string $message
 * @param string $keyAsBinary
 * @return string
 */
public static function encryptAndPrependNonce($message, $keyAsBinary) {
    $nonce = random_bytes(self::NONCE_BYTES); // NONCE = Number to be used ONCE, for each message        
    $encrypted = ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_encrypt($message, $nonce, $nonce, $keyAsBinary);
    return $nonce . $encrypted;
}
  • Hi Ryan, it seems you have many questions about security and cryptography. To attract better responses perhaps you might consider focusing on one question. – this.josh Aug 15 '18 at 5:58
2

It seems like a nonce is a type of salt of a specific size that can only be used once because otherwise replay attacks can happen. Can you help me understand this further? Wikipedia and other sites got too complicated.

A nonce is required to make sure that the ciphertext doesn't leak information. Such a nonce or IV is required for any CPA secure cipher as repeated plaintext may otherwise leak information.

For stream ciphers - as used by most if not all AEAD constructions - a unique nonce is required otherwise you will leak all of the plaintext. It may also compromise the (internal) hash key, leading to a full compromise of the authenticity property (!!!).

To a layman, how would you describe AEAD and how to use the "Authenticated Associated Data (unencrypted)" parameter?

The AAD can contain any data that needs to be authenticated but doesn't need to be - or cannot be - kept confidential. For instance: it may be a good idea to protect the recipient field of a message in a protocol, but if it is hidden then the network may not be able to get it to the destination in the first place.

In the PHP function (which I copied from https://paragonie.com/b/kIqqEWlp3VUOpRD7), does using the nonce not just as a nonce but also as the "Authenticated Associated Data" reduce the security compared to some other approach where the sender would add some value here and communicate offline to the receiver what the receiver should expect this value to be?

Adding the nonce to the AAD never increases security, because AEAD ciphers already include it into the calculation of the authentication tag.

How much "some value" adds to the security depends of course on the protocol and possible attack vectors. Note that a nonce must be unique, but your protocol may not use it against replay attacks. In that case adding data to the AAD may help prevent such attacks.

Note that AEAD already requires a secret key and a nonce; there is no need to add another key or nonce (to secure the cipher itself). Adding the identity of the sender / receiver odes generally makes sense.

Why would the JavaScript version accept different parameters? JS asks for a secret_nonce separate from a public_nonce, and in order for the results to match PHP's function, I need to supply identical values for secret_nonce and public_nonce. So what's the point?

The JS library doesn't actually use the secret nonce parameter called nsec. If you read the documentation you will always find:

nsec is not used by this particular construction and should always be NULL.

Note that poly1305 requires two keys, of which the second "key" is handled as a number. However, the second key - usually denoted r - may be generated pseudo-randomly as specified here.

Generally it is preferred to use a single key for the cipher alone, so implementations will probably not support the dual key version and generate r internally. But I think that is why the nsec parameter exists in the first place.

For the GCM / GHASH the key is universally created internally as well.

  • 1
    I was wondering why that nsec parameter was there when it's never used. Seemed odd. How is nsec related to (r,s) though? Do you think it's just used directly as the key, and called a nonce because it's only supposed to be used once? – AndrolGenhald Aug 15 '18 at 15:20
  • 1
    Well, usually symmetric keys are not numbers, they are bit strings. There are additional constraints to r. For more information I would suggest you simply read the original specs. Questions directly on nsec and / or r are probably better suited to crypto.stackexchange.com by the way. – Maarten Bodewes Aug 15 '18 at 15:34
  • I accepted this answer even though I don't understand it and was hoping for something simpler. I REALLY appreciate the time you took and all the detail, though. It's awesome. (To anyone else who can provide a simplified answer, I'd be interested since I'm still learning.) And it's still bizarre to me (especially now that I know the docs say "nsec is not used by this particular construction and should always be NULL") that my Libsodium-js never matches my Libsodium-php unless I supply identical values for secret_nonce and public_nonce. – Ryan Aug 15 '18 at 15:43
  • To have to specify the same nonce twice is weird. Could you try explicitly specifying NULL for the parameter? With JavaScript it is easy to bugger up the method call, with default values and no type checking and all. – Maarten Bodewes Aug 15 '18 at 15:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.