pep_engine_sequoia_backend 1.1.0

An implementation of the p≡p Engine's cryptotech interface using Sequoia.
Documentation
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
use std::ops::BitAnd;
use std::convert::TryFrom;

#[cfg(not(windows))]
use libc::tm;

use sequoia_openpgp as openpgp;
use openpgp::KeyHandle;

mod session;
pub use session::Session;
mod identity;
pub use identity::{
    PepIdentityTemplate,
    PepIdentity,
    PepIdentityListItem,
    PepIdentityList,
};

mod stringlist;
pub use stringlist::{
    StringListItem,
    StringList,
    StringListIterMut,
    StringListIter,
};

// Transforms an error from some error type to the pep::Error.
macro_rules! wrap_err {
    ($e:expr, $err:ident, $msg:expr) => {
        $e.map_err(|err| {
            eprintln!("Error: {}: {}\n{:?}",
                      err, $msg, backtrace::Backtrace::new());
            crate::pep::Error::$err(
                anyhow::Error::from(err).into(),
                String::from($msg))
        })
    }
}

// We use Error rather than anyhow's error so that we force the
// function to convert the error into a form that we can easily pass
// back to the engine.
pub type Result<T> = std::result::Result<T, Error>;

// The pEp engine's error type.
pub type ErrorCode = i32;

#[enumber::into]
// XXX: This should be ErrorCode, but we can't use type aliases here :/.
#[repr(i32)]
#[non_exhaustive]
#[derive(thiserror::Error, Debug)]
#[allow(unused)]
pub enum Error {
    #[error("Success")]
    StatusOk = 0,
    #[error("Initializing the crypto library failed: {0}")]
    InitCryptoLibInitFailed(String) = 0x0111,

    // PEP_INIT_CANNOT_LOAD_CRYPTO_LIB                 = 0x0110,
    // PEP_INIT_CRYPTO_LIB_INIT_FAILED                 = 0x0111,
    // PEP_INIT_NO_CRYPTO_HOME                         = 0x0112,
    // PEP_INIT_NETPGP_INIT_FAILED                     = 0x0113,
    // PEP_INIT_CANNOT_DETERMINE_CRYPTO_VERSION        = 0x0114,
    // PEP_INIT_UNSUPPORTED_CRYPTO_VERSION             = 0x0115,
    // PEP_INIT_CANNOT_CONFIG_CRYPTO_AGENT             = 0x0116,
    //
    // PEP_INIT_SQLITE3_WITHOUT_MUTEX                  = 0x0120,

    #[error("Opening the database failed: {1}")]
    InitCannotOpenDB(#[source] anyhow::Error, String) = 0x0121,

    // PEP_INIT_CANNOT_OPEN_SYSTEM_DB                  = 0x0122,
    // PEP_INIT_DB_DOWNGRADE_VIOLATION                 = 0x0123,

    #[error("Database error: {1}")]
    UnknownDbError(#[source] anyhow::Error, String) = 0x01ff,

    #[error("Key {0} not present in database")]
    KeyNotFound(KeyHandle) = 0x0201,
    // PEP_KEY_HAS_AMBIG_NAME                          = 0x0202,

    #[error("Getting key: {1}")]
    GetKeyFailed(#[source] anyhow::Error, String) = 0x0203,

    // PEP_CANNOT_EXPORT_KEY                           = 0x0204,
    // PEP_CANNOT_EDIT_KEY                             = 0x0205,
    #[error("Key unsuitable: {0}")]
    KeyUnsuitable(#[source] anyhow::Error, String) = 0x0206,

    // PEP_MALFORMED_KEY_RESET_MSG                     = 0x0210,
    // PEP_KEY_NOT_RESET                               = 0x0211,
    #[error("Cannot delete key: {0}")]
    CannotDeleteKey(#[source] anyhow::Error, String) = 0x0212,
    #[error("Imported key")]
    KeyImported = 0x0220,
    #[error("No key imported")]
    NoKeyImported = 0x0221,
    // PEP_KEY_IMPORT_STATUS_UNKNOWN                   = 0x0222,
    #[error("Some keys imported")]
    SomeKeysImported = 0x0223,

    // PEP_CANNOT_FIND_IDENTITY                        = 0x0301,
    // PEP_CANNOT_SET_PERSON                           = 0x0381,
    // PEP_CANNOT_SET_PGP_KEYPAIR                      = 0x0382,
    // PEP_CANNOT_SET_IDENTITY                         = 0x0383,
    // PEP_CANNOT_SET_TRUST                            = 0x0384,
    // PEP_KEY_BLACKLISTED                             = 0x0385,
    // PEP_CANNOT_FIND_PERSON                          = 0x0386,
    // PEP_CANNOT_SET_PEP_VERSION                      = 0X0387,
    //
    // PEP_CANNOT_FIND_ALIAS                           = 0x0391,
    // PEP_CANNOT_SET_ALIAS                            = 0x0392,
    // PEP_NO_OWN_USERID_FOUND                         = 0x0393,
    //
    #[error("Message not encrypted and not verified")]
    Unencrypted = 0x0400,
    #[error("Message not encrypted, but verified")]
    Verified = 0x0401,
    #[error("Decrypted message")]
    Decrypted = 0x0402,
    #[error("Decrypted and verified message")]
    DecryptedAndVerified = 0x0403,
    #[error("Decrypted failed: wrong format")]
    DecryptWrongFormat = 0x0404,
    #[error("Decrypted failed: no key")]
    DecryptNoKey(#[source] anyhow::Error) = 0x0405,
    #[error("Decrypted failed: signature does not match")]
    DecryptSignatureDoesNotMatch = 0x0406,
    #[error("Verification failed: no key")]
    VerifyNoKey(#[source] anyhow::Error) = 0x0407,
    // PEP_VERIFIED_AND_TRUSTED                        = 0x0408,
    // PEP_CANNOT_REENCRYPT                            = 0x0409,
    #[error("Signer's key is revoked")]
    VerifySignerKeyRevoked = 0x040a,
    #[error("Cannot decrypt: {0}")]
    CannotDecryptUnknown(String) = 0x04ff,
    //
    //
    // PEP_TRUSTWORD_NOT_FOUND                         = 0x0501,
    // PEP_TRUSTWORDS_FPR_WRONG_LENGTH                 = 0x0502,
    // PEP_TRUSTWORDS_DUPLICATE_FPR                    = 0x0503,
    //
    #[error("Cannot create key")]
    CannotCreateKey(#[source] anyhow::Error, String) = 0x0601,
    // PEP_CANNOT_SEND_KEY                             = 0x0602,
    //
    // PEP_PHRASE_NOT_FOUND                            = 0x0701,
    //
    // PEP_SEND_FUNCTION_NOT_REGISTERED                = 0x0801,
    // PEP_CONTRAINTS_VIOLATED                         = 0x0802,
    // PEP_CANNOT_ENCODE                               = 0x0803,
    //
    // PEP_SYNC_NO_NOTIFY_CALLBACK                     = 0x0901,
    // PEP_SYNC_ILLEGAL_MESSAGE                        = 0x0902,
    // PEP_SYNC_NO_INJECT_CALLBACK                     = 0x0903,
    // PEP_SYNC_NO_CHANNEL                             = 0x0904,
    // PEP_SYNC_CANNOT_ENCRYPT                         = 0x0905,
    // PEP_SYNC_NO_MESSAGE_SEND_CALLBACK               = 0x0906,
    // PEP_SYNC_CANNOT_START                           = 0x0907,
    //
    // PEP_CANNOT_INCREASE_SEQUENCE                    = 0x0971,
    //
    // PEP_STATEMACHINE_ERROR                          = 0x0980,
    // PEP_NO_TRUST                                    = 0x0981,
    // PEP_STATEMACHINE_INVALID_STATE                  = 0x0982,
    // PEP_STATEMACHINE_INVALID_EVENT                  = 0x0983,
    // PEP_STATEMACHINE_INVALID_CONDITION              = 0x0984,
    // PEP_STATEMACHINE_INVALID_ACTION                 = 0x0985,
    // PEP_STATEMACHINE_INHIBITED_EVENT                = 0x0986,
    // PEP_STATEMACHINE_CANNOT_SEND                    = 0x0987,
    //
    #[error("Passphrase required")]
    PassphraseRequired = 0x0a00,
    #[error("Bad passphrase")]
    WrongPassphrase(#[source] anyhow::Error, String) = 0x0a01,
    #[error("Passphrase required for new keys")]
    PassphraseForNewKeysRequired = 0x0a02,
    //
    // PEP_CANNOT_CREATE_GROUP                         = 0x0b00,
    // PEP_CANNOT_FIND_GROUP_ENTRY                     = 0x0b01,
    // PEP_GROUP_EXISTS                                = 0x0b02,
    // PEP_GROUP_NOT_FOUND                             = 0x0b03,
    // PEP_CANNOT_ENABLE_GROUP                         = 0x0b04,
    // PEP_CANNOT_DISABLE_GROUP                        = 0x0b05,
    // PEP_CANNOT_ADD_GROUP_MEMBER                     = 0x0b06,
    // PEP_CANNOT_DEACTIVATE_GROUP_MEMBER              = 0x0b07,
    // PEP_NO_MEMBERSHIP_STATUS_FOUND                  = 0x0b08,
    // PEP_CANNOT_LEAVE_GROUP                          = 0x0b09,
    // PEP_CANNOT_JOIN_GROUP                           = 0x0b0a,
    // PEP_CANNOT_RETRIEVE_MEMBERSHIP_INFO             = 0x0b0b,
    //
    // PEP_DISTRIBUTION_ILLEGAL_MESSAGE                = 0x1002,
    // PEP_STORAGE_ILLEGAL_MESSAGE                     = 0x1102,
    //
    // PEP_COMMIT_FAILED                               = 0xff01,
    // PEP_MESSAGE_CONSUME                             = 0xff02,
    // PEP_MESSAGE_IGNORE                              = 0xff03,
    #[error("Invalid configuration: {0}")]
    CannotConfig(String) = 0xff04,
    //
    // PEP_RECORD_NOT_FOUND                            = -6,
    // PEP_CANNOT_CREATE_TEMP_FILE                     = -5,
    #[error("Illegal value: {0}")]
    IllegalValue(String) = -4,

    // PEP_BUFFER_TOO_SMALL                            = -3,
    #[error("Out of memory: {1} bytes for {0}")]
    OutOfMemory(String, usize) = -2,
    #[error("Unknown error: {1}")]
    UnknownError(#[source] anyhow::Error, String) = -1,

    // PEP_VERSION_MISMATCH                            = -7,
}

// See pEpEngine/src/timestamp.h
//
//   https://gitea.pep.foundation/pEp.foundation/pEpEngine/src/branch/master/src/timestamp.h
#[cfg(not(windows))]
pub type Timestamp = tm;

#[cfg(windows)]
use libc::{c_int, c_long};
#[cfg(windows)]
#[repr(C)]
// for time values all functions are using POSIX struct tm
pub struct Timestamp {
    pub tm_sec: c_int,
    pub tm_min: c_int,
    pub tm_hour: c_int,
    pub tm_mday: c_int,
    pub tm_mon: c_int,
    pub tm_year: c_int,
    pub tm_wday: c_int,
    pub tm_yday: c_int,
    pub tm_isdst: c_int,
    pub tm_gmtoff: c_long, // offset from GMT in seconds
}

// See pEpEngine/src/pEpEngine.h:PEP_comm_format.
//
//   https://gitea.pep.foundation/pEp.foundation/pEpEngine/src/branch/master/src/pEpEngine.h#L697
#[repr(C)]
#[allow(unused)]
#[derive(PartialOrd, Ord, PartialEq, Eq, Copy, Clone, Debug)]
pub enum PepCommType {
    Unknown = 0,

    // range 0x01 to 0x09: no encryption, 0x0a to 0x0e: nothing reasonable

    NoEncryption = 0x01,                // generic
    NoEncryptedChannel = 0x02,
    KeyNotFound = 0x03,
    KeyExpired = 0x04,
    KeyRevoked = 0x05,
    KeyB0rken = 0x06,
    KeyExpiredButConfirmed = 0x07, // NOT with confirmed bit. Just retaining info here in case of renewal.
    MyKeyNotIncluded = 0x09,

    SecurityByObscurity = 0x0a,
    B0rkenCrypto = 0x0b,
    KeyTooShort = 0x0c,

    Compromised = 0x0e,                  // known compromised connection
    Mistrusted = 0x0f,                   // known mistrusted key

    // range 0x10 to 0x3f: unconfirmed encryption

    UnconfirmedEncryption = 0x10,       // generic
    OpenPgpWeakUnconfirmed = 0x11,     // RSA 1024 is weak

    ToBeChecked = 0x20,                // generic
    SMimeUnconfirmed = 0x21,
    CmsUnconfirmed = 0x22,

    StrongButUnconfirmed = 0x30,       // generic
    OpenPgpUnconfirmed = 0x38,          // key at least 2048 bit RSA or EC
    OtrUnconfirmed = 0x3a,

    // range 0x40 to 0x7f: unconfirmed encryption and anonymization

    UnconfirmedEncAnon = 0x40,         // generic
    PepUnconfirmed = 0x7f,

    Confirmed = 0x80,                    // this bit decides if trust is confirmed

    // range 0x81 to 0x8f: reserved
    // range 0x90 to 0xbf: confirmed encryption

    ConfirmedEncryption = 0x90,         // generic
    OpenPgpWeak = 0x91,                 // RSA 1024 is weak (unused)

    ToBeCheckedConfirmed = 0xa0,      // generic
    SMime = 0xa1,
    Cms = 0xa2,

    StrongEncryption = 0xb0,            // generic
    OpenPgp = 0xb8,                      // key at least 2048 bit RSA or EC
    Otr = 0xba,

    // range 0xc0 to 0xff: confirmed encryption and anonymization

    ConfirmedEncAnon = 0xc0,           // generic
    Pep = 0xff
}

// See pEpEngine/src/pEpEngine.h:PEP_enc_format.
//
//   https://gitea.pep.foundation/pEp.foundation/pEpEngine/src/branch/master/src/pEpEngine.h#L179
#[repr(C)]
#[allow(unused)]
pub enum PepEncFormat {
    None = 0,                       // message is not encrypted
    Pieces = 1,                     // inline PGP + PGP extensions, was removed
    // Inline = 1,                     // still there
    SMime = 2,                      // RFC5751
    PgpMime = 3,                    // RFC3156
    Pep = 4,                        // pEp encryption format
    PgpMimeOutlook1 = 5,            // Message B0rken by Outlook type 1
    InlineEA = 6,
    Auto = 255                      // figure out automatically where possible
}

// See pEpEngine/src/pEpEngine.h:identity_flags.
//
//   https://gitea.pep.foundation/pEp.foundation/pEpEngine/src/branch/master/src/pEpEngine.h#L765
#[repr(C)]
#[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum PepIdentityFlags {
    // the first octet flags are app defined settings:

    // don't use this identity for sync
    NotForSync = 0x0001,
    // identity of list of persons
    List = 0x0002,

    // the second octet flags are calculated:

    // identity of a device group member
    DeviceGroup = 0x0100,
    // identity is associated with an org (i.e. NOT a private account
    // - could be company email)
    OrgIdent = 0x0200,
    // identity is a group identity (e.g. mailing list) - N.B. not
    // related to device group!
    GroupIdent = 0x0400,
}


impl BitAnd for PepIdentityFlags {
    type Output = usize;

    fn bitand(self, rhs: Self) -> Self::Output {
        (self as usize) & (rhs as usize)
    }
}

impl PepIdentityFlags {
    /// Returns whether the specified flag is set.
    pub fn is_set(&self, flag: PepIdentityFlags) -> bool {
        let flag = flag as usize;
        assert_eq!(flag.count_ones(), 1);
        ((*self as usize) & flag) != 0
    }
}

// See pEpEngine/src/pEpEngine.h:PEP_CIPHER_SUITE.
//
//   https://gitea.pep.foundation/pEp.foundation/pEpEngine/src/branch/master/src/pEpEngine.h#L395
#[repr(C)]
#[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum PepCipherSuite {
    Default = 0,
    Cv25519 = 1,
    P256 = 2,
    P384 = 3,
    P521 = 4,
    Rsa2K = 5,
    Rsa3K = 6,
    Rsa4K = 7,
    Rsa8K = 8,
}

impl Default for PepCipherSuite {
    fn default() -> Self {
        PepCipherSuite::Default
    }
}

impl TryFrom<PepCipherSuite> for openpgp::cert::CipherSuite {
    type Error = Error;

    fn try_from(cs: PepCipherSuite) -> Result<openpgp::cert::CipherSuite> {
        use openpgp::cert::CipherSuite::*;
        match cs {
            PepCipherSuite::Default => Ok(RSA2k),
            PepCipherSuite::Cv25519 => Ok(Cv25519),
            PepCipherSuite::P256 => Ok(P256),
            PepCipherSuite::P384 => Ok(P384),
            PepCipherSuite::P521 => Ok(P521),
            PepCipherSuite::Rsa2K => Ok(RSA2k),
            PepCipherSuite::Rsa3K => Ok(RSA3k),
            PepCipherSuite::Rsa4K => Ok(RSA4k),
            _ => Err(Error::IllegalValue(
                format!("Unknown cipher suite: {}", cs as usize)))
        }
    }
}