-
Notifications
You must be signed in to change notification settings - Fork 106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial Draft for PASETO v3/v4 specifications #127
Conversation
👋
Edit: it seems IEEE-P1363 style signature is already implied given that the verify step 3 says Edit2: RFC 6979 is indeed not supported by openssl yet. It is unfortunate that because of this yet another version.purpose can't be supported without bundling additional crypto runtimes like libsodium into openssl-capable environments. |
The intent of RFC 6979 is to be compatible with FIPS 186-5 when it's no longer a draft. However, there is also the argument that randomness makes some side-channel attacks more difficult in certain environments. Consequently, the IETF has shown interest in so-called hedged signatures. Relaxing this restriction may be pragmatic but it's still dangerous. |
We've relaxed the RFC 6979 requirement from MUST to SHOULD. When it's not possible, a CSPRNG MUST be used for k-value generation. |
I'm going to quickly tag everyone who has contributed a PASETO implementation so they're aware of v3/v4 and have ample opportunity to provide feedback. @sjudson @peter-evans @nbaars @atholbro @sethbattin @minus7 @eislambey @bdemers @scottbrady91 @shuLhan @brycx @vk-rv @o1egl @Ianleeclark @shuLhan @daviddesmet @dustinsoftware @bricej13 @rlittlefield @mguymon @AnIrishDuck @aidantwoods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given a JWK representation of a public key, e.g.
{
crv: 'P-384',
kty: 'EC',
x: 'lkB6sw4ohIlg00fBle-TzK8_OyUGNpE74vhi1BQ45NkCJtscEV1BUdrzhk_OTrvP',
y: 'fH7l2JEKPnR3pywmodMy6pBIi_h7JxwuQoPYhF5REvGwuJUxPlNSWBCj7IvxKdmq'
}
What is the pk
to pack in v3.public.
? Is it as simple as sign
followed by x
where sign is 0x02 if y is even, 0x03 if y is odd?
NB: SPKI representation of the same key
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAElkB6sw4ohIlg00fBle+TzK8/OyUGNpE7
4vhi1BQ45NkCJtscEV1BUdrzhk/OTrvPfH7l2JEKPnR3pywmodMy6pBIi/h7Jxwu
QoPYhF5REvGwuJUxPlNSWBCj7IvxKdmq
-----END PUBLIC KEY-----
I've also contributed an implementation (the first rust one) and luckily just happened to check my email 😅 to see this. I don't see any problems with this on the start -- for rust it might be a little tough since: crypto_stream_xchacha20_xor is from libsodium (whose currently in a murky state looking for maintainers: https://github.com/sodiumoxide/sodiumoxide/issues/442 -- and we don't have any crate in the Rust-Crypto/dalek-cryptography/etc. that does this particular flavor of XOR'ing xchacha , just normal XChaCha). Though I'm not going to say this shouldn't move forward just because one language doesn't have a good way to use a solid proven crypto library that we honestly should support. Otherwise this all seems fairly cut and dry. Thanks for getting this out. |
The public key, when hex-encoded, will look like this:
Online documentation for this appears to be unclear about which bit of |
If it's unclear, why not use the uncompressed representation instead? |
Sorry about the oversight; I was gauging based on pull requests. I'll make sure I didn't miss anyone.
I think this should be an easy patch to write. Let me follow up with the crypto rustaceans I know and hopefully we can prevent this from making PASETO painful to implement. |
What's unclear is the informal answers you find on forums like Stack Exchange, not the SECGv2 paper. Its definition is very precise. Compressed points prevent implementations from accidentally becoming susceptible to off-curve attacks. e.g. https://auth0.com/blog/critical-vulnerability-in-json-web-encryption/ |
Just FYI, the difference is simply that the XOR'ing flavor XORs the XChaCha pseduo-random stream with the input plaintext. Should be straight-forward if plain XChaCha is available. Orion (disclaimer: I'm the maintainer) supports this XORing flavor, for inspiration. Not as battle-tested as libsodium though. |
@brycx thanks. I had figured it wouldn't be too hard but to be honest I don't trust myself to make those determinations. I didn't see Orion -- guess my GoogleFu was weak. But it's good to know there's some prior art. |
I'm aware of that, but it makes little difference to the Exclusive Ownership enhancement itself, doesn't it? For the record I'm okay with either, I am just looking at ways of making this less error-prone when it comes to interoperability of implementations. As a bit of feedback, when it comes to vectors it would be amazing if key representations were provided in various formats - compressed public, SPKI PEM as well as JWK. |
@paragonie-security Thanks for the ping. I'll be taking a closer look when implementing, but seems good to me so far. Glad to see the focus on key-commitment. Re the XChaCha20-Blake2b construction: Are there existing test vectors for this combination available, outside the context of PASETO, or will implementers have to rely on the implicit test vectors through PASETO only? Thanks for the work you've put into this! |
They're not just used in the EO enhancement, but also a MUST for the actual public key encoding for PASETO v3.
There will be test vectors. We're also working on a related specification for key serialization right now to obviate JWK, but we have no problem building test vectors from (x, y) pairs that can be JWK-alike. |
Halite has been using this construction for years (albeit with Salsa20 instead of ChaCha). Halite v5 will almost certainly use XChaCha. |
I typically reference section 4.3.6 step 2.2 of this document 1 for point compression. It seems to be the normative way that sec2 originally defined it and has been reliably interoperable for us when dealing with compressed key formats from the secg curves. It aligns with the way you pointed out as well which is sign based on if it's even or odd. Does that help as a point of reference? |
@kdenhartog it sure does, thank you. |
I've pushed a commit (and updated the top-level comment) to explain the operational security requirements imposed by ECDSA for v3.public tokens. |
Co-authored-by: Filip Skokan <panva.ip@gmail.com>
A mechanism that should remain uncontroversial is to enforce a maximum depth of N (where N is very small; for example, 2). If you're storing expressive claims, that probably doesn't belong in the footer. |
I agree, but setting the max depth of a JSON parser may not be possible depending on the JSON lib used (and resolving it after the fact is too late) This section in the spec implies the footer could be used to store data for "unencrypted, but authenticated, tokens" If a |
How exactly would you recommend enforcing this safely? |
To expand our previous query with a little more context: One of the mechanisms we're introducing with PASERK is the ability to wrap a single token key against multiple wrapping keys ( (PASERK is one motivation for why V3/V4 must be RKR-secure and why V3 always supports point compression for P-384 keys.) It's very possible that someone will want to use PASETO with PASERK such that there is no one footer = JSON.stringify({
"wrapped-keys": [
"k4.seal.AAAAAAAAAAAAA...",
"k4.local-wrap.pie.AAA..."
]
}) Alternatively, if there's only one wrapped key, it's reasonable to only use that as the footer. footer = "k4.local-wrap.pie.AAA..." If JSON in the footer is a security problem for Java libraries, then PASETO needs to try to mitigate it at a design level. (That doesn't guarantee that we'll succeed, but it's a good use of our time.) |
…rsers We're actively looking for more feedback on this
2df496e adds a Parser rule called FooterJSON that allows assertions to be made about the footer:
EDIT: 1e2daa2 makes it reject tokens that are too deep without needing to actually parse it with a JSON library. This rule should make it more difficult to target PASETO with Hash-DoS attacks. @bdemers what do you think? |
@paragonie-security in the typescript example -> Step 6 is an infinite loop. Give it a whirl with the following inputs: |
All 12 v2 test vectors are working fine against my implementation, 3 for v2.public plus 9 for v2.local as seen here Do I understand correctly that implicit assertions are a new v3 / v4 feature and won't be a feature in v1 / v2? I noticed that |
I must've made a mistake with my regex. Thanks for checking, I'll fix it.
Excellent!
Yes, their inclusion in the test vectors was to signal that they are not supported in v1/v2. The main reason is that there are over a dozen v1/v2 implementations in the wild and none of them supported this feature, so it would be breaking backwards compatibility. |
I had been thinking about using the footer text as the An additional challenge with any option is there is no standard key rotation mechanism. This isn't a PASETO spec problem though, I'm guessing that would/should be defined in any spec that depends on PASETO. But this does mean the implementations need to pass this flexibility down to the user/developer of a PASETO lib (this is already spelled out in the guides). Ideally, there would be a single definitive way to get a key-id. That said, my musing on this is probably short-sighted given the complex keys mentioned above and the simple fact any sort of key-id representation is optional. I'm mostly thinking out loud anyway, but your resulting JSON bits are a great addition to the guide! |
You can use PASERK to serialize local or public keys and either stick them in the footer or send them out-of-band (and include them as an implicit assertion in v3/v4).
Thanks! I hope it helps. |
See https://eprint.iacr.org/2010/264 Definition 7 for more information
We changed the way HKDF is used in Version 3 of PASETO: 6c6eb51 |
This comment has been minimized.
This comment has been minimized.
It looks like we agree: 7e72e5f |
Version 3 is defined as follows: - NIST P-384: 192-bit security level - AES-256-CTR: 256-bit security level - HMAC-SHA-384: 192-bit security level ...why were we using 256-bit keys (128-bit birthday collision level) and reducing the security if the authentication tag is already 48 bytes? This change makes V3's cryptographic primitives consistently at or above the 192-bit security level.
Updated 3-E-* test vectors with 092629c
|
Barring any discoveries from cryptography experts, that should be the last change we've made to V3/V4. Thanks for your patience and diligence @panva! |
There are no significant changes as far as active implementations are concerned. The only thing "noteworthy" is the delineation between the version (which is format-agnostic) and the token (which is JSON-specific) in naming convention. This is so that future designers might specify PASETO over an encoding format other than JSON (Protobuf, CBOR, etc.) without having to re-define the cryptography protocols. If this happens, `v4c` would be simply, "Version 4 with CBOR". You can think of the tokens as `v4j` if that helps (although JSON gets "no suffix" reserved for it). If you don't care about these kinds of future-proofing discussions, then this commit will be uninteresting to you.
ccbab6a
to
c349cab
Compare
Due to the feedback we've received since this PR was opened (and a UX problem with Github), we're doing to merge this, but don't take that as a signal that discussion has closed prematurely. If you have any additional feedback, questions, concerns, etc. please raise them here or in a new issue (whichever you prefer). Per #128, the comment period is open until at least 2021-08-02, but you should take us releasing |
Per #78 we've moved the documentation to https://github.com/paseto-standard/paseto-spec Please feel free to toss issues at that repository. |
Spec has been finalized. |
No Code Changes; only specification
See #128 for the implementation.
(Copied from
Rationale-V3-V4.md
)Primary Motivations for New Versions
v4.local
v2.local was originally specified to use XChaCha20-Poly1305, a boring
AEAD mode that's obviously secure. However, we've since learned about
key- and message-commitment, which is an important security property
in systems with multiple possible symmetric keys.
Since PASETO added footers to support key-ids and key rotation
strategies, this means we MUST take attacks that depend on
random-key robustness seriously.
PASETO v4.local uses XChaCha20 to encrypt the message, but then uses
a keyed BLAKE2b hash (which acts as HMAC) for the authentication tag.
v3.public
We specified RSA for PASETO v1.public tokens, under the assumption that
applications that must ONLY support NIST algorithms (e.g. because they
MUST only use FIPS 140-2 validated modules to maintain compliance) would
be adequately served by RSA signatures. This assumption turned out to be
incorrect, and elliptic curve cryptography is now preferred.
To better meet the needs of applications that are NIST-dependent, PASETO
v3.public tokens will support ECDSA over NIST's P-384 curve, with SHA-384,
and (preferably) using RFC 6979 deterministic signatures. (RFC 6979 is a
SHOULD, not a MUST, due to library availability issues and
fault attacks.)
ECDSA Security
ECDSA is much more dangerous to implement than Ed25519:
k
is never reused for differentmessages, or you leak your secret key.
k
deterministically, you have to take extracare to ensure your random number generator isn't biased. If you fail
to ensure this, attackers can determine your secret key through
lattice attacks.
k^-1 (mod p)
must be constant-time to avoid leakingk
.inverse function, but cryptography libraries often do. This is something
a security auditor will need to verify for each implementation.
There are additional worries with ECDSA with different curves,
but we side-step most of these problems by hard-coding one NIST curve and
refusing to support any others. The outstanding problems are:
NIST-approved algorithms). Use v4 instead.
constant-time ladder or offer
complete addition formulas.
There are additional protocol-level security concerns for ECDSA, namely:
Point Compression.
uncompressed public key points, but if they do, they MUST validate
that the public key is a point on the curve.
we stopped avoiding its usage as an industry.
Because of these concerns, we previously forbid any implementation of ECDSA
without RFC 6979 deterministic k-values in a future version.
However, given the real-world requirements of applications and systems that
must comply with NIST guidance on cryptography algorithms, we've relaxed this
requirement.
Additionally, deterministic k-values make signers more susceptible to fault
attacks than randomized signatures. If you're implementing PASETO signing in
embedded devices, or environments where fault injection may be a practical
risk, there are two things you can do:
This randomness doesn't need to be signed.
Questions For Security Auditors
Due to the risks inherent to ECDSA, security assessors should take care to
cover the following questions in any review of a PASETO implementation that
supports
v3.public
tokens (in addition to their own investigations).k^-1 (mod p)
) constant-time?curve (P-384)?
for NIST P-384?
is constant-time. (This affects the security of key generation.)
Affirmative answers to these questions should provide assurance that the
ECDSA implementation is safe to use with P-384, and security auditors can
focus their attention on other topics of interest.
v3.local / v4.public
No specific changes were needed from (v1.local, v2.public) respectively.
See below for some broader changes.
Beneficial Changes to V3/V4
No More Nonce-Hashing (Change)
The initial motivation for hashing the random nonce with the message was
to create an SIV-like construction to mitigate the consequences of weak
random number generators, such as OpenSSL's (which isn't
fork-safe).
However, this creates an unfortunate failure mode: If your RNG fails,
the resultant nonce is a hash of your message, which can be used to
perform offline attacks on the plaintext. This was first discovered by
Thái Dương.
To avoid this failure mode, neither v3.local nor v4.local will pre-hash
the message and random value to derive a nonce. Instead, it will trust
the CSPRNG to be secure.
Implicit Assertions (Feature)
PASETO v3 and v4 tokens will support optional additional authenticated
data that IS NOT stored in the token, but IS USED to calculate the
authentication tag (local) or signature (public).
These are called implicit assertions. These can be any application-specific
data that must be provided when validating tokens, but isn't appropriate to
store in the token itself (e.g. sensitive internal values).
One example where implicit assertions might be desirable is ensuring that a PASETO
is only used by a specific user in a multi-tenant system. Simply providing the
user's account ID when minting and consuming PASETOs will bind the token to the
desired context.
Better Use of HKDF Salts (Change)
With v1.local, half of the 32-byte random value was used as an HKDF salt and
half was used as an AES-CTR nonce. This is tricky to analyze and didn't extend
well for the v4.local proposal.
For the sake of consistency and easy-to-analyze security designs, in both v3.local
and v4.local, we now use the entire 32-byte random value in the HKDF step.
Instead of being used as a salt, however, it will be appended to the info tag.
This subtle change allows us to use the
standard security definition for HKDF
in arguments for PASETO's security, rather than treating it as just a
pseudo-random function (PRF). This security definition requires only one salt
to be used, but for many contexts (info tags).
The nonce used by AES-256-CTR and XChaCha20 will be derived from the HKDF output
(which is now 48 bytes for v3.local and 56 bytes for v4.local). The first 32
bytes of each HKDF output will be used as the key. The remaining bytes will be
used as the nonce for the underlying cipher.
Local PASETOs in v3 and v4 will always have a predictable storage size, and the
security of these constructions is more obvious:
key, for a total of 512 bits.
(If they were larger than the input domain of 512 bits, that would be a
blunder.)
a single collision occurring). Setting the safety threshold to 2^-32
(which is roughly a 1 in 4 billion chance) for a space of 2^256
yields 2^112.
V3 Signatures Prove Exclusive Ownership (Enhancement)
RSA and ECDSA signatures do not prove Exclusive Ownership.
This is almost never a problem for most protocols, unless you expect this property
to hold when it doesn't.
Section 3.3 of the paper linked above describes how to achieve Universal Exclusive
Ownership (UEO) without increasing the signature size: Always include the public
key in the message that's being signed.
Consequently,
v3.public
PASETOs will include the raw bytes of the publickey in the PAE step for calculating signatures. The public key is always a
compressed point (
0x02
or0x03
, followed by the X coordinate, for a totalof 49 bytes).
We decided to use point compression in the construction of the tokens as a
forcing function so that all PASETO implementations support compressed points
(and don't just phone it in with PEM-encoded uncompressed points).
Ed25519, by design, does not suffer from this,
since Ed25519 already includes public key with the hash function when signing
messages. Therefore, we can safely omit this extra step in
v4.public
tokens.Miscellaneous Changes
Define Mechanism for Extending PASETO for non-JSON Encodings
PASETO serializes its payload as a JSON string. Future documents MAY specify using
PASETO with non-JSON encoding. When this happens, a suffix will be appended to the version tag
when a non-JSON encoding rule is used.
Questions and Answers
Why Not AES-GCM in
v3.local
?While it's true that AES-GCM is more broadly supported in environments that use
NIST and FIPS-approved cryptography, GMAC is neither
message-committing nor key-committing.
The techniques for turning an AEAD scheme into an AEAD scheme is well known,
but it requires publishing an additional SHA2 hash (or KDF output) of the
key being used.
Using GCM would require us to also publish an additional hash anyway. At
that point, it doesn't offer any clear advantage over CTR+HMAC.
CTR+HMAC (with separate keys and PAE) is a secure construction and provides
the cryptographic properties we need to use PASETO in threat models where
multiple keys are used or partitioning oracles
are possible.
Why P-384 in
v3.public
instead of P-256 or P-521?Security experts that work heavily with NIST algorithms expressed a
slight preference for P-384 over P-521 and P-256 when we asked. This is also
congruent for our choice of SHA-384 as a hash function over SHA-256 or SHA-512.
The security considerations for the NIST curves are mostly
congruent (albeit the ECDLP security and performance differs a bit).
If you want smaller tokens or better performance than P-384, make sure Ed25519
lands in FIPS 186-5 and use
v4.public
instead.