Skip to content

Commit

Permalink
Crypto work
Browse files Browse the repository at this point in the history
  • Loading branch information
locka99 committed Oct 1, 2017
1 parent caeeb90 commit 4af99f2
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 223 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"name": "Debug - core test",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/target/debug/opcua_core-2a9e06f08a4bdb15.exe",
"program": "${workspaceRoot}/target/debug/opcua_core-eea3830d2f94e35b.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceRoot}",
Expand Down
43 changes: 23 additions & 20 deletions core/src/comms/secure_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ pub struct SecureChannel {
/// Token identifier
pub token_id: UInt32,
/// Our nonce generated while handling open secure channel
pub nonce: Vec<u8>,
pub local_nonce: Vec<u8>,
/// Our certificate
pub cert: Option<X509>,
/// Our private key
pub private_key: Option<PKey>,
/// Their nonce provided by open secure channel
pub their_nonce: Vec<u8>,
pub remote_nonce: Vec<u8>,
/// Their certificate
pub their_cert: Option<X509>,
/// Symmetric Signing Key, Encrypt Key, IV
Expand All @@ -56,10 +56,10 @@ impl SecureChannel {
token_id: 0,
token_created_at: DateTime::now(),
token_lifetime: 0,
nonce: Vec::with_capacity(64),
local_nonce: Vec::with_capacity(64),
cert: None,
private_key: None,
their_nonce: Vec::with_capacity(64),
remote_nonce: Vec::with_capacity(64),
their_cert: None,
remote_keys: None,
local_keys: None,
Expand All @@ -83,11 +83,11 @@ impl SecureChannel {
token_id: 0,
token_created_at: DateTime::now(),
token_lifetime: 0,
nonce: Vec::with_capacity(64),
local_nonce: Vec::with_capacity(64),
cert,
private_key,
remote_keys: None,
their_nonce: Vec::with_capacity(64),
remote_nonce: Vec::with_capacity(64),
their_cert: None,
local_keys: None,
}
Expand Down Expand Up @@ -117,21 +117,21 @@ impl SecureChannel {
if self.security_policy != SecurityPolicy::None && (self.security_mode == MessageSecurityMode::Sign || self.security_mode == MessageSecurityMode::SignAndEncrypt) {
use rand::{self, Rng};
let mut rng = rand::thread_rng();
self.nonce = vec![0u8; self.security_policy.symmetric_key_size()];
rng.fill_bytes(&mut self.nonce);
self.local_nonce = vec![0u8; self.security_policy.symmetric_key_size()];
rng.fill_bytes(&mut self.local_nonce);
} else {
self.nonce = vec![0u8; 1];
self.local_nonce = vec![0u8; 1];
}
}

/// Set their nonce which should be the same as the symmetric key
pub fn set_their_nonce(&mut self, their_nonce: &ByteString) -> Result<(), StatusCode> {
pub fn set_remote_nonce(&mut self, remote_nonce: &ByteString) -> Result<(), StatusCode> {
if self.security_policy != SecurityPolicy::None && (self.security_mode == MessageSecurityMode::Sign || self.security_mode == MessageSecurityMode::SignAndEncrypt) {
if let Some(ref their_nonce) = their_nonce.value {
if their_nonce.len() != self.security_policy.symmetric_key_size() {
if let Some(ref remote_nonce) = remote_nonce.value {
if remote_nonce.len() != self.security_policy.symmetric_key_size() {
return Err(BAD_NONCE_INVALID);
}
self.their_nonce = their_nonce.to_vec();
self.remote_nonce = remote_nonce.to_vec();
Ok(())
} else {
Err(BAD_NONCE_INVALID)
Expand Down Expand Up @@ -175,10 +175,10 @@ impl SecureChannel {
/// are used to secure Messages sent by the Server.
///
pub fn derive_keys(&mut self) {
self.remote_keys = Some(self.security_policy.make_secure_channel_keys(&self.their_nonce, &self.nonce));
self.local_keys = Some(self.security_policy.make_secure_channel_keys(&self.nonce, &self.their_nonce));
trace!("Their nonce = {:?}", self.their_nonce);
trace!("Our nonce = {:?}", self.nonce);
self.remote_keys = Some(self.security_policy.make_secure_channel_keys(&self.remote_nonce, &self.local_nonce));
self.local_keys = Some(self.security_policy.make_secure_channel_keys(&self.local_nonce, &self.remote_nonce));
trace!("Remote nonce = {:?}", self.remote_nonce);
trace!("Local nonce = {:?}", self.local_nonce);
trace!("Derived remote keys = {:?}", self.remote_keys);
trace!("Derived local keys = {:?}", self.local_keys);
}
Expand Down Expand Up @@ -690,7 +690,7 @@ impl SecureChannel {
debug!("signed_range = {:?}, signature range = {:?}, signature len = {}", signed_range, signature_range, signature_size);

// Sign the message header, security header, sequence header, body, padding
let key = &(self.remote_keys.as_ref().unwrap()).0;
let key = &(self.local_keys.as_ref().unwrap()).0;
self.security_policy.symmetric_sign(key, &src[signed_range.clone()], &mut signature)?;

debug!("Signature, len {} = {:?}", signature.len(), signature);
Expand Down Expand Up @@ -728,7 +728,7 @@ impl SecureChannel {
dst[all].copy_from_slice(&src[all]);
// Verify signature
debug!("Verifying range from {:?} to signature {}..", signed_range, signed_range.end);
let key = &(self.local_keys.as_ref().unwrap()).0;
let key = &(self.remote_keys.as_ref().unwrap()).0;
self.security_policy.symmetric_verify_signature(key, &dst[signed_range.clone()], &dst[signed_range.end..])?;

Ok(encrypted_range.end)
Expand All @@ -755,13 +755,16 @@ impl SecureChannel {
debug!("Secure decrypt called with encrypted range {:?}", encrypted_range);
let decrypted_size = self.security_policy.symmetric_decrypt(key, iv, &src[encrypted_range.clone()], &mut decrypted_tmp[..])?;

// log_buffer("Encrypted buffer", &src[..encrypted_range.end]);
let encrypted_range = encrypted_range.start..(encrypted_range.start + decrypted_size);
dst[encrypted_range.clone()].copy_from_slice(&decrypted_tmp[..decrypted_size]);
log_buffer("Decrypted buffer", &dst[..decrypted_size]);
log_buffer("Decrypted buffer", &dst[..encrypted_range.end]);

// Verify signature (after encrypted portion)
let signature_range = (encrypted_range.end - self.security_policy.symmetric_signature_size())..encrypted_range.end;
let key = &(self.remote_keys.as_ref().unwrap()).0;

debug!("signed range = {:?}, signature range = {:?}", signed_range, signature_range);
self.security_policy.symmetric_verify_signature(key, &dst[signed_range.clone()], &dst[signature_range])?;
Ok(encrypted_range.end)
}
Expand Down
184 changes: 2 additions & 182 deletions core/src/tests/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,6 @@ use crypto::x509::X509;

use tests::*;

struct Test;

impl Test {
pub fn setup() -> Test {
::init_logging();
Test {}
}
}

fn sample_secure_channel_request_data_security_none() -> MessageChunk {
let sample_data = vec![
47, 0, 0, 0, 104, 116, 116, 112, 58, 47, 47, 111, 112, 99, 102, 111, 117, 110, 100, 97,
Expand Down Expand Up @@ -54,45 +45,6 @@ fn sample_secure_channel_request_data_security_none() -> MessageChunk {
chunk
}


fn make_open_secure_channel_response() -> OpenSecureChannelResponse {
OpenSecureChannelResponse {
response_header: ResponseHeader {
timestamp: DateTime::now(),
request_handle: 444,
service_result: BAD_PROTOCOL_VERSION_UNSUPPORTED,
service_diagnostics: DiagnosticInfo::new(),
string_table: None,
additional_header: ExtensionObject::null(),
},
server_protocol_version: 0,
security_token: ChannelSecurityToken {
channel_id: 1,
token_id: 2,
created_at: DateTime::now(),
revised_lifetime: 777,
},
server_nonce: ByteString::null(),
}
}

fn make_sample_message() -> SupportedMessage {
SupportedMessage::GetEndpointsRequest(GetEndpointsRequest {
request_header: RequestHeader {
authentication_token: NodeId::new(0, 99),
timestamp: DateTime::now(),
request_handle: 1,
return_diagnostics: 0,
audit_entry_id: UAString::null(),
timeout_hint: 123456,
additional_header: ExtensionObject::null(),
},
endpoint_url: UAString::null(),
locale_ids: None,
profile_uris: None,
})
}

fn set_chunk_sequence_number(chunk: &mut MessageChunk, secure_channel: &SecureChannel, sequence_number: UInt32) -> UInt32 {
// Read the sequence header
let mut chunk_info = chunk.chunk_info(&secure_channel).unwrap();
Expand Down Expand Up @@ -321,138 +273,6 @@ fn open_secure_channel() {
assert_eq!(open_secure_channel_response, new_open_secure_channel_response);
}

fn test_encrypt_decrypt(message: SupportedMessage, security_mode: MessageSecurityMode, security_policy: SecurityPolicy) {
let mut secure_channel = SecureChannel::new_no_certificate_store();
secure_channel.security_mode = security_mode;
secure_channel.security_policy = security_policy;
// Both nonces are the same because we shall be encrypting and decrypting our own blocks
secure_channel.nonce = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
secure_channel.their_nonce = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
secure_channel.derive_keys();

let mut chunks = Chunker::encode(1, 1, 0, 0, &secure_channel, &message).unwrap();
assert_eq!(chunks.len(), 1);

{
let chunk = &mut chunks[0];

let mut encrypted_data = vec![0u8; chunk.data.len() + 4096];
let encrypted_size = secure_channel.apply_security(&chunk, &mut encrypted_data[..]).unwrap();
trace!("Result of applying security = {}", encrypted_size);

// We can't strip padding, so just compare up to original length
let chunk2 = secure_channel.verify_and_remove_security(&encrypted_data[..encrypted_size]).unwrap();

// Why offset 12? So we don't compare message_size part which may differ when padding is added. Less than ideal
assert_eq!(&chunk.data[12..], &chunk2.data[12..chunk.data.len()]);
}

let message2 = Chunker::decode(&chunks, &secure_channel, None).unwrap();
assert_eq!(message, message2);
}

fn test_asymmetric_encrypt_decrypt(message: SupportedMessage, security_mode: MessageSecurityMode, security_policy: SecurityPolicy) {
let mut secure_channel = SecureChannel::new_no_certificate_store();
secure_channel.security_mode = security_mode;
secure_channel.security_policy = security_policy;

// Create a cert and private key pretending to be us and them
let (our_cert, our_key) = make_test_cert();
let (their_cert, their_key) = make_test_cert();

// First we shall sign with our private key and encrypt with their public.
secure_channel.cert = Some(our_cert);
secure_channel.their_cert = Some(their_cert);
secure_channel.private_key = Some(our_key);

let mut chunks = Chunker::encode(1, 1, 0, 0, &secure_channel, &message).unwrap();
assert_eq!(chunks.len(), 1);

let chunk = &mut chunks[0];

let mut encrypted_data = vec![0u8; chunk.data.len() + 4096];
let encrypted_size = secure_channel.apply_security(&chunk, &mut encrypted_data[..]).unwrap();
trace!("Result of applying security = {}", encrypted_size);

// Now we shall try to decrypt what has been encrypted by flipping the keys around
let tmp = secure_channel.cert;
secure_channel.cert = secure_channel.their_cert;
secure_channel.their_cert = tmp;
secure_channel.private_key = Some(their_key);

// We can't strip padding, so just compare up to original length
let chunk2 = secure_channel.verify_and_remove_security(&encrypted_data[..encrypted_size]).unwrap();

assert_eq!(&chunk.data[12..], &chunk2.data[12..chunk.data.len()]);
}

#[test]
fn asymmetric_sign_and_encrypt_message_chunk_basic128rsa15() {
let _ = Test::setup();
error!("asymmetric_sign_and_encrypt_message_chunk_basic128rsa15");
test_asymmetric_encrypt_decrypt(SupportedMessage::OpenSecureChannelResponse(make_open_secure_channel_response()), MessageSecurityMode::SignAndEncrypt, SecurityPolicy::Basic128Rsa15);
}

#[test]
fn asymmetric_sign_and_encrypt_message_chunk_basic256() {
let _ = Test::setup();
error!("asymmetric_sign_and_encrypt_message_chunk_basic256");
test_asymmetric_encrypt_decrypt(SupportedMessage::OpenSecureChannelResponse(make_open_secure_channel_response()), MessageSecurityMode::SignAndEncrypt, SecurityPolicy::Basic256);
}

#[test]
fn asymmetric_sign_and_encrypt_message_chunk_basic256sha256() {
let _ = Test::setup();
error!("asymmetric_sign_and_encrypt_message_chunk_basic256sha256");
test_asymmetric_encrypt_decrypt(SupportedMessage::OpenSecureChannelResponse(make_open_secure_channel_response()), MessageSecurityMode::SignAndEncrypt, SecurityPolicy::Basic256Sha256);
}

/// Create a message, encode it to a chunk, sign the chunk, verify the signature and decode back to message
#[test]
fn symmetric_sign_message_chunk_basic128rsa15() {
let _ = Test::setup();
error!("symmetric_sign_message_chunk_basic128rsa15");
test_encrypt_decrypt(make_sample_message(), MessageSecurityMode::Sign, SecurityPolicy::Basic128Rsa15);
}

#[test]
fn symmetric_sign_message_chunk_basic256() {
let _ = Test::setup();
error!("symmetric_sign_message_chunk_basic256");
test_encrypt_decrypt(make_sample_message(), MessageSecurityMode::Sign, SecurityPolicy::Basic256);
}

#[test]
fn symmetric_sign_message_chunk_basic256sha256() {
let _ = Test::setup();
error!("symmetric_sign_message_chunk_basic256sha256");
test_encrypt_decrypt(make_sample_message(), MessageSecurityMode::Sign, SecurityPolicy::Basic256Sha256);
}

/// Create a message, encode it to a chunk, sign the chunk, encrypt, decrypt, verify the signature and decode back to message
#[test]
fn symmetric_sign_and_encrypt_message_chunk_basic128rsa15() {
let _ = Test::setup();
error!("symmetric_sign_and_encrypt_message_chunk_basic128rsa15");
test_encrypt_decrypt(make_sample_message(), MessageSecurityMode::SignAndEncrypt, SecurityPolicy::Basic128Rsa15);
}

/// Create a message, encode it to a chunk, sign the chunk, encrypt, decrypt, verify the signature and decode back to message
#[test]
fn symmetric_sign_and_encrypt_message_chunk_basic256() {
let _ = Test::setup();
error!("symmetric_sign_and_encrypt_message_chunk_basic256");
test_encrypt_decrypt(make_sample_message(), MessageSecurityMode::SignAndEncrypt, SecurityPolicy::Basic256);
}

/// Create a message, encode it to a chunk, sign the chunk, encrypt, decrypt, verify the signature and decode back to message
#[test]
fn symmetric_sign_and_encrypt_message_chunk_basic256sha256() {
let _ = Test::setup();
error!("symmetric_sign_and_encrypt_message_chunk_basic256sha256");
test_encrypt_decrypt(make_sample_message(), MessageSecurityMode::SignAndEncrypt, SecurityPolicy::Basic256Sha256);
}

#[test]
fn security_policy_symmetric_encrypt_decrypt() {
// Encrypt and decrypt directly to the security policy, make sure all is well
Expand All @@ -462,8 +282,8 @@ fn security_policy_symmetric_encrypt_decrypt() {
secure_channel.security_mode = MessageSecurityMode::SignAndEncrypt;
secure_channel.security_policy = security_policy;
// Both nonces are the same because we shall be encrypting and decrypting our own blocks
secure_channel.nonce = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
secure_channel.their_nonce = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
secure_channel.local_nonce = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
secure_channel.remote_nonce = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
secure_channel.derive_keys();

let src = vec![0u8; 100];
Expand Down
14 changes: 7 additions & 7 deletions core/src/tests/comms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ pub fn secure_channel_nonce() {
sc.security_mode = MessageSecurityMode::SignAndEncrypt;
sc.security_policy = SecurityPolicy::Basic256;
// Nonce which is not 32 bytes long is an error
assert!(sc.set_their_nonce(&ByteString::null()).is_err());
assert!(sc.set_their_nonce(&ByteString::from(b"")).is_err());
assert!(sc.set_their_nonce(&ByteString::from(b"1")).is_err());
assert!(sc.set_their_nonce(&ByteString::from(b"0123456789012345678901234567890")).is_err());
assert!(sc.set_their_nonce(&ByteString::from(b"012345678901234567890123456789012".as_ref())).is_err());
assert!(sc.set_remote_nonce(&ByteString::null()).is_err());
assert!(sc.set_remote_nonce(&ByteString::from(b"")).is_err());
assert!(sc.set_remote_nonce(&ByteString::from(b"1")).is_err());
assert!(sc.set_remote_nonce(&ByteString::from(b"0123456789012345678901234567890")).is_err());
assert!(sc.set_remote_nonce(&ByteString::from(b"012345678901234567890123456789012".as_ref())).is_err());
// Nonce which is 32 bytes long is good
assert!(sc.set_their_nonce(&ByteString::from(b"01234567890123456789012345678901")).is_ok());
}
assert!(sc.set_remote_nonce(&ByteString::from(b"01234567890123456789012345678901")).is_ok());
}
4 changes: 2 additions & 2 deletions core/src/tests/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ fn derkeys_from_nonce() {
fn derive_keys_from_nonce_basic128rsa15() {
let security_policy = SecurityPolicy::Basic128Rsa15;

// This test takes two nonces generated from a client / server
// This test takes two nonces generated from a real client / server session
let local_nonce = vec![0x88, 0x65, 0x13, 0xb6, 0xee, 0xad, 0x68, 0xa2, 0xcb, 0xa7, 0x29, 0x0f, 0x79, 0xb3, 0x84, 0xf3];
let remote_nonce = vec![0x17, 0x0c, 0xe8, 0x68, 0x3e, 0xe6, 0xb3, 0x80, 0xb3, 0xf4, 0x67, 0x5c, 0x1e, 0xa2, 0xcc, 0xb1];

Expand All @@ -351,7 +351,7 @@ fn derive_keys_from_nonce_basic128rsa15() {
let remote_iv = vec![0xab, 0xc6, 0x26, 0x78, 0xb9, 0xa4, 0xe6, 0x93, 0x21, 0x9e, 0xc1, 0x7e, 0xd5, 0x8b, 0x0e, 0xf2];

// Make the keys using the two nonce values
let local_keys = security_policy.make_secure_channel_keys(&remote_nonce, &local_nonce);
let local_keys = security_policy.make_secure_channel_keys(&remote_nonce,&local_nonce);
let remote_keys = security_policy.make_secure_channel_keys(&local_nonce, &remote_nonce);

// Compare the keys we received against the expected
Expand Down
Loading

0 comments on commit 4af99f2

Please sign in to comment.