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
151
152
153
use crate::{
    aead::{Aead, AeadTag},
    kdf::Kdf as KdfTrait,
    kem::{EncappedKey, Kem as KemTrait},
    kex::KeyExchange,
    op_mode::{OpModeR, OpModeS},
    setup::{setup_receiver, setup_sender},
    HpkeError,
};

use rand::{CryptoRng, RngCore};

// def SealAuthPSK(pkR, info, aad, pt, psk, pskID, skS):
//   enc, ctx = SetupAuthPSKS(pkR, info, psk, pskID, skS)
//   ct = ctx.Seal(aad, pt)
//   return enc, ct
/// Does a `setup_sender` and `AeadCtx::seal` in one shot. That is, it does a key encapsulation to
/// the specified recipient and encrypts the provided plaintext in place. See `setup::setup_sender`
/// and `AeadCtx::seal` for more detail.
///
/// Return Value
/// ============
/// Returns `Ok((encapped_key, tag))` on success. If an error happened during key exchange, returns
/// `Err(HpkeError::InvalidKeyExchange)`. If an unspecified error happened during encryption,
/// returns `Err(HpkeError::Encryption)`. In this case, the contents of `plaintext` is undefined.
pub fn single_shot_seal<A, Kdf, Kem, R>(
    mode: &OpModeS<Kem::Kex, Kdf>,
    pk_recip: &<Kem::Kex as KeyExchange>::PublicKey,
    info: &[u8],
    plaintext: &mut [u8],
    aad: &[u8],
    csprng: &mut R,
) -> Result<(EncappedKey<Kem::Kex>, AeadTag<A>), HpkeError>
where
    A: Aead,
    Kdf: KdfTrait,
    Kem: KemTrait,
    R: CryptoRng + RngCore,
{
    // Encap a key
    let (encapped_key, mut aead_ctx) =
        setup_sender::<A, Kdf, Kem, R>(mode, pk_recip, info, csprng)?;
    // Encrypt
    let tag = aead_ctx.seal(plaintext, aad)?;

    Ok((encapped_key, tag))
}

// def OpenAuthPSK(enc, skR, info, aad, ct, psk, pskID, pkS):
//   ctx = SetupAuthPSKR(enc, skR, info, psk, pskID, pkS)
//   return ctx.Open(aad, ct)
/// Does a `setup_receiver` and `AeadCtx::open` in one shot. That is, it does a key decapsulation
/// for the specified recipient and decrypts the provided plaintext in place. See
/// `setup::setup_reciever` and `AeadCtx::open` for more detail.
///
/// Return Value
/// ============
/// Returns `Ok()` on success. If an error happened during key exchange, returns
/// `Err(HpkeError::InvalidKeyExchange)`. If an unspecified error happened during decryption,
/// returns `Err(HpkeError::Encryption)`. In this case, the contents of `ciphertext` is undefined.
pub fn single_shot_open<A, Kdf, Kem>(
    mode: &OpModeR<Kem::Kex, Kdf>,
    sk_recip: &<Kem::Kex as KeyExchange>::PrivateKey,
    encapped_key: &EncappedKey<Kem::Kex>,
    info: &[u8],
    ciphertext: &mut [u8],
    aad: &[u8],
    tag: &AeadTag<A>,
) -> Result<(), HpkeError>
where
    A: Aead,
    Kdf: KdfTrait,
    Kem: KemTrait,
{
    // Decap the key
    let mut aead_ctx = setup_receiver::<A, Kdf, Kem>(mode, sk_recip, encapped_key, info)?;
    // Decrypt
    aead_ctx.open(ciphertext, aad, tag)
}

#[cfg(test)]
mod test {
    use super::{single_shot_open, single_shot_seal};
    use crate::{
        aead::ChaCha20Poly1305,
        kdf::HkdfSha256,
        kem::{Kem as KemTrait, X25519HkdfSha256},
        kex::KeyExchange,
        op_mode::{OpModeR, OpModeS},
        test_util::gen_psk_bundle,
    };

    /// Tests that `single_shot_open` can open a `single_shot_seal` ciphertext. This doens't need
    /// to be tested for all ciphersuite combinations, since its correctness follows from the
    /// correctness of `seal/open` and `setup_sender/setup_receiver`.
    #[test]
    fn test_single_shot_correctness() {
        type A = ChaCha20Poly1305;
        type Kd = HkdfSha256;
        type Ke = X25519HkdfSha256;
        type Kex = <Ke as KemTrait>::Kex;

        let msg = b"Good night, a-ding ding ding ding ding";
        let aad = b"Five four three two one";

        let mut csprng = rand::thread_rng();

        // Set up an arbitrary info string, a random PSK, and an arbitrary PSK ID
        let info = b"why would you think in a million years that that would actually work";
        let psk_bundle = gen_psk_bundle::<Kd>();

        // Generate the sender's and receiver's long-term keypairs
        let (sk_sender_id, pk_sender_id) = Kex::gen_keypair(&mut csprng);
        let (sk_recip, pk_recip) = Kex::gen_keypair(&mut csprng);

        // Construct the sender's encryption context, and get an encapped key
        let sender_mode =
            OpModeS::<Kex, _>::AuthPsk((sk_sender_id, pk_sender_id.clone()), psk_bundle.clone());

        // Use the encapped key to derive the reciever's encryption context
        let receiver_mode = OpModeR::<Kex, _>::AuthPsk(pk_sender_id, psk_bundle);

        // Encrypt with the first context
        let mut ciphertext = msg.clone();
        let (encapped_key, tag) = single_shot_seal::<A, _, Ke, _>(
            &sender_mode,
            &pk_recip,
            &info[..],
            &mut ciphertext[..],
            aad,
            &mut csprng,
        )
        .expect("single_shot_seal() failed");

        // Make sure seal() isn't a no-op
        assert!(&ciphertext[..] != &msg[..]);

        // Decrypt with the second context
        single_shot_open::<A, _, Ke>(
            &receiver_mode,
            &sk_recip,
            &encapped_key,
            info,
            &mut ciphertext[..],
            aad,
            &tag,
        )
        .expect("single_shot_open() failed");
        // Change name for clarity
        let decrypted = ciphertext;
        assert_eq!(&decrypted[..], &msg[..]);
    }
}