use crate::ops::security_detection::detector_support::{spans, ByteSpan, DetectionError};
pub const SPEC_TOML: &str = r#"schema_version = 1
id = "security_detection.detect_pem_block"
archetype = "match-bytes-pattern"
display_name = "Detect PEM Block"
summary = "Returns offset-length spans for PEM armor blocks with matching labels."
category = "C"
[intrinsic]
wgsl = "security_detection_detect_pem_block"
[signature]
inputs = ["Bytes", "Bytes"]
output = "Bytes"
laws = []
equivalence_classes = ["matched_label", "unclosed_block", "bad_label", "t47_cap"]
workgroup_size = [64, 1, 1]
tags = ["security-detection", "pem", "secret-scan", "t47"]
fixtures_dir = "fixtures/"
"#;
pub const REFERENCE_VECTORS_TOML: &str = r#"[[case]]
name = "positive_private_key"
input = "x-----BEGIN PRIVATE KEY-----\nQUJD\n-----END PRIVATE KEY-----y"
expected_spans = [{ offset = 1, len = 59 }]
[[case]]
name = "negative_unclosed"
input = "-----BEGIN PRIVATE KEY-----\nQUJD\n"
expected_spans = []
"#;
pub mod lowering {
#[must_use]
pub const fn source() -> &'static str {
r#"struct Params {
input_len: u32,
max_spans: u32,
_pad0: u32,
_pad1: u32,
}
struct SpanOutput {
count: atomic<u32>,
data: array<u32>,
}
@group(0) @binding(0) var<storage, read> input: array<u32>;
@group(0) @binding(1) var<storage, read_write> output: SpanOutput;
@group(0) @binding(2) var<uniform> params: Params;
fn is_upper_or_space(byte: u32) -> bool {
return (byte >= 65u && byte <= 90u) || byte == 32u;
}
fn matches_begin(index: u32) -> bool {
if (index + 11u > params.input_len) {
return false;
}
let begin = array<u32, 11>(45u, 45u, 45u, 45u, 45u, 66u, 69u, 71u, 73u, 78u, 32u);
for (var offset = 0u; offset < 11u; offset = offset + 1u) {
if (input[index + offset] != begin[offset]) {
return false;
}
}
return true;
}
fn matches_five_dashes(index: u32) -> bool {
if (index + 5u > params.input_len) {
return false;
}
for (var offset = 0u; offset < 5u; offset = offset + 1u) {
if (input[index + offset] != 45u) {
return false;
}
}
return true;
}
fn label_end(label_start: u32) -> u32 {
var index = label_start;
loop {
if (index + 5u > params.input_len) {
return 0xffffffffu;
}
if (matches_five_dashes(index)) {
return index;
}
if (!is_upper_or_space(input[index])) {
return 0xffffffffu;
}
index = index + 1u;
}
}
fn matches_end_marker(index: u32, label_start: u32, label_len: u32) -> bool {
let prefix = array<u32, 9>(45u, 45u, 45u, 45u, 45u, 69u, 78u, 68u, 32u);
if (index + 9u + label_len + 5u > params.input_len) {
return false;
}
for (var offset = 0u; offset < 9u; offset = offset + 1u) {
if (input[index + offset] != prefix[offset]) {
return false;
}
}
for (var offset = 0u; offset < label_len; offset = offset + 1u) {
if (input[index + 9u + offset] != input[label_start + offset]) {
return false;
}
}
return matches_five_dashes(index + 9u + label_len);
}
fn emit_span(offset: u32, len: u32) {
let slot = atomicAdd(&output.count, 1u);
if (slot < params.max_spans) {
output.data[slot * 2u] = offset;
output.data[slot * 2u + 1u] = len;
}
}
@compute @workgroup_size(64)
fn security_detection_detect_pem_block(@builtin(global_invocation_id) gid: vec3<u32>) {
let begin = gid.x;
if (!matches_begin(begin)) {
return;
}
let label_start_index = begin + 11u;
let label_end_index = label_end(label_start_index);
if (label_end_index == 0xffffffffu || label_end_index == label_start_index) {
return;
}
let label_len = label_end_index - label_start_index;
var scan = label_end_index + 5u;
loop {
if (scan >= params.input_len) {
return;
}
if (matches_end_marker(scan, label_start_index, label_len)) {
let end = scan + 9u + label_len + 5u;
emit_span(begin, end - begin);
return;
}
scan = scan + 1u;
}
}
"#
}
}
pub fn detect_pem_block(input: &[u8]) -> Result<Vec<ByteSpan>, DetectionError> {
spans::pem_spans(input)
}
pub mod implementation {
pub use super::detect_pem_block;
pub mod kernel {
pub use super::super::detect_pem_block;
}
pub mod lowering {
pub mod wgsl {
pub use super::super::super::lowering::source;
}
}
}