Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compatibility fixes and a standalone mockhsm example #529

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[[example]]
name = "connector_http_server"
required-features = ["http-server", "usb"]

[[example]]
name = "mockhsm"
required-features = ["http-server", "mockhsm"]
30 changes: 30 additions & 0 deletions examples/mockhsm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! `yubihsm-connector` compatible HTTP server example.
//!
//! This exposes an HTTP server which provides an API that is compatible with
//! the `yubihsm-connector` executable which comes with the YubiHSM SDK.
//!
//! It allows utilities like `yubihsm-shell` or other things written with
//! `libyubihsm` to function in tandem with a Rust application
//! communicating directly with the YubiHSM2 over USB.

fn main() {
println!("using mockhsm");
let connector = yubihsm::Connector::mockhsm();

// http://127.0.0.1:12345
let http_config = yubihsm::connector::HttpConfig::default();

println!(
"starting server at http://{}:{}",
&http_config.addr, http_config.port
);

let server = yubihsm::connector::http::Server::new(&http_config, connector).unwrap();

println!("server started! connect by running:\n");
println!(" $ yubihsm-shell");
println!(" yubihsm> connect");
println!(" yubihsm> session open 1 <password>");

server.run().unwrap();
}
6 changes: 1 addition & 5 deletions src/audit/commands/set_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
//! For more information, see:
//! <https://developers.yubico.com/YubiHSM2/Commands/Set_Option.html>

use crate::{
audit::*,
command::{self, Command},
response::Response,
};
use crate::{audit::*, command::Command, response::Response};
use serde::{Deserialize, Serialize};

/// Request parameters for `command::put_option`
Expand Down
2 changes: 0 additions & 2 deletions src/connector/http/client/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ use std::{
io::Write,
net::{TcpStream, ToSocketAddrs},
ops::DerefMut,
string::String,
sync::Mutex,
time::Duration,
vec::Vec,
};

use super::{error::Error, path::PathBuf, request, response, HTTP_VERSION, USER_AGENT};
Expand Down
5 changes: 1 addition & 4 deletions src/connector/http/client/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
#![allow(unused_macros)]

use std::{fmt, num::ParseIntError, str::Utf8Error};
use std::{
io,
string::{FromUtf8Error, String, ToString},
};
use std::{io, string::FromUtf8Error};

/// Error type
#[derive(Debug)]
Expand Down
2 changes: 1 addition & 1 deletion src/connector/http/client/response/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use super::Body;
use crate::connector::http::client::Error;
use std::{io::Read, str, vec::Vec};
use std::{io::Read, str};

const TRANSFER_ENCODING_HEADER: &str = "Transfer-Encoding: ";
const HEADER_DELIMITER: &[u8] = b"\r\n\r\n";
Expand Down
11 changes: 5 additions & 6 deletions src/connector/http/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
},
uuid,
};
use std::{io, process, time::Instant};
use std::{fmt::Write, io, process, time::Instant};
use tiny_http as http;

/// `yubihsm-connector` compatible HTTP server
Expand Down Expand Up @@ -107,11 +107,10 @@ impl Server {
("port", &self.port.to_string()),
];

let body = status
.iter()
.map(|(k, v)| [*k, *v].join("\n"))
.collect::<Vec<_>>()
.join("\n");
let body = status.iter().fold(String::new(), |mut body, (k, v)| {
let _ = writeln!(body, "{k}={v}");
body
});

Ok(http::Response::from_string(body))
}
Expand Down
42 changes: 41 additions & 1 deletion src/mockhsm/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ pub(crate) fn session_message(
Code::SetLogIndex => SetLogIndexResponse {}.serialize(),
Code::SignEcdsa => sign_ecdsa(state, &command.data),
Code::SignEddsa => sign_eddsa(state, &command.data),
Code::SignPss => sign_rsa_pss(state, &command.data),
Code::GetStorageInfo => get_storage_info(),
Code::VerifyHmac => verify_hmac(state, &command.data),
unsupported => panic!("unsupported command type: {unsupported:?}"),
Expand Down Expand Up @@ -156,7 +157,7 @@ fn delete_object(state: &mut State, cmd_data: &[u8]) -> response::Message {
}

/// Generate a mock device information report
fn device_info() -> response::Message {
pub(crate) fn device_info() -> response::Message {
let info = device::Info {
major_version: 2,
minor_version: 0,
Expand Down Expand Up @@ -685,6 +686,45 @@ fn sign_eddsa(state: &State, cmd_data: &[u8]) -> response::Message {
}
}

/// Sign a message using the RSA PSS signature algorithm
#[cfg(feature = "untested")]
fn sign_rsa_pss(state: &State, cmd_data: &[u8]) -> response::Message {
use crate::rsa::pss::{commands::*, Signature};
use ::rsa::pss::SigningKey;
use ::signature::{hazmat::RandomizedPrehashSigner, SignatureEncoding};

let command: SignPssCommand =
deserialize(cmd_data).unwrap_or_else(|e| panic!("error parsing Code::SignRSA: {e:?}"));

if let Some(obj) = state
.objects
.get(command.key_id, object::Type::AsymmetricKey)
{
if let Payload::RsaKey(signing_key) = &obj.payload {
let signing_key = SigningKey::<Sha256>::new_with_salt_len(
signing_key.clone(),
command.salt_len.into(),
);
let signature = signing_key
.sign_prehash_with_rng(&mut OsRng, command.digest.as_ref())
.expect("RSA PSS signature failed")
.to_vec();
SignPssResponse(Signature(signature)).serialize()
} else {
debug!("not an RSA key: {:?}", obj.algorithm());
device::ErrorKind::InvalidCommand.into()
}
} else {
debug!("no such object ID: {:?}", command.key_id);
device::ErrorKind::ObjectNotFound.into()
}
}

#[cfg(not(feature = "untested"))]
fn sign_rsa_pss(_: &State, _: &[u8]) -> response::Message {
unimplemented!("RSASSA-PSS support disabled (use --features untested)")
}

/// Compute the HMAC tag for the given data
fn sign_hmac(state: &State, cmd_data: &[u8]) -> response::Message {
let command: SignHmacCommand =
Expand Down
1 change: 1 addition & 0 deletions src/mockhsm/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ impl Connection for MockConnection {
Code::CreateSession => command::create_session(&mut state, &command),
Code::AuthenticateSession => command::authenticate_session(&mut state, &command),
Code::SessionMessage => command::session_message(&mut state, command),
Code::DeviceInfo => Ok(command::device_info().into()),
unsupported => fail!(ConnectionFailed, "unsupported command: {:?}", unsupported),
}
.map(Message::from)
Expand Down
2 changes: 1 addition & 1 deletion src/rsa/pss/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl Command for SignPssCommand {

/// RSASSA-PSS signatures (ASN.1 DER encoded)
#[derive(Serialize, Deserialize, Debug)]
pub struct SignPssResponse(rsa::pss::Signature);
pub struct SignPssResponse(pub(crate) rsa::pss::Signature);

impl Response for SignPssResponse {
const COMMAND_CODE: command::Code = command::Code::SignPss;
Expand Down
36 changes: 18 additions & 18 deletions src/serialization/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ impl<'a, W: Write> serde::Serializer for &'a mut Serializer<W> {
unimplemented!();
}

fn serialize_some<T: ?Sized>(self, _v: &T) -> Result<(), Error>
fn serialize_some<T>(self, _v: &T) -> Result<(), Error>
where
T: serde::Serialize,
T: serde::Serialize + ?Sized,
{
unimplemented!();
}
Expand Down Expand Up @@ -195,9 +195,9 @@ impl<'a, W: Write> SerializeSeq for SerializeHelper<'a, W> {
type Error = Error;

#[inline]
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::Serialize,
T: serde::Serialize + ?Sized,
{
value.serialize(&mut *self.ser)
}
Expand All @@ -213,9 +213,9 @@ impl<'a, W: Write> SerializeTuple for SerializeHelper<'a, W> {
type Error = Error;

#[inline]
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::Serialize,
T: serde::Serialize + ?Sized,
{
value.serialize(&mut *self.ser)
}
Expand All @@ -231,9 +231,9 @@ impl<'a, W: Write> SerializeTupleStruct for SerializeHelper<'a, W> {
type Error = Error;

#[inline]
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::Serialize,
T: serde::Serialize + ?Sized,
{
value.serialize(&mut *self.ser)
}
Expand All @@ -249,9 +249,9 @@ impl<'a, W: Write> SerializeTupleVariant for SerializeHelper<'a, W> {
type Error = Error;

#[inline]
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::Serialize,
T: serde::Serialize + ?Sized,
{
value.serialize(&mut *self.ser)
}
Expand All @@ -267,17 +267,17 @@ impl<'a, W: Write> SerializeMap for SerializeHelper<'a, W> {
type Error = Error;

#[inline]
fn serialize_key<K: ?Sized>(&mut self, value: &K) -> Result<(), Error>
fn serialize_key<K>(&mut self, value: &K) -> Result<(), Error>
where
K: serde::Serialize,
K: serde::Serialize + ?Sized,
{
value.serialize(&mut *self.ser)
}

#[inline]
fn serialize_value<V: ?Sized>(&mut self, value: &V) -> Result<(), Error>
fn serialize_value<V>(&mut self, value: &V) -> Result<(), Error>
where
V: serde::Serialize,
V: serde::Serialize + ?Sized,
{
value.serialize(&mut *self.ser)
}
Expand All @@ -293,9 +293,9 @@ impl<'a, W: Write> SerializeStruct for SerializeHelper<'a, W> {
type Error = Error;

#[inline]
fn serialize_field<T: ?Sized>(&mut self, _key: &'static str, value: &T) -> Result<(), Error>
fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<(), Error>
where
T: serde::Serialize,
T: serde::Serialize + ?Sized,
{
value.serialize(&mut *self.ser)
}
Expand All @@ -311,9 +311,9 @@ impl<'a, W: Write> SerializeStructVariant for SerializeHelper<'a, W> {
type Error = Error;

#[inline]
fn serialize_field<T: ?Sized>(&mut self, _key: &'static str, value: &T) -> Result<(), Error>
fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<(), Error>
where
T: serde::Serialize,
T: serde::Serialize + ?Sized,
{
value.serialize(&mut *self.ser)
}
Expand Down
1 change: 0 additions & 1 deletion src/session/securechannel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,6 @@ fn compute_icv(cipher: &Aes128, counter: u32) -> GenericArray<u8, U16> {
#[cfg(all(test, feature = "mockhsm"))]
mod tests {
use super::*;
use crate::authentication;

const PASSWORD: &[u8] = b"password";
const HOST_CHALLENGE: &[u8] = &[0u8; 8];
Expand Down
2 changes: 1 addition & 1 deletion src/session/securechannel/challenge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl Challenge {
}

/// Borrow the challenge value as a slice
#[cfg_attr(clippy, allow(clippy::trivially_copy_pass_by_ref))]
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn as_slice(&self) -> &[u8] {
&self.0
}
Expand Down
3 changes: 1 addition & 2 deletions tests/command/decrypt_oaep.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{generate_asymmetric_key, TEST_KEY_ID};
use rand_core;
use sha2::{self, Digest};
use sha2::Digest;
use yubihsm::{asymmetric, Capability};

/// Test RSA OAEP decryption
Expand Down