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
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ register_toolchains(
"@pigweed//pw_toolchain/arm_clang:arm_clang_cc_toolchain_cortex-m4",
"@pigweed//pw_toolchain/riscv_clang:riscv_clang_cc_toolchain_rv32imc",
"@pw_rust_toolchains//:all",
"@lowrisc_opentitan//third_party/rust:bindgen_toolchain",
)

crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate")
Expand Down
2 changes: 2 additions & 0 deletions presubmit/license.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
# keep-sorted: end
# Data files
# keep-sorted: start
r"\.a$",
r"\.bin$",
r"\.csv$",
r"\.der$",
Expand Down Expand Up @@ -77,6 +78,7 @@
# keep-sorted: end
# Generated files
# keep-sorted: start
r"\btarget/earlgrey/cryptolib/src/otcrypto_sys\.rs$",
r"\btarget/earlgrey/registers/.*",
# keep-sorted: end
# Generated third-party files
Expand Down
79 changes: 79 additions & 0 deletions target/earlgrey/cryptolib/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

load("@rules_rust//rust:defs.bzl", "rust_library")
load("@rules_rust_bindgen//:defs.bzl", "rust_bindgen")
load("//target/earlgrey:defs.bzl", "TARGET_COMPATIBLE_WITH")

package(default_visibility = ["//visibility:public"])

cc_library(
name = "otcrypto_headers",
hdrs = [
"@lowrisc_opentitan//sw/device/lib/base/freestanding:assert.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:float.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:iso646.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:limits.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:stdalign.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:stdarg.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:stdbool.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:stddef.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:stdint.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:stdnoreturn.h",
"@lowrisc_opentitan//sw/device/lib/base/freestanding:string.h",
],
target_compatible_with = TARGET_COMPATIBLE_WITH,
deps = [
"@lowrisc_opentitan//sw/device/lib/crypto/include:crypto_hdrs",
],
)

rust_bindgen(
name = "otcrypto_raw",
bindgen_flags = [
"--use-core",
"--default-enum-style=rust",
"--allowlist-item=otcrypto_.*|hardened_bool.*|status_t.*|status",
"--no-layout-tests",
],
cc_lib = ":otcrypto_headers",
clang_flags = [
"-target",
"riscv32-unknown-none-elf",
"-nostdinc",
"-isystem",
"external/lowrisc_opentitan+/sw/device/lib/base/freestanding",
],
header = "@lowrisc_opentitan//sw/device/lib/crypto/include:otcrypto.h",
target_compatible_with = TARGET_COMPATIBLE_WITH,
)

cc_import(
name = "libotcrypto",
static_library = "libotcrypto.a",
target_compatible_with = TARGET_COMPATIBLE_WITH,
)

genrule(
name = "otcrypto_sys_gen",
srcs = [":otcrypto_raw"],
outs = ["src/otcrypto_sys.rs"],
cmd = "$(location //tools/otcrypto_bindgen) --input $< --out $@",
tools = ["//tools/otcrypto_bindgen"],
)

rust_library(
name = "cryptolib",
srcs = [
"src/lib.rs",
":otcrypto_sys_gen",
],
crate_name = "earlgrey_cryptolib",
edition = "2024",
target_compatible_with = TARGET_COMPATIBLE_WITH,
visibility = ["//visibility:public"],
deps = [
":libotcrypto",
"@rust_crates//:zerocopy",
],
)
Binary file added target/earlgrey/cryptolib/libotcrypto.a
Binary file not shown.
172 changes: 172 additions & 0 deletions target/earlgrey/cryptolib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

//! Safe Rust wrapper and FFI bindings for OpenTitan `libotcrypto` on Earlgrey.

#![no_std]

pub mod otcrypto_sys;

pub use otcrypto_sys::{
hmac_hash_sha256, hmac_hash_sha384, hmac_hash_sha512, AesGcmTagLen, AesKeyMode, AesMode,
AesOperation, AesPadding, BlindedKey, ByteBuf, ConstByteBuf, ConstWord32Buf, HardenedBool,
HashDigest, HashMode, HmacKeyMode, KeyConfig, KeyMode, KeySecurityLevel, KeyType, LibVersion,
RsaPadding, RsaSize, Status, StatusValue, UnblindedKey, Word32Buf,
};

/// Error types for OpenTitan cryptolib operations.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CryptoError {
/// Invalid arguments passed to cryptolib.
BadArgs,
/// Internal hardware or driver error.
InternalError,
/// Fatal error, requires reset or re-initialization.
FatalError,
/// Asynchronous operation incomplete.
AsyncIncomplete,
/// Requested cryptographic primitive is not implemented.
NotImplemented,
/// Unknown status code returned by C library.
Unknown(i32),
}

impl CryptoError {
/// Returns a string representation of the error.
pub const fn as_str(&self) -> &'static str {
match self {
CryptoError::BadArgs => "BadArgs",
CryptoError::InternalError => "InternalError",
CryptoError::FatalError => "FatalError",
CryptoError::AsyncIncomplete => "AsyncIncomplete",
CryptoError::NotImplemented => "NotImplemented",
CryptoError::Unknown(_) => "Unknown",
}
}
}

/// Converts a Status returned by libotcrypto into a Rust Result.
pub fn status_to_result(status: Status) -> Result<(), CryptoError> {
if status.value == StatusValue::Ok as i32 {
Ok(())
} else if status.value == StatusValue::BadArgs as i32 {
Err(CryptoError::BadArgs)
} else if status.value == StatusValue::InternalError as i32 {
Err(CryptoError::InternalError)
} else if status.value == StatusValue::FatalError as i32 {
Err(CryptoError::FatalError)
} else if status.value == StatusValue::AsyncIncomplete as i32 {
Err(CryptoError::AsyncIncomplete)
} else if status.value == StatusValue::NotImplemented as i32 {
Err(CryptoError::NotImplemented)
} else {
Err(CryptoError::Unknown(status.value))
}
}

pub const INIT_INTEGRITY_CHECKSUM: u32 = 0x5a3;

#[inline]
fn compute_buf_checksum(data: *const u8, len: usize) -> u32 {
INIT_INTEGRITY_CHECKSUM
.wrapping_add(data as u32)
.wrapping_add(len as u32)
}

impl ConstByteBuf {
/// Constructs a `ConstByteBuf` from an immutable byte slice.
pub fn from_slice(slice: &[u8]) -> Self {
let data = slice.as_ptr();
let len = slice.len();
Self {
data,
len,
ptr_checksum: compute_buf_checksum(data, len),
}
}
}

impl ByteBuf {
/// Constructs a `ByteBuf` from a mutable byte slice.
pub fn from_mut_slice(slice: &mut [u8]) -> Self {
let data = slice.as_mut_ptr();
let len = slice.len();
Self {
data,
len,
ptr_checksum: compute_buf_checksum(data, len),
}
}
}

impl ConstWord32Buf {
/// Constructs a `ConstWord32Buf` from an immutable 32-bit word slice.
pub fn from_words(words: &[u32]) -> Self {
let data = words.as_ptr();
let len = words.len();
Self {
data,
len,
ptr_checksum: compute_buf_checksum(data as *const u8, len),
}
}
}

impl Word32Buf {
/// Constructs a `Word32Buf` from a mutable 32-bit word slice.
pub fn from_mut_words(words: &mut [u32]) -> Self {
let data = words.as_mut_ptr();
let len = words.len();
Self {
data,
len,
ptr_checksum: compute_buf_checksum(data as *const u8, len),
}
}
}

impl BlindedKey {
/// Attaches key material buffer to the blinded key structure.
pub fn with_key_material(&mut self, km: &[u8]) -> &mut Self {
self.set_keyblob(km.as_ptr() as *mut u32);
self
}
}

impl UnblindedKey {
/// Attaches key material buffer to the unblinded key structure.
pub fn with_key_material(&mut self, km: &[u8]) -> &mut Self {
self.set_key(km.as_ptr() as *mut u32);
self
}
}

/// Initializes the cryptolib with the given security level.
pub fn init(security_level: KeySecurityLevel) -> Result<(), CryptoError> {
let status = unsafe { otcrypto_sys::init(security_level) };
status_to_result(status)
}

/// Computes SHA-256 digest over the input buffer in a single shot using Earlgrey HMAC HWIP.
pub fn sha256(data: &[u8], digest_out: &mut [u8; 32]) -> Result<(), CryptoError> {
let buf = ConstByteBuf::from_slice(data);
let status =
unsafe { otcrypto_sys::hmac_hash_sha256(&buf, digest_out.as_mut_ptr() as *mut u32) };
status_to_result(status)
}

/// Computes SHA-384 digest over the input buffer in a single shot using Earlgrey HMAC HWIP.
pub fn sha384(data: &[u8], digest_out: &mut [u8; 48]) -> Result<(), CryptoError> {
let buf = ConstByteBuf::from_slice(data);
let status =
unsafe { otcrypto_sys::hmac_hash_sha384(&buf, digest_out.as_mut_ptr() as *mut u32) };
status_to_result(status)
}

/// Computes SHA-512 digest over the input buffer in a single shot using Earlgrey HMAC HWIP.
pub fn sha512(data: &[u8], digest_out: &mut [u8; 64]) -> Result<(), CryptoError> {
let buf = ConstByteBuf::from_slice(data);
let status =
unsafe { otcrypto_sys::hmac_hash_sha512(&buf, digest_out.as_mut_ptr() as *mut u32) };
status_to_result(status)
}
114 changes: 114 additions & 0 deletions target/earlgrey/tests/cryptolib_smoke/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

load("@pigweed//pw_kernel/tooling:rust_app.bzl", "rust_app")
load("@pigweed//pw_kernel/tooling:system_image.bzl", "system_image")
load("@pigweed//pw_kernel/tooling:target_codegen.bzl", "target_codegen")
load("@pigweed//pw_kernel/tooling:target_linker_script.bzl", "target_linker_script")
load("@rules_rust//rust:defs.bzl", "rust_binary")
load("//target/earlgrey:defs.bzl", "TARGET_COMPATIBLE_WITH")
load("//target/earlgrey/signing/keys:defs.bzl", "FPGA_ECDSA_KEY")
load("//target/earlgrey/tooling:opentitan_runner.bzl", "opentitan_test")

rust_app(
name = "test_cryptolib",
srcs = [
"test_cryptolib.rs",
],
codegen_crate_name = "test_cryptolib_codegen",
edition = "2024",
system_config = "@pigweed//pw_kernel/target:system_config_file",
tags = ["kernel"],
target_compatible_with = TARGET_COMPATIBLE_WITH,
visibility = ["//visibility:public"],
deps = [
"//target/earlgrey/cryptolib",
"@pigweed//pw_kernel/userspace",
"@pigweed//pw_log/rust:pw_log",
"@pigweed//pw_status/rust:pw_status",
"@rust_crates//:zerocopy",
],
)

system_image(
name = "cryptolib_smoke",
apps = [
":test_cryptolib",
],
kernel = ":target",
platform = "//target/earlgrey",
system_config = ":system_config",
tags = ["kernel"],
)

target_linker_script(
name = "linker_script",
system_config = ":system_config",
tags = ["kernel"],
template = "//target/earlgrey:linker_script_template",
)

filegroup(
name = "system_config",
srcs = ["system.json5"],
)

target_codegen(
name = "codegen",
arch = "@pigweed//pw_kernel/arch/riscv:arch_riscv",
system_config = ":system_config",
)

rust_binary(
name = "target",
srcs = [
"target.rs",
],
edition = "2024",
tags = ["kernel"],
target_compatible_with = TARGET_COMPATIBLE_WITH,
deps = [
":codegen",
":linker_script",
"//target/earlgrey:entry",
"@pigweed//pw_kernel/arch/riscv:arch_riscv",
"@pigweed//pw_kernel/kernel",
"@pigweed//pw_kernel/subsys/console:console_backend",
"@pigweed//pw_kernel/target:target_common",
"@pigweed//pw_kernel/userspace",
"@pigweed//pw_log/rust:pw_log",
],
)

opentitan_test(
name = "cryptolib_smoke_hyper310_test",
ecdsa_key = FPGA_ECDSA_KEY,
environment = "//target/earlgrey/env:hyper310",
interface = "hyper310",
tags = [
"hardware",
"hyper310",
],
target = ":cryptolib_smoke",
)

opentitan_test(
name = "cryptolib_smoke_hyper340_test",
ecdsa_key = FPGA_ECDSA_KEY,
environment = "//target/earlgrey/env:hyper340",
interface = "hyper340",
tags = [
"hardware",
"hyper340",
],
target = ":cryptolib_smoke",
)

opentitan_test(
name = "cryptolib_smoke_qemu_test",
timeout = "moderate",
environment = "//target/earlgrey/env:qemu",
interface = "qemu",
tags = ["qemu"],
target = ":cryptolib_smoke",
)
Loading