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

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

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

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

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

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

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

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

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

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

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

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

            
366
impl Debug for Ed25519Identity {
367
35506
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
368
35506
        write!(f, "Ed25519Identity {{ {} }}", self)
369
35506
    }
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
82
    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376
82
        write_start_redacted(f, &Base64Unpadded::encode_string(self.id.as_ref()), 2, "…")
377
82
    }
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
35674
    pub fn new(key: PublicKey, sig: Signature, text: &[u8]) -> Self {
470
35674
        ValidatableEd25519Signature {
471
35674
            key,
472
35674
            sig,
473
35674
            entire_text_of_signed_thing: text.into(),
474
35674
        }
475
35674
    }
476

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

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

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

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