GithubHelp home page GithubHelp logo

paragonie / sodium-plus Goto Github PK

View Code? Open in Web Editor NEW
170.0 10.0 20.0 1.33 MB

Developer-friendly libsodium interface

License: Other

JavaScript 99.48% Shell 0.52%
sodium cryptography libsodium-bindings javascript nodejs node es2019

sodium-plus's Introduction

Sodium-Plus (Na+)

Build Status npm version

Sodium-Plus delivers a positive cryptography experience for JavaScript developers.

Sodium-Plus brings you all the benefits of using libsodium in your application without any of the headaches introduced by the incumbent APIs.

Sodium-Plus is permissively licensed (ISC) and free to use.

Features

  • Cross-platform.
  • Pluggable backend with an auto-loader:
  • Fully async/await ready (aside from object constructors).
  • Type-safe API:
    • Instead of just passing around Buffer objects and hoping you got your argument order correct, sodium-plus will throw an Error if you provide the wrong key type. This prevents you from accidentally introducing a severe security risk into your application.

Installing

Installing as a Node.js Module

With NPM:

npm install sodium-plus

You can optionally install sodium-native alongside sodium-plus if you want better performance.

The default configuration is a bit slower, but has a wider reach (e.g. web browsers).

Installing in a Web Page

See this section of the documentation for getting started with Sodium-Plus in a web browser.

Using Sodium-Plus in Your Projects

SodiumPlus is meant to be used asynchronously, like so:

const { SodiumPlus } = require('sodium-plus');

(async function() {
    // Select a backend automatically
    let sodium = await SodiumPlus.auto();

    let key = await sodium.crypto_secretbox_keygen();
    let nonce = await sodium.randombytes_buf(24);
    let message = 'This is just a test message';
    // Message can be a string, buffer, array, etc.

    let ciphertext = await sodium.crypto_secretbox(message, nonce, key);
    console.log(ciphertext);
    let decrypted = await sodium.crypto_secretbox_open(ciphertext, nonce, key);
    console.log(decrypted.toString('utf-8'));
})();

This should produce output similar to below (but with different random-looking bytes):

<Buffer 00 b7 66 89 3d b4 4d e9 7e 0f 66 91 fd d1 ca fd be bb 7f 00 89 76 5b 48 ec ed 80 cc 87 76 54 1b b5 ea 87 9b e5 19 ee 4c 31 c5 63>
This is just a test message

Documentation

The documentation is available online on Github!

Support Contracts

If your company uses this library in their products or services, you may be interested in purchasing a support contract from Paragon Initiative Enterprises.

sodium-plus's People

Contributors

andreiled avatar aral avatar danielruf avatar jviide avatar mature-woman avatar norbloc-vitalii avatar paragonie-scott avatar paragonie-security avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sodium-plus's Issues

function crypto_box_seed_keypair is missing

Hi, I think the function:
crypto_box_seed_keypair
is missing.

When I do:

const { SodiumPlus } = require('sodium-plus');
await sodium.crypto_box_seed_keypair(seed);

I get this error back:
TypeError: sodium.crypto_box_seed_keypair is not a function

Can you add it?
Thank you!

How to extract Authentication Tag from ciphertext?

I need to build a proper JWE using crypto_aead_xchacha20poly1305_ietf_encrypt function as AEAD construction.

The official libsodium documentation says that "In combined mode, the authentication tag is directly appended to the encrypted message" but it is not clear for me how to extract this tag from ciphertext to use it as tag property of JWE.

There is another function in libsodium called crypto_aead_xchacha20poly1305_ietf_encrypt_detached which returns authentication tag separately, but it is not implemented in sodium-plus.

I have two workaround ideas:

  • use crypto_auth function to compute auth tag from ciphertext and crypto_auth_verify to verify it
  • slice some bytes from M to N from ciphertext to get the tag, but I don't know the M and N

so, how to obtain authentication tag?

sodium.crypto_secretbox () or decrypted.toString(); does not handle UTF8 characters

Not sure if this is an issue or maybe I need to put charset="UTF-8" somewhere but if the original plaintext has UTF8 characters the resulting decrypted text returns garbage.
For example:
plaintext = 'El camión tenía piñas';

ciphertext.toString('hex') == bbbbe889db929838ddd6b62b0712479655c4abb10890bdd835d3298436234746d19da673c8

decrypted.toString() == El cami�n ten�a pi�as

I was about to swap the bytes but it seemed crazy, so I preferred to ask.

sodium-native 3.0.0

sodium-plus has

  "peerDependencies": {
    "sodium-native": "^2.4.6"
  },

Latest sodium-native is 3.0.0 so I get this warning:

npm WARN [email protected] requires a peer of sodium-native@^2.4.6 but none is installed. You must install peer dependencies yourself.

even though sodium-native is installed.

Problem in crypto_secretstream_xchacha20poly1305_init_push (not iterable)

When trying the example of crypto_secretstream_xchacha20poly1305, browser version I find out there is something going wrong in sodium.crypto_secretstream_xchacha20poly1305_init_push.

if (!sodium) sodium = await SodiumPlus.auto(); let key = await sodium.crypto_secretstream_xchacha20poly1305_keygen(); let pushState, pullState, header; [pushState, header] = await sodium.crypto_secretstream_xchacha20poly1305_init_push(key);

this throws the following error (example):

Uncaught (in promise) TypeError: ({header:{0:92, 1:72, 2:132, 3:111, 4:18, 5:255, 6:141, 7:212, 8:69, 9:48, 10:71, 11:216, 12:242, 13:163, 14:199, 15:73, 16:137, 17:175, 18:148, 19:68, 20:139, 21:57, 22:95, 23:200}, push:function bound crypto_secretstream_xchacha20poly1305_push() { [native code] }, rekey:function bound crypto_secretstream_xchacha20poly1305_rekey() { [native code] }}) is not iterable

As it is in the example itself it makes it hard for my to find out what is going wrong.

crypto.timingSafeEqual missing

When I try to load the following example found in the documentation, there seems to be a missing function.

// Check that we don't need to rotate hashes
 let stale = await sodium.crypto_pwhash_str_needs_rehash(
     pwhash,
     sodium.CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
     sodium.CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
 );

The code to generate the hash itself works perfectly though

It throws the following error: crypto.timingSafeEqual is not a function (sodium-plus.js:2035:24)
I am using version 0.9.0 from unpkg

I am using the browser version and insert the library like this:
<script src="~/lib/sodium-plus/dist/sodium-plus.js"></script>

TypeScript support

We should ensure it's easy to use this library from a TypeScript project.

Browserify and Minify

We should aim for v0.4.0 to work in a web browser with a simple <script /> tag, and allow our v0.4.0 release to be shipped on CDNs.

Generate nonces within calls

I was wondering whether sodium-plus could remove nonce as an argument to calls generate the nonces within the library.
This would prevent apps mistakenly re-using the same nonce.
The nonce would be returned along with the result, so the API would need changing.

Encrypted Streams API is not OOP-friendly

Problem Statement

Current Encrypted Streams API falls short of the claim that Sodium-Plus is the libsodium API that JavaScript developers deserve made in the Sodium-Plus: A Positive Cryptography Experience for JavaScript Developers blog post.
Specifically:

  1. It looks like a classic C API rather than an object-oriented API: push and pull method require to explicitly pass raw reference to the state object returned by the corresponding init method.
    This not only makes it harder to understand how to use these APIs (even with the documentation), but also makes the code look heavier.
  2. It exposes an internal state object akin to a raw pointer in C/C++ increasing the risk of misusing it.
  3. It is too easy for a developer to mess up and call a wrong flavor of the push/pull method, especially if/when additional algorithms aside from XChaCha20 Poly1305 will be added in future (I know that the goal is to avoid having an unnecessarily wide selection of algorithms to prevent developers from accidentally choosing a bad one, but having at least a couple of algorithms available would certainly be beneficial in some situations).

Solution

I suggest to:

  1. Update crypto_secretstream_xchacha20poly1305_init_push to return encryptor object with:
    • header read-only property
    • push(message, ad = '', tag = 0) method
    • rekey method
  2. Update crypto_secretstream_xchacha20poly1305_init_pull method to return a new decryptor object with:
    • pull(ciphertext, ad = '', tag = 0) method

This change is not backward-compatible, but I still consider it acceptable since:

  • Based on the crypto_secretstream_xchacha20poly1305_init_* method names alone, they look perfect for the job.
    In other words, I believe that coming up with new weird names to house the new interface would create a confusion for the initiated when choosing the right method to use.
  • Considering, how hidden this particular API is, I believe it is safe to assume that there aren't to many usages of it.

Version 0.4.0 - Release Checklist

Normally I'd drop this in the milestone description, but that's less visible than a new issue.

First, we need a new libsodium-wrappers release so we can stop including our fork in package.json. This is being tracked in jedisct1/libsodium.js#214

Next, we need to update the documentation to reflect the changes since v0.2.0.

Finally, we should ensure TypeScript compatibility is maintained, either via @types/sodium-plus or a .ts file in our repository. A Twitter user named "Le suisse" offered to help with this. I'll circle back with them later this week and see if they're still going to send a pull request. If not, I'll tackle it.

In short:

  • Upstream release (v0.7.5 -> ???)
  • Documentation updates
  • TypeScript #13
  • Browserify Fixes #21

Edit: decided to just get v0.3.0 out tonight. We can do v0.4.0 for these changes.

GitHub Actions

GHA is very interesting and I would provide a PR to migrate to it.

Did you already signup to the GHA beta? =)

Missing functions from libsodium backends

I'm trying to port one of my projects from sodium-native (which isn't cross-platform) to sodium-plus (which is). I have a snippet of code that looks like this:

        let blake2bFox = await sodium.crypto_generichash('red fox (vulpes vulpes)');
        let foxKeypair = await sodium.crypto_sign_seed_keypair(blake2bFox);

And it throws the following:

TypeError: sodium.crypto_sign_seed_keypair is not a function

I'm absolutely sure I'm loading the SodiumPlus object correctly. I followed the documentation and if there was a problem with that, the preceding line (the generichash call) would have thrown a TypeError instead.

My hypothesis is that a few functions were missed in your wrapper. sodium_sign_seed_keypair() being one of the ones I need for my unit test suite.

Encrypt on frontend (sodium-plus.js) with public-key from backend (PHP sodium)

I originally posted this on S/O yesterday, but figured it is probably more appropiate and straightforward to ask here.

I would like to achieve an anonymous public-key encryption in a web browser using sodium-plus.js with keys generated in PHP sodium like this:

    $keyPair = sodium_crypto_box_keypair();

    $privateKey = sodium_crypto_box_secretkey($keyPair);
    $publicKey = sodium_crypto_box_publickey($keyPair);

The keys generated with this method work fine in PHP with the sodium_crypto_box_seal and sodium_crypto_box_seal_open methods, but however, I am unable to make it work on the frontend. My approach:

    <script type='text/javascript' src='js/sodium-plus.min.js?v=0.4.2'></script>
    <script>
    async function getPublicKey() {
    
        return X25519PublicKey.from(
            '<?php echo sodium_bin2hex($publicKey); ?>', // 6a00b1550ccdeff3886a469b9cd4e5dc9aecd30f5deb3dd3e29fd01f8a32103f
            'hex'
        );
    
    }
    
    async function encryptString(clearText, publicKey) {
    
        if (!window.sodium) window.sodium = await SodiumPlus.auto();
    
        let cipherText = await sodium.crypto_box_seal(clearText, publicKey);
    
        return cipherText.toString('hex');
    
    }

    (async function () {

        let clearText = "String that contains secret.";
        let publicKey = await getPublicKey();

        console.log(await encryptString(clearText,publicKey));

    })();
    </script>

This returns TypeError: Argument 2 must be an instance of X25519PublicKey in the console.

Notes:

  1. A public-key that is derived from sodium.crypto_box_keypair() on the frontend works.
  2. Tried with CryptographyKey.from() instead of X25519PublicKey.from() – did not work.
  3. The getPublicKey() function returns an object wit buffer: Uint8Array(32) [ … ], while the public-key derived from sodium.crypto_box_keypair() returns an object with buffer: Uint8Array(32) [ … ], keyType: "x25519", publicKey: true.

Concept is based on:

  1. https://github.com/paragonie/sodium-plus/blob/master/docs/SodiumPlus/sealed-boxes.md
  2. https://dev.to/paragonie/message-encryption-in-javascript-and-php-cg9
  3. https://stackoverflow.com/a/34058638

hex to buffer?

We looked through the docs but could not find how to turn a hex string into a buffer using the Utils, so we are using this function to return a Uint8Array (and use it as buffer):

function hexToBuffer(hexString) {
   return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
}

...but crypto_secretbox_open rejects the ciphertext ("Ciphertext must be a buffer of at least 16 bytes") and nonce ("Nonce must be a buffer of exactly 24 bytes") created this way. They are 16< and =24 bytes, but they are not buffers according to sodium-plus (that is why the thrown SodiumError).

What would be the appropriate way to convert hex to buffer?

EDIT:
We also tried return Buffer.from(hexString) but it throws a ReferenceError: Buffer is not defined.

Is there a recommended way to store derived keys in the browser?

I am successfully deriving a key with crypto_pwhash() that I then split into a public and private x25519 key in the browser environment. This is derived from an end user's passphrase.

Is there a recommended way to store the Private Key safely in the browser environment? I know that for libraries based on WebCryptography API, Keys can be marked as non extractable and then the object saved to IndexedDB - but I don't think keys generated through Sodium-plus have similar extractability property. Is there a recommended way to store private keys in the browser for a session?

Publish dist folder

Hi,

Is it intentional that dist folder is missing from files key in package.json or in other words it is not published with the package?

I don't want to copy dist file to my project or use a cdn but would prefer to include it from node_modules since this is making it easier to maintain and all packages in a project are required in described way.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.