1use ring::signature::{Ed25519KeyPair, UnparsedPublicKey, ED25519};
2
3pub struct PrivateKey(Ed25519KeyPair);
4
5impl PrivateKey {
6 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
7 Ed25519KeyPair::from_seed_unchecked(bytes).map(Self).ok()
8 }
9
10 pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
11 self.0.sign(msg).as_ref().to_vec()
12 }
13
14 pub fn from_base64<T: ?Sized + AsRef<[u8]>>(input: &T) -> Option<Self> {
15 base64::decode_config(input, base64::URL_SAFE_NO_PAD)
16 .ok()
17 .and_then(|seed| Self::from_bytes(&seed))
18 }
19}
20
21#[derive(Clone)]
22pub struct PublicKey(UnparsedPublicKey<Vec<u8>>);
23
24impl PublicKey {
25 pub fn from_bytes(bytes: &[u8]) -> Self {
26 Self(UnparsedPublicKey::new(&ED25519, bytes.to_vec()))
27 }
28
29 pub fn from_base64<T: ?Sized + AsRef<[u8]>>(input: &T) -> Option<Self> {
30 base64::decode_config(input, base64::URL_SAFE_NO_PAD)
31 .map(|bytes| Self(UnparsedPublicKey::new(&ED25519, bytes)))
32 .ok()
33 }
34
35 pub fn verify(&self, message: &[u8], signature: &[u8]) -> bool {
36 self.0.verify(message, signature).is_ok()
37 }
38}
39
40const SEPARATOR: u8 = b'.';
41
42pub struct SignedMessage {
43 message: Vec<u8>,
44 signature: Vec<u8>,
45}
46
47impl SignedMessage {
48 pub fn create(message: Vec<u8>, key: &PrivateKey) -> Self {
49 let signature = key.sign(&message);
50 Self { message, signature }
51 }
52
53 pub fn verify(&self, key: &PublicKey) -> bool {
54 key.verify(&self.message, &self.signature)
55 }
56
57 pub fn message(&self) -> &[u8] {
58 &self.message
59 }
60
61 pub fn signature(&self) -> &[u8] {
62 &self.signature
63 }
64
65 pub fn encode(&self) -> String {
66 fn base64_encode_buf(input: &[u8], buf: &mut String) {
67 base64::encode_config_buf(input, base64::URL_SAFE_NO_PAD, buf)
68 }
69 let Self { message, signature } = self;
70 let mut output = String::with_capacity(self.get_encoded_len());
71 base64_encode_buf(message, &mut output);
72 output.push(char::from(SEPARATOR));
73 base64_encode_buf(signature, &mut output);
74 output
75 }
76
77 pub fn decode<T: AsRef<[u8]>>(input: T) -> Option<Self> {
78 let mut iter = input.as_ref().split(|&b| b == SEPARATOR);
79 let decode = |input| base64::decode_config(input, base64::URL_SAFE_NO_PAD).ok();
80 match (iter.next(), iter.next()) {
81 (Some(message), Some(signature)) => Some(SignedMessage {
82 message: decode(message)?,
83 signature: decode(signature)?,
84 }),
85 _ => None,
86 }
87 }
88
89 fn get_encoded_len(&self) -> usize {
90 char::from(SEPARATOR).len_utf8()
91 + url_safe_no_pad_len(&self.message)
92 + url_safe_no_pad_len(&self.signature)
93 }
94}
95
96fn url_safe_no_pad_len(input: &[u8]) -> usize {
97 let x = input.len() * 4;
98 (x / 3) + (((x % 3) > 0) as usize)
99}
100
101#[cfg(test)]
102pub mod tests {
103 use super::*;
104
105 pub fn get_test_public_key() -> String {
106 String::from("y9OTFvZmHe41kMjCYtDd8574bv46CSDKexUKN9R7mgM")
107 }
108
109 pub fn get_test_private_key() -> String {
110 String::from("aMWX1G0p36BRx7YqAJaBJ7hnMDxqIbln0toRQcWQfoA")
111 }
112
113 #[test]
114 fn serialization() {
115 let sm1 = SignedMessage {
116 message: "message".as_bytes().to_vec(),
117 signature: "signature".as_bytes().to_vec(),
118 };
119 let sm2 = SignedMessage::decode(&sm1.encode()).unwrap();
120 assert_eq!(sm1.message, sm2.message);
121 assert_eq!(sm1.signature, sm2.signature);
122 }
123
124 #[test]
125 fn create_should_return_predicable_result() {
126 let message = "message".as_bytes().to_vec();
127 let key = PrivateKey::from_base64(&get_test_private_key()).unwrap();
128 let sm = SignedMessage::create(message, &key);
129 assert_eq!(sm.encode(), String::from("bWVzc2FnZQ.gH3fe9YO9tEv7f8adiZ2w7F6-7doNp3yyaDrfWuQNCuJi6bwF2jqm7v4p-wANdOahO1wvULOH96JJDnQlUoEDw"));
130 }
131
132 #[test]
133 fn should_verify_previous_encoded() {
134 let sm = SignedMessage::decode("bWVzc2FnZQ.gH3fe9YO9tEv7f8adiZ2w7F6-7doNp3yyaDrfWuQNCuJi6bwF2jqm7v4p-wANdOahO1wvULOH96JJDnQlUoEDw")
135 .expect("valid encoded");
136 let public_key = PublicKey::from_base64(&get_test_public_key()).unwrap();
137 assert!(sm.verify(&public_key));
138 }
139}