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
5510
    fn from(value: &'a Keypair) -> Self {
57
5510
        PublicKey((&value.0).into())
58
5510
    }
59
}
60

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

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

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

            
133
23490
    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
134
23490
        Ok(Self(ed25519_dalek::VerifyingKey::try_from(value)?))
135
23490
    }
136
}
137
impl<'a> From<&'a [u8; 32]> for Keypair {
138
9396
    fn from(value: &'a [u8; 32]) -> Self {
139
9396
        Self(ed25519_dalek::SigningKey::from(value))
140
9396
    }
141
}
142
impl From<[u8; 64]> for Signature {
143
14825
    fn from(value: [u8; 64]) -> Self {
144
14825
        Signature(value.into())
145
14825
    }
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
50240
    pub fn public(&self) -> &PublicKey {
184
50240
        &self.public
185
50240
    }
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
5777
    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
5777
        Signature(ed25519_dalek::hazmat::raw_sign::<Sha512>(
196
5777
            &self.secret,
197
5777
            message,
198
5777
            &self.public.0,
199
5777
        ))
200
5777
    }
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
7006
    pub fn to_secret_key_bytes(&self) -> [u8; 64] {
207
7006
        let mut output = [0_u8; 64];
208
7006
        output[0..32].copy_from_slice(&self.secret.scalar.to_bytes());
209
7006
        output[32..64].copy_from_slice(&self.secret.hash_prefix);
210
7006
        output
211
7006
    }
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
24239
    pub fn from_secret_key_bytes(bytes: [u8; 64]) -> Option<Self> {
220
24239
        let scalar = Option::from(Scalar::from_bytes_mod_order(
221
24239
            bytes[0..32].try_into().expect("wrong length on slice"),
222
        ))?;
223
24239
        let hash_prefix = bytes[32..64].try_into().expect("wrong length on slice");
224
24239
        let secret = ExpandedSecretKey {
225
24239
            scalar,
226
24239
            hash_prefix,
227
24239
        };
228
24239
        let public = PublicKey((&secret).into());
229
24239
        Some(Self { secret, public })
230
24239
    }
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
893
    fn from(kp: &'a Keypair) -> ExpandedKeypair {
239
893
        ExpandedKeypair {
240
893
            secret: kp.as_bytes().into(),
241
893
            public: kp.into(),
242
893
        }
243
893
    }
244
}
245

            
246
impl From<ExpandedKeypair> for PublicKey {
247
324
    fn from(ekp: ExpandedKeypair) -> PublicKey {
248
324
        ekp.public
249
324
    }
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
3791614
    pub fn new(id: [u8; 32]) -> Self {
294
3791614
        Ed25519Identity { id: id.into() }
295
3791614
    }
296
    /// If `id` is of the correct length, wrap it in an Ed25519Identity.
297
2912519
    pub fn from_bytes(id: &[u8]) -> Option<Self> {
298
2912519
        Some(Ed25519Identity::new(id.try_into().ok()?))
299
2912519
    }
300
    /// Return a reference to the bytes in this key.
301
296460
    pub fn as_bytes(&self) -> &[u8] {
302
296460
        &self.id.as_ref()[..]
303
296460
    }
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
164
    pub fn from_base64(s: &str) -> Option<Self> {
309
164
        let bytes = Base64Unpadded::decode_vec(s).ok()?;
310
164
        Ed25519Identity::from_bytes(&bytes)
311
164
    }
312
}
313

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

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

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

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

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

            
347
impl TryFrom<Ed25519Identity> for PublicKey {
348
    type Error = ed25519_dalek::SignatureError;
349
52245
    fn try_from(id: Ed25519Identity) -> Result<PublicKey, Self::Error> {
350
52245
        (&id).try_into()
351
52245
    }
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
82782
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
362
82782
        write!(f, "{}", Base64Unpadded::encode_string(self.id.as_ref()))
363
82782
    }
364
}
365

            
366
impl Debug for Ed25519Identity {
367
35478
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
368
35478
        write!(f, "Ed25519Identity {{ {} }}", self)
369
35478
    }
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
81
    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376
81
        write_start_redacted(f, &Base64Unpadded::encode_string(self.id.as_ref()), 2, "…")
377
81
    }
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
18718
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
386
18718
    where
387
18718
        S: serde::Serializer,
388
    {
389
18718
        if serializer.is_human_readable() {
390
18716
            serializer.serialize_str(&Base64Unpadded::encode_string(self.id.as_ref()))
391
        } else {
392
2
            serializer.serialize_bytes(&self.id.as_ref()[..])
393
        }
394
18718
    }
395
}
396

            
397
impl<'de> serde::Deserialize<'de> for Ed25519Identity {
398
578
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
399
578
    where
400
578
        D: serde::Deserializer<'de>,
401
    {
402
578
        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
574
                fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
411
574
                where
412
574
                    E: serde::de::Error,
413
                {
414
574
                    let bytes = Base64Unpadded::decode_vec(s).map_err(E::custom)?;
415
574
                    Ed25519Identity::from_bytes(&bytes)
416
574
                        .ok_or_else(|| E::custom("wrong length for Ed25519 public key"))
417
574
                }
418
            }
419

            
420
574
            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
4
                fn visit_bytes<E>(self, bytes: &[u8]) -> Result<Self::Value, E>
430
4
                where
431
4
                    E: serde::de::Error,
432
                {
433
4
                    Ed25519Identity::from_bytes(bytes)
434
4
                        .ok_or_else(|| E::custom("wrong length for ed25519 public key"))
435
4
                }
436
            }
437
4
            deserializer.deserialize_bytes(EdIdentityVisitor)
438
        }
439
578
    }
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
39370
    pub fn new(key: PublicKey, sig: Signature, text: &[u8]) -> Self {
470
39370
        ValidatableEd25519Signature {
471
39370
            key,
472
39370
            sig,
473
39370
            entire_text_of_signed_thing: text.into(),
474
39370
        }
475
39370
    }
476

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

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

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

            
495
24057
    fn as_ed25519(&self) -> Option<&ValidatableEd25519Signature> {
496
24057
        Some(self)
497
24057
    }
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
12150
pub fn validate_batch(sigs: &[&ValidatableEd25519Signature]) -> bool {
512
    use crate::pk::ValidatableSignature;
513
12150
    if sigs.is_empty() {
514
        // ed25519_dalek has nonzero cost for a batch-verification of
515
        // zero sigs.
516
1539
        true
517
10611
    } else if sigs.len() == 1 {
518
        // Validating one signature in the traditional way is faster.
519
4617
        sigs[0].is_valid()
520
    } else {
521
5994
        let mut ed_msgs = Vec::new();
522
5994
        let mut ed_sigs = Vec::new();
523
5994
        let mut ed_pks = Vec::new();
524
20250
        for ed_sig in sigs {
525
20250
            let (pk, sig, msg) = ed_sig.as_parts();
526
20250
            ed_sigs.push(sig.0);
527
20250
            ed_pks.push(pk.0);
528
20250
            ed_msgs.push(msg);
529
20250
        }
530
5994
        ed25519_dalek::verify_batch(&ed_msgs[..], &ed_sigs[..], &ed_pks[..]).is_ok()
531
    }
532
12150
}
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
42768
    fn public_key(&self) -> PublicKey {
542
42768
        Keypair::verifying_key(self)
543
42768
    }
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
35154
    fn sign(&self, message: &[u8]) -> Signature {
560
35154
        Keypair::sign(self, message)
561
35154
    }
562
}
563
impl Ed25519SigningKey for ExpandedKeypair {
564
162
    fn sign(&self, message: &[u8]) -> Signature {
565
162
        ExpandedKeypair::sign(self, message)
566
162
    }
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
}