Closed panva closed 3 years ago
If you need test vectors, I created some in the PHP implementation: https://github.com/paragonie/paseto/pull/128
If you need test vectors, I created some in the PHP implementation: paragonie/paseto#128
The json files are missing the input nonces. And while having limited use, signed vector corresponding private keys would be nice, as well as PEM key values where applicable.
NB:
v1
"pem-public-key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyaTgTt53ph3p5GHgwoGW\nwz5hRfWXSQA08NCOwe0FEgALWos9GCjNFCd723nCHxBtN1qd74MSh/uN88JPIbwx\nKheDp4kxo4YMN5trPaF0e9G6Bj1N02HnanxFLW+gmLbgYO/SZYfWF/M8yLBcu5Y1\nOt0ZxDDDXS9wIQTtBE0ne3YbxgZJAZTU5XqyQ1DxdzYyC5lF6yBaR5UQtCYTnXAA\npVRuUI2Sd6L1E2vl9bSBumZ5IpNxkRnAwIMjeTJB/0AIELh0mE5vwdihOCbdV6al\nUyhKC1+1w/FW6HWcp/JG1kKC8DPIidZ78Bbqv9YFzkAbNni5eSBOsXVBKG78Zsc8\nowIDAQAB\n-----END PUBLIC KEY-----",
"pem-private-key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJpOBO3nemHenk\nYeDCgZbDPmFF9ZdJADTw0I7B7QUSAAtaiz0YKM0UJ3vbecIfEG03Wp3vgxKH+43z\nwk8hvDEqF4OniTGjhgw3m2s9oXR70boGPU3TYedqfEUtb6CYtuBg79Jlh9YX8zzI\nsFy7ljU63RnEMMNdL3AhBO0ETSd7dhvGBkkBlNTlerJDUPF3NjILmUXrIFpHlRC0\nJhOdcAClVG5QjZJ3ovUTa+X1tIG6Znkik3GRGcDAgyN5MkH/QAgQuHSYTm/B2KE4\nJt1XpqVTKEoLX7XD8VbodZyn8kbWQoLwM8iJ1nvwFuq/1gXOQBs2eLl5IE6xdUEo\nbvxmxzyjAgMBAAECggEAXbaMsNrfjIp2ezep93u2j4LcPmFHMBwyfoDO9/2pz5XJ\nsQjpGeNMfENlYrkRqNI/j+xDXl7yK9STQmhZ0nnd94v6GdC/CxpvbyCCFKCGvEza\nQbAYDVeA75JVrCom3xKO8T5D7//TVkorQ7IDRwMmNfcv1Gg9Q3+agx4A8XDSGqQU\nSGbvYZJUIRjV5j5w5eYveJzGeyeVQP/9JL2rfdHEXbLmiJbV52pxGqFb/svXJgxv\nEcVRzFaHxZTCOfUACGgI5TMXxkH7Rf88+H7WdiirrHCovRkoPCtDxIOFb+s0Mt47\n2CJzmuXsR61FBFsW+xnjQB2xfbBT6VYVSQHjYCO6gQKBgQDnP8JeoMV9nwxcI8A5\n66LzLkc0O27fRpH4c5ewEOdEjyG5ewY0PTgV+ZdHueSCGvTx8VUOYTg94pSTzXRg\nlv4gAmjzCs5nTrzafrIQw7nyiaoqKf6IhRWs9ctIujkIKGurDYni+DOiXM/Dc3kj\nwUl0uiZv0WStXzgf4i+I8jKXIQKBgQDfOfAAbb1BfoBMR8WkZU5yiCiscKe13ieJ\npYKrmd7mwUv4TAt5VzXMP3KPVrFwN6UgINnEJnKdTuQOetBwWRPRuMf+ALwmTe/t\ndP2XVQMOoLVvuub9fnAUhn+uY1rWtVmEvWj+0rHm28zazInWr3m/s13KAUgQhb6p\nsgptzpbPQwKBgGxRl1AP6rH/ECEQtffrgjZ6lOvIcxSuz60bKBBWup2Ilfl1wOAz\nVNQmR1BXqMuwqM+zhW3o6BlEyue4syyTTZHczyAZDbmiTh/ifLIRnEYZadW6Ofnk\nrNSJhaEZaaGCnXxQKShhrn39D2yz6ChxX2EH2P1Dje8PzRBSOIXjPQNBAoGALtdB\nfVWJuQyKb3dACdcYNwBLSKP7DTaopUGNweRv2YwGHPwYDEY4i7tklp9ibGHAzJUY\nHQjUVB4RzNgIlQqcFg3oKWyODpucFP/PlsnH8nHWoLNfdSHq8uOmNzmx/gvf1PLJ\n7W7Y1dCZk/AHnH0F1ywUKidKr+zgrUsm1RPcoXECgYEA0ZxDyc5uLebjBE7IquQJ\nZxbEUUyenDG/Slbvbsef3C5o6zhRt6wKfCalwxN/MZQO7NhcK0CrakmXrgcbrCx2\nRaaMFMkSmbpv2Js4E3eoVXbNDQfLIqUxbEi5VKP2A6jrWEXtQf1cHpHgdF2WkE64\nhuABZnjp2SP38cz2i90/QjI=\n-----END PRIVATE KEY-----\n",
v2, v4
"pem-public-key": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAHrnbu7wEfAP9cGBOAHHwmH4Wsot1ciXBHwBBXQ4gsaI=\n-----END PUBLIC KEY-----",
"pem-private-key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEILTL+0PfTOIQcn2VPkpxMwf6Gbt9n4UEFDjZ4RuUKjd0\n-----END PRIVATE KEY-----",
v3
"pem-public-key": "-----BEGIN PUBLIC KEY-----\nMEYwEAYHKoZIzj0CAQYFK4EEACIDMgAC+8t8ae4cYFeb56M0E0h42cXFvzXVUtq2\nPAFAOX7RTO9jfXcgklxEaZ6jDnKHTHL7\n-----END PUBLIC KEY-----",
"pem-private-key": "????"
Could you let me know the v3 private key used to generate the vectors?
If you want the -pem
suffix, it will only be made available in v3 because it doesn't make sense for v2/v4 and v1 is RSA so it's already PEM encoded.
EDIT: https://github.com/paragonie/paseto/pull/128/commits/bdcd113b080811249a1e0d2721632e12aca6934f :)
doesn't make sense for v2/v4
There were people for whom the conversion between spki/pkcs8 and the raw bytes format that sodium uses was a problem.
I'd kindly ask that we stop making a big deal out of representing keying material with standard based representations, as such all four versions signed paseto keys CAN be represented with spki public keys as well as pkcs8 private keys. SPKI and PKCS8 even allow for compressed representations for v3 too.
For now all I'm lacking is the v3 private key, keep the vectors in any shape you'd like - I have filled in the blanks myself, aside of v3.public. private key.
The specs already say that each implementation must make sure the points are actually on their respective curves, that ought to be enough to relax the need to only represent keys the "one way" that libsodium uses.
As an aside:
I have an implementation of a conversion function between your "raw" keys and Node's KeyObject (which always makes sure points are on their respective curves). Do you have a suggestion for what these should be called?
There were people for whom the conversion between spki/pkcs8 and the raw bytes format that sodium uses was a problem.
I didn't think an ASN.1 object identifier even existed for Ed25519, but it does, in RFC 8410, so I think I can do that.
For now all I'm lacking is the v3 private key, keep the vectors in any shape you'd like - I have filled in the blanks myself, aside of v3.public. private key.
No, they're in the JSON. secret-key-pem
.
https://github.com/paragonie/paseto/pull/128/commits/bdcd113b080811249a1e0d2721632e12aca6934f has the PEM-encoded keys.
@paragonie-security the v2/v4 pem private keys in your vector files fail to pass openssl as valid.
the valid value would be
"-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEILTL+0PfTOIQcn2VPkpxMwf6Gbt9n4UEFDjZ4RuUKjd0\n-----END PRIVATE KEY-----"
OK, I'll make the changes.
We only ever use v2/v4 with libsodium which doesn't touch ASN.1, so I had no way of testing this. The PEM we provided was from the following snippet:
const { composePrivateKey, composePublicKey } = require('@credify/crypto-key-composer');
function privateToPem(privateKey) {
var decomposed = {
format: 'pkcs8-pem',
keyAlgorithm: { id: 'ed25519' },
keyData: {
seed: privateKey,
},
}
return composePrivateKey(decomposed)
}
function publicToPem(publicKey) {
var decomposed = {
format: 'spki-pem',
keyAlgorithm: { id: 'ed25519' },
keyData: {
bytes: publicKey,
},
}
return composePublicKey(decomposed)
}
const sk = Buffer.from('b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2', 'hex');
const pk = Buffer.from('1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2', 'hex');
console.log(privateToPem(sk), publicToPem(pk));
OK, I'll make the changes.
We only ever use v2/v4 with libsodium which doesn't touch ASN.1, so I had no way of testing this. The PEM we provided was from the following snippet:
const { composePrivateKey, composePublicKey } = require('@credify/crypto-key-composer'); function privateToPem(privateKey) { var decomposed = { format: 'pkcs8-pem', keyAlgorithm: { id: 'ed25519' }, keyData: { seed: privateKey, }, } return composePrivateKey(decomposed) } function publicToPem(publicKey) { var decomposed = { format: 'spki-pem', keyAlgorithm: { id: 'ed25519' }, keyData: { bytes: publicKey, }, } return composePublicKey(decomposed) } const sk = Buffer.from('b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2', 'hex'); const pk = Buffer.from('1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2', 'hex'); console.log(privateToPem(sk), publicToPem(pk));
Shouldn't the secret-key used only be the private 32 bytes? It is currently private+public which is what you've sent into that snippet and produced that.
Technically no, but maybe seed
should have been bytes
. Mea culpa!
Technically no, but maybe
seed
should have beenbytes
. Mea culpa!
And what's the public part technically good for?
It's an optimization since the public key is used in the Ed25519 signing process. https://duo.com/labs/tech-notes/whats-an-eddsa#section5
This provides a property that ECDSA doesn't provide. https://www.bolet.org/~pornin/2005-acns-pornin+stern.pdf
Yeah but as you can see the private pem leads to the same signatures your vectors do without this information since the public key can always be recalculated when needed for signing I assume?
Anyway I'll just grab the first 32 bytes for this libs use
I'm sure that's totally fine. We're coming from the "how libsodium does it" perspective.
I've noticed.
You're the first person to ask us for PEM encoding, so whatever's easier for you is what we're going to go with. :)
If it turns out down the line to slow down signing without the public key precomputed, implementors are free to use the full 64 bytes in their secret keys, and this shouldn't require any changes to the test vectors. (Though, I wonder if we should provide a test vector for each key size. Do you have any concerns about that?)
Isn't precomputing dangerous the same way uncompressed public ec key is?
Anyway I don't see a way to instantiate an OpenSSL backed KeyObject with both private and public keys, or at least didn't figure out the ASN.1 encoding for it. And the vector outcome is the same for me with raw private as it is for you with the combined key using sodium.
Isn't precomputing dangerous the same way uncompressed public ec key is?
Nope. The uncompressed EC public key attack doesn't work on EdDSA.
Anyway I don't see a way to instantiate an OpenSSL backed KeyObject with both private and public keys, or at least didn't figure out the ASN.1 encoding for it. And the vector outcome is the same for me with raw private as it is for you with the combined key using sodium.
I've been trying and couldn't either. I think we landed on the correct PEM-encoded private key for Ed25519. Thanks for taking the time to chase this answer down.
Now that I have an implementation of a conversion function between the sodium keys and Node's KeyObject. Do you have a suggestion for what these should be called when exposed to users? I think these would aid in scenarios where devs need different implementations to interoperate.
E.g. paseto.V2.importSodiumPrivateKey ?
I think that's a good descriptive name. importRawBytes()
would also be appropriate, but probably less meaningful.
@paragonie-security can you take a look?
NB: that being said these functions are now purely for optimization because one can pass the raw bytes directly to sign/verify now.
These helper functions look good to us.
Sorry, we just realized Github is sending a notification here every time we rebase and force push in our repository.
Sorry, we just realized Github is sending a notification here every time we rebase and force push in our repository.
I also get a notification because of the commit which has @panva in the message ;) But don't worry, I don't mind and we're nearing the end of this process anyway.
@paragonie-security even with the inclusion of the only viable libsodium bundle for javascript (libsodium-wrappers) I could not get Version 4 implemented. crypto_stream_xchacha20_xor
is not available, see the only available methods:
This was merely an exercise on my part, I would not bundle 500kb worth of sodium to this library only to end up with a synchronous execution blocking code anyway.
See https://github.com/paragonie/paseto/pull/127
This PR will be updated as the draft evolves.
Similar to
v2.local
- the newv4.local
will not be implemented because it is not possible to do so without requiring libsodium. This will be revisited as XChaCha20-Poly1305 and XChaCha20 lands in OpenSSL 3.x and it being bundled with Node.js. Other features may be still be missing then though so at this point it is unlikely to be considered even long-term.This will be a breaking release (v3.0.0). It will require Node.js >= 16.0.0 due to a number of new crypto APIs that simplify the codebase.