|
4 | 4 |
|
5 | 5 | #![allow(clippy::unwrap_used, clippy::arithmetic_side_effects)] |
6 | 6 |
|
7 | | -use std::str::FromStr; |
8 | | - |
9 | | -use der::asn1::BitString; |
10 | | -use ed25519_dalek::ed25519::signature::Signer; |
11 | | -use ed25519_dalek::{Signature as Ed25519DalekSignature, SigningKey, VerifyingKey}; |
12 | | -use httptest::matchers::request; |
13 | | -use httptest::responders::json_encoded; |
14 | | -use httptest::{Expectation, Server}; |
15 | | -use polyproto::api::HttpClient; |
16 | | -use polyproto::api::core::current_unix_time; |
17 | | -use polyproto::certs::PublicKeyInfo; |
18 | | -use polyproto::errors::CertificateConversionError; |
19 | | -use polyproto::key::{PrivateKey, PublicKey}; |
20 | | -use polyproto::signature::Signature; |
21 | | -use polyproto::types::routes::core::v1::GET_CHALLENGE_STRING; |
22 | | -use serde_json::json; |
23 | | -use spki::{AlgorithmIdentifierOwned, ObjectIdentifier, SignatureBitStringEncoding}; |
24 | | -use url::Url; |
25 | | - |
26 | | -async fn setup_example() -> Server { |
27 | | - let server = Server::run(); |
28 | | - server.expect( |
29 | | - Expectation::matching(request::method_path( |
30 | | - GET_CHALLENGE_STRING.method.as_str(), |
31 | | - GET_CHALLENGE_STRING.path, |
32 | | - )) |
33 | | - .respond_with(json_encoded(json!({ |
34 | | - "challenge": "abcd".repeat(8), |
35 | | - "expires": current_unix_time() + 100 |
36 | | - }))), |
37 | | - ); |
38 | | - server |
39 | | -} |
40 | | - |
41 | | -#[cfg(not(test))] |
42 | | -#[tokio::main] |
43 | | -async fn main() { |
44 | | - let server = setup_example().await; |
45 | | - let url = format!("http://{}", server.addr()); |
46 | | - |
47 | | - // The actual example starts here. |
48 | | - // Create a new HTTP client |
49 | | - let client = HttpClient::new().unwrap(); |
50 | | - // Create an authorized session, if you need it |
51 | | - let _session: polyproto::api::Session<Ed25519Signature, Ed25519PrivateKey> = |
52 | | - polyproto::api::Session::new(&client, "12345", Url::parse(&url).unwrap(), None); |
53 | | - // You can now use the client and session to make requests to the polyproto home server! |
54 | | - // The client is responsible for all unauthenticated requests, while sessions handle all |
55 | | - // the routes needing authentication of some sort. |
56 | | - // Routes are documented under <https://docs.polyphony.chat/APIs/core/>, and each route has a |
57 | | - // corresponding method in the `HttpClient` struct. For example, if we wanted to get the certificate |
58 | | - // of the home server, we'd call: |
59 | | - let cert = client |
60 | | - .get_server_id_cert(None, &Url::parse("https://example.com/").unwrap()) |
61 | | - .await |
62 | | - .unwrap(); |
63 | | - dbg!(cert); |
64 | | -} |
65 | | - |
66 | | -#[cfg(test)] |
67 | | -fn main() { |
68 | | - main() |
69 | | -} |
| 7 | +#[cfg(feature = "reqwest")] |
| 8 | +mod reqwest_example { |
| 9 | + use std::str::FromStr; |
| 10 | + |
| 11 | + use der::asn1::BitString; |
| 12 | + use ed25519_dalek::ed25519::signature::Signer; |
| 13 | + use ed25519_dalek::{Signature as Ed25519DalekSignature, SigningKey, VerifyingKey}; |
| 14 | + use httptest::matchers::request; |
| 15 | + use httptest::responders::json_encoded; |
| 16 | + use httptest::{Expectation, Server}; |
| 17 | + use polyproto::api::HttpClient; |
| 18 | + use polyproto::api::core::current_unix_time; |
| 19 | + use polyproto::certs::PublicKeyInfo; |
| 20 | + use polyproto::errors::CertificateConversionError; |
| 21 | + use polyproto::key::{PrivateKey, PublicKey}; |
| 22 | + use polyproto::signature::Signature; |
| 23 | + use polyproto::types::routes::core::v1::GET_CHALLENGE_STRING; |
| 24 | + use serde_json::json; |
| 25 | + use spki::{AlgorithmIdentifierOwned, ObjectIdentifier, SignatureBitStringEncoding}; |
| 26 | + use url::Url; |
| 27 | + |
| 28 | + pub async fn setup_example() -> Server { |
| 29 | + let server = Server::run(); |
| 30 | + server.expect( |
| 31 | + Expectation::matching(request::method_path( |
| 32 | + GET_CHALLENGE_STRING.method.as_str(), |
| 33 | + GET_CHALLENGE_STRING.path, |
| 34 | + )) |
| 35 | + .respond_with(json_encoded(json!({ |
| 36 | + "challenge": "abcd".repeat(8), |
| 37 | + "expires": current_unix_time() + 100 |
| 38 | + }))), |
| 39 | + ); |
| 40 | + server |
| 41 | + } |
70 | 42 |
|
71 | | -#[derive(Debug, PartialEq, Eq, Clone)] |
72 | | -pub(crate) struct Ed25519Signature { |
73 | | - pub(crate) signature: Ed25519DalekSignature, |
74 | | - pub(crate) algorithm: AlgorithmIdentifierOwned, |
75 | | -} |
| 43 | + #[cfg(not(test))] |
| 44 | + pub async fn run_example() { |
| 45 | + let server = setup_example().await; |
| 46 | + let url = format!("http://{}", server.addr()); |
| 47 | + |
| 48 | + // The actual example starts here. |
| 49 | + // Create a new HTTP client |
| 50 | + let client = HttpClient::new().unwrap(); |
| 51 | + // Create an authorized session, if you need it |
| 52 | + let _session: polyproto::api::Session<Ed25519Signature, Ed25519PrivateKey> = |
| 53 | + polyproto::api::Session::new(&client, "12345", Url::parse(&url).unwrap(), None); |
| 54 | + // You can now use the client and session to make requests to the polyproto home server! |
| 55 | + // The client is responsible for all unauthenticated requests, while sessions handle all |
| 56 | + // the routes needing authentication of some sort. |
| 57 | + // Routes are documented under <https://docs.polyphony.chat/APIs/core/>, and each route has a |
| 58 | + // corresponding method in the `HttpClient` struct. For example, if we wanted to get the certificate |
| 59 | + // of the home server, we'd call: |
| 60 | + let cert = client |
| 61 | + .get_server_id_cert(None, &Url::parse("https://example.com/").unwrap()) |
| 62 | + .await |
| 63 | + .unwrap(); |
| 64 | + dbg!(cert); |
| 65 | + } |
76 | 66 |
|
77 | | -impl std::fmt::Display for Ed25519Signature { |
78 | | - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
79 | | - write!(f, "{:?}", self.signature) |
| 67 | + #[cfg(test)] |
| 68 | + pub fn test_main() { |
| 69 | + tokio::runtime::Runtime::new() |
| 70 | + .unwrap() |
| 71 | + .block_on(run_example()) |
80 | 72 | } |
81 | | -} |
82 | 73 |
|
83 | | -// We implement the Signature trait for our signature type. |
84 | | -impl Signature for Ed25519Signature { |
85 | | - // We define the signature type from the ed25519-dalek crate as the associated type. |
86 | | - type Signature = Ed25519DalekSignature; |
| 74 | + #[tokio::main] |
| 75 | + pub async fn main() { |
| 76 | + run_example().await |
| 77 | + } |
87 | 78 |
|
88 | | - // This is straightforward: we return a reference to the signature. |
89 | | - fn as_signature(&self) -> &Self::Signature { |
90 | | - &self.signature |
| 79 | + #[derive(Debug, PartialEq, Eq, Clone)] |
| 80 | + pub(crate) struct Ed25519Signature { |
| 81 | + pub(crate) signature: Ed25519DalekSignature, |
| 82 | + pub(crate) algorithm: AlgorithmIdentifierOwned, |
91 | 83 | } |
92 | 84 |
|
93 | | - // The algorithm identifier for a given signature implementation is constant. We just need |
94 | | - // to define it here. |
95 | | - fn algorithm_identifier() -> AlgorithmIdentifierOwned { |
96 | | - AlgorithmIdentifierOwned { |
97 | | - // This is the OID for Ed25519. It is defined in the IANA registry. |
98 | | - oid: ObjectIdentifier::from_str("1.3.101.112").unwrap(), |
99 | | - // For this example, we don't need or want any parameters. |
100 | | - parameters: None, |
| 85 | + impl std::fmt::Display for Ed25519Signature { |
| 86 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 87 | + write!(f, "{:?}", self.signature) |
101 | 88 | } |
102 | 89 | } |
103 | 90 |
|
104 | | - fn from_bytes(signature: &[u8]) -> Self { |
105 | | - let mut signature_vec = signature.to_vec(); |
106 | | - signature_vec.resize(64, 0); |
107 | | - let signature_array: [u8; 64] = { |
108 | | - let mut array = [0; 64]; |
109 | | - array.copy_from_slice(&signature_vec[..]); |
110 | | - array |
111 | | - }; |
112 | | - Self { |
113 | | - signature: Ed25519DalekSignature::from_bytes(&signature_array), |
114 | | - algorithm: Self::algorithm_identifier(), |
| 91 | + // We implement the Signature trait for our signature type. |
| 92 | + impl Signature for Ed25519Signature { |
| 93 | + // We define the signature type from the ed25519-dalek crate as the associated type. |
| 94 | + type Signature = Ed25519DalekSignature; |
| 95 | + |
| 96 | + // This is straightforward: we return a reference to the signature. |
| 97 | + fn as_signature(&self) -> &Self::Signature { |
| 98 | + &self.signature |
| 99 | + } |
| 100 | + |
| 101 | + // The algorithm identifier for a given signature implementation is constant. We just need |
| 102 | + // to define it here. |
| 103 | + fn algorithm_identifier() -> AlgorithmIdentifierOwned { |
| 104 | + AlgorithmIdentifierOwned { |
| 105 | + // This is the OID for Ed25519. It is defined in the IANA registry. |
| 106 | + oid: ObjectIdentifier::from_str("1.3.101.112").unwrap(), |
| 107 | + // For this example, we don't need or want any parameters. |
| 108 | + parameters: None, |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + fn from_bytes(signature: &[u8]) -> Self { |
| 113 | + let mut signature_vec = signature.to_vec(); |
| 114 | + signature_vec.resize(64, 0); |
| 115 | + let signature_array: [u8; 64] = { |
| 116 | + let mut array = [0; 64]; |
| 117 | + array.copy_from_slice(&signature_vec[..]); |
| 118 | + array |
| 119 | + }; |
| 120 | + Self { |
| 121 | + signature: Ed25519DalekSignature::from_bytes(&signature_array), |
| 122 | + algorithm: Self::algorithm_identifier(), |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + fn as_bytes(&self) -> Vec<u8> { |
| 127 | + self.as_signature().to_vec() |
115 | 128 | } |
116 | 129 | } |
117 | 130 |
|
118 | | - fn as_bytes(&self) -> Vec<u8> { |
119 | | - self.as_signature().to_vec() |
| 131 | + // The `SignatureBitStringEncoding` trait is used to convert a signature to a bit string. We implement |
| 132 | + // it for our signature type. |
| 133 | + impl SignatureBitStringEncoding for Ed25519Signature { |
| 134 | + fn to_bitstring(&self) -> der::Result<der::asn1::BitString> { |
| 135 | + BitString::from_bytes(&self.as_signature().to_bytes()) |
| 136 | + } |
120 | 137 | } |
121 | | -} |
122 | 138 |
|
123 | | -// The `SignatureBitStringEncoding` trait is used to convert a signature to a bit string. We implement |
124 | | -// it for our signature type. |
125 | | -impl SignatureBitStringEncoding for Ed25519Signature { |
126 | | - fn to_bitstring(&self) -> der::Result<der::asn1::BitString> { |
127 | | - BitString::from_bytes(&self.as_signature().to_bytes()) |
| 139 | + // Next, we implement the key traits. We start by defining the private key type. |
| 140 | + #[derive(Debug, Clone, PartialEq, Eq)] |
| 141 | + pub(crate) struct Ed25519PrivateKey { |
| 142 | + // Defined below |
| 143 | + pub(crate) public_key: Ed25519PublicKey, |
| 144 | + // The private key from the ed25519-dalek crate |
| 145 | + pub(crate) key: SigningKey, |
128 | 146 | } |
129 | | -} |
130 | 147 |
|
131 | | -// Next, we implement the key traits. We start by defining the private key type. |
132 | | -#[derive(Debug, Clone, PartialEq, Eq)] |
133 | | -pub(crate) struct Ed25519PrivateKey { |
134 | | - // Defined below |
135 | | - pub(crate) public_key: Ed25519PublicKey, |
136 | | - // The private key from the ed25519-dalek crate |
137 | | - pub(crate) key: SigningKey, |
138 | | -} |
| 148 | + impl PrivateKey<Ed25519Signature> for Ed25519PrivateKey { |
| 149 | + type PublicKey = Ed25519PublicKey; |
139 | 150 |
|
140 | | -impl PrivateKey<Ed25519Signature> for Ed25519PrivateKey { |
141 | | - type PublicKey = Ed25519PublicKey; |
| 151 | + // Return a reference to the public key |
| 152 | + fn pubkey(&self) -> &Self::PublicKey { |
| 153 | + &self.public_key |
| 154 | + } |
142 | 155 |
|
143 | | - // Return a reference to the public key |
144 | | - fn pubkey(&self) -> &Self::PublicKey { |
145 | | - &self.public_key |
| 156 | + // Signs a message. The beauty of having to wrap the ed25519-dalek crate is that we can |
| 157 | + // harness all of its functionality, such as the `sign` method. |
| 158 | + fn sign(&self, data: &[u8]) -> Ed25519Signature { |
| 159 | + let signature = self.key.sign(data); |
| 160 | + Ed25519Signature { |
| 161 | + signature, |
| 162 | + algorithm: self.algorithm_identifier(), |
| 163 | + } |
| 164 | + } |
146 | 165 | } |
147 | 166 |
|
148 | | - // Signs a message. The beauty of having to wrap the ed25519-dalek crate is that we can |
149 | | - // harness all of its functionality, such as the `sign` method. |
150 | | - fn sign(&self, data: &[u8]) -> Ed25519Signature { |
151 | | - let signature = self.key.sign(data); |
152 | | - Ed25519Signature { |
153 | | - signature, |
154 | | - algorithm: self.algorithm_identifier(), |
155 | | - } |
| 167 | + // Same thing as above for the public key type. |
| 168 | + #[derive(Debug, Clone, PartialEq, Eq)] |
| 169 | + pub(crate) struct Ed25519PublicKey { |
| 170 | + // The public key type from the ed25519-dalek crate |
| 171 | + pub(crate) key: VerifyingKey, |
156 | 172 | } |
157 | | -} |
158 | 173 |
|
159 | | -// Same thing as above for the public key type. |
160 | | -#[derive(Debug, Clone, PartialEq, Eq)] |
161 | | -pub(crate) struct Ed25519PublicKey { |
162 | | - // The public key type from the ed25519-dalek crate |
163 | | - pub(crate) key: VerifyingKey, |
164 | | -} |
| 174 | + impl PublicKey<Ed25519Signature> for Ed25519PublicKey { |
| 175 | + // Verifies a signature. We use the `verify_strict` method from the ed25519-dalek crate. |
| 176 | + // This method is used to mitigate weak key forgery. |
| 177 | + fn verify_signature( |
| 178 | + &self, |
| 179 | + signature: &Ed25519Signature, |
| 180 | + data: &[u8], |
| 181 | + ) -> Result<(), polyproto::errors::composite::PublicKeyError> { |
| 182 | + match self.key.verify_strict(data, signature.as_signature()) { |
| 183 | + Ok(_) => Ok(()), |
| 184 | + Err(_) => Err(polyproto::errors::composite::PublicKeyError::BadSignature), |
| 185 | + } |
| 186 | + } |
165 | 187 |
|
166 | | -impl PublicKey<Ed25519Signature> for Ed25519PublicKey { |
167 | | - // Verifies a signature. We use the `verify_strict` method from the ed25519-dalek crate. |
168 | | - // This method is used to mitigate weak key forgery. |
169 | | - fn verify_signature( |
170 | | - &self, |
171 | | - signature: &Ed25519Signature, |
172 | | - data: &[u8], |
173 | | - ) -> Result<(), polyproto::errors::composite::PublicKeyError> { |
174 | | - match self.key.verify_strict(data, signature.as_signature()) { |
175 | | - Ok(_) => Ok(()), |
176 | | - Err(_) => Err(polyproto::errors::composite::PublicKeyError::BadSignature), |
| 188 | + // Returns the public key info. Public key info is used to encode the public key in a |
| 189 | + // certificate or a CSR. It is named after the `SubjectPublicKeyInfo` type from the X.509 |
| 190 | + // standard, and thus includes the information needed to encode the public key in a certificate |
| 191 | + // or a CSR. |
| 192 | + fn public_key_info(&self) -> PublicKeyInfo { |
| 193 | + PublicKeyInfo { |
| 194 | + algorithm: Ed25519Signature::algorithm_identifier(), |
| 195 | + public_key_bitstring: BitString::from_bytes(&self.key.to_bytes()).unwrap(), |
| 196 | + } |
177 | 197 | } |
178 | | - } |
179 | 198 |
|
180 | | - // Returns the public key info. Public key info is used to encode the public key in a |
181 | | - // certificate or a CSR. It is named after the `SubjectPublicKeyInfo` type from the X.509 |
182 | | - // standard, and thus includes the information needed to encode the public key in a certificate |
183 | | - // or a CSR. |
184 | | - fn public_key_info(&self) -> PublicKeyInfo { |
185 | | - PublicKeyInfo { |
186 | | - algorithm: Ed25519Signature::algorithm_identifier(), |
187 | | - public_key_bitstring: BitString::from_bytes(&self.key.to_bytes()).unwrap(), |
| 199 | + fn try_from_public_key_info( |
| 200 | + public_key_info: PublicKeyInfo, |
| 201 | + ) -> Result<Self, CertificateConversionError> { |
| 202 | + let mut key_vec = public_key_info.public_key_bitstring.raw_bytes().to_vec(); |
| 203 | + key_vec.resize(32, 0); |
| 204 | + let signature_array: [u8; 32] = { |
| 205 | + let mut array = [0; 32]; |
| 206 | + array.copy_from_slice(&key_vec[..]); |
| 207 | + array |
| 208 | + }; |
| 209 | + Ok(Self { |
| 210 | + key: VerifyingKey::from_bytes(&signature_array).unwrap(), |
| 211 | + }) |
188 | 212 | } |
189 | 213 | } |
| 214 | +} |
190 | 215 |
|
191 | | - fn try_from_public_key_info( |
192 | | - public_key_info: PublicKeyInfo, |
193 | | - ) -> Result<Self, CertificateConversionError> { |
194 | | - let mut key_vec = public_key_info.public_key_bitstring.raw_bytes().to_vec(); |
195 | | - key_vec.resize(32, 0); |
196 | | - let signature_array: [u8; 32] = { |
197 | | - let mut array = [0; 32]; |
198 | | - array.copy_from_slice(&key_vec[..]); |
199 | | - array |
200 | | - }; |
201 | | - Ok(Self { |
202 | | - key: VerifyingKey::from_bytes(&signature_array).unwrap(), |
203 | | - }) |
204 | | - } |
| 216 | +#[cfg(feature = "reqwest")] |
| 217 | +#[tokio::main] |
| 218 | +async fn main() { |
| 219 | + reqwest_example::run_example().await |
| 220 | +} |
| 221 | + |
| 222 | +#[cfg(not(feature = "reqwest"))] |
| 223 | +fn main() { |
| 224 | + eprintln!("This example requires you to compile with the 'reqwest' feature enabled.") |
205 | 225 | } |
0 commit comments