Skip to content
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
15 changes: 4 additions & 11 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -145,26 +145,19 @@ crate.annotation(
)
crate.annotation(
crate = "cryptoki",
patch_args = ["-p2"],
patches = [
"@lowrisc_opentitan//third_party/rust/patches:cryptoki-vendor-defined-mechanism-type.patch",
"@lowrisc_opentitan//third_party/rust/patches:cryptoki-profile.patch",
],
repositories = ["crate_index"],
)
crate.annotation(
additive_build_file_content = """
filegroup(
name = "binding_srcs",
srcs = [
"src/lib.rs",
"src/bindings/x86_64-unknown-linux-gnu.rs",
],
name = "cryptoki-sys-binding-srcs",
srcs = glob(["src/bindings/*.rs"]),
visibility = ["//visibility:public"],
)
""",
crate = "cryptoki-sys",
extra_aliased_targets = {
"cryptoki-sys-binding-srcs": "binding_srcs",
"cryptoki-sys-binding-srcs": "cryptoki-sys-binding-srcs",
},
repositories = ["crate_index"],
)
Expand Down
325 changes: 240 additions & 85 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions sw/host/hsmtool/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ rust_library(
"src/commands/kdf/generate.rs",
"src/commands/kdf/import.rs",
"src/commands/kdf/mod.rs",
"src/commands/mldsa/export.rs",
"src/commands/mldsa/export_csr.rs",
"src/commands/mldsa/generate.rs",
"src/commands/mldsa/import.rs",
"src/commands/mldsa/mod.rs",
"src/commands/mldsa/sign.rs",
"src/commands/mldsa/verify.rs",
"src/commands/mod.rs",
"src/commands/object/destroy.rs",
"src/commands/object/list.rs",
Expand Down Expand Up @@ -149,6 +156,7 @@ rust_library(
"src/util/escape.rs",
"src/util/helper.rs",
"src/util/key/ecdsa.rs",
"src/util/key/mldsa.rs",
"src/util/key/mod.rs",
"src/util/key/rsa.rs",
"src/util/mod.rs",
Expand Down Expand Up @@ -177,6 +185,7 @@ rust_library(
"@crate_index//:anyhow",
"@crate_index//:base64ct",
"@crate_index//:clap",
"@crate_index//:const-oid",
"@crate_index//:cryptoki",
"@crate_index//:cryptoki-sys",
"@crate_index//:der",
Expand All @@ -186,8 +195,11 @@ rust_library(
"@crate_index//:hex",
"@crate_index//:indexmap",
"@crate_index//:log",
"@crate_index//:ml-dsa",
"@crate_index//:once_cell",
"@crate_index//:p256",
"@crate_index//:pem-rfc7468",
"@crate_index//:pkcs8",
"@crate_index//:rand",
"@crate_index//:regex",
"@crate_index//:reqwest",
Expand All @@ -197,9 +209,12 @@ rust_library(
"@crate_index//:serde_bytes",
"@crate_index//:serde_json",
"@crate_index//:sha2",
"@crate_index//:signature",
"@crate_index//:spki",
"@crate_index//:strum",
"@crate_index//:thiserror",
"@crate_index//:typetag",
"@crate_index//:x509-cert",
"@crate_index//:zeroize",
"@lowrisc_serde_annotate//serde_annotate",
],
Expand Down
76 changes: 48 additions & 28 deletions sw/host/hsmtool/scripts/pkcs11_consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@
'CKM_DSA_PROBABLISTIC_PARAMETER_GEN', # Typo of CKM_DSA_PROBABILISTIC_PARAMETER_GEN
]

# Map of canonical name to list of aliases.
ALIASES = {
'CKK_ML_DSA': ['CKK_MLDSA', 'MLDSA'],
'CKM_ML_DSA_KEY_PAIR_GEN': ['CKM_MLDSA_KEY_PAIR_GEN', 'MLDSA_KEY_PAIR_GEN'],
'CKM_ML_DSA': ['CKM_MLDSA', 'MLDSA'],
}


class EnumGen(object):

Expand Down Expand Up @@ -124,7 +131,8 @@ def get_names(self):
raise Exception(
'Expected objects to all be of uniform type',
name, tp, m.group(2))
names.append(name)
if name not in names:
names.append(name)
return (names, tp)

@staticmethod
Expand Down Expand Up @@ -159,29 +167,57 @@ def emit_enum(self, names):
"""
typename = self.ki_type.split('::')[-1]
prefix = len(self.prefix) + 1
enumerators = map(lambda name: self.to_studlycaps(name[prefix:]),
names)
traits = [
'Clone', 'Copy', 'Debug', 'PartialEq', 'Eq', 'Hash'
]
enumerators = list(
map(lambda name: self.to_studlycaps(name[prefix:]), names))
traits = ['Clone', 'Copy', 'Debug', 'PartialEq', 'Eq', 'Hash']
if self.serde:
traits.extend(['serde::Serialize', 'serde::Deserialize'])
if self.strum:
traits.extend(
['strum::Display', 'strum::EnumString', 'strum::EnumIter', 'strum::FromRepr'])
['strum::Display', 'strum::EnumString', 'strum::EnumIter'])
self.emit(f'#[derive({", ".join(traits)})]')
self.emit('#[repr(u64)]')
self.emit(f'pub enum {typename} {{')
for (en, val) in zip(enumerators, names):
snake_case = val[4:].lower()
if self.serde:
self.emit(f' #[serde(rename="{val}")]')
aliases = ALIASES.get(val, [])
alias_str = ''.join([f', alias="{a}"' for a in aliases])
self.emit(f' #[serde(rename="{val}"{alias_str})]')
if self.strum:
self.emit(
f' #[strum(serialize="{val}", serialize="{en}", serialize="{snake_case}")]'
)
aliases = ALIASES.get(val, [])
# Also add StudlyCaps version of aliases to strum
alias_enum_names = [
self.to_studlycaps(a[prefix:]) for a in aliases
]
alias_snake_names = [a[prefix:].lower() for a in aliases]
strum_aliases = aliases + alias_enum_names + alias_snake_names
alias_str = ''.join(
[f', serialize="{a}"' for a in strum_aliases])
self.emit(f' #[strum(serialize="{val}", serialize="{en}", '
f'serialize="{snake_case}"{alias_str})]')
self.emit(f' {en} = {val},')
self.emit(f' Unknown{typename} = u64::MAX,')
self.emit(f' Unknown{typename}(u64) = u64::MAX,')
self.emit('}')
self.emit()
self.emit(f'impl From<u64> for {typename} {{')
self.emit(' fn from(val: u64) -> Self {')
self.emit(' match val {')
for (en, val) in zip(enumerators, names):
self.emit(f' {val} => Self::{en},')
self.emit(f' _ => Self::Unknown{typename}(val),')
self.emit(' }')
self.emit(' }')
self.emit('}')
self.emit()
self.emit(f'impl From<{typename}> for u64 {{')
self.emit(f' fn from(val: {typename}) -> Self {{')
self.emit(' match val {')
for (en, val) in zip(enumerators, names):
self.emit(f' {typename}::{en} => {val},')
self.emit(f' {typename}::Unknown{typename}(x) => x,')
self.emit(' }')
self.emit(' }')
self.emit('}')

def emit_conversions(self, reprtype):
Expand All @@ -191,22 +227,6 @@ def emit_conversions(self, reprtype):
reprtype: str; The low-level PKCS#11 representation type.
"""
typename = self.ki_type.split('::')[-1]
if self.strum:
self.emit(f"""
impl From<u64> for {typename} {{
fn from(val: u64) -> Self {{
{typename}::from_repr(val)
.unwrap_or({typename}::Unknown{typename})
}}
}}

impl From<{typename}> for u64 {{
fn from(val: {typename}) -> u64 {{
val as u64
}}
}}
""")

self.emit(f"""
impl From<{self.ki_type}> for {typename} {{
fn from(val: {self.ki_type}) -> Self {{
Expand Down
95 changes: 95 additions & 0 deletions sw/host/hsmtool/src/commands/mldsa/export.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::{Result, anyhow};
use cryptoki::object::{Attribute, ObjectHandle};
use cryptoki::session::Session;
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::fs;
use std::path::PathBuf;

use crate::commands::{BasicResult, Dispatch};
use crate::error::HsmError;
use crate::module::Module;
use crate::util::attribute::{AttributeMap, KeyType, ObjectClass};
use crate::util::helper;
use crate::util::key::KeyEncoding;
use crate::util::key::mldsa;
use crate::util::wrap::{Wrap, WrapPrivateKey};

#[derive(clap::Args, Debug, Serialize, Deserialize)]
pub struct Export {
/// Unique identifier of the key.
#[arg(long)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you provide doc comments for each parameter?

id: Option<String>,
/// Label of the key.
#[arg(short, long)]
label: Option<String>,
/// Export the private key.
#[arg(long)]
private: bool,
/// Wrap the exported key with a wrapping key.
#[arg(long)]
wrap: Option<String>,
/// Wrapping key mechanism. Required when wrap is specified.
#[arg(long, default_value = "aes-key-wrap-pad")]
wrap_mechanism: Option<WrapPrivateKey>,
/// Encoding format of the exported key.
#[arg(short, long, value_enum, default_value = "der")]
format: KeyEncoding,
/// Path to the file where the key will be saved.
filename: PathBuf,
}

impl Export {
fn export(&self, session: &Session, object: ObjectHandle) -> Result<()> {
let map = AttributeMap::from_object(session, object)?;
if self.private {
let key = mldsa::MldsaSigningKey::try_from(&map)?;
mldsa::save_private_key(&self.filename, &key, self.format)?;
} else {
let key = mldsa::MldsaVerifyingKey::try_from(&map)?;
mldsa::save_public_key(&self.filename, &key, self.format)?;
}
Ok(())
}

fn wrap_key(&self, session: &Session, object: ObjectHandle) -> Result<()> {
let wrapper: Wrap = self
.wrap_mechanism
.ok_or(anyhow!("wrap_mechanism is required when wrap is specified"))?
.into();
let wrapped = wrapper.wrap(session, object, self.wrap.as_deref())?;
fs::write(&self.filename, &wrapped)?;
Ok(())
}
}

#[typetag::serde(name = "mldsa-export")]
impl Dispatch for Export {
fn run(
&self,
_context: &dyn Any,
_hsm: &Module,
session: Option<&Session>,
) -> Result<Box<dyn erased_serde::Serialize>> {
let session = session.ok_or(HsmError::SessionRequired)?;
let mut attrs = helper::search_spec(self.id.as_deref(), self.label.as_deref())?;
attrs.push(Attribute::KeyType(KeyType::MlDsa.try_into()?));
if self.private {
attrs.push(Attribute::Class(ObjectClass::PrivateKey.try_into()?));
} else {
attrs.push(Attribute::Class(ObjectClass::PublicKey.try_into()?));
}
let object = helper::find_one_object(session, &attrs)?;

if self.wrap.is_some() {
self.wrap_key(session, object)?;
} else {
self.export(session, object)?;
}
Ok(Box::<BasicResult>::default())
}
}
Loading
Loading