mirror of https://github.com/rwf2/Rocket.git
Preserve 'secret_key' in 'Config' provider data.
Also fixes emission of 'secret_key' warnings when 'secrets' feature is disabled. Resolves #1505. Fixes #1510.
This commit is contained in:
parent
92af8fca72
commit
28976a5bd3
|
@ -24,6 +24,10 @@ mod key {
|
||||||
pub fn derive_from(_: &[u8]) -> Self { Key }
|
pub fn derive_from(_: &[u8]) -> Self { Key }
|
||||||
pub fn generate() -> Self { Key }
|
pub fn generate() -> Self { Key }
|
||||||
pub fn try_generate() -> Option<Self> { Some(Key) }
|
pub fn try_generate() -> Option<Self> { Some(Key) }
|
||||||
|
pub fn master(&self) -> &[u8] {
|
||||||
|
static ZERO: &'static [u8; 64] = &[0; 64];
|
||||||
|
&ZERO[..]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Key {
|
impl PartialEq for Key {
|
||||||
|
|
|
@ -223,18 +223,17 @@ impl Config {
|
||||||
panic!("aborting due to configuration error(s)")
|
panic!("aborting due to configuration error(s)")
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(all(feature = "secrets", not(test), not(rocket_unsafe_secret_key)))] {
|
#[cfg(all(feature = "secrets", not(test), not(rocket_unsafe_secret_key)))]
|
||||||
if !config.secret_key.is_provided() {
|
if !config.secret_key.is_provided() {
|
||||||
if figment.profile() != Self::DEBUG_PROFILE {
|
if figment.profile() != Self::DEBUG_PROFILE {
|
||||||
crate::logger::try_init(LogLevel::Debug, true, false);
|
crate::logger::try_init(LogLevel::Debug, true, false);
|
||||||
error!("secrets enabled in non-`debug` without `secret_key`");
|
error!("secrets enabled in non-`debug` without `secret_key`");
|
||||||
info_!("disable `secrets` feature or configure a `secret_key`");
|
info_!("disable `secrets` feature or configure a `secret_key`");
|
||||||
panic!("aborting due to configuration error(s)")
|
panic!("aborting due to configuration error(s)")
|
||||||
}
|
|
||||||
|
|
||||||
// in debug, generate a key for a bit more security
|
|
||||||
config.secret_key = SecretKey::generate().unwrap_or(SecretKey::zero());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in debug, generate a key for a bit more security
|
||||||
|
config.secret_key = SecretKey::generate().unwrap_or(SecretKey::zero());
|
||||||
}
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
|
@ -284,6 +283,7 @@ impl Config {
|
||||||
false => launch_info_!("tls: {}", Paint::default("disabled").bold()),
|
false => launch_info_!("tls: {}", Paint::default("disabled").bold()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "secrets", not(test), not(rocket_unsafe_secret_key)))]
|
||||||
if !self.secret_key.is_provided() {
|
if !self.secret_key.is_provided() {
|
||||||
warn!("secrets enabled without a configured `secret_key`");
|
warn!("secrets enabled without a configured `secret_key`");
|
||||||
info_!("disable `secrets` feature or configure a `secret_key`");
|
info_!("disable `secrets` feature or configure a `secret_key`");
|
||||||
|
@ -313,7 +313,15 @@ impl Provider for Config {
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn data(&self) -> Result<Map<Profile, Dict>> {
|
fn data(&self) -> Result<Map<Profile, Dict>> {
|
||||||
Serialized::defaults(self).data()
|
let mut map: Map<Profile, Dict> = Serialized::defaults(self).data()?;
|
||||||
|
// We need to special-case `secret_key` since its serializer zeroes.
|
||||||
|
if !self.secret_key.is_zero() {
|
||||||
|
if let Some(map) = map.get_mut(&Profile::Default) {
|
||||||
|
map.insert("secret_key".into(), self.secret_key.master().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn profile(&self) -> Option<Profile> {
|
fn profile(&self) -> Option<Profile> {
|
||||||
|
|
|
@ -27,7 +27,10 @@ enum Kind {
|
||||||
/// let figment = Figment::from(rocket::Config::default())
|
/// let figment = Figment::from(rocket::Config::default())
|
||||||
/// .merge(("secret_key", "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="));
|
/// .merge(("secret_key", "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="));
|
||||||
///
|
///
|
||||||
|
/// # #[cfg(feature = "secrets")]
|
||||||
/// assert!(!rocket::Config::from(figment).secret_key.is_zero());
|
/// assert!(!rocket::Config::from(figment).secret_key.is_zero());
|
||||||
|
/// # #[cfg(not(feature = "secrets"))]
|
||||||
|
/// # assert!(rocket::Config::from(figment).secret_key.is_zero());
|
||||||
///
|
///
|
||||||
/// let figment = Figment::from(rocket::Config::default())
|
/// let figment = Figment::from(rocket::Config::default())
|
||||||
/// .merge(("secret_key", vec![0u8; 64]));
|
/// .merge(("secret_key", vec![0u8; 64]));
|
||||||
|
@ -42,16 +45,16 @@ enum Kind {
|
||||||
///
|
///
|
||||||
/// [private cookies]: https://rocket.rs/master/guide/requests/#private-cookies
|
/// [private cookies]: https://rocket.rs/master/guide/requests/#private-cookies
|
||||||
/// [configuration guide]: https://rocket.rs/master/guide/configuration/#secret-key
|
/// [configuration guide]: https://rocket.rs/master/guide/configuration/#secret-key
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SecretKey {
|
pub struct SecretKey {
|
||||||
key: Key,
|
key: Key,
|
||||||
kind: Kind,
|
provided: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecretKey {
|
impl SecretKey {
|
||||||
/// Returns a secret key that is all zeroes.
|
/// Returns a secret key that is all zeroes.
|
||||||
pub(crate) fn zero() -> SecretKey {
|
pub(crate) fn zero() -> SecretKey {
|
||||||
SecretKey { key: Key::from(&[0; 64]), kind: Kind::Zero }
|
SecretKey { key: Key::from(&[0; 64]), provided: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a `SecretKey` from a 512-bit `master` key. For security,
|
/// Creates a `SecretKey` from a 512-bit `master` key. For security,
|
||||||
|
@ -70,12 +73,7 @@ impl SecretKey {
|
||||||
/// let key = SecretKey::from(&master);
|
/// let key = SecretKey::from(&master);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from(master: &[u8]) -> SecretKey {
|
pub fn from(master: &[u8]) -> SecretKey {
|
||||||
let kind = match master.iter().all(|&b| b == 0) {
|
SecretKey { key: Key::from(master), provided: true }
|
||||||
true => Kind::Zero,
|
|
||||||
false => Kind::Provided
|
|
||||||
};
|
|
||||||
|
|
||||||
SecretKey { key: Key::from(master), kind }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derives a `SecretKey` from 256 bits of cryptographically random
|
/// Derives a `SecretKey` from 256 bits of cryptographically random
|
||||||
|
@ -94,7 +92,7 @@ impl SecretKey {
|
||||||
/// let key = SecretKey::derive_from(&material);
|
/// let key = SecretKey::derive_from(&material);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn derive_from(material: &[u8]) -> SecretKey {
|
pub fn derive_from(material: &[u8]) -> SecretKey {
|
||||||
SecretKey { key: Key::derive_from(material), kind: Kind::Provided }
|
SecretKey { key: Key::derive_from(material), provided: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to generate a `SecretKey` from randomness retrieved from the
|
/// Attempts to generate a `SecretKey` from randomness retrieved from the
|
||||||
|
@ -108,7 +106,7 @@ impl SecretKey {
|
||||||
/// let key = SecretKey::generate();
|
/// let key = SecretKey::generate();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn generate() -> Option<SecretKey> {
|
pub fn generate() -> Option<SecretKey> {
|
||||||
Some(SecretKey { key: Key::try_generate()?, kind: Kind::Generated })
|
Some(SecretKey { key: Key::try_generate()?, provided: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if `self` is the `0`-key.
|
/// Returns `true` if `self` is the `0`-key.
|
||||||
|
@ -123,7 +121,7 @@ impl SecretKey {
|
||||||
/// assert!(key.is_zero());
|
/// assert!(key.is_zero());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn is_zero(&self) -> bool {
|
pub fn is_zero(&self) -> bool {
|
||||||
self.kind == Kind::Zero
|
self == &Self::zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if `self` was not automatically generated and is not zero.
|
/// Returns `true` if `self` was not automatically generated and is not zero.
|
||||||
|
@ -142,7 +140,7 @@ impl SecretKey {
|
||||||
/// assert!(!key.is_provided());
|
/// assert!(!key.is_provided());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn is_provided(&self) -> bool {
|
pub fn is_provided(&self) -> bool {
|
||||||
self.kind == Kind::Provided
|
self.provided && !self.is_zero()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +153,13 @@ impl Deref for SecretKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for SecretKey {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
// `Key::partial_eq()` is a constant-time op.
|
||||||
|
self.key == other.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[crate::async_trait]
|
#[crate::async_trait]
|
||||||
impl<'a, 'r> FromRequest<'a, 'r> for &'a SecretKey {
|
impl<'a, 'r> FromRequest<'a, 'r> for &'a SecretKey {
|
||||||
type Error = std::convert::Infallible;
|
type Error = std::convert::Infallible;
|
||||||
|
@ -228,10 +233,13 @@ impl<'de> Deserialize<'de> for SecretKey {
|
||||||
|
|
||||||
impl fmt::Debug for SecretKey {
|
impl fmt::Debug for SecretKey {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self.kind {
|
if self.is_zero() {
|
||||||
Kind::Zero => f.write_str("[zero]"),
|
f.write_str("[zero]")
|
||||||
Kind::Generated => f.write_str("[generated]"),
|
} else {
|
||||||
Kind::Provided => f.write_str("[provided]"),
|
match self.provided {
|
||||||
|
true => f.write_str("[provided]"),
|
||||||
|
false => f.write_str("[generated]"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
use rocket::figment::Figment;
|
||||||
|
use rocket::config::{Config, SecretKey};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn secret_key_in_config_not_zero() {
|
||||||
|
let original_key = SecretKey::generate().expect("get key");
|
||||||
|
|
||||||
|
let config = Config { secret_key: original_key.clone(), ..Default::default() };
|
||||||
|
let figment = Figment::from(config);
|
||||||
|
let figment_key: SecretKey = figment.extract_inner("secret_key").unwrap();
|
||||||
|
assert_eq!(original_key, figment_key);
|
||||||
|
}
|
Loading…
Reference in New Issue