Giter Club home page Giter Club logo

paseto's People

Contributors

aidantwoods avatar bluetechy avatar cab avatar carnage avatar carusogabriel avatar dconnolly avatar dennisbirkholz avatar djmarland avatar dustinsoftware avatar etherealflux avatar frederikbosch avatar hansott avatar kellerfuchs avatar minus7 avatar mjpieters avatar mstaack avatar o1egl avatar oojacoboo avatar panva avatar paragonie-scott avatar paragonie-security avatar peter-evans avatar rlittlefield avatar shulhan avatar slamdunk avatar spantaleev 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  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

paseto's Issues

Naming issue

I really like this initiative, so we can get rid of pesky JWT.
Although I probably don't understand all the dangers, I know there are some, which make me uncertain even if I were to do JWT seemingly done right :/

And although I really like the name itself, the usability of the acronym is really bad. It's more or less impossible to search for. Is there any chance of an acronym change?

I don't have any good suggestions off the top of my head, but I'm willing to put some effort into it if you're open to an acronym change of some sort?

How to generate keys

Thanks for this project. I have been reading through trying to get a grasp on things, and I think an important function for your API would be generating keys in a secure matter.

I see from your tests that symmetric keys for v2 are generated from 32 random bytes. Is 256 bits considered secure enough here? Could this be documented somewhere?

I think it would be useful if ProtocolInteface had two extra methods that would correspond to the algorithms in use:

  • generateAsymmetricKeys(): generates asymmetric keys
  • generateSymmetricKey(): generates a symmetric key

It would be up to each version to decide what is considered a secure enough key. These could take an optional key length, preferably with guidance in the documentation as to which length to choose?

Payload key ordering

I couldn't find any information about what is the expected order of keys in the payload, it would be useful that in any implementation such code:

encode(decode(Payload)) == Payload

Currently from what I see there is nothing that ensures such behaviour. This could be achieved with #90 and usage of ASN.1 DER format for example, but right now standard requires payload to be correct Base64 encoded JSON token.

Footer/key-id support in pypaseto

I'm trying to figure out if I should support key identifiers in pypaseto. I'm inclined to just say no, but I noticed a reference in this repo to what appears to be key ids:

$footer = (string) \json_encode(['key-id' => 'gandalf0']);

But I also found this closed issue about it:

#1

As far as I can tell, there is no built-in way to parse a footer in advance of decoding. I could provide a "peek at the footer" function that just base64 decodes the last segment, but I don't want people using the data in the footer unauthenticated. They could be required to write their own "peek" function, but I don't want to encourage that either (as they might be tempted to use it for things apart from key-selection).

I almost wonder if there is a benefit to telling people it is unsupported. For users who insist, they can reference their alternative keys out-of-band (query string, post parameter, extra junk they add to the end of the token, etc.). They could then load the key based on what was provided out-of-band, which is unauthenticated (as they can't authenticate without the key anyhow).

Any suggestions?

Paseto can be use is production ?

Hi,

I just finish to read your post about JWT security issues.
Now I need to plan to migrate my code from JWT to another solution.

I saw you made 3 Pre-release, but you set your version 0.3.0 as stable.
So I don't know if you consider PAST production ready.

PAST isn't stable/secure enough to be use in production ?

Thanks for your work ;)

Payload processing specification

Since there's no issue open specifically for this:

Currently the documentation only goes so far as to specify the Paseto message format, but does not specify how to process this payload after decrypting or verifying it.

From reading the reference implementation I can probably answer most of the following myself, the goal here is to highlight questions that the spec should answer if processing the payload is going to be part of the specification, it's not just a series of personal questions :p

  • How payloads represented as strings should be encoded into bytes (utf8?)

  • Is a JSON encoded payload part of the spec? Is it required that (received||sent) payloads are in this format, or optional?

  • Specify the type (or even data) structure (if any) that the JSON payload should conform too

    • Reference implementation appears to hint that this is array<string, string>, but I think it actually allows array<string, mixed> due to a type hint diverging with the doc block. See also probable need for type validation on bulk setting of claims.
  • Are arbitrary keys allowed to be set? (or is there a reserved character set for example?)

  • Are any keys reserved for certain purposes? (e.g. the ones used for rules like expiry)

  • Are these reservations case sensitive? (e.g. should an expiry key with incorrect case be ignored?)

  • Should users be allowed to set reserved keys like arbitrary other keys, or should distinct mechanisms be enforced?

  • If users can set these, do we fail if the user puts unexpected (e.g. unparseable) data in these reserved fields (i.e. should writes to reserved fields be validated)?

  • Are rules part of the spec? (if so what are they?)

  • Are rules specified as reserved keys, or are they in a structurally different part of the JSON payload to user data?

  • Is rule validation required? (if no, what should be the default?)

  • Do we fail open or closed if a rule is of an unexpected format?

  • Will payload processing receive spec updates (e.g. potential changes to rules, addition of rules), how should this be versioned?

  • If payload processing is versioned, where do we put this version?

Can probably ask at least the encoding question for processing the footer too, though that seems really intended just to be a string.

Key objects / JWK equivalent

For paseto to become a viable JOSE alternative, it probably needs to cover more of the JOSE suite including JWK.

At DC26 CiPHPerCoder talked about how key objects in library implementations should be typed and not just byte arrays. To this end, paseto should have a JSON object format for keys to a) guide implementations of key classes and b) provide simple standard ways of serializing/deserializing keys.

Format of the "exp" field

There's an RFC 3339 specifying timestamps format for use in Internet protocols.

I think it's a good idea to:
1) explicitly state that "exp" (if present) contents MUST follow that RFC.
2) fix the examples and test vectors to conform with that RFC. Currently the examples in README contain "exp": "2039-01-01T00:00:00", which doesn't have a timezone offset specified, in violation of RFC 3339. As a consequence, RFC-conformant parsers will refuse to parse this timestamp (saying from my personal experience, trying to implement PAST in Rust language).

Use cases explanation in README

In README a "Caution" quote explains one use case where PAST should not be used.

Can we have more details about the use cases?
For example by having two sections "When to use PAST", "When NOT to use PAST".

Abuse-Prone Feature: Key Identifiers

Some people might want to allow the token to possess some sort of identifier for the key used to encrypt it. If we decide to implement this, it's going to be:

  1. Optional.
  2. Not enabled by default (require an explicit call).
  3. Appended after the payload.

Arbitrary payload signing use cases?

In its current state, can paseto be used to simply sign arbitrary payloads ... e.g. generic JSON or any base64 encoded value? Looking for an alternative to JWS and came across paseto, but it seems to be primarily focused on claims and an alternative to JWTs.

Structured payloads to avoid key collisions

Right now, the payload, as defined, seems to have only one key, exp. It seems like, somewhere and somewhen, somebody is going to try to stick a colliding key into the payload. Also, down the line, it strikes me that there may be additional metadata to add to the token (which could cause forward-upgrade issues for users with colliding keys). Maybe it'd be better for the basic payload structure to be:

{
  "exp": <expiry>,
  "d": <any legal JSON token>
}

For ease of use this could be constrained such that d must be a JSON object (in particular that'd probably make static-language implementations a little bit easier).

Thoughts?

dummy usage example...

Hello,
In the documentation we got example to create and validate token, but to avoid miss-usage it could be interesting to have a dummy example.

So, for me, I will do something like this :

init.php

<?php
use ParagonIE\Paseto\Keys\{
    AsymmetricSecretKey,
    SymmetricKey    
};

$privateKey = new AsymmetricSecretKey(sodium_crypto_sign_keypair());
$publicKey = $privateKey->getPublicKey();

$sharedKey = new SymmetricKey(random_bytes(32));
// shared key is stored in a config file? a database? ....

getToken.php

<?php
use ParagonIE\Paseto\Builder;
use ParagonIE\Paseto\Purpose;
use ParagonIE\Paseto\Keys\SymmetricKey;
use ParagonIE\Paseto\Protocol\Version2;

// reuse our shared key
$token = Builder::getLocal($sharedKey, new Version2());

// the user is authenticated insight the app
$userId = $app->getTheUserId();

$token = (new Builder())
    ->setKey($sharedKey)
    ->setVersion(new Version2())
    ->setPurpose(Purpose::local())
    ->setExpiration(
        (new DateTime())->add(new DateInterval('P1D'))
    )
    ->setClaims([
        'user_id' => $userId,
    ]);

echo $token;
// make the token available in the frontend.

bar.js

$.ajax({
  method: 'POST',
  url: 'foo.php',
  data: { token: tokenPreviouslyGivenByTheServer, data: 'some-data-to-save }
})
  .done(function( msg ) {
    alert( "Data Saved: " + msg );
  });

foo.php

<?php
use ParagonIE\Paseto\Exception\PasetoException;
use ParagonIE\Paseto\Keys\SymmetricKey;
use ParagonIE\Paseto\Parser;
use ParagonIE\Paseto\Purpose;
use ParagonIE\Paseto\Rules\{
    IssuedBy,
    NotExpired
};
use ParagonIE\Paseto\ProtocolCollection;

$providedToken = htmlspecialchars($_POST['token']);

/**
 * @var string $providedToken
 * @var SymmetricKey $sharedKey
 */
$parser = Parser::getLocal($sharedKey, ProtocolCollection::v2());
// This is the same as:
$parser = (new Parser())
    ->setKey($sharedKey)
    // Adding rules to be checked against the token
    ->addRule(new NotExpired)
    ->addRule(new IssuedBy('issuer defined during creation'))
    ->setPurpose(Purpose::local())
    // Only allow version 2
    ->setAllowedVersions(ProtocolCollection::v2());

try {
    $token = $parser->parse($providedToken);
} catch (PasetoException $ex) {
    /* Handle invalid token cases here. */
  header('HTTP/1.0 401 Unauthenticated');
}

if (!($token instanceof \ParagonIE\Paseto\JsonToken))
{
  header('HTTP/1.0 401 Unauthenticated');
}

$userId = $token->get('user_id');
// ....
// do something with the posted data

Is this something that could be considered as valid? In the other case, how should we handle it? An example is always a starting point

thanks

Specification unclear

Hello, I'd like to write few implementations in node and java but find the documentation fairly inadequate.
Is there a well written specification document available?

test vectors 2-E-5, 2-E-6, RFC vs reference implementation

There is an inconsistency between the test vector specification in the RFC and reference implementation for test vectors 2-E-5, 2-E-6 and 2-S-2.

For example, for 2-E-5, published rfc draft and rfc source specify footer {"kid":"UbkK8Y6iv4GZhFp6Tx3IWLWLfNXSEvJcdT3zdR65YZxo"} while
reference implementation actually uses footer with kid zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN.

The same discrepancy exists for 2-E-6.

For 2-S-2, published rfc draft and rfc source use kid dYkISylxQeecEcHELfzF88UZrwbLolNiCdpzUHGw9Uqn while reference implementation uses kid zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN.

Please confirm, is it safe to assume that reference implementation contains the correct test vector and rfc source should be amended?

Edit: added 2-S-2

Keys and protocols

For example in

public function __construct(
string $keyData,
ProtocolInterface $protocol = null
) {
$protocol = $protocol ?? new Version2;
if (\hash_equals($protocol::header(), Version2::HEADER)) {
$len = Binary::safeStrlen($keyData);
if ($len === SODIUM_CRYPTO_SIGN_KEYPAIRBYTES) {

The given protocol version is used to determine conditions for key construction. However the protocol version of a key is not checked when using the key directly with a protocol, e.g. in

public static function sign(
string $data,
AsymmetricSecretKey $key,
string $footer = ''
): string {
$header = self::HEADER . '.public.';
$signature = \sodium_crypto_sign_detached(
Util::preAuthEncode($header, $data, $footer),
$key->raw()
);
if ($footer) {
return $header .
Base64UrlSafe::encodeUnpadded($data . $signature) .
'.' .
Base64UrlSafe::encodeUnpadded($footer);
}
return $header . Base64UrlSafe::encodeUnpadded($data . $signature);

Which means that the conditions checked in the key's constructor cannot be guaranteed (since version 1 AsymmetricSecretKey key may be constructed and passed to version 2 as a key for example).

This I suppose leads to the more general question: if keys are dependent on a version, should there be distinct types for these different versions?

At the very least I think that the protocols should be checking the version associated with any keys they receive?

Rename "publickey" to "tokenkey" in readme

The term public key as used in the token specification is slightly confusing, I'd suggest renaming it to token key to better reflect it's usage.

eg

version.purpose.tokenkey.ciphertext

Specifying nonce in pre-authentication encoding

After discussing with @aidantwoods in a separate issue (aidantwoods/swift-paseto#5), it looks like PAE assumes that deterministic encryption is available in XChaCha20-Poly1305 encryption.

Even if specifying nonces has been made optional in paseto-rfc-01, it is still assumed that the same random bytes will be used in PAE and in the subsequent encryption/decryption:

   2.  Generate 24 random bytes from the OS's CSPRNG.

   3.  Optionally, calculate [...] This will be our nonce, "n".

       *  If this step is omitted, the output of step 2 is "n" instead.

   4.  Pack "h", "n", and "f" together (in that order) using PAE (see
       Section 2.2).  We'll call this "preAuth".

This is problematic because @jedisct1 plans to remove specifying nonces from the public libsodium APIs (jedisct1/swift-sodium#173 (comment)).

Examples are wrong?

So I thought I'd add an example from the readme to my test suite, and it turns out there are some typos (unless of course my implementation is somehow completing words and changing a year ;p)

screen shot 2018-03-08 at 19 00 44

I think "exp": should be "expires": and the year should be 2019 instead of 2039.

(correct example)

I would have opened this as a PR, but I wanted to check whether the second example also has the same problem. It appears to, but I can't conclude this because there isn't a public key available to verify the message ;-) (this is a request to add the public key to the readme)

paseto.io website

I noticed that paseto.io is registered and currently just points to chronicle. I'm not sure if there are any plans yet. I think some of JWT's usage/success is driven from the utility and visibility of that marketing-style site.

Some ideas:

  • token sandbox
  • curated list of implementations with feature matrix (v1,v2,public,local,registered claims)
  • link to reference implementation
  • mirror for the RFC
  • examples of valid use-cases
  • documentation of what not to do / why paseto vs jwt (maybe even link to the paragonie blog)
  • list of projects using paseto (non libraries) in a non abhorrent way

AsymmetricSecretKey slow

Hello! I'm building this example

require __DIR__ . '/vendor/autoload.php';

use ParagonIE\Paseto\Keys\{
   AsymmetricSecretKey,
   SymmetricKey
};  
       
$privateKey = new AsymmetricSecretKey(sodium_crypto_sign_keypair());
$publicKey = $privateKey->getPublicKey();

$sharedKey = new SymmetricKey(random_bytes(32));

When I ran this code in Eclipse, it stay very slow ! Do I need to use other way for it?

My last test, the code spent 10 minutes to finish! What could be happening?

Thank you!

Protocol Change: Greatly Simplifying our Use-Case

The first draft (current as of v0.2 of this repository) defined four purposes:

  • Shared-key Authentication (auth)
  • Shared-key Encryption (enc)
  • Public-key Authentication (sign)
  • Public-key Encryption (seal)

However, seal has since been removed due to a lack of a good real-world use case.

I've been discussing this with other crypto/security experts, and I think we might be able to get the same utility out of PAST if we dropped auth, and changed our paradigm to look like this:

  • Shared-key AEAD (local)
  • Digital signatures (public)

If you need the equivalent use-case of v2.auth (e.g. so you can inspect some data in the token before decoding/verifying it normally), you can simply encrypt an empty string and append your public data in the footer.

This would simplify our design, reduce our technical debt, and minimize the attack surface to a reasonable level. However, this is also a secure-by-default feature: If you're just storing data in a token and using the user as a data mule for local usage, it won't be plaintext.

Official logo

Would be great to have an official PASETO logo. I know from a technical standpoint PASETO speaks for itself, but for whatever reason having a logo does seem to give projects a greater sense of credibility. I think something as simple as that would help to grow the community.

ReferenceError: cb is not defined

Error happens on macOs when trying to generate the token

ReferenceError: cb is not defined
    at encoder.encrypt (/Users/.../server/src/utils.js:556:16)
    at /Users/.../server/node_modules/paseto.js/lib/utils.js:239:39
    at sodium.ready.then (/Users/.../server/node_modules/paseto.js/lib/protocol/V2.js:146:14)

Edit:
This happens when i try to use something that is not the callback function
Error: Failed to construct 'TextDecoder': the 'fatal' option is unsupported.

I will move this to the Paseto.js

`Parser` shouldn't produce signable `Builder`s?

In the 'public' case of:

paseto/src/Parser.php

Lines 190 to 220 in c44d334

case 'public':
if (!($this->key instanceof AsymmetricPublicKey)) {
throw new InvalidKeyException('Invalid key type');
}
$footer = (\count($pieces) > 4)
? Base64UrlSafe::decode($pieces[4])
: '';
try {
/** @var string $decoded */
$decoded = $protocol::verify($tainted, $this->key, $footer);
} catch (\Throwable $ex) {
throw new PasetoException('An error occurred', 0, $ex);
}
break;
}
// Did we get data?
if (!isset($decoded)) {
throw new PasetoException('Unsupported purpose or version.');
}
/** @var array<string, string> $claims */
$claims = \json_decode((string) $decoded, true);
if (!\is_array($claims)) {
throw new EncodingException('Not a JSON token.');
}
// Let's build the token object.
$token = (new JsonToken())
->setVersion($header)
->setPurpose($purpose)
->setKey($this->key)

$this->key is checked to be an instance of AsymmetricPublicKey , which is later passed to JsonToken::setKey. However, this requires the given key to be an instance of AsymmetricSecretKey in the 'public' case.

paseto/src/JsonToken.php

Lines 374 to 382 in c44d334

case 'public':
if (!($key instanceof AsymmetricSecretKey)) {
throw new InvalidKeyException(
'Invalid key type. Expected ' .
AsymmetricSecretKey::class .
', got ' .
\get_class($key)
);
}

Obviously there's going to be a type mis-match here, but on a more general note:

Given that the JsonToken is constructed with a key for signing, I don't think it would ever make sense to have the parser produce a JsonToken in the public case, because it would require knowledge of the secret key to sign, but the parser is only really concerned with verification which requires only the public key (which is likely all the receiver of a signed object has?).

I think perhaps the key/signing should be decoupled from the JsonToken so that the parser can produce these objects without requiring a key, and the key should be provided when signing takes place (perhaps in a new SignableJsonToken class for example that is constructed using a JsonToken and a key).

How can I set invalid before token expired?

Hello

This is a very nice project, and I'm considering to switch from JWT.

I know JWT also doesn't have this feature, But I want to know paseto project can support "set token invalid before expired" feature now? or in the future?

thx.

token->toString returns empty value

Code in this repository: https://github.com/conferencetools/auth-module/blob/master/src/Auth/Extractor/PasetoCookie.php results in an empty cookie being set on my production env; works fine in dev.

PHP environment is

FROM php:7.2-fpm-alpine3.7 as php

RUN docker-php-ext-install pdo_mysql

Fairly sure it's an environment thing, but I'd expect an exception instead of an empty string.

Interesting addendum: switched to V1 and it works fine. Suspect it might be something alpine/libsoduim related.

body and footer questions

I became interested in your paseto standard after feeling a bit uneasy about JWT's, but I have a few questions and wasn't sure how else to ask but to make a github issue (feel free to add the answers to your documentation).

  1. Is the body and its claims always encrypted? If not, when it is it not encrypted?

  2. Is the footer always unencrypted?

  3. Is the footer signed?

  4. If you want the website frontend to see certain claims or data, would you advise putting that in a json objected in the footer, instead of into the body?

  5. Is the footer validated against anything? Or is it just a grab bag of whatever I want to put in it, including nothing?

  6. Which claims are "built into the standard" and are expected to be validated or read by the implementing libraries?

  7. Should Paseto be used for sending session id's and non-confidential data about the user to the website frontend, to be stored in secure cookies? (ie: what most people use JWT's for)

Inconsistent use of 'expires' and 'exp' in tests.

There are two instances of 'expires' in the Version2VectorTest tests:

Everything else uses 'exp'. Should these be altered for consistency?

Past does not rhyme with frost

Like the English word "pasta" without the final "a". It rhymes with "frost" and the first syllable in "roster".

I don't know about you, but in Philly, we pronounce frost as frauwst :P

Reconsider the Utility of "seal"

Three of the purposes defined thus far are:

  • auth, which is authenticated with a shared secret key
  • enc, which is encrypted with a shared secret key, but uses authenticated encryption
  • sign, which is authenticated with a secret key and verified with a public key

But seal is a bit of an oddball. It implements what libsodium refers to as "anonymous public-key encryption". The ciphertext has integrity assurance (via AEAD), but the sender is anonymous.

Do we want anonymous public-key encryption in PAST? Is there a valid real-world use-case for it?

I suspect the answer is "no", and will be proposing a replacement that uses authenticated public-key encryption. The interface will, instead, look something like this:

public function asymmetricEncrypt(
    string $message,
    AsymmetricPublicKey $recipientPublicKey,
    AsymmetricSecretKey $mySecretKey,
    string $footer = ''
): string;

public function asymmetricDecrypt(
    string $token,
    AsymmetricPublicKey $senderSecretKey,
    AsymmetricSecretKey $mySecretKey,
    string $footer = ''
): string;

Details:

  • For v1 (RSA): Encrypt then Sign.
  • For v2 (Curve25519): KX without ephemeral keys. (Use the sender's key, instead.)

Using sodium_compat slows down requests

Hello, I'm wondering if my system is set up the wrong way or if this library is supposed to force the use of sodium_compat? As far as I can tell from some xdebugging and analysis with Blackfire, when executingVersion2::aeadDecrypt() (here), there is never an attempt to call sodium_crypto_aead_xchacha20poly1305_ietf_decrypt directly, but \ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_decrypt is used. Modifying that line of code to use the sodium function cuts the cost from 25 ms to 4 ms, with most of that being CPU time.

Am I doing something wrong, or is this an oversight? The sodium_compat documentation seems to specify that calling the library directly with ParagonIE_Sodium_Compat:: is for implementations targetting version below PHP 5.3, but as this library targets PHP 7.0, I can't see how that is relevant.

Thanks!

Spec question on UInt64 requirement and language results for sizes

Regarding the spec definition of LE64:

LE64() encodes a 64-bit unsigned integer into a little-endian binary string.

When this is used with PAE, LE64 is exclusively given lengths (of an array and of a byte string)

function PAE(pieces) {
    if (!Array.isArray(pieces)) {
        throw TypeError('Expected an array.');
    }
    var count = pieces.length;
    var output = LE64(count);
    for (var i = 0; i < count; i++) {
        output += LE64(pieces[i].length);
        output += pieces[i];
    }
    return output;
}

However, some languages (e.g. PHP, Swift, ...) give signed integer results for these lengths: reducing the effective number of bytes that LE64 will ever act on to 63 (even after conversion to a UInt64 if possible).

Given the goal of platform agnosticism I think the spec should define what to do in the case that the lengths used by PAE cannot be more than 63 (effective) bytes in the implemented language. (Indeed the reference PHP implementation has this restriction). Perhaps even given that languages with this restriction exist, implementations in languages that do give unsigned 64 bit lengths should instead replicate the behaviour, e.g. be instructed to throw if the most significant bit is ever populated (i.e. the result is greater than something like UInt64.max/2).

(actually hitting these numbers isn't at all likely to happen in practice of course by any sensible interpretation of the word, this mainly being a pedantic clarification on what the "right" (defined) thing to do is)

Secret keys must be 32 or 64 bytes long; 96 given.

Following along with the usage documentation here: https://github.com/paragonie/past/tree/master/docs/02-PHP-Library, I've ran into the following exception when executing the first block of code:

PHP Fatal error:  Uncaught Exception: Secret keys must be 32 or 64 bytes long; 96 given. in vendor/paragonie/past/src/Keys/AsymmetricSecretKey.php:40
Stack trace:
#0 encrypt.php(11): ParagonIE\PAST\Keys\AsymmetricSecretKey->__construct('\x84\x9E\xAC\xFB6t^\xBD\xEC\xC3Hq\xFA\xAD\x06...', 'v2')
#1 {main}
  thrown in vendor/paragonie/past/src/Keys/AsymmetricSecretKey.php on line 40

This is the code I'm using:

<?php

require('vendor/autoload.php');

use ParagonIE\PAST\Keys\{
    AsymmetricSecretKey,
    SymmetricKey
};

$privateKey = new AsymmetricSecretKey(sodium_crypto_sign_keypair());
$publicKey = $privateKey->getPublicKey();

$sharedKey = new SymmetricKey(random_bytes(32));

I'm running this on macOS with PHP 7.1. I ran it with both libsodium and gmp installed and uninstalled, but got the same exception.

Did anything change in the implementation and is the documentation not up-to-date, or is there something else going on?

Duplicate test vector name

I found some test names that have been re-used, probably in error:

  • There are two 'Test Vector S-6' entries in the Vector2VectorTest tests file. Should the second, which adds a non-empty footer to the first test, be renamed to S-7?

    See:

    $message = \json_encode(['data' => 'this is a signed message', 'expires' => '2019-01-01T00:00:00+00:00']);
    $footer = 'Paragon Initiative Enterprises';
    $this->assertSame(
    'v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwaXJlcyI6IjIwMTktMDEtMDFUMDA6MDA6MDArMDA6MDAifSUGY_L1YtOvo1JeNVAWQkOBILGSjtkX_9-g2pVPad7_SAyejb6Q2TDOvfCOpWYH5DaFeLOwwpTnaTXeg8YbUwI',
    Version2::sign($message, $this->privateKey),
    'Test Vector S-6'
    );
    $this->assertSame(
    'v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwaXJlcyI6IjIwMTktMDEtMDFUMDA6MDA6MDArMDA6MDAifcMYjoUaEYXAtzTDwlcOlxdcZWIZp8qZga3jFS8JwdEjEvurZhs6AmTU3bRW5pB9fOQwm43rzmibZXcAkQ4AzQs.UGFyYWdvbiBJbml0aWF0aXZlIEVudGVycHJpc2Vz',
    Version2::sign($message, $this->privateKey, $footer),
    'Test Vector S-6'
    );

  • There is a signing test that re-uses a name from an encryption test. It should probably be renamed.

    See:

    $message = \json_encode(['data' => 'this is a signed message', 'exp' => '2019-01-01T00:00:00+00:00']);
    $footer = \json_encode(['kid' => 'zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN']);
    $this->assertSame(
    'v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAxOS0wMS0wMVQwMDowMDowMCswMDowMCJ9flsZsx_gYCR0N_Ec2QxJFFpvQAs7h9HtKwbVK2n1MJ3Rz-hwe8KUqjnd8FAnIJZ601tp7lGkguU63oGbomhoBw.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9',
    Version2::sign($message, $this->privateKey, $footer),
    'Test Vector 2E-6'
    );

    This is probably a copy-and-paste error when the corrensponding encryption test was re-used. I think it should be named S-8 instead.

Reconsider RNG Failure Mitigation Strategy

From Thai Duong:

In the case of an RNG failure (i.e. all zero bytes), the BLAKE2b of the nonce and message leaks the BLAKE2b hash of the message, which might be enough information (along with the plaintext length) to allow an attacker to recover a plaintext without knowing the key.

The story is similar for v1 with HMAC-SHA384.

Proposed solution: Instead of hashing the message and random nonce together, also include the secret key in the calculation. This changes the attack calculus from "find the input to a hash function with a known output" to "break a keyed PRF without knowing the key".

This solution is backwards compatible with what v1 and v2 are already doing (since the output is just a plaintext nonce that gets stored in the token). However, to make this behavior change explicit and easy to reason about, I'm thinking about incorporating these changes into a v3 and v4.

setClaims overwrites what was defined with setExpiration in the php example

Using the php example code at https://github.com/paragonie/past/tree/master/docs/02-PHP-Library, setClaims overwrites what was defined with setExpiration

$token     = (new JsonToken())
    ->setKey($sharedKey)
    ->setVersion(Version2::HEADER)
    ->setPurpose('local')
    ->setExpiration((new DateTime())->add(new DateInterval('P01D')))
    ->setClaims(['example' => 'Hello world', 'security' => 'Now as easy as PIE']);

For an expiration to be set correctly, setClaims needs to come before setExpiration.

$token     = (new JsonToken())
    ->setKey($sharedKey)
    ->setVersion(Version2::HEADER)
    ->setPurpose('local')
    ->setClaims(['example' => 'Hello world', 'security' => 'Now as easy as PIE'])
    ->setExpiration((new DateTime())->add(new DateInterval('P01D')));

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.