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
142
143
144
145
146
147
148
149
150
use crate::kem::Kem as KemTrait;

/// Contains preshared key bytes and an identifier. This is intended to go inside an `OpModeR` or
/// `OpModeS` struct.
///
/// Requirements
/// ============
/// `psk` MUST contain at least 32 bytes of entropy. Further, `psk.len()` SHOULD be at least as
/// long as an extracted key from the KDF you use with `setup_sender`/`setup_receiver`, i.e., at
/// least `Kdf::extracted_key_size()`.
#[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 HPKE session (receiver's view). This is how the sender authenticates
/// their identity to the receiver. This authentication information can include a preshared key,
/// the identity key of the sender, both, or neither. `Base` is the only mode that does not provide
/// any kind of sender identity authentication.
pub enum OpModeR<'a, Kem: KemTrait> {
    /// 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(Kem::PublicKey),
    /// Both of the above
    AuthPsk(Kem::PublicKey, PskBundle<'a>),
}

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

/// The operation mode of the HPKE session (sender's view). This is how the sender authenticates
/// their identity to the receiver. This authentication information can include a preshared key,
/// the identity key of the sender, both, or neither. `Base` is the only mode that does not provide
/// any kind of sender identity authentication.
pub enum OpModeS<'a, Kem: KemTrait> {
    /// No extra information included
    Base,
    /// A preshared key known to the sender and receiver
    Psk(PskBundle<'a>),
    /// The identity keypair of the sender
    Auth((Kem::PrivateKey, Kem::PublicKey)),
    /// Both of the above
    AuthPsk((Kem::PrivateKey, Kem::PublicKey), PskBundle<'a>),
}

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

/// Represents the convenience methods necessary for getting default values out of the operation
/// mode
pub(crate) trait OpMode<Kem: KemTrait> {
    /// Gets the mode ID (hardcoded based on variant)
    fn mode_id(&self) -> u8;
    /// If this is a PSK mode, returns the PSK. Otherwise returns the empty string.
    fn get_psk_bytes(&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, Kem: KemTrait> OpMode<Kem> for OpModeR<'a, Kem> {
    // Defined in RFC 9180 §5 Table 1
    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(&self) -> &[u8] {
        // RFC 9180 §5.1: default_psk = ""
        match self {
            OpModeR::Psk(bundle) => bundle.psk,
            OpModeR::AuthPsk(_, bundle) => bundle.psk,
            _ => &[],
        }
    }

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

// 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, Kem: KemTrait> OpMode<Kem> for OpModeS<'a, Kem> {
    // Defined in RFC 9180 §5 Table 1
    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(&self) -> &[u8] {
        // RFC 9180 §5.1: default_psk = ""
        match self {
            OpModeS::Psk(bundle) => bundle.psk,
            OpModeS::AuthPsk(_, bundle) => bundle.psk,
            _ => &[],
        }
    }

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