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
36234
    fn ct_eq(&self, other: &Self) -> Choice {
63
36234
        self.as_bytes().ct_eq(other.as_bytes())
64
36234
    }
65
}
66

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

            
73
    /// Return a reference to the byte representation of this public key.
74
276836
    pub fn as_bytes(&self) -> &[u8; 32] {
75
276836
        self.0.as_bytes()
76
276836
    }
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
5363
    pub fn generate<R: rand_core::Rng + rand_core::CryptoRng>(csprng: &mut R) -> Self {
91
5363
        Self(ed25519_dalek::SigningKey::generate(&mut RngCompat::new(
92
5363
            csprng,
93
5363
        )))
94
5363
    }
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
102172
    pub fn verifying_key(&self) -> PublicKey {
109
102172
        PublicKey(*self.0.as_ref())
110
102172
    }
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
48134
    pub fn sign(&self, message: &[u8]) -> Signature {
117
48134
        Signature(self.0.sign(message))
118
48134
    }
119
}
120
impl Signature {
121
    /// Construct this signature from its byte representation.
122
36654
    pub fn from_bytes(bytes: &[u8; 64]) -> Self {
123
36654
        Self(ed25519_dalek::Signature::from_bytes(bytes))
124
36654
    }
125
    /// Return the byte representation of this signature.
126
54858
    pub fn to_bytes(&self) -> [u8; 64] {
127
54858
        self.0.to_bytes()
128
54858
    }
129
}
130
impl<'a> TryFrom<&'a [u8]> for PublicKey {
131
    type Error = signature::Error;
132

            
133
26404
    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
134
26404
        Ok(Self(ed25519_dalek::VerifyingKey::try_from(value)?))
135
26404
    }
136
}
137
impl<'a> From<&'a [u8; 32]> for Keypair {
138
10168
    fn from(value: &'a [u8; 32]) -> Self {
139
10168
        Self(ed25519_dalek::SigningKey::from(value))
140
10168
    }
141
}
142
impl From<[u8; 64]> for Signature {
143
7956
    fn from(value: [u8; 64]) -> Self {
144
7956
        Signature(value.into())
145
7956
    }
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
56108
    pub fn public(&self) -> &PublicKey {
184
56108
        &self.public
185
56108
    }
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
6422
    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
6422
        Signature(ed25519_dalek::hazmat::raw_sign::<Sha512>(
196
6422
            &self.secret,
197
6422
            message,
198
6422
            &self.public.0,
199
6422
        ))
200
6422
    }
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
7748
    pub fn to_secret_key_bytes(&self) -> [u8; 64] {
207
7748
        let mut output = [0_u8; 64];
208
7748
        output[0..32].copy_from_slice(&self.secret.scalar.to_bytes());
209
7748
        output[32..64].copy_from_slice(&self.secret.hash_prefix);
210
7748
        output
211
7748
    }
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
27162
    pub fn from_secret_key_bytes(bytes: [u8; 64]) -> Option<Self> {
220
27162
        let scalar = Option::from(Scalar::from_bytes_mod_order(
221
27162
            bytes[0..32].try_into().expect("wrong length on slice"),
222
        ))?;
223
27162
        let hash_prefix = bytes[32..64].try_into().expect("wrong length on slice");
224
27162
        let secret = ExpandedSecretKey {
225
27162
            scalar,
226
27162
            hash_prefix,
227
27162
        };
228
27162
        let public = PublicKey((&secret).into());
229
27162
        Some(Self { secret, public })
230
27162
    }
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
3825222
    pub fn new(id: [u8; 32]) -> Self {
294
3825222
        Ed25519Identity { id: id.into() }
295
3825222
    }
296
    /// If `id` is of the correct length, wrap it in an Ed25519Identity.
297
2951920
    pub fn from_bytes(id: &[u8]) -> Option<Self> {
298
2951920
        Some(Ed25519Identity::new(id.try_into().ok()?))
299
2951920
    }
300
    /// Return a reference to the bytes in this key.
301
309632
    pub fn as_bytes(&self) -> &[u8] {
302
309632
        &self.id.as_ref()[..]
303
309632
    }
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
818936
    fn from(id: [u8; ED25519_ID_LEN]) -> Self {
316
818936
        Ed25519Identity::new(id)
317
818936
    }
318
}
319

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

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

            
332
impl From<&PublicKey> for Ed25519Identity {
333
164738
    fn from(pk: &PublicKey) -> Self {
334
        // This unwrap is safe because the public key is always 32 bytes
335
        // long.
336
164738
        Ed25519Identity::from_bytes(pk.as_bytes()).expect("Ed25519 public key had wrong length?")
337
164738
    }
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
84050
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
362
84050
        write!(f, "{}", Base64Unpadded::encode_string(self.id.as_ref()))
363
84050
    }
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
46248
    fn public_key(&self) -> PublicKey {
542
46248
        Keypair::verifying_key(self)
543
46248
    }
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
39196
    fn sign(&self, message: &[u8]) -> Signature {
560
39196
        Keypair::sign(self, message)
561
39196
    }
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
}