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
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use std::borrow::Borrow;

use curve25519_dalek::{constants, ristretto, scalar};
use subtle::ConstantTimeEq;

use super::*;

pub struct EphemeralKeyDerivationBuilder {
    r: PrivateKey,
}

#[derive(Clone)]
pub struct Recogniser {
    inner: ristretto::RistrettoPoint,
}

impl Recogniser {
    pub fn bytes(&self) -> [u8; 32] {
        self.inner.compress().to_bytes()
    }
    // XXX: make this a Result
    pub fn from_bytes(bytes: [u8; 32]) -> Recogniser {
        Recogniser {
            inner: ristretto::CompressedRistretto(bytes).decompress().unwrap(),
        }
    }
}

impl EphemeralKeyDerivationBuilder {
    pub fn new(sk: PrivateKey) -> Self {
        EphemeralKeyDerivationBuilder { r: sk }
    }

    pub fn default<R: rand::SecureRandom>(rng: &R) -> crate::crypto::Result<Self> {
        Ok(Self::new(PrivateKey::generate(rng)?))
    }

    pub fn derive(&self, public_base: &PublicKey) -> crate::crypto::Result<PublicKey> {
        let hash = agree_to_scalar(public_base, &self.r)?;
        // P' = P + aG
        Ok(PublicKey {
            inner: public_base.inner + &hash * &constants::RISTRETTO_BASEPOINT_TABLE,
        })
    }

    pub fn finish(self) -> crate::crypto::Result<Recogniser> {
        self.r
            .compute_public_key()
            .map(|k| Recogniser { inner: k.inner })
    }
}

// Not sure whether we want recogniser to be a reference.
pub struct EphemeralKeyRecovery<'sk> {
    master_key: &'sk PrivateKey,
    scalar: scalar::Scalar,
    target: PublicKey,
}

impl<'sk> EphemeralKeyRecovery<'sk> {
    pub fn new(
        recogniser: &Recogniser,
        master_key: &'sk PrivateKey,
    ) -> crate::crypto::Result<Self> {
        let recogniser = PublicKey {
            inner: recogniser.inner,
        };

        // Precalculate P'=H(Ra) + A
        //
        // We don't precalculate p'=H(Ra) + a yet,
        // since the key recovery doesn't always have to return it.
        let master_public_key = master_key.compute_public_key()?;
        let agreed = agree_to_scalar(&recogniser, master_key)?;

        Ok(EphemeralKeyRecovery {
            target: PublicKey {
                inner: master_public_key.inner + &agreed * &constants::RISTRETTO_BASEPOINT_TABLE,
            },
            master_key,
            scalar: agreed,
        })
    }

    pub fn recognise(&self, public_key: &PublicKey) -> crate::crypto::Result<Option<PrivateKey>> {
        if public_key.inner.ct_eq(&self.target.inner).unwrap_u8() == 0 {
            return Ok(None);
        }

        // public_key == self.target
        Ok(Some(PrivateKey {
            inner: self.master_key.inner + self.scalar,
        }))
    }

    pub fn recognise_one<Key, I>(&self, i: I) -> crate::crypto::Result<Option<PrivateKey>>
    where
        Key: Borrow<PublicKey>,
        I: Iterator<Item = Key>,
    {
        // XXX: constant time?
        for key in i {
            if let Some(sk) = self.recognise(key.borrow())? {
                return Ok(Some(sk));
            }
        }
        Ok(None)
    }
}