-
Notifications
You must be signed in to change notification settings - Fork 10
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
Signatures for .eln files #56
Comments
Very good summary, thank you! Just writing some thoughts... Let's assume we export from instance A and import in instance B.
Notes:
|
As was already mentioned in our last meeting, I think it might be worth it to raise this issue with the RO-Crate people directly as well. I couldn't find anything similar when I searched on https://github.com/ResearchObject/ro-crate, but it could still be interesting for either them or other users. It also seems like a larger undertaking to me, so it would be beneficial to get some more output from the outside anyways. |
I agree with @nicobrandt and I'm surprised this topic seem to never have come up on RO-Crate! @FlorianRhiem can we let you open an issue on RO-Crate and link it here? |
I have tried to use CMS directly with a server certificate, however OpenSSL reports that the purpose of the server certificate is invalid for signing messages and refuses to verify any generated signatures. An alternative that isn't bound as tightly to S/MIME and seems to work perfectly fine with TLS certificates are the openssl dgst functions This archive contains the openssl dgst -sha256 -sign private.pem -out signature.sha256 ro-crate-metadata.json To verify a signature, you can first verify the certificate: openssl verify -untrusted certificate-with-chain.pem certificate-with-chain.pem Next, you can query the certificate subject to find out what the source of the .eln file is: openssl x509 -noout -subject -in certificate-with-chain.pem Then you need to extract the public key for the certificate, to use it for verifying the actual signature: openssl x509 -pubkey -out public.pem -in certificate-with-chain.pem And lastly, you need to verify the signature: openssl dgst -sha256 -verify public.pem -signature signature.sha256 ro-crate-metadata.json This signature file and the certificate chain could be placed in a well known directory within the zip file, so that implementations know where to look for them. A potential difficulty is how to deal with expired certificates. They should not be trusted anymore, however that would mean that an .eln file created with an old certificate would not be trusted either. This should not be an issue for a typical use case of exchanging information using .eln files, would however pose an issue for importing old .eln files, e.g. when revisiting old data. |
For expired certs, I'm guessing a graceful degradation with a warning that can be bypassed could work. Same thing browsers do when you visit a site with expired cert. Unless valid signature is required. |
What does everyone think about using It's the right tool for the job I believe. It does one thing, and does it well. Here is how to test, after installing # generate an asymmetric key pair (-W is no passphrase)
minisign -GW
# now sign the ro-crate-metadata.json file in the current dir
# we add a trusted comment with the URL to the instance (or pub key?)
minisign -S -t 'created by https://eln.example.org' -m ro-crate-metadata.json
# verify
minisign -V -m ro-crate-metadata.json
# or more realistically on an instance
minisign -V -p /path/to/trusted/keystore/eln.pub -m ro-crate-metadata.json
So in order to verify that the ro-crate-metadata.json file is authentic, one need to know which pub key to use (the signature file gives a hint), have it, and trust it. So there is in any case a step of adding a list of trusted pub keys (like all signature schemes, at some point you need to trust something), and their corresponding instances. This could easily be done with a public repository, not an issue. Then target instances could chose to trust the whole set of keys in this repo, or just pick a few. On our side, we would merge PR coming from verified instance owner, basically asking for the key to be at https://eln.example.org/.well-known/minisign.pub. And if the instance is not reachable, find another way to assert trust. But let's not focus on this for now. Once we have verified the signature, we know:
We can then make an informed choice about what trust level to apply when importing. And can even add some info such as: Trusted import from https://eln.example.org To summarize:
What do you think? |
Hello everyone, So I spent all night re-implementing I started this work after my message above, that made me realize that this would be a very good approach for Experiments signature. It probably means that exported .eln might contain signature file, too. I'm not there yet though. But what this made me realize is that the whole thing is very sound and does exactly what we want/need. There is a python implementation: https://github.com/x13a/py-minisign but you'll probably need to figure out the quircks like I did (such as the SK being 64 bits now instead of 32 bits in the PHP implementation not updated since 4 years). But the whole thing is pretty straightforward to implement, once you know what tools to use and how to use them. It does require a bit of familiarity with crypto primitives, though. Or you can just shell out to the binary and be done with it ;) Make sure to use Ed25519ph (https://datatracker.ietf.org/doc/html/rfc8032): the ph stands for pre-hashed, and that's what we want to use, allowing us to sign be files without loading them in memory (because we sign the hash). There are other advantages, see jedisct1/minisign#104. Anyway, just wanted to let you know that now that I'm using this for user signing experiments, it would make a lot of sense to also use it to sign an exported .eln. Here is what a signature could look like:
First line is a hint as to which key has been used (8 random bytes are used as KeyId), but it's untrusted. The public key looks like:
And the encrypted private key looks like:
The private key is encrypted and we unlock it with a passphrase. The I'm thinking I'll save the signature in a zip file with:
The only thing left to the verifier is to trust the public key, that it matches with a particular human. As conclusion, I'd say using minisign is very good and I strongly vote for using this for the .eln. As you can see the files are small, and it's much better than the whole x509 shenanigans. I'll also look at some point about FIDO2 implementation, see jedisct1/minisign#100 (comment). Because being able to sign a notebook entry with hardware key and produce a verifiable signature bit is the end goal. I'll go back to hacking now :D |
Hey, does anybody know how APK, AAP are signed? Do I understand it correctly that the author, not Google, signed them and they are just zip? |
My bad, Google has to know the key to verify it. |
It's described here: https://source.android.com/docs/security/features/apksigning/v3 It uses X509. You can find a description of the process in eLab here: https://github.com/elabftw/elabdoc/blob/next/doc/user-guide.rst#advanced-cryptographic-signatures I really like the fact that the signature can easily be verified by an external tool such as minisign. |
After looking into minisign and playing around with an implementation, I'm a bit torn on my opinion. It is fairly easy to implement and embed in applications (as long as an implementation of Ed25519 is available, which does all the actual signing and verification work), but it doesn't help with the part that makes X.509 so useful: reliably knowing what public keys belong to whom. X.509 certificates allow verification to happen in complete isolation as CA certificates are usually already present on systems. From the two methods you propose for solving this, using a well-known URL, i.e. using X.509 certificates as part of HTTPS, feels much 'cleaner' than relying on a central repository of trusted public keys and their corresponding ELNs, however it requires that the exporting ELN has to be reachable to check its public key, which isn't great either. Something we could do is store the Ed25519 public key in an X.509 certificate. That way we could use minisign or anything else based on Ed25519 for signing and verification, and the certificate chain based on root CAs for knowing we can trust the public key, and avoid relying on the OpenSSL |
Not necessarily. For instance, I know an instance that has custom cert, signed by local authority, and all the browsers have the CA in their trust store added via GPO. So in that case, the CA system fails in an external context. Whereas as long as you can tie a pubkey to an instance, you can then verify that whatever is signed is correct, and coming from that instance. Verification can be done by GET .well-known/minisign.pub. And we endup at what I suggested earlier: a curated list of instances and their public keys for the ones behind a firewall. The trusted comment contains json such as: {
"firstname": "Toto",
"lastname": "Le sysadmin",
"email": "[email protected]",
"created_at": "2024-03-18T00:48:39+01:00",
"site_url": "https://elab.local:3148",
"created_by": "eLabFTW 50100",
"meaning": "Approval"
} So we grab the site_url, try a GET to the .well-known/pubkey, if it fails, get the pub key from the curated public list, and do the verification with that pubkey. If it's ok, then you are certain that this data has been signed by that instance, and the whole x509 verification happens at the TLS/cert level! (or indirectly via our curated list). We want to keep things simple, and adding x509 inside minisign would be the worst option IMHO. So fetching the pubkey from the instance directly seems to be a very good option:
|
@NicolasCARPi Can you link to an example .eln-style file that follows your suggestion? Using that example, one can understand these items better |
@SteffenBrinckmann , see this file: Extract it, go into the folder minisign -H -V -p ro-crate.pubkey -m ro-crate-metadata.json Now you've verified that the signature is correct, and has been created with the secret key that corresponds to the public key present in the archive. In order to increase our trust about the fact that this public key is indeed coming from the instance that this archive is saying it's coming from, we fetch it at Simply comparing that both public keys are the same is enough. Or you can verify the crate with that key instead. Anyway, now we've validated that this archive comes from that instance, and has NOT been tempered with. Which is exactly what we wanted to do in the first place 🎉 So we can give to the content of that archive the same level of trust we could give to that instance operators. If the (source) instance cannot be reached, we should have a way to ask the sysadmin about this key. This can be documented in our respective elns. Let's first think about the happy path, and then we can think about edge cases. As you can see, the process is pretty straightforward:
|
Great, I got it to work and I understand the file-structure. Could you include the location of the public key inside minisign's trusted comment. Then the process is easy:
Do we trust / not-trust public key-stores? |
I don't think it's a good idea to use something like This is why we only mention the As a side historical note, this is why web servers are historically on port 80, requiring root access to bind to that port, so you can trust that the webserver is run by root operator, not some user on the system! edit:
What do you mean by that? The only thing we can trust is the |
We could also store pubkeys on http://keyserver.pgp.com ? |
No, keyservers are a failure, also they are only for GPG/PGP keys AFAIK. See: https://gist.github.com/rjhansen/67ab921ffb4084c865b3618d6955275f |
How could a desktop eln-software create a server, that it does not rely on? Pasta could only store the pub-keys in a dedicated server location |
I see. If it's not on a server, I'd say your "instance level" key is the same as the "user level" key, no? In the desktop world, a user is the same as an instance. So we end up with the need to verify that this .eln archive was generated by this particular user. AFAIK there are no real standards about this of course, because not everyone has a personal website where they can publish their keys. We could think of:
Or simply display the key, ask the importer what level of trust could be attached to that key. Similar to GPG, there is a trust level for keys. But here 0 or 1 is enough. GPG simply displays a warning: Checking integrity of /var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_testing_i386_5.11 with gpg. So if the pubkey of the source user is not present in the target instance/app, it needs to be imported with low trust level. |
That would work, for me |
Meeting decision for the URL: use .well-known/keys.json, which is json-ld and can describe several keys (such as past keys), and different keys formats. Let's try and make an example and continue the discussion based on concrete implementation of this. |
I started implementing this for SampleDB. The URL
In a current draft, the URL-attribute for each element can be used to get the public key. |
@Izjgxj maybe use And you could add |
Thanks for your feedback.
|
Currently, there is no mechanism to ensure that an .eln file was actually created by a specific ELN, which poses issues related to data provenance and in how far the information inside the .eln can be trusted.
Motivation
While ELNs generally assume that the information from users can be trusted, there are cases where it may be necessary to know that the information was entered at a specific time by a specific person, and where proof is needed for this instead of relying on the users to be trustworthy. To achieve this, many ELNs include mechanisms such as timestamping, versioning or signing of entered information.
For those ELNs, importing an .eln file must not circumvent these mechanisms, as that would render them useless, and as such all information has to be marked as coming from an import from user X at date/time Y. A chain of trust for information from .eln files could potentially improve the situation there, as another ELN might be more trust-worthy than users in these cases. It would still be necessary to mark the information as coming from an import, but it could be marked as coming from a specific ELN instead of coming from a user.
Ideas / Suggestions
The .eln file consists of an ro-crate-metadata.json and various other files, which should be listed in the ro-crate-metadata.json file. If possible, the ro-crate-metadata.json should include SHA256 hash values for those files, which allows us to trust that those files have not been tampered with (or suffered from data rot) as long as the ro-crate-metadata.json itself is trustworthy. As such, implementing a system of trust for the ro-crate-metadata.json should be satisfactory to ensure that the whole .eln file can be trusted.
A typical approach for this would to provide a signature for the ro-crate-metadata.json alongside the file itself. To do this, we need to figure out what digital signature scheme we would want to use, how keys are to be discovered and how the signature should be stored inside the .eln file.
Digital signature scheme
There are various schemes for how to generate keys, how to sign a series of bytes and how to verify the signature, and I'm not knowledgable enough in this area to suggest a specific scheme. I would suggest using an already widely used and supported scheme though.
Key distribution/discovery
As digital signature schemes use a key create the signature (or rather, a key pair), we will need a method of distributing or discovering the key (or rather, the public key) used for an .eln file.
One approach would be to have a chain of trust for those keys, similar (or ideally identical to) the one used for X.509 certificates used in TLS / HTTPS. This would have the advantage that it's a well-known scheme, already implemented widely and most web-based ELNs would already have a certificate (and associated key). The certificate chain could be provided alongside the signature, so that the signature can be checked as long as the root certificate authority is known (and trusted) and the .eln file certificates have not expired or revoked.
Another approach that would work for web-based ELNs would be to either query the origin ELN for its public key via HTTPS, or to submit the signature and ro-crate-metadata.json to the origin ELN for validation. The latter would have the advantage that we do not need to agree on a digital signature scheme at all, and that there's no need for key pairs, etc. as the signature could be generated and validated in various ways depending on the preferences of the ELN developer. The big disadvantage for both of these, of course, is that the origin ELN would need to be accessible, which is not always the case due to network issues or simply ELNs running behind a firewall.
Signature storage
The signature could either be stored in an additional file inside the .eln archive at a well-known location or it could be included in the
extra
field of the ZIP file. I personally think the simplicity of storing it in afile might be preferable, but for programs both methods should be equally easy to implement.Personally, I think piggybacking off the infrastructure and expertise behind TLS / HTTPS would be easiest. Doing it this way, importing an .eln file should have the same security as directly importing the information from the origin ELN via HTTPS, and the origin ELNs domain could be shown as the source of the information without an additional caveat.
What are your thoughts on this?
The text was updated successfully, but these errors were encountered: