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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use crate::{kdf::Kdf as KdfTrait, kex::KeyExchange, util::static_zeros};

/// Contains preshared key bytes and an identifier
#[derive(Clone, Copy)]
pub struct PskBundle<'a> {
    /// The preshared key
    pub psk: &'a [u8],
    /// A bytestring that uniquely identifies this PSK
    pub psk_id: &'a [u8],
}

/// The operation mode of the receiver's side of HPKE. This determines what information is folded
/// into the encryption context derived in the `setup_receiver` functions. You can include a
/// preshared key, the identity key of the sender, both, or neither.
pub enum OpModeR<'a, Kex: KeyExchange> {
    /// No extra information included
    Base,
    /// A preshared key known to the sender and receiver
    Psk(PskBundle<'a>),
    /// The identity public key of the sender
    Auth(Kex::PublicKey),
    /// Both of the above
    AuthPsk(Kex::PublicKey, PskBundle<'a>),
}

// Helper function for setup_receiver
impl<'a, Kex: KeyExchange> OpModeR<'a, Kex> {
    /// Returns the sender's identity pubkey if it's specified
    pub(crate) fn get_pk_sender_id(&self) -> Option<&Kex::PublicKey> {
        match self {
            OpModeR::Auth(pk) => Some(pk),
            OpModeR::AuthPsk(pk, _) => Some(pk),
            _ => None,
        }
    }
}

/// The operation mode of the sender's side of HPKE. This determines what information is folded
/// into the encryption context derived in the `setup_sender` functions. You can include a
/// preshared key, the identity key of the sender, both, or neither.
pub enum OpModeS<'a, Kex: KeyExchange> {
    /// No extra information included
    Base,
    /// A preshared key known to the sender and receiver
    Psk(PskBundle<'a>),
    /// The identity keypair of the sender
    Auth((Kex::PrivateKey, Kex::PublicKey)),
    /// Both of the above
    AuthPsk((Kex::PrivateKey, Kex::PublicKey), PskBundle<'a>),
}

// Helpers functions for setup_sender and testing
impl<'a, Kex: KeyExchange> OpModeS<'a, Kex> {
    /// Returns the sender's identity pubkey if it's specified
    pub(crate) fn get_sender_id_keypair(&self) -> Option<&(Kex::PrivateKey, Kex::PublicKey)> {
        match self {
            OpModeS::Auth(keypair) => Some(keypair),
            OpModeS::AuthPsk(keypair, _) => Some(keypair),
            _ => None,
        }
    }
}

/// Represents the convenience methods necessary for getting default values out of the operation
/// mode. These are defined in draft02 §6.1.
pub(crate) trait OpMode<Kex: KeyExchange> {
    /// Gets the mode ID (hardcoded based on variant)
    fn mode_id(&self) -> u8;
    /// If this is a PSK mode, returns the PSK. Otherwise returns zeros.
    fn get_psk_bytes<Kdf: KdfTrait>(&self) -> &[u8];
    /// If this is a PSK mode, returns the PSK ID. Otherwise returns the empty string.
    fn get_psk_id(&self) -> &[u8];
}

impl<'a, Kex: KeyExchange> OpMode<Kex> for OpModeR<'a, Kex> {
    // Defined in draft02 §5.0
    fn mode_id(&self) -> u8 {
        match self {
            OpModeR::Base => 0x00,
            OpModeR::Psk(..) => 0x01,
            OpModeR::Auth(..) => 0x02,
            OpModeR::AuthPsk(..) => 0x03,
        }
    }

    // Returns the preshared key bytes if it's set in the mode, otherwise returns
    // [0u8; Kdf::HashImpl::OutputSize]
    fn get_psk_bytes<Kdf: KdfTrait>(&self) -> &[u8] {
        // draft02 §6.1: default_psk = zero(Nh)
        match self {
            OpModeR::Psk(bundle) => &bundle.psk,
            OpModeR::AuthPsk(_, bundle) => &bundle.psk,
            _ => static_zeros::<Kdf>(),
        }
    }

    // Returns the preshared key ID if it's set in the mode, otherwise returns the emtpy string
    fn get_psk_id(&self) -> &[u8] {
        // draft02 §6.1: default_pskID = zero(0)
        match self {
            OpModeR::Psk(p) => &p.psk_id,
            OpModeR::AuthPsk(_, p) => &p.psk_id,
            _ => b"",
        }
    }
}

// I know there's a bunch of code reuse here, but it's not so much that I feel the need to abstract
// something away
impl<'a, Kex: KeyExchange> OpMode<Kex> for OpModeS<'a, Kex> {
    // Defined in draft02 §5.0
    fn mode_id(&self) -> u8 {
        match self {
            OpModeS::Base => 0x00,
            OpModeS::Psk(..) => 0x01,
            OpModeS::Auth(..) => 0x02,
            OpModeS::AuthPsk(..) => 0x03,
        }
    }

    // Returns the preshared key bytes if it's set in the mode, otherwise returns
    // [0u8; Kdf::Hashfunction::OutputSize]
    fn get_psk_bytes<Kdf: KdfTrait>(&self) -> &[u8] {
        // draft02 §6.1: default_psk = zero(Nh)
        match self {
            OpModeS::Psk(bundle) => &bundle.psk,
            OpModeS::AuthPsk(_, bundle) => &bundle.psk,
            _ => static_zeros::<Kdf>(),
        }
    }

    // Returns the preshared key ID if it's set in the mode, otherwise returns the emtpy string
    fn get_psk_id(&self) -> &[u8] {
        // draft02 §6.1: default_pskID = zero(0)
        match self {
            OpModeS::Psk(p) => &p.psk_id,
            OpModeS::AuthPsk(_, p) => &p.psk_id,
            _ => b"",
        }
    }
}