fxa_client/internal/commands/
keys.rs
1use serde::{de::DeserializeOwned, Deserialize, Serialize};
8
9use super::super::device::Device;
10use super::super::scopes;
11use crate::{Error, Result, ScopedKey};
12use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
13use rc_crypto::ece::{self, EcKeyComponents};
14use sync15::{EncryptedPayload, KeyBundle};
15
16#[derive(Serialize, Deserialize, Clone)]
17pub(crate) enum VersionedPrivateCommandKeys {
18 V1(PrivateCommandKeysV1),
19}
20
21#[derive(Serialize, Deserialize, Clone)]
22pub(crate) struct PrivateCommandKeysV1 {
23 p256key: EcKeyComponents,
24 auth_secret: Vec<u8>,
25}
26pub(crate) type PrivateCommandKeys = PrivateCommandKeysV1;
27
28impl PrivateCommandKeys {
29 pub(crate) fn serialize(&self) -> Result<String> {
34 Ok(serde_json::to_string(&VersionedPrivateCommandKeys::V1(
35 self.clone(),
36 ))?)
37 }
38
39 pub(crate) fn deserialize(s: &str) -> Result<Self> {
40 let versionned: VersionedPrivateCommandKeys = serde_json::from_str(s)?;
41 match versionned {
42 VersionedPrivateCommandKeys::V1(prv_key) => Ok(prv_key),
43 }
44 }
45}
46
47impl PrivateCommandKeys {
48 pub fn from_random() -> Result<Self> {
49 rc_crypto::ensure_initialized();
50 let (key_pair, auth_secret) = ece::generate_keypair_and_auth_secret()?;
51 Ok(Self {
52 p256key: key_pair.raw_components()?,
53 auth_secret: auth_secret.to_vec(),
54 })
55 }
56
57 pub fn p256key(&self) -> &EcKeyComponents {
58 &self.p256key
59 }
60
61 pub fn auth_secret(&self) -> &[u8] {
62 &self.auth_secret
63 }
64}
65
66#[derive(Serialize, Deserialize)]
67struct CommandKeysPayload {
68 kid: String,
70 #[serde(rename = "IV")]
72 iv: String,
73 hmac: String,
75 ciphertext: String,
77}
78
79impl CommandKeysPayload {
80 fn decrypt(self, scoped_key: &ScopedKey) -> Result<PublicCommandKeys> {
81 let (ksync, kxcs) = extract_oldsync_key_components(scoped_key)?;
82 if hex::decode(self.kid)? != kxcs {
83 return Err(Error::MismatchedKeys);
84 }
85 let key = KeyBundle::from_ksync_bytes(&ksync)?;
86 let encrypted_payload = EncryptedPayload {
87 iv: self.iv,
88 hmac: self.hmac,
89 ciphertext: self.ciphertext,
90 };
91 Ok(encrypted_payload.decrypt_into(&key)?)
92 }
93}
94
95#[derive(Serialize, Deserialize)]
96pub struct PublicCommandKeys {
97 #[serde(rename = "publicKey")]
99 public_key: String,
100 #[serde(rename = "authSecret")]
102 auth_secret: String,
103}
104
105impl PublicCommandKeys {
106 fn encrypt(&self, scoped_key: &ScopedKey) -> Result<CommandKeysPayload> {
107 let (ksync, kxcs) = extract_oldsync_key_components(scoped_key)?;
108 let key = KeyBundle::from_ksync_bytes(&ksync)?;
109 let encrypted_payload = EncryptedPayload::from_cleartext_payload(&key, &self)?;
110 Ok(CommandKeysPayload {
111 kid: hex::encode(kxcs),
112 iv: encrypted_payload.iv,
113 hmac: encrypted_payload.hmac,
114 ciphertext: encrypted_payload.ciphertext,
115 })
116 }
117 pub fn as_command_data(&self, scoped_key: &ScopedKey) -> Result<String> {
118 let encrypted_public_keys = self.encrypt(scoped_key)?;
119 Ok(serde_json::to_string(&encrypted_public_keys)?)
120 }
121 pub(crate) fn public_key(&self) -> &str {
122 &self.public_key
123 }
124 pub(crate) fn auth_secret(&self) -> &str {
125 &self.auth_secret
126 }
127}
128
129impl From<PrivateCommandKeys> for PublicCommandKeys {
130 fn from(internal: PrivateCommandKeys) -> Self {
131 Self {
132 public_key: URL_SAFE_NO_PAD.encode(internal.p256key.public_key()),
133 auth_secret: URL_SAFE_NO_PAD.encode(&internal.auth_secret),
134 }
135 }
136}
137
138fn extract_oldsync_key_components(oldsync_key: &ScopedKey) -> Result<(Vec<u8>, Vec<u8>)> {
139 if oldsync_key.scope != scopes::OLD_SYNC {
140 return Err(Error::IllegalState(
141 "Only oldsync scoped keys are supported at the moment.",
142 ));
143 }
144 let kxcs: &str = oldsync_key.kid.splitn(2, '-').collect::<Vec<_>>()[1];
145 let kxcs = URL_SAFE_NO_PAD.decode(kxcs)?;
146 let ksync = oldsync_key.key_bytes()?;
147 Ok((ksync, kxcs))
148}
149
150#[derive(Debug, Serialize, Deserialize)]
151struct EncryptedCommandPayload {
152 encrypted: String,
154}
155
156impl EncryptedCommandPayload {
157 pub(crate) fn decrypt<T: DeserializeOwned>(self, keys: &PrivateCommandKeys) -> Result<T> {
158 rc_crypto::ensure_initialized();
159 let encrypted = URL_SAFE_NO_PAD.decode(self.encrypted)?;
160 let decrypted = ece::decrypt(keys.p256key(), keys.auth_secret(), &encrypted)?;
161 Ok(serde_json::from_slice(&decrypted)?)
162 }
163}
164
165fn encrypt_payload<T: Serialize>(
166 payload: &T,
167 keys: PublicCommandKeys,
168) -> Result<EncryptedCommandPayload> {
169 rc_crypto::ensure_initialized();
170 let bytes = serde_json::to_vec(payload)?;
171 let public_key = URL_SAFE_NO_PAD.decode(keys.public_key())?;
172 let auth_secret = URL_SAFE_NO_PAD.decode(keys.auth_secret())?;
173 let encrypted = ece::encrypt(&public_key, &auth_secret, &bytes)?;
174 let encrypted = URL_SAFE_NO_PAD.encode(encrypted);
175 Ok(EncryptedCommandPayload { encrypted })
176}
177
178pub(crate) fn encrypt_command<T: Serialize>(
180 scoped_key: &ScopedKey,
181 target: &Device,
182 command: &'static str,
183 payload: &T,
184) -> Result<serde_json::Value> {
185 let public_keys = get_public_keys(scoped_key, target, command)?;
186 let encrypted_payload = encrypt_payload(payload, public_keys)?;
187 Ok(serde_json::to_value(encrypted_payload)?)
188}
189
190pub(crate) fn get_public_keys(
192 scoped_key: &ScopedKey,
193 target: &Device,
194 command: &'static str,
195) -> Result<PublicCommandKeys> {
196 let command = target
197 .available_commands
198 .get(command)
199 .ok_or(Error::UnsupportedCommand(command))?;
200 let bundle: CommandKeysPayload = serde_json::from_str(command)?;
201 bundle.decrypt(scoped_key)
202}
203
204pub(crate) fn decrypt_command<T: DeserializeOwned>(
206 v: serde_json::Value,
207 keys: &PrivateCommandKeys,
208) -> Result<T> {
209 let encrypted_payload: EncryptedCommandPayload = serde_json::from_value(v)?;
210 encrypted_payload.decrypt(keys)
211}