Skip to content
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

Replace Crypt::NaCl::Sodium with Crypt::Ed25519 #1182

Merged
merged 7 commits into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions lib/SyTest/Crypto.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# crypto-related utility functions for SyTest


package SyTest::Crypto;

use Crypt::Ed25519;

use Exporter qw( import );
our @EXPORT_OK = qw( ed25519_nacl_keypair );

=head2 ed25519_nacl_keypair

( $public_key, $secret_key ) = ed25519_nacl_keypair( [ $seed ] );

A drop in replacement for Crypt::NaCl::Sodium->sign->keypair.

Generate a new Ed25519 keypair, in a format compatible with the NaCl API.

If the optional seed is given, that is used to determiniatically derive the
public and secret key.

NaCl (http://nacl.cr.yp.to/) uses "secret keys" which are actually 64-byte
tuples of (seed, public key) (whereas most other libraries either use just the
seed, or a "preprocessed" 64-byte private key, which is deterministically
derived from the seed. SyTest includes a bunch of code which relies on the NaCl
format, so for now we have this shim to create them.

Deprecated: it's better just to use the `Crypt::Ed25519::eddsa_*` APIs directly.

=cut

sub ed25519_nacl_keypair {
my ( $seed ) = @_;
$seed //= Crypt::Ed25519::eddsa_secret_key();
clokep marked this conversation as resolved.
Show resolved Hide resolved
my $public_key = Crypt::Ed25519::eddsa_public_key($seed);
return ( $public_key, $seed.$public_key );
}

8 changes: 3 additions & 5 deletions lib/SyTest/Identity/Server.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use warnings;

use base qw( Net::Async::HTTP::Server );

use Crypt::NaCl::Sodium;
use SyTest::Crypto qw( ed25519_nacl_keypair );
use List::Util qw( any );
use List::UtilsBy qw( extract_first_by );
use Protocol::Matrix qw( encode_base64_unpadded sign_json );
Expand All @@ -15,8 +15,6 @@ use HTTP::Response;
use Digest::SHA qw( sha256 );
use Struct::Dumb qw( struct );

my $crypto_sign = Crypt::NaCl::Sodium->sign;

my $next_token = 0;

# Perpetually correct access token for authenticating with v2 Identity Service API endpoints.
Expand Down Expand Up @@ -64,8 +62,8 @@ sub rotate_keys
{
my $self = shift;

( $self->{public_key}, $self->{private_key} ) = $crypto_sign->keypair;
( $self->{ephemeral_public_key}, $self->{ephemeral_private_key} ) = $crypto_sign->keypair;
( $self->{public_key}, $self->{private_key} ) = ed25519_nacl_keypair;
( $self->{ephemeral_public_key}, $self->{ephemeral_private_key} ) = ed25519_nacl_keypair;

$self->{keys} = {
"ed25519:0" => encode_base64_unpadded( $self->{public_key} ),
Expand Down
22 changes: 10 additions & 12 deletions tests/41end-to-end-keys/08-cross-signing.pl
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use Crypt::NaCl::Sodium;
use SyTest::Crypto qw( ed25519_nacl_keypair );
use MIME::Base64;
use Protocol::Matrix qw( sign_json );

my $crypto_sign = Crypt::NaCl::Sodium->sign;

test "Can upload self-signing keys",
requires => [ local_user_fixture() ],

Expand Down Expand Up @@ -109,8 +107,8 @@
my $user_id = $user1->user_id;
my $device_id = $user1->device_id;

my ( $master_pubkey, $master_secret_key ) = $crypto_sign->keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my ( $self_signing_pubkey, $self_signing_secret_key ) = $crypto_sign->keypair( decode_base64( "HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8" ) );
my ( $master_pubkey, $master_secret_key ) = ed25519_nacl_keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my ( $self_signing_pubkey, $self_signing_secret_key ) = ed25519_nacl_keypair( decode_base64( "HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8" ) );
my $cross_signature;

matrix_sync( $user1 )->then(sub {
Expand Down Expand Up @@ -259,9 +257,9 @@
my $device_id = $user1->device_id;
my $user2_id = $user2->user_id;

my ( $master_pubkey, $master_secret_key ) = $crypto_sign->keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my ( $self_signing_pubkey, $self_signing_secret_key ) = $crypto_sign->keypair( decode_base64( "HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8" ) );
my ( $user_signing_pubkey, $user_signing_secret_key ) = $crypto_sign->keypair( decode_base64( "4TL4AjRYwDVwD3pqQzcor+ez/euOB1/q78aTJ+czDNs" ) );
my ( $master_pubkey, $master_secret_key ) = ed25519_nacl_keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my ( $self_signing_pubkey, $self_signing_secret_key ) = ed25519_nacl_keypair( decode_base64( "HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8" ) );
my ( $user_signing_pubkey, $user_signing_secret_key ) = ed25519_nacl_keypair( decode_base64( "4TL4AjRYwDVwD3pqQzcor+ez/euOB1/q78aTJ+czDNs" ) );
my $cross_signature;

matrix_sync( $user1 )->then(sub {
Expand Down Expand Up @@ -422,7 +420,7 @@

my $user2_id = $user2->user_id;

my ( $master_pubkey, $master_secret_key ) = $crypto_sign->keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my ( $master_pubkey, $master_secret_key ) = ed25519_nacl_keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my $self_signing_key = {
# private key: HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8
"user_id" => $user2_id,
Expand Down Expand Up @@ -493,7 +491,7 @@

my $room_id;

my ( $master_pubkey, $master_secret_key ) = $crypto_sign->keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my ( $master_pubkey, $master_secret_key ) = ed25519_nacl_keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my $self_signing_key = {
# private key: HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8
"user_id" => $user2_id,
Expand Down Expand Up @@ -592,8 +590,8 @@

my $room_id;

my ( $master_pubkey, $master_secret_key ) = $crypto_sign->keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my ( $self_signing_pubkey, $self_signing_secret_key ) = $crypto_sign->keypair( decode_base64( "HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8" ) );
my ( $master_pubkey, $master_secret_key ) = ed25519_nacl_keypair( decode_base64( "2lonYOM6xYKdEsO+6KrC766xBcHnYnim1x/4LFGF8B0" ) );
my ( $self_signing_pubkey, $self_signing_secret_key ) = ed25519_nacl_keypair( decode_base64( "HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8" ) );
my $self_signing_key = {
# private key: HvQBbU+hc2Zr+JP1sE0XwBe1pfZZEYtJNPJLZJtS+F8
"user_id" => $user2_id,
Expand Down
5 changes: 2 additions & 3 deletions tests/50federation/00prepare.pl
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@

require IO::Async::SSL;

use Crypt::NaCl::Sodium;

use SyTest::Federation::Datastore;
use SyTest::Federation::Client;
use SyTest::Federation::Server;
use SyTest::Crypto qw( ed25519_nacl_keypair );

push our @EXPORT, qw( INBOUND_SERVER OUTBOUND_CLIENT create_federation_server );

Expand All @@ -28,7 +27,7 @@ sub create_federation_server
# common name.
my $server_name = sprintf "%s:%d", $BIND_HOST, $sock->sockport;

my ( $pkey, $skey ) = Crypt::NaCl::Sodium->sign->keypair;
my ( $pkey, $skey ) = ed25519_nacl_keypair;

my $datastore = SyTest::Federation::Datastore->new(
server_name => $server_name,
Expand Down
17 changes: 7 additions & 10 deletions tests/50federation/01keys.pl
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use List::Util qw( first );

use Crypt::NaCl::Sodium;

use Crypt::Ed25519;
use Protocol::Matrix qw( encode_json_for_signing sign_json encode_base64_unpadded );

my $crypto_sign = Crypt::NaCl::Sodium->sign;
use SyTest::Crypto qw( ed25519_nacl_keypair );

test "Federation key API allows unsigned requests for keys",
requires => [ $main::HOMESERVER_INFO[0], $main::HTTP_CLIENT ],
Expand Down Expand Up @@ -65,7 +62,7 @@

log_if_fail "Signed bytes", $signed_bytes ;

$crypto_sign->verify( $signature, $signed_bytes, $key ) or
Crypt::Ed25519::verify( $signed_bytes, $key, $signature ) or
clokep marked this conversation as resolved.
Show resolved Hide resolved
die "Signature verification failed";

# old_verify_keys is mandatory, even if it's empty
Expand Down Expand Up @@ -157,7 +154,7 @@ sub key_query_via_post {
)->then( sub {
my ( $server_key ) = @_;

$crypto_sign->verify( $signature, $signed_bytes, $server_key ) or
Crypt::Ed25519::verify( $signed_bytes, $server_key, $signature ) or
die "Signature verification failed";

Future->done(1);
Expand All @@ -173,7 +170,7 @@ sub key_query_via_post {
my ( $notary_server, $http_client, $http_server ) = @_;
my $test_server_name = $http_server->server_name;

my ( $pkey, $skey ) = Crypt::NaCl::Sodium->sign->keypair;
my ( $pkey, $skey ) = ed25519_nacl_keypair;
my $key_id = "ed25519:key_0";
my $key_expiry = int(( time - 86400 ) * 1000); # -24h in msec
my $key_response = build_key_response(
Expand Down Expand Up @@ -280,7 +277,7 @@ sub key_query_via_post {
# origin server, and key_2, which is just used to sign an itermediate
# response.

my ( $pkey1, $skey1 ) = Crypt::NaCl::Sodium->sign->keypair;
my ( $pkey1, $skey1 ) = ed25519_nacl_keypair;
my $key_id_1 = "ed25519:key_1";
my $key1_expiry = int(( time - 86400 ) * 1000); # -24h in msec

Expand Down Expand Up @@ -324,7 +321,7 @@ sub key_query_via_post {

log_if_fail "Request 2 from notary server: " . $request->method . " " . $request->path;

my ( $pkey2, $skey2 ) = Crypt::NaCl::Sodium->sign->keypair;
my ( $pkey2, $skey2 ) = ed25519_nacl_keypair;
$request->respond_json( build_key_response(
server_name => $test_server_name,
key => $skey2,
Expand Down
4 changes: 3 additions & 1 deletion tests/50federation/02server-names.pl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use SyTest::Crypto qw( ed25519_nacl_keypair );

# check that the rules around server_names are enforced

test "Non-numeric ports in server names are rejected",
Expand All @@ -6,7 +8,7 @@
do => sub {
my ( $info, $user ) = @_;

my ( $pkey, $skey ) = Crypt::NaCl::Sodium->sign->keypair;
my ( $pkey, $skey ) = ed25519_nacl_keypair;

my $datastore = SyTest::Federation::Datastore->new(
server_name => "localhost:http",
Expand Down