1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
use crate::utils::as_array_32;
use core::fmt;
use core::mem::size_of;
#[cfg(feature = "dalek")]
use rand::{CryptoRng, RngCore};
use zerocopy::{AsBytes, FromBytes};
use zeroize::Zeroize;

#[cfg(all(feature = "dalek", not(feature = "force_sodium")))]
use crate::dalek::sign;
#[cfg(all(
    feature = "sodium",
    any(feature = "force_sodium", not(feature = "dalek"))
))]
use crate::sodium::sign;

/// A public/secret long-term key pair.
///
/// This is an [Ed25519](https://en.wikipedia.org/wiki/EdDSA) key pair.
#[derive(Clone, AsBytes, FromBytes)]
#[repr(C)]
pub struct Keypair {
    /// The secret half of the key pair. Keep private.
    pub secret: SecretKey,

    /// The public half of the key pair. Feel free to share.
    pub public: PublicKey,
}

impl Keypair {
    /// Size of a key pair, in bytes (== 64).
    pub const SIZE: usize = size_of::<Self>();

    /// Deserialize a keypair from a byte slice.
    ///
    /// The slice length must be 64; where the first 32 bytes
    /// are the secret key, and the second 32 bytes are the public key
    /// (libsodium's standard layout).
    pub fn from_slice(s: &[u8]) -> Option<Self> {
        if s.len() == Self::SIZE {
            Some(Keypair {
                secret: SecretKey(as_array_32(&s[..32])),
                public: PublicKey(as_array_32(&s[32..])),
            })
        } else {
            None
        }
    }

    /// Deserialize from the bas64 representation. Ignores optional .ed25519 suffix.
    ///
    /// # Example
    /// ```rust
    /// let s = "R6DKoOCt1Cj/IB2/ocvj2Eyp8AgmFdoJ9hH2TO4Tl8Yfapd5Lmw4pSpoY0WBEnqpHjz6UB4/QL2Wr0hWVAyi1w==.ed25519";
    /// if let Some(keypair) = ssb_crypto::Keypair::from_base64(s) {
    ///   // let auth = keypair.sign("hello".to_bytes());
    ///   // ...
    /// } else {
    ///     panic!()
    /// }
    /// ```
    #[cfg(feature = "b64")]
    pub fn from_base64(s: &str) -> Option<Self> {
        let mut buf = [0; 64];
        if crate::b64::decode(s, &mut buf, Some(".ed25519")) {
            Self::from_slice(&buf)
        } else {
            None
        }
    }

    /// Generate a new random keypair.
    #[cfg(any(feature = "sodium", all(feature = "dalek", feature = "getrandom")))]
    pub fn generate() -> Keypair {
        sign::generate_keypair()
    }

    /// Generate a new random keypair using the given cryptographically-secure
    /// random number generator.
    #[cfg(feature = "dalek")]
    pub fn generate_with_rng<R>(r: &mut R) -> Keypair
    where
        R: CryptoRng + RngCore,
    {
        crate::dalek::sign::generate_keypair_with_rng(r)
    }

    /// Generate a signature for a given byte slice.
    #[cfg(any(feature = "sodium", feature = "dalek"))]
    pub fn sign(&self, b: &[u8]) -> Signature {
        sign::sign(&self, b)
    }
}

/// The secret half of a [`Keypair`].
///
/// Note that a libsodium "secret key" is actually a pair of secret and public keys,
/// in the same buffer. This is only the secret half, which isn't much use on its own.
/// For signing, and deserializing a libsodium secretkey encoded in base64
/// (as in the ~/.ssb/secret file), see [`Keypair`].
///
/// The underlying memory is zeroed on drop.
///
/// [`Keypair`]: ./struct.Keypair.html
#[derive(AsBytes, FromBytes, Clone, Zeroize)]
#[zeroize(drop)]
#[repr(C)]
pub struct SecretKey(pub [u8; 32]);
impl SecretKey {
    /// Size of a secret key, in bytes (32).
    pub const SIZE: usize = size_of::<Self>();
}

/// The public half of a [`Keypair`].
///
/// [`Keypair`]: ./struct.Keypair.html
#[derive(AsBytes, FromBytes, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(C)]
pub struct PublicKey(pub [u8; 32]);
impl PublicKey {
    /// Size of a public key, in bytes (== 32).
    pub const SIZE: usize = size_of::<Self>();

    /// Deserialize a public key from a byte slice. The slice length must be 32.
    pub fn from_slice(s: &[u8]) -> Option<Self> {
        if s.len() == Self::SIZE {
            let mut out = Self([0; Self::SIZE]);
            out.0.copy_from_slice(s);
            Some(out)
        } else {
            None
        }
    }

    /// Deserialize from the base-64 representation. Ignores optional leading '@' sigil and '.ed25519' suffix.
    ///
    /// # Example
    /// ```rust
    /// use ssb_crypto::PublicKey;
    /// let author = "@H2qXeS5sOKUqaGNFgRJ6qR48+lAeP0C9lq9IVlQMotc=.ed25519";
    /// let pk = PublicKey::from_base64(author).unwrap();
    /// ```
    #[cfg(feature = "b64")]
    pub fn from_base64(mut s: &str) -> Option<Self> {
        if s.starts_with('@') {
            s = &s[1..];
        }
        let mut buf = [0; Self::SIZE];
        if crate::b64::decode(s, &mut buf, Some(".ed25519")) {
            Some(Self(buf))
        } else {
            None
        }
    }

    /// Verify that a signature was generated by this key's secret half for the given
    /// bytes.
    #[cfg(any(feature = "sodium", feature = "dalek"))]
    pub fn verify(&self, sig: &Signature, b: &[u8]) -> bool {
        sign::verify(self, sig, b)
    }
}

/// A cryptographic signature of some content, generated by [`Keypair::sign`]
/// and verified by [`PublicKey::verify`].
///
/// [`Keypair::sign`]: ./struct.Keypair.html#method.sign
/// [`PublicKey::verify`]: ./struct.PublicKey.html#method.verify
#[derive(AsBytes, FromBytes, Copy, Clone)]
#[repr(C)]
pub struct Signature(pub [u8; 64]);
impl Signature {
    /// Size of a signature, in bytes (== 64).
    pub const SIZE: usize = size_of::<Self>();

    /// Deserialize a signature from a byte slice. The slice length must be 64.
    pub fn from_slice(s: &[u8]) -> Option<Self> {
        if s.len() == Self::SIZE {
            let mut out = Self([0; Self::SIZE]);
            out.0.copy_from_slice(s);
            Some(out)
        } else {
            None
        }
    }

    /// Deserialize a signature from a base-64 encoded string. Ignores optional .sig.ed25519 suffix.
    ///
    /// # Example
    /// ```rust
    /// use ssb_crypto::Signature;
    /// let s = "QTsCZ+INzDENs1dAdej14Lsp1v2UCXUtRZBv4HlDGo6WZn29ZYM5lZtxnyNC53LxX0ucY1x8NlC1A1RjY7FHBA==.sig.ed25519";
    /// let sig = Signature::from_base64(s).unwrap();
    /// ```
    #[cfg(feature = "b64")]
    pub fn from_base64(s: &str) -> Option<Self> {
        let mut buf = [0; Self::SIZE];
        if crate::b64::decode(s, &mut buf, Some(".sig.ed25519")) {
            Some(Self(buf))
        } else {
            None
        }
    }
}

impl fmt::Debug for Signature {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Signature({:?})", &self.0[..])
    }
}
impl Eq for Signature {}
impl PartialEq for Signature {
    fn eq(&self, other: &Self) -> bool {
        self.0.as_ref().eq(other.0.as_ref())
    }
}