isolation.rs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use std::array::TryFromSliceError;
  5. use std::borrow::Cow;
  6. use std::fmt::{Debug, Formatter};
  7. use std::string::FromUtf8Error;
  8. use aes_gcm::aead::Aead;
  9. use aes_gcm::{aead::NewAead, Aes256Gcm, Nonce};
  10. use once_cell::sync::OnceCell;
  11. use serialize_to_javascript::{default_template, Template};
  12. #[cfg(not(feature = "isolation"))]
  13. mod ring_impl {
  14. #[cfg(not(feature = "__isolation-docs"))]
  15. compile_error!(
  16. "Isolation random number generator was used without enabling the `isolation` feature."
  17. );
  18. pub struct Unspecified;
  19. pub struct SystemRandom;
  20. impl SystemRandom {
  21. pub fn new() -> Self {
  22. unimplemented!()
  23. }
  24. }
  25. pub struct Random;
  26. impl Random {
  27. pub fn expose(self) -> [u8; 32] {
  28. unimplemented!()
  29. }
  30. }
  31. pub fn rand_generate(_rng: &SystemRandom) -> Result<Random, super::Error> {
  32. unimplemented!()
  33. }
  34. }
  35. #[cfg(feature = "isolation")]
  36. mod ring_impl {
  37. pub use ring::error::Unspecified;
  38. pub use ring::rand::generate as rand_generate;
  39. pub use ring::rand::SystemRandom;
  40. }
  41. use ring_impl::*;
  42. /// Cryptographically secure pseudo-random number generator.
  43. static RNG: OnceCell<SystemRandom> = OnceCell::new();
  44. /// The style for the isolation iframe.
  45. pub const IFRAME_STYLE: &str = "#__tauri_isolation__ { display: none !important }";
  46. /// Errors that can occur during Isolation keys generation.
  47. #[derive(Debug, thiserror::Error)]
  48. #[non_exhaustive]
  49. pub enum Error {
  50. /// Something went wrong with the CSPRNG.
  51. #[error("Unspecified CSPRNG error")]
  52. Csprng,
  53. /// Something went wrong with decryping an AES-GCM payload
  54. #[error("AES-GCM")]
  55. Aes,
  56. /// Nonce was not 96 bits
  57. #[error("Nonce: {0}")]
  58. NonceSize(#[from] TryFromSliceError),
  59. /// Payload was not valid utf8
  60. #[error("{0}")]
  61. Utf8(#[from] FromUtf8Error),
  62. /// Invalid json format
  63. #[error("{0}")]
  64. Json(#[from] serde_json::Error),
  65. }
  66. impl From<Unspecified> for Error {
  67. fn from(_: Unspecified) -> Self {
  68. Self::Csprng
  69. }
  70. }
  71. /// A formatted AES-GCM cipher instance along with the key used to initialize it.
  72. #[derive(Clone)]
  73. pub struct AesGcmPair {
  74. raw: [u8; 32],
  75. key: Aes256Gcm,
  76. }
  77. impl Debug for AesGcmPair {
  78. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  79. write!(f, "AesGcmPair(...)")
  80. }
  81. }
  82. impl AesGcmPair {
  83. fn new() -> Result<Self, Error> {
  84. let rng = RNG.get_or_init(SystemRandom::new);
  85. let raw: [u8; 32] = ring_impl::rand_generate(rng)?.expose();
  86. let key = aes_gcm::Key::from_slice(&raw);
  87. Ok(Self {
  88. raw,
  89. key: Aes256Gcm::new(key),
  90. })
  91. }
  92. /// The raw value used to create the AES-GCM key
  93. pub fn raw(&self) -> &[u8; 32] {
  94. &self.raw
  95. }
  96. /// The formatted AES-GCM key
  97. pub fn key(&self) -> &Aes256Gcm {
  98. &self.key
  99. }
  100. }
  101. /// All cryptographic keys required for Isolation encryption
  102. #[derive(Debug, Clone)]
  103. pub struct Keys {
  104. /// AES-GCM key
  105. aes_gcm: AesGcmPair,
  106. }
  107. impl Keys {
  108. /// Securely generate required keys for Isolation encryption.
  109. pub fn new() -> Result<Self, Error> {
  110. AesGcmPair::new()
  111. .map(|aes_gcm| Self { aes_gcm })
  112. .map_err(Into::into)
  113. }
  114. /// The AES-GCM data (and raw data).
  115. pub fn aes_gcm(&self) -> &AesGcmPair {
  116. &self.aes_gcm
  117. }
  118. /// Decrypts a message using the generated keys.
  119. pub fn decrypt(&self, raw: RawIsolationPayload<'_>) -> Result<String, Error> {
  120. let RawIsolationPayload { nonce, payload } = raw;
  121. let nonce: [u8; 12] = nonce.as_ref().try_into()?;
  122. let bytes = self
  123. .aes_gcm
  124. .key
  125. .decrypt(Nonce::from_slice(&nonce), payload.as_ref())
  126. .map_err(|_| self::Error::Aes)?;
  127. String::from_utf8(bytes).map_err(Into::into)
  128. }
  129. }
  130. /// Raw representation of
  131. #[derive(Debug, serde::Deserialize)]
  132. pub struct RawIsolationPayload<'a> {
  133. nonce: Cow<'a, [u8]>,
  134. payload: Cow<'a, [u8]>,
  135. }
  136. impl<'a> TryFrom<&'a str> for RawIsolationPayload<'a> {
  137. type Error = Error;
  138. fn try_from(value: &'a str) -> Result<Self, Self::Error> {
  139. serde_json::from_str(value).map_err(Into::into)
  140. }
  141. }
  142. /// The Isolation JavaScript template meant to be injected during codegen.
  143. ///
  144. /// Note: This struct is not considered part of the stable API
  145. #[derive(Template)]
  146. #[default_template("isolation.js")]
  147. pub struct IsolationJavascriptCodegen {
  148. // this template intentionally does not include the runtime field
  149. }
  150. /// The Isolation JavaScript template meant to be injected during runtime.
  151. ///
  152. /// Note: This struct is not considered part of the stable API
  153. #[derive(Template)]
  154. #[default_template("isolation.js")]
  155. pub struct IsolationJavascriptRuntime<'a> {
  156. /// The key used on the Rust backend and the Isolation Javascript
  157. pub runtime_aes_gcm_key: &'a [u8; 32],
  158. }
  159. #[cfg(test)]
  160. mod test {
  161. #[test]
  162. fn create_keys() -> Result<(), Box<dyn std::error::Error>> {
  163. let _ = super::Keys::new()?;
  164. Ok(())
  165. }
  166. }