Skip to content

Commit

Permalink
Implement server side password encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
locka99 committed Apr 30, 2019
1 parent 1f71d97 commit 11bb88f
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 90 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ Planned future work is listed at the bottom.
## 0.7 (in progress)
- Address space nodes have been made more memory efficient, saving about 3MB of runtime space with
the standard node set.
- Client and server side support for encrypted passwords in user name identity tokens.
- TODO address space. Add a create on demand callback
- TODO gen_types.js. Refactor so it could be used to generate code for any model
- TODO support events
- TODO More control over limits on the server - number of subscriptions, monitored items, sessions
- Client side UserNameIdentityToken with encrypted password support. Plaintext password is already supported
- TODO Server side UserNameIdentityToken with encrypted password support.
- TODO X509IdentityToken support
- TODO Integration tests are broken and need to be fixed.
- TODO Multiple chunk support in client and server, sending and receiving
Expand Down
2 changes: 1 addition & 1 deletion client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{client::*, config::*};
///
/// # Example
///
/// ```rust,no_run
/// ```no_run
/// use opcua_client::prelude::*;
///
/// fn main() {
Expand Down
6 changes: 3 additions & 3 deletions client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl Client {
///
/// # Example
///
/// ```rust,no_run
/// ```no_run
/// use opcua_client::prelude::*;
/// use std::path::PathBuf;
///
Expand Down Expand Up @@ -331,7 +331,7 @@ impl Client {
///
/// # Example
///
/// ```rust,no_run
/// ```no_run
/// use opcua_client::prelude::*;
/// use std::path::PathBuf;
///
Expand Down Expand Up @@ -472,7 +472,7 @@ impl Client {
///
/// # Example
///
/// ```rust,no_run
/// ```no_run
/// use opcua_client::prelude::*;
/// let endpoints = [
/// EndpointDescription::from("opc.tcp://foo:123"),
Expand Down
2 changes: 1 addition & 1 deletion core/src/crypto/user_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub fn make_user_name_identity_token(channel_security_policy: SecurityPolicy, us
pub fn decrypt_user_identity_token_password(user_identity_token: &UserNameIdentityToken, server_nonce: &[u8], server_key: &PrivateKey) -> Result<String, StatusCode> {
if user_identity_token.encryption_algorithm.is_empty() {
// Assumed to be UTF-8 plain text
String::from_utf8(user_identity_token.password.as_ref().to_vec()).map_err(|_| StatusCode::BadDecodingError)
user_identity_token.plaintext_password()
} else {
// Determine the padding from the algorithm.
let padding = match user_identity_token.encryption_algorithm.as_ref() {
Expand Down
7 changes: 2 additions & 5 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,9 @@ The following security policies are supported - None, Basic128Rsa15, Basic256, B
The server and client support the following user identities

1. Anonymous - i.e. no identity
2. UserName - plaintext password only, i.e. the encryption algorithm field supplied with the identity token must be a
null string.
2. UserName - encrypted and plaintext. User/pass identities are defined by configuration.

User/pass identities are defined by configuration

X509 and UserName with encrypted passwords are intended for a future release.
X509 is intended for a future release.

## Crypto

Expand Down
8 changes: 8 additions & 0 deletions samples/server.conf
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ endpoints:
security_policy: Basic128Rsa15
security_mode: Sign
security_level: 2
password_security_policy: ~
user_token_ids:
- ANONYMOUS
- sample_user
Expand All @@ -33,6 +34,7 @@ endpoints:
security_policy: Basic128Rsa15
security_mode: SignAndEncrypt
security_level: 2
password_security_policy: ~
user_token_ids:
- ANONYMOUS
- sample_user
Expand All @@ -41,6 +43,7 @@ endpoints:
security_policy: Basic256
security_mode: Sign
security_level: 3
password_security_policy: ~
user_token_ids:
- ANONYMOUS
- sample_user
Expand All @@ -49,6 +52,7 @@ endpoints:
security_policy: Basic256
security_mode: SignAndEncrypt
security_level: 3
password_security_policy: ~
user_token_ids:
- ANONYMOUS
- sample_user
Expand All @@ -57,6 +61,7 @@ endpoints:
security_policy: Basic256Sha256
security_mode: Sign
security_level: 4
password_security_policy: ~
user_token_ids:
- ANONYMOUS
- sample_user
Expand All @@ -65,6 +70,7 @@ endpoints:
security_policy: Basic256Sha256
security_mode: SignAndEncrypt
security_level: 4
password_security_policy: ~
user_token_ids:
- ANONYMOUS
- sample_user
Expand All @@ -73,12 +79,14 @@ endpoints:
security_policy: None
security_mode: None
security_level: 1
password_security_policy: ~
user_token_ids: []
none:
path: /
security_policy: None
security_mode: None
security_level: 1
password_security_policy: ~
user_token_ids:
- ANONYMOUS
- sample_user
Expand Down
14 changes: 12 additions & 2 deletions server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ pub struct ServerUserToken {
pub x509: Option<PathBuf>,
}


impl ServerUserToken {
pub fn new_user_pass<T>(user: T, pass: T) -> Self where T: Into<String> {
ServerUserToken {
Expand Down Expand Up @@ -80,6 +79,8 @@ pub struct ServerEndpoint {
pub security_mode: String,
/// Security level, higher being more secure
pub security_level: u8,
/// Password security policy when a client supplies a user name identity token
pub password_security_policy: Option<String>,
/// User tokens
pub user_token_ids: BTreeSet<String>,
}
Expand All @@ -92,6 +93,7 @@ impl<'a> From<(&'a str, SecurityPolicy, MessageSecurityMode, &'a [&'a str])> for
security_policy: v.1.to_string(),
security_mode: v.2.to_string(),
security_level: Self::security_level(v.1),
password_security_policy: None,
user_token_ids: v.3.iter().map(|id| id.to_string()).collect(),
}
}
Expand All @@ -104,6 +106,7 @@ impl ServerEndpoint {
security_policy: security_policy.to_string(),
security_mode: security_mode.to_string(),
security_level: Self::security_level(security_policy),
password_security_policy: None,
user_token_ids: user_token_ids.iter().map(|id| id.clone()).collect(),
}
}
Expand Down Expand Up @@ -162,6 +165,14 @@ impl ServerEndpoint {
}
}

if let Some(ref password_security_policy) = self.password_security_policy {
let password_security_policy = SecurityPolicy::from_str(password_security_policy).unwrap();
if password_security_policy == SecurityPolicy::Unknown {
error!("Endpoint {} is invalid. Password security policy \"{}\" is invalid. Valid values are None, Basic128Rsa15, Basic256, Basic256Sha256", id, password_security_policy);
valid = false;
}
}

// Validate the security policy and mode
let security_policy = SecurityPolicy::from_str(&self.security_policy).unwrap();
let security_mode = MessageSecurityMode::from(self.security_mode.as_ref());
Expand Down Expand Up @@ -288,7 +299,6 @@ impl Config for ServerConfig {
fn product_uri(&self) -> UAString { UAString::from(self.product_uri.as_ref()) }
}


impl Default for ServerConfig {
fn default() -> Self {
let pki_dir = PathBuf::from("./pki");
Expand Down
2 changes: 1 addition & 1 deletion server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//!
//! This is a minimal server which runs with the default address space on the default port.
//!
//! ```rust,no_run
//! ```no_run
//! use opcua_server::prelude::*;
//!
//! fn main() {
Expand Down
7 changes: 5 additions & 2 deletions server/src/services/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,18 @@ impl SessionService {
StatusCode::Good
};

// Authenticate the user identity token
if service_result.is_good() {
service_result = server_state.authenticate_endpoint(endpoint_url, security_policy, security_mode, &request.user_identity_token);
if let Err(err) = server_state.authenticate_endpoint(endpoint_url, security_policy, security_mode, &request.user_identity_token, &session.session_nonce) {
service_result = err;
}
}

// Authenticate the user identity token
let response = if service_result.is_good() {
session.activated = true;
session.session_nonce = server_nonce;
let diagnostic_infos = None;

ActivateSessionResponse {
response_header: ResponseHeader::new_good(&request.request_header),
server_nonce: session.session_nonce.clone(),
Expand Down
Loading

0 comments on commit 11bb88f

Please sign in to comment.