SWI-Prolog/roadmap

Digital signatures for downloads

Closed this issue · 1 comments

Ideally, all files that are available from the download page are digitally signed to establish their authenticity.

library(crypto) can be used to:

  • compute hashes of these files and
  • sign them.

In recent versions, both RSA and ECDSA are available as digital signature mechanisms.

Ideally, the whole SWI web site should use HTTPS for authenticity. Until this becomes possible at last, the public keys that are necessary to verify the signatures can be distributed as part of the source via Github to benefit from its HTTPS scheme for tamper-proof transmission.

Here is a short walkthrough of how this could work, using ECDSA as the signature method. Several of the curves may be insecure, but using this method is definitely still far better than nothing at all.

Initial Setup

You need to perform two simple steps only once.

First, generate a private key ecdsa.key:

$ openssl ecparam -out ecdsa.key -name secp384r1 -genkey

You must keep this file completely secret, and you can also encrypt it if you want.

Extract the public key ecdsa.pub:

$ openssl ec -in ecdsa.key -pubout -out ecdsa.pub

For example, the public key will look like this:

-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEDtxhO6NSgrKc3AEi7X5XhKyZo2cs2t3i
iB5kVIAYgtsZK2/mu+UUhSRXul4fBZz9QRr1T9vmW/SB/Kjq0LEHAF+a5wVyvEPT
jpnqEv3MGOBr8UOB8LaAp+94MbcSspwi
-----END PUBLIC KEY-----

You can distribute this key as part of the SWI source code on Github.

Signing a release

You can compute the hash of each file that you distribute (source or binary) with crypto_file_hash/3.

For example, using the 7.3.35 source tarball:

?- crypto_file_hash('swipl-7.3.35.tar.gz', Hash, [encoding(octet)]).
Hash = d1a9f7ef77b6dddb08477055b6355a0f2f3a5018e1a027a2dc917a8e57b0cf70.

You then sign such hashes with your private key, using for example:

:- use_module(library(ssl)).
:- use_module(library(crypto)).

file_signature(File, Signature) :-
        crypto_file_hash(File, Hash, [encoding(octet)]),
        setup_call_cleanup(
            open('ecdsa.key', read, In, []),
            load_private_key(In, '', Key),
            close(In)),
        ecdsa_sign(Key, Hash, Signature, []).

Sample query and answer:

?- file_signature('swipl-7.3.35.tar.gz', Signature).
Signature = "306402304ED2B590F43FA020683ADCE7C15EF13B999748BB82CACEC88CD4C1748DC2BDA8253AB978AE2A3A2E069528730E72D6DA023047FC2AB9CDDBF2E6CC4EE6AFE66CA7FBEED1D07ED65F19412889104A0BCF42B342B4866D3BDE0D7677E1BB423F19C44C".

This is a possible signature that you publish on the download page together with each file you offer.

Verify integrity and authenticity of downloads

Everyone can verify a signature, using the known public key.

For example:

:- use_module(library(ssl)).
:- use_module(library(crypto)).

verify_signature(File, Signature) :-
        crypto_file_hash(File, Hash, [encoding(octet)]),
        setup_call_cleanup(
            open('ecdsa.pub', read, In, []),
            load_public_key(In, Key),
            close(In)),
        ecdsa_verify(Key, Hash, Signature, []).

Example, using the already known signature in continuation of the previous query:

?- verify_signature('swipl-7.3.35.tar.gz', $Signature).
Signature = "306402304ED2B590F43FA020683ADCE7C15EF13B999748BB82CACEC88CD4C1748DC2BDA8253AB978AE2A3A2E069528730E72D6DA023047FC2AB9CDDBF2E6CC4EE6AFE66CA7FBEED1D07ED65F19412889104A0BCF42B342B4866D3BDE0D7677E1BB423F19C44C".

Thus, the signature is successfully verified, and downloaders can be reasonably certain that they have received the file that you intended to distribute. This offers more guarantees than a simple hash of the file, because there is also reasonable certainty that you actually signed this file, using the private key that only you have.

If I make the slightest change to the tarball, I get:

?- verify_signature('swipl-7.3.35.tar.gz', $Signature).
false.

This shows that the signature or file is not the intended one.