1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Copyright (c) 2021 Open Community Project Association https://ocpa.ch
// This software is published under the AGPLv3 license.

//! # Curve 25519 Operations
//!
//! Conversation of Ed25519 to Montgomery Curve25519 representation.

use libp2p::identity::{Keypair, PublicKey};
use sha2::{Digest, Sha512};

pub struct Crypto25519 {}

impl Crypto25519 {
    /// Convert ed25519 private key to montgomery form
    ///
    /// libqaul's Keypair is an `ed25519::KeyPair`
    /// To get to the montgomery form, the following actions are done:
    ///
    /// 1) Get the `SecretKey` from the KeyPair structure.
    /// 2) Convert the `EdwardsPoint` to a `MontgomeryPoint` by hashing it with sha512.
    /// 3) This gives us the bytes of the curve25519_dalek secret key in the Montgomery form to be used with for the X25519 Diffie-Hellman.
    ///
    /// ## References
    ///
    /// * libqaul's `Keypair` = [`libp2p::identity::ed25519::Keypair`](https://docs.rs/libp2p/latest/libp2p/identity/enum.Keypair.html)
    /// * `EdwardsPoint` = [`curve25519_dalek::edwards::EdwardsPoint`](https://doc.dalek.rs/curve25519_dalek/edwards/struct.EdwardsPoint.html)
    /// * `MontgomeryPoint` = [`curve25519_dalek::montgomery::MontgomeryPoint`](https://doc.dalek.rs/curve25519_dalek/montgomery/struct.MontgomeryPoint.html)
    /// * [`x25519_dalek::PublicKey`](https://doc.dalek.rs/x25519_dalek/struct.PublicKey.html)
    ///
    pub fn private_key_to_montgomery(key: Keypair) -> Option<Vec<u8>> {
        // get ed25519 keypair
        if let Keypair::Ed25519(ed25519_keypair) = key {
            // get dalek keypair as bytes
            //
            // unfortunately the dalek keypair is private in the
            // libp2p keypair structure, therefore we have to
            // make the detour via the bytes.
            let ed25519_dalek_keypair_bytes = ed25519_keypair.encode();

            // create dalek ed25519 keypair
            if let Ok(ed25519_dalek_keypair) =
                ed25519_dalek::Keypair::from_bytes(&ed25519_dalek_keypair_bytes)
            {
                // get dalek secret key as bytes
                let ed25519_dalek_secret_bytes = ed25519_dalek_keypair.secret.to_bytes();

                // transform into dalek curve25519 secret key as bytes
                let mut curve25519_dalek_secret: [u8; 32] = [0; 32];
                let hash = Sha512::digest(ed25519_dalek_secret_bytes.as_ref());
                curve25519_dalek_secret.copy_from_slice(&hash[..32]);

                return Some(curve25519_dalek_secret.to_vec());
            }
        }

        None
    }

    /// Convert ed25519 public key to montgomery form
    ///
    /// Libqaul's PublicKey is a `CompressedEdwardsY` point from the `curve25519_dalek` library.
    /// To get to the montgomery form, the following actions are done:
    ///
    /// 1) Decompress `CompressedEdwardsY` point, which returns an `EdwardsPoint`.
    /// 2) Convert the `EdwardsPoint` to a `MontgomeryPoint`
    /// 3) The bytes of the `MontgomeryPoint` are equal to the `x25519_dalek::PublicKey`
    ///
    /// ## References
    ///
    /// * libqaul's `PublicKey` = [`libp2p::identity::ed25519::PublicKey`](https://docs.rs/libp2p/latest/libp2p/identity/ed25519/struct.PublicKey.html)
    /// * `EdwardsPoint` = [`curve25519_dalek::edwards::EdwardsPoint`](https://doc.dalek.rs/curve25519_dalek/edwards/struct.EdwardsPoint.html)
    /// * `CompressedEdwardsY` = [`curve25519_dalek::edwards::CompressedEdwardsY`](https://doc.dalek.rs/curve25519_dalek/edwards/struct.CompressedEdwardsY.html)
    /// * `MontgomeryPoint` = [`curve25519_dalek::montgomery::MontgomeryPoint`](https://doc.dalek.rs/curve25519_dalek/montgomery/struct.MontgomeryPoint.html)
    /// * [`x25519_dalek::PublicKey`](https://doc.dalek.rs/x25519_dalek/struct.PublicKey.html)
    ///
    pub fn public_key_to_montgomery(key: PublicKey) -> Option<x25519_dalek::PublicKey> {
        // get ed25519 structure
        if let PublicKey::Ed25519(ed25519_pub) = key {
            // convert to dalek public key in bytes form
            let dalek_pub_bytes = ed25519_pub.encode();

            // generate Montgomery form
            // x25519_dalek::PublicKey internal is private, we have to go via bytes
            let montgomery_bytes = curve25519_dalek::edwards::CompressedEdwardsY(dalek_pub_bytes)
                .decompress()
                .expect("An Ed25519 public key is a valid point by construction.")
                .to_montgomery()
                .0;

            return Some(x25519_dalek::PublicKey::from(montgomery_bytes));
        }

        None
    }
}