1
//! Re-exporting Ed25519 implementations, and related utilities.
2
//!
3
//! Here we re-export types from [`ed25519_dalek`] that implement the
4
//! Ed25519 signature algorithm.  (TODO: Eventually, this module
5
//! should probably be replaced with a wrapper that uses the ed25519
6
//! trait and the Signature trait.)
7
//!
8
//! We additionally provide an `Ed25519Identity` type to represent the
9
//! unvalidated Ed25519 "identity keys" that we use throughout the Tor
10
//! protocol to uniquely identify a relay.
11

            
12
use base64ct::{Base64Unpadded, Encoding as _};
13
use curve25519_dalek::Scalar;
14
use derive_deftly::Deftly;
15
use safelog::util::write_start_redacted;
16
use std::fmt::{self, Debug, Display, Formatter};
17
use subtle::{Choice, ConstantTimeEq};
18

            
19
#[cfg(feature = "memquota-memcost")]
20
use tor_memquota_cost::derive_deftly_template_HasMemoryCost;
21

            
22
use ed25519_dalek::hazmat::ExpandedSecretKey;
23
use ed25519_dalek::{Signer as _, Verifier as _};
24

            
25
use crate::util::{
26
    ct::{
27
        CtByteArray, derive_deftly_template_ConstantTimeEq,
28
        derive_deftly_template_PartialEqFromCtEq,
29
    },
30
    rng::RngCompat,
31
};
32

            
33
/// An Ed25519 signature.
34
///
35
/// See [`ed25519_dalek::Signature`] for more information.
36
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37
pub struct Signature(pub(crate) ed25519_dalek::Signature);
38

            
39
/// An Ed25519 keypair.
40
///
41
/// (We do not provide a separate "private key only" type.)
42
///
43
/// See [`ed25519_dalek::SigningKey`] for more information.
44
#[derive(Debug, Deftly)]
45
#[derive_deftly(ConstantTimeEq)]
46
pub struct Keypair(pub(crate) ed25519_dalek::SigningKey);
47

            
48
/// An Ed25519 public key.
49
///
50
/// See [`ed25519_dalek::VerifyingKey`] for more information.
51
#[derive(Clone, Copy, Debug, Eq, Deftly)]
52
#[derive_deftly(PartialEqFromCtEq)]
53
pub struct PublicKey(pub(crate) ed25519_dalek::VerifyingKey);
54

            
55
impl<'a> From<&'a Keypair> for PublicKey {
56
6371
    fn from(value: &'a Keypair) -> Self {
57
6371
        PublicKey((&value.0).into())
58
6371
    }
59
}
60

            
61
impl ConstantTimeEq for PublicKey {
62
40531
    fn ct_eq(&self, other: &Self) -> Choice {
63
40531
        self.as_bytes().ct_eq(other.as_bytes())
64
40531
    }
65
}
66

            
67
impl PublicKey {
68
    /// Construct a public key from its byte representation.
69
266301
    pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, signature::Error> {
70
266301
        Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(bytes)?))
71
266301
    }
72

            
73
    /// Return a reference to the byte representation of this public key.
74
336511
    pub fn as_bytes(&self) -> &[u8; 32] {
75
336511
        self.0.as_bytes()
76
336511
    }
77
    /// Return the byte representation of this public key.
78
19322
    pub fn to_bytes(&self) -> [u8; 32] {
79
19322
        self.0.to_bytes()
80
19322
    }
81
    /// Verify a signature using this public key.
82
    ///
83
    /// See [`ed25519_dalek::VerifyingKey::verify`] for more information.
84
84193
    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), signature::Error> {
85
84193
        self.0.verify(message, &signature.0)
86
84193
    }
87
}
88
impl Keypair {
89
    /// Generate a new random ed25519 keypair.
90
5489
    pub fn generate<R: rand_core::Rng + rand_core::CryptoRng>(csprng: &mut R) -> Self {
91
5489
        Self(ed25519_dalek::SigningKey::generate(&mut RngCompat::new(
92
5489
            csprng,
93
5489
        )))
94
5489
    }
95
    /// Construct an ed25519 keypair from the byte representation of its secret key.
96
182
    pub fn from_bytes(bytes: &[u8; 32]) -> Self {
97
182
        Self(ed25519_dalek::SigningKey::from_bytes(bytes))
98
182
    }
99
    /// Return a reference to the byte representation of the secret key in this keypair.
100
11922
    pub fn as_bytes(&self) -> &[u8; 32] {
101
11922
        self.0.as_bytes()
102
11922
    }
103
    /// Return to the byte representation of the secret key in this keypair.
104
365
    pub fn to_bytes(&self) -> [u8; 32] {
105
365
        self.0.to_bytes()
106
365
    }
107
    /// Return the public key in this keypair.
108
116025
    pub fn verifying_key(&self) -> PublicKey {
109
116025
        PublicKey(*self.0.as_ref())
110
116025
    }
111
    /// Verify a signature generated with this keypair.
112
273
    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), signature::Error> {
113
273
        self.0.verify(message, &signature.0)
114
273
    }
115
    /// Sign a message using this keypair.
116
54691
    pub fn sign(&self, message: &[u8]) -> Signature {
117
54691
        Signature(self.0.sign(message))
118
54691
    }
119
}
120
impl Signature {
121
    /// Construct this signature from its byte representation.
122
96915
    pub fn from_bytes(bytes: &[u8; 64]) -> Self {
123
96915
        Self(ed25519_dalek::Signature::from_bytes(bytes))
124
96915
    }
125
    /// Return the byte representation of this signature.
126
70434
    pub fn to_bytes(&self) -> [u8; 64] {
127
70434
        self.0.to_bytes()
128
70434
    }
129
}
130
impl<'a> TryFrom<&'a [u8]> for PublicKey {
131
    type Error = signature::Error;
132

            
133
29484
    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
134
29484
        Ok(Self(ed25519_dalek::VerifyingKey::try_from(value)?))
135
29484
    }
136
}
137
impl<'a> From<&'a [u8; 32]> for Keypair {
138
11466
    fn from(value: &'a [u8; 32]) -> Self {
139
11466
        Self(ed25519_dalek::SigningKey::from(value))
140
11466
    }
141
}
142
impl From<[u8; 64]> for Signature {
143
38039
    fn from(value: [u8; 64]) -> Self {
144
38039
        Signature(value.into())
145
38039
    }
146
}
147
impl<'a> From<&'a [u8; 64]> for Signature {
148
    fn from(value: &'a [u8; 64]) -> Self {
149
        Signature(value.into())
150
    }
151
}
152

            
153
/// The length of an ED25519 identity, in bytes.
154
pub const ED25519_ID_LEN: usize = 32;
155

            
156
/// The length of an ED25519 signature, in bytes.
157
pub const ED25519_SIGNATURE_LEN: usize = 64;
158

            
159
/// A variant of [`Keypair`] containing an [`ExpandedSecretKey`].
160
///
161
/// In the Tor protocol, we use this type for blinded onion service identity keys
162
/// (KS_hs_blind_id).  Since their scalar values are computed, rather than taken
163
/// directly from a
164
/// SHA-512 transformation of a SecretKey, we cannot use the regular `Keypair`
165
/// type.
166
#[allow(clippy::exhaustive_structs)]
167
#[derive(Deftly)]
168
#[derive_deftly(ConstantTimeEq)]
169
pub struct ExpandedKeypair {
170
    /// The secret part of the key.
171
    pub(crate) secret: ExpandedSecretKey,
172
    /// The public part of this key.
173
    ///
174
    /// NOTE: As with [`ed25519_dalek::SigningKey`], this public key _must_ be
175
    /// the public key matching `secret`.  Putting a different public key in
176
    /// here would enable a class of attacks against ed25519 and enable secret
177
    /// key recovery.
178
    pub(crate) public: PublicKey,
179
}
180

            
181
impl ExpandedKeypair {
182
    /// Return the public part of this expanded keypair.
183
62891
    pub fn public(&self) -> &PublicKey {
184
62891
        &self.public
185
62891
    }
186

            
187
    // NOTE: There is deliberately no secret() function.  If we had one, we
188
    // would be exposing an unescorted secret key, which is part of
189
    // ed25519::hazmat.
190

            
191
    /// Compute a signature over a message using this keypair.
192
7202
    pub fn sign(&self, message: &[u8]) -> Signature {
193
        use sha2::Sha512;
194
        // See notes on ExpandedKeypair about why this hazmat is okay to use.
195
7202
        Signature(ed25519_dalek::hazmat::raw_sign::<Sha512>(
196
7202
            &self.secret,
197
7202
            message,
198
7202
            &self.public.0,
199
7202
        ))
200
7202
    }
201

            
202
    /// Return a representation of the secret key in this keypair.
203
    ///
204
    /// (Since it is an expanded secret key, we represent it as its scalar part
205
    /// followed by its hash_prefix.)
206
8756
    pub fn to_secret_key_bytes(&self) -> [u8; 64] {
207
8756
        let mut output = [0_u8; 64];
208
8756
        output[0..32].copy_from_slice(&self.secret.scalar.to_bytes());
209
8756
        output[32..64].copy_from_slice(&self.secret.hash_prefix);
210
8756
        output
211
8756
    }
212

            
213
    /// Reconstruct a key from its byte representation as returned by
214
    /// `to_secret_key_bytes()`.
215
    ///
216
    /// Return None if the input cannot be the output of `to_secret_key_bytes()`.
217
    //
218
    // NOTE: Returning None is a bit silly, but that's what Dalek does.
219
30313
    pub fn from_secret_key_bytes(bytes: [u8; 64]) -> Option<Self> {
220
30313
        let scalar = Option::from(Scalar::from_bytes_mod_order(
221
30313
            bytes[0..32].try_into().expect("wrong length on slice"),
222
        ))?;
223
30313
        let hash_prefix = bytes[32..64].try_into().expect("wrong length on slice");
224
30313
        let secret = ExpandedSecretKey {
225
30313
            scalar,
226
30313
            hash_prefix,
227
30313
        };
228
30313
        let public = PublicKey((&secret).into());
229
30313
        Some(Self { secret, public })
230
30313
    }
231

            
232
    // NOTE: There is deliberately no constructor here that takes a (secret,
233
    // public) pair.  If there were, you could construct a pair with a
234
    // mismatched public key.
235
}
236

            
237
impl<'a> From<&'a Keypair> for ExpandedKeypair {
238
1184
    fn from(kp: &'a Keypair) -> ExpandedKeypair {
239
1184
        ExpandedKeypair {
240
1184
            secret: kp.as_bytes().into(),
241
1184
            public: kp.into(),
242
1184
        }
243
1184
    }
244
}
245

            
246
impl From<ExpandedKeypair> for PublicKey {
247
364
    fn from(ekp: ExpandedKeypair) -> PublicKey {
248
364
        ekp.public
249
364
    }
250
}
251

            
252
/// An unchecked, unvalidated Ed25519 key.
253
///
254
/// This key is an "identity" in the sense that it identifies (up to) one
255
/// Ed25519 key.  It may also represent the identity for a particular entity,
256
/// such as a relay or an onion service.
257
///
258
/// This type is distinct from an Ed25519 [`PublicKey`] for several reasons:
259
///  * We're storing it in a compact format, whereas the public key
260
///    implementation might want an expanded form for more efficient key
261
///    validation.
262
///  * This type hasn't checked whether the bytes here actually _are_ a valid
263
///    Ed25519 public key.
264
#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq)]
265
#[cfg_attr(
266
    feature = "memquota-memcost",
267
    derive(Deftly),
268
    derive_deftly(HasMemoryCost)
269
)]
270
pub struct Ed25519Identity {
271
    /// A raw unchecked Ed25519 public key.
272
    id: CtByteArray<ED25519_ID_LEN>,
273
}
274

            
275
impl Ed25519Identity {
276
    /// Construct a new Ed25519 identity from a 32-byte sequence.
277
    ///
278
    /// This might or might not actually be a valid Ed25519 public key.
279
    ///
280
    /// ```
281
    /// use tor_llcrypto::pk::ed25519::{Ed25519Identity, PublicKey};
282
    ///
283
    /// let bytes = b"klsadjfkladsfjklsdafkljasdfsdsd!";
284
    /// let id = Ed25519Identity::new(*bytes);
285
    /// let pk: Result<PublicKey,_> = (&id).try_into();
286
    /// assert!(pk.is_ok());
287
    ///
288
    /// let bytes = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
289
    /// let id = Ed25519Identity::new(*bytes);
290
    /// let pk: Result<PublicKey,_> = (&id).try_into();
291
    /// assert!(pk.is_err());
292
    /// ```
293
4401763
    pub fn new(id: [u8; 32]) -> Self {
294
4401763
        Ed25519Identity { id: id.into() }
295
4401763
    }
296
    /// If `id` is of the correct length, wrap it in an Ed25519Identity.
297
3337062
    pub fn from_bytes(id: &[u8]) -> Option<Self> {
298
3337062
        Some(Ed25519Identity::new(id.try_into().ok()?))
299
3337062
    }
300
    /// Return a reference to the bytes in this key.
301
345345
    pub fn as_bytes(&self) -> &[u8] {
302
345345
        &self.id.as_ref()[..]
303
345345
    }
304
    /// Decode an `Ed25519Identity` from a base64-encoded string.
305
    ///
306
    /// The string must have no padding, spaces, or any extra characters.
307
    // We decode without padding to match the serde deserialize impl.
308
183
    pub fn from_base64(s: &str) -> Option<Self> {
309
183
        let bytes = Base64Unpadded::decode_vec(s).ok()?;
310
183
        Ed25519Identity::from_bytes(&bytes)
311
183
    }
312
}
313

            
314
impl From<[u8; ED25519_ID_LEN]> for Ed25519Identity {
315
910274
    fn from(id: [u8; ED25519_ID_LEN]) -> Self {
316
910274
        Ed25519Identity::new(id)
317
910274
    }
318
}
319

            
320
impl From<Ed25519Identity> for [u8; ED25519_ID_LEN] {
321
26299
    fn from(value: Ed25519Identity) -> Self {
322
26299
        value.id.into()
323
26299
    }
324
}
325

            
326
impl From<PublicKey> for Ed25519Identity {
327
157976
    fn from(pk: PublicKey) -> Self {
328
157976
        (&pk).into()
329
157976
    }
330
}
331

            
332
impl From<&PublicKey> for Ed25519Identity {
333
211029
    fn from(pk: &PublicKey) -> Self {
334
        // This unwrap is safe because the public key is always 32 bytes
335
        // long.
336
211029
        Ed25519Identity::from_bytes(pk.as_bytes()).expect("Ed25519 public key had wrong length?")
337
211029
    }
338
}
339

            
340
impl TryFrom<&Ed25519Identity> for PublicKey {
341
    type Error = ed25519_dalek::SignatureError;
342
194285
    fn try_from(id: &Ed25519Identity) -> Result<PublicKey, Self::Error> {
343
194285
        PublicKey::from_bytes(id.id.as_ref())
344
194285
    }
345
}
346

            
347
impl TryFrom<Ed25519Identity> for PublicKey {
348
    type Error = ed25519_dalek::SignatureError;
349
144781
    fn try_from(id: Ed25519Identity) -> Result<PublicKey, Self::Error> {
350
144781
        (&id).try_into()
351
144781
    }
352
}
353

            
354
impl ConstantTimeEq for Ed25519Identity {
355
    fn ct_eq(&self, other: &Self) -> Choice {
356
        self.id.ct_eq(&other.id)
357
    }
358
}
359

            
360
impl Display for Ed25519Identity {
361
94549
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
362
94549
        write!(f, "{}", Base64Unpadded::encode_string(self.id.as_ref()))
363
94549
    }
364
}
365

            
366
impl Debug for Ed25519Identity {
367
39858
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
368
39858
        write!(f, "Ed25519Identity {{ {} }}", self)
369
39858
    }
370
}
371

            
372
impl safelog::Redactable for Ed25519Identity {
373
    /// Warning: This displays 12 bits of the ed25519 identity, which is
374
    /// enough to narrow down a public relay by a great deal.
375
91
    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376
91
        write_start_redacted(f, &Base64Unpadded::encode_string(self.id.as_ref()), 2, "…")
377
91
    }
378

            
379
    fn debug_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380
        write!(f, "Ed25519Identity {{ {} }}", self.redacted())
381
    }
382
}
383

            
384
impl serde::Serialize for Ed25519Identity {
385
18174
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
386
18174
    where
387
18174
        S: serde::Serializer,
388
    {
389
18174
        if serializer.is_human_readable() {
390
18173
            serializer.serialize_str(&Base64Unpadded::encode_string(self.id.as_ref()))
391
        } else {
392
1
            serializer.serialize_bytes(&self.id.as_ref()[..])
393
        }
394
18174
    }
395
}
396

            
397
impl<'de> serde::Deserialize<'de> for Ed25519Identity {
398
418
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
399
418
    where
400
418
        D: serde::Deserializer<'de>,
401
    {
402
418
        if deserializer.is_human_readable() {
403
            /// Helper for deserialization
404
            struct EdIdentityVisitor;
405
            impl<'de> serde::de::Visitor<'de> for EdIdentityVisitor {
406
                type Value = Ed25519Identity;
407
                fn expecting(&self, fmt: &mut std::fmt::Formatter<'_>) -> fmt::Result {
408
                    fmt.write_str("base64-encoded Ed25519 public key")
409
                }
410
416
                fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
411
416
                where
412
416
                    E: serde::de::Error,
413
                {
414
416
                    let bytes = Base64Unpadded::decode_vec(s).map_err(E::custom)?;
415
416
                    Ed25519Identity::from_bytes(&bytes)
416
416
                        .ok_or_else(|| E::custom("wrong length for Ed25519 public key"))
417
416
                }
418
            }
419

            
420
416
            deserializer.deserialize_str(EdIdentityVisitor)
421
        } else {
422
            /// Helper for deserialization
423
            struct EdIdentityVisitor;
424
            impl<'de> serde::de::Visitor<'de> for EdIdentityVisitor {
425
                type Value = Ed25519Identity;
426
                fn expecting(&self, fmt: &mut std::fmt::Formatter<'_>) -> fmt::Result {
427
                    fmt.write_str("ed25519 public key")
428
                }
429
2
                fn visit_bytes<E>(self, bytes: &[u8]) -> Result<Self::Value, E>
430
2
                where
431
2
                    E: serde::de::Error,
432
                {
433
2
                    Ed25519Identity::from_bytes(bytes)
434
2
                        .ok_or_else(|| E::custom("wrong length for ed25519 public key"))
435
2
                }
436
            }
437
2
            deserializer.deserialize_bytes(EdIdentityVisitor)
438
        }
439
418
    }
440
}
441

            
442
/// An ed25519 signature, plus the document that it signs and its
443
/// public key.
444
#[derive(Clone, Debug)]
445
#[cfg_attr(
446
    feature = "memquota-memcost",
447
    derive(Deftly),
448
    derive_deftly(HasMemoryCost)
449
)]
450
pub struct ValidatableEd25519Signature {
451
    /// The key that allegedly produced the signature
452
    #[cfg_attr(feature = "memquota-memcost", deftly(has_memory_cost(copy)))]
453
    key: PublicKey,
454
    /// The alleged signature
455
    #[cfg_attr(feature = "memquota-memcost", deftly(has_memory_cost(copy)))]
456
    sig: Signature,
457
    /// The entire body of text that is allegedly signed here.
458
    ///
459
    /// TODO: It's not so good to have this included here; it
460
    /// would be better to have a patch to ed25519_dalek to allow
461
    /// us to pre-hash the signed thing, and just store a digest.
462
    /// We can't use that with the 'prehash' variant of ed25519,
463
    /// since that has different constants.
464
    entire_text_of_signed_thing: Vec<u8>,
465
}
466

            
467
impl ValidatableEd25519Signature {
468
    /// Create a new ValidatableEd25519Signature
469
65340
    pub fn new(key: PublicKey, sig: Signature, text: &[u8]) -> Self {
470
65340
        ValidatableEd25519Signature {
471
65340
            key,
472
65340
            sig,
473
65340
            entire_text_of_signed_thing: text.into(),
474
65340
        }
475
65340
    }
476

            
477
    /// View the interior of this signature object.
478
22750
    pub(crate) fn as_parts(&self) -> (&PublicKey, &Signature, &[u8]) {
479
22750
        (&self.key, &self.sig, &self.entire_text_of_signed_thing[..])
480
22750
    }
481

            
482
    /// Return a reference to the underlying Signature.
483
546
    pub fn signature(&self) -> &Signature {
484
546
        &self.sig
485
546
    }
486
}
487

            
488
impl super::ValidatableSignature for ValidatableEd25519Signature {
489
28121
    fn is_valid(&self) -> bool {
490
28121
        self.key
491
28121
            .verify(&self.entire_text_of_signed_thing[..], &self.sig)
492
28121
            .is_ok()
493
28121
    }
494

            
495
48139
    fn as_ed25519(&self) -> Option<&ValidatableEd25519Signature> {
496
48139
        Some(self)
497
48139
    }
498
}
499

            
500
/// Perform a batch verification operation on the provided signatures
501
///
502
/// Return `true` if _every_ signature is valid; otherwise return `false`.
503
///
504
/// Note that the mathematics for batch validation are slightly
505
/// different than those for normal one-signature validation.  Because
506
/// of this, it is possible for an ostensible signature that passes
507
/// one validation algorithm might fail the other.  (Well-formed
508
/// signatures generated by a correct Ed25519 implementation will
509
/// always pass both kinds of validation, and an attacker should not
510
/// be able to forge a signature that passes either kind.)
511
34762
pub fn validate_batch(sigs: &[&ValidatableEd25519Signature]) -> bool {
512
    use crate::pk::ValidatableSignature;
513
34762
    if sigs.is_empty() {
514
        // ed25519_dalek has nonzero cost for a batch-verification of
515
        // zero sigs.
516
1729
        true
517
33033
    } else if sigs.len() == 1 {
518
        // Validating one signature in the traditional way is faster.
519
26299
        sigs[0].is_valid()
520
    } else {
521
6734
        let mut ed_msgs = Vec::new();
522
6734
        let mut ed_sigs = Vec::new();
523
6734
        let mut ed_pks = Vec::new();
524
22750
        for ed_sig in sigs {
525
22750
            let (pk, sig, msg) = ed_sig.as_parts();
526
22750
            ed_sigs.push(sig.0);
527
22750
            ed_pks.push(pk.0);
528
22750
            ed_msgs.push(msg);
529
22750
        }
530
6734
        ed25519_dalek::verify_batch(&ed_msgs[..], &ed_sigs[..], &ed_pks[..]).is_ok()
531
    }
532
34762
}
533

            
534
/// An object that has an Ed25519 [`PublicKey`].
535
pub trait Ed25519PublicKey {
536
    /// Get the Ed25519 [`PublicKey`].
537
    fn public_key(&self) -> PublicKey;
538
}
539

            
540
impl Ed25519PublicKey for Keypair {
541
53417
    fn public_key(&self) -> PublicKey {
542
53417
        Keypair::verifying_key(self)
543
53417
    }
544
}
545

            
546
impl Ed25519PublicKey for ExpandedKeypair {
547
    fn public_key(&self) -> PublicKey {
548
        self.public
549
    }
550
}
551

            
552
/// An object that can generate Ed25519 signatures.
553
pub trait Ed25519SigningKey {
554
    /// Sign a message with this key.
555
    fn sign(&self, message: &[u8]) -> Signature;
556
}
557

            
558
impl Ed25519SigningKey for Keypair {
559
43862
    fn sign(&self, message: &[u8]) -> Signature {
560
43862
        Keypair::sign(self, message)
561
43862
    }
562
}
563
impl Ed25519SigningKey for ExpandedKeypair {
564
182
    fn sign(&self, message: &[u8]) -> Signature {
565
182
        ExpandedKeypair::sign(self, message)
566
182
    }
567
}
568

            
569
#[cfg(test)]
570
mod test {
571
    // @@ begin test lint list maintained by maint/add_warning @@
572
    #![allow(clippy::bool_assert_comparison)]
573
    #![allow(clippy::clone_on_copy)]
574
    #![allow(clippy::dbg_macro)]
575
    #![allow(clippy::mixed_attributes_style)]
576
    #![allow(clippy::print_stderr)]
577
    #![allow(clippy::print_stdout)]
578
    #![allow(clippy::single_char_pattern)]
579
    #![allow(clippy::unwrap_used)]
580
    #![allow(clippy::unchecked_time_subtraction)]
581
    #![allow(clippy::useless_vec)]
582
    #![allow(clippy::needless_pass_by_value)]
583
    #![allow(clippy::string_slice)] // See arti#2571
584
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
585

            
586
    use super::*;
587

            
588
    #[test]
589
    fn ed_from_base64() {
590
        let id =
591
            Ed25519Identity::from_base64("qpL/LxLYVEXghU76iG3LsSI/UW7MBpIROZK0AB18560").unwrap();
592

            
593
        assert_eq!(
594
            id,
595
            Ed25519Identity::from([
596
                0xaa, 0x92, 0xff, 0x2f, 0x12, 0xd8, 0x54, 0x45, 0xe0, 0x85, 0x4e, 0xfa, 0x88, 0x6d,
597
                0xcb, 0xb1, 0x22, 0x3f, 0x51, 0x6e, 0xcc, 0x06, 0x92, 0x11, 0x39, 0x92, 0xb4, 0x00,
598
                0x1d, 0x7c, 0xe7, 0xad
599
            ]),
600
        );
601
    }
602
}