ENHANCED: Use crrl for Ed25519 signing and signature verification.

The main motivation for this change is the introduction of the newly
available predicate ed25519_seed_keypair/2, allowing to generate a key
pair from a given seed. In this way, a key pair can be dynamically
generated from (for example) a password, using crypto_password_hash/3
in combination with crypto_data_hkdf/4 to generate the seed. The
advantage of this method is that the private key need not be stored at
all anywhere.

It is not possible to add a corresponding feature to ring, since it is
closed as "not planned": https://github.com/briansmith/ring/issues/1003

I also used this opportunity to move more of the logic to Prolog. We
now have total control of the key pair representation, and I also
changed the representation to conform to the PKCS#8 v2 standard,
something that only later ring versions do, while still being
backwards compatible with tools that produce a wrong representation
including earlier ring versions.

Another great advantage we get from this change is that the Ed25519
predicates now also run on the 32-bit and WASM versions of Scryer.
This commit is contained in:
Markus Triska
2023-12-26 07:24:18 +01:00
parent 799035c692
commit 47ec5eb6c6
4 changed files with 99 additions and 113 deletions

View File

@@ -25,6 +25,7 @@
crypto_password_hash/3, % +Password, -Hash, +Options
crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options
crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options
ed25519_seed_keypair/2, % +Seed, -KeyPair
ed25519_new_keypair/1, % -KeyPair
ed25519_keypair_public_key/2, % +KeyPair, +PublicKey
ed25519_sign/4, % +KeyPair, +Data, -Signature, +Options
@@ -612,6 +613,34 @@ encoding_chars(utf8, Cs, Cs) :-
===============================
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
%% ed25519_seed_keypair(+Seed, -Pair)
%
% Use Seed to deterministically generate an Ed25519 key pair Pair, a
% list of characters. Seed must be a list of 32 bytes. It can be
% chosen at random (using for example `crypto_n_random_bytes/2`) or
% derived from input keying material (IKM) using for example
% `crypto_data_hkdf/4`. The pair contains the private key and must be
% kept absolutely secret. Pair can be used for signing. Its public
% key can be obtained with `ed25519_keypair_public_key/2`.
ed25519_seed_keypair(Seed, Pair) :-
must_be_bytes(Seed, ed25519_keypair_from_seed/2),
length(Seed, 32),
'$ed25519_seed_to_public_key'(Seed, Public),
maplist(char_code, Public, PublicBytes),
phrase(([0x30,81], % a sequence of 81 bytes follows
[2,1], % the integer 1 denoting version 2 (awesome design!)
[1], % the public key is also present
[48,5], % an element of 5 bytes follows
[6,3,43,101,112], % OID of Ed25519
[4,34], % an octet string of 34 bytes follows
[4,32], % an octet string of 32 bytes follows
seq(Seed), % the seed is the private key
[129,33],
[0], % 32 bytes is divisible by 8
seq(PublicBytes)), ASN1),
maplist(char_code, Pair, ASN1).
%% ed25519_new_keypair(-Pair)
%
% Yields a new Ed25519 key pair Pair, a list of characters. The
@@ -620,7 +649,8 @@ encoding_chars(utf8, Cs, Cs) :-
% with `ed25519_keypair_public_key/2`.
ed25519_new_keypair(Pair) :-
'$ed25519_new_keypair'(Pair).
crypto_n_random_bytes(32, Bytes),
ed25519_seed_keypair(Bytes, Pair).
%% ed25519_keypair_public_key(+Pair, -PublicKey)
%
@@ -629,8 +659,11 @@ ed25519_new_keypair(Pair) :-
% The public key is represented as a list of characters.
ed25519_keypair_public_key(Pair, PublicKey) :-
must_be_octet_chars(Pair, ed25519_keypair_public_key),
'$ed25519_keypair_public_key'(Pair, PublicKey).
must_be_octet_chars(Pair, ed25519_keypair_public_key/2),
reverse(Pair, RPs),
length(RPublicKey, 32),
phrase((seq(RPublicKey),...), RPs),
reverse(RPublicKey, PublicKey).
%% ed25519_sign(+Key, +Data, -Signature, +Options)
%
@@ -638,10 +671,14 @@ ed25519_keypair_public_key(Pair, PublicKey) :-
% PKCS#8 v2 format as generated by `ed25519_new_keypair/1`. Sign Data
% with Key, yielding Signature as a list of hexadecimal characters.
ed25519_sign(Key, Data0, Signature, Options) :-
must_be_octet_chars(Key, ed25519_sign),
ed25519_sign(KeyPair, Data0, Signature, Options) :-
must_be_octet_chars(KeyPair, ed25519_sign/4),
length(Prefix, 16),
length(PrivateKeyChars, 32),
phrase((seq(Prefix),seq(PrivateKeyChars),...), KeyPair),
maplist(char_code, PrivateKeyChars, PrivateKey),
options_data_chars(Options, Data0, Data, Encoding),
'$ed25519_sign'(Key, Data, Encoding, Signature0),
'$ed25519_sign_raw'(PrivateKey, Data, Encoding, Signature0),
hex_bytes(Signature, Signature0).
%% ed25519_verify(+Key, +Data, +Signature, +Options)
@@ -658,10 +695,10 @@ ed25519_sign(Key, Data0, Signature, Options) :-
% which treats Data as a list of raw bytes.
ed25519_verify(Key, Data0, Signature0, Options) :-
must_be_octet_chars(Key, ed25519_verify),
must_be_octet_chars(Key, ed25519_verify/4),
options_data_chars(Options, Data0, Data, Encoding),
hex_bytes(Signature0, Signature),
'$ed25519_verify'(Key, Data, Encoding, Signature).
'$ed25519_verify_raw'(Key, Data, Encoding, Signature).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
X25519: ECDH key exchange over Curve25519