From 59c6151dbc1bc615fb0637a9a2b9c24a9a32f2bd Mon Sep 17 00:00:00 2001 From: Rob Knight Date: Tue, 29 Jul 2025 23:50:10 +0100 Subject: [PATCH] Secret keys in Podlang (#365) --- src/lang/grammar.pest | 6 ++++++ src/lang/mod.rs | 26 +++++++++++++++++++------- src/lang/pretty_print.rs | 15 +++++++++++++++ src/lang/processor.rs | 26 ++++++++++++++++++++++++-- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/lang/grammar.pest b/src/lang/grammar.pest index 3d3d350..01000cb 100644 --- a/src/lang/grammar.pest +++ b/src/lang/grammar.pest @@ -60,6 +60,7 @@ anchored_key = { wildcard ~ "[" ~ literal_string ~ "]" } // Literal Values (ordered to avoid ambiguity, e.g., string before int) literal_value = { literal_public_key | + literal_secret_key | literal_dict | literal_set | literal_array | @@ -95,6 +96,11 @@ base58_char = { '1'..'9' | 'A'..'H' | 'J'..'N' | 'P'..'Z' | 'a'..'k' | 'm'..'z' base58_string = @{ base58_char+ } literal_public_key = { "PublicKey" ~ "(" ~ base58_string ~ ")" } +// SecretKey(...) +base64_char = { 'a'..'z' | 'A'..'Z' | '0'..'9' | "+" | "/" | "=" } +base64_string = @{ base64_char+ } +literal_secret_key = { "SecretKey" ~ "(" ~ base64_string ~ ")" } + // Container Literals (recursive definition using literal_value) literal_array = { "[" ~ (literal_value ~ ("," ~ literal_value)*)? ~ "]" } literal_set = { "#[" ~ (literal_value ~ ("," ~ literal_value)*)? ~ "]" } diff --git a/src/lang/mod.rs b/src/lang/mod.rs index 233d566..e5cf9a3 100644 --- a/src/lang/mod.rs +++ b/src/lang/mod.rs @@ -29,6 +29,7 @@ mod tests { use super::*; use crate::{ + backends::plonky2::primitives::ec::schnorr::SecretKey, lang::error::ProcessorError, middleware::{ hash_str, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key, @@ -859,25 +860,32 @@ mod tests { #[test] fn test_e2e_literals() -> Result<(), LangError> { let pk = crate::backends::plonky2::primitives::ec::curve::Point::generator(); - let pk_b58 = pk.to_string(); let pod_id = PodId(hash_str("test")); let raw = RawValue::from(1); let string = "hello"; let int = 123; let bool = true; + let sk = SecretKey::new_rand(); let input = format!( r#" REQUEST( - Equal(?A["pk"], PublicKey({})) - Equal(?B["pod_id"], {:#}) - Equal(?C["raw"], Raw({:#})) - Equal(?D["string"], "{}") + Equal(?A["pk"], {}) + Equal(?B["pod_id"], {}) + Equal(?C["raw"], {}) + Equal(?D["string"], {}) Equal(?E["int"], {}) Equal(?F["bool"], {}) + Equal(?G["sk"], {}) ) "#, - pk_b58, pod_id, raw, string, int, bool + Value::from(pk).to_podlang_string(), + Value::from(pod_id).to_podlang_string(), + Value::from(raw).to_podlang_string(), + Value::from(string).to_podlang_string(), + Value::from(int).to_podlang_string(), + Value::from(bool).to_podlang_string(), + Value::from(sk.clone()).to_podlang_string() ); /* REQUEST( @@ -894,7 +902,7 @@ mod tests { let processed = parse(&input, ¶ms, &[])?; let request_templates = processed.request_templates; - assert_eq!(request_templates.len(), 6); + assert_eq!(request_templates.len(), 7); let expected_templates = vec![ StatementTmpl { @@ -921,6 +929,10 @@ mod tests { pred: Predicate::Native(NativePredicate::Equal), args: vec![sta_ak(("F", 5), "bool"), sta_lit(Value::from(bool))], }, + StatementTmpl { + pred: Predicate::Native(NativePredicate::Equal), + args: vec![sta_ak(("G", 6), "sk"), sta_lit(Value::from(sk))], + }, ]; assert_eq!(request_templates, expected_templates); diff --git a/src/lang/pretty_print.rs b/src/lang/pretty_print.rs index b36a159..d5f5ca1 100644 --- a/src/lang/pretty_print.rs +++ b/src/lang/pretty_print.rs @@ -192,6 +192,7 @@ fn fmt_predicate_signature( mod tests { use super::*; use crate::{ + backends::plonky2::primitives::ec::schnorr::SecretKey, lang::parse, middleware::{ CustomPredicate, Key, NativePredicate, Params, Predicate, StatementTmpl, @@ -492,6 +493,20 @@ mod tests { assert_round_trip(input); } + #[test] + fn test_round_trip_secret_key() { + let sk = SecretKey::new_rand(); + let input = format!( + r#" + secret_key_test(Pod) = AND( + Equal(?Pod["sk"], {}) + ) + "#, + Value::from(sk.clone()).to_podlang_string() + ); + assert_round_trip(&input); + } + #[test] fn test_pretty_print_demonstration() { let input = r#" diff --git a/src/lang/processor.rs b/src/lang/processor.rs index fb08b33..306eae3 100644 --- a/src/lang/processor.rs +++ b/src/lang/processor.rs @@ -8,9 +8,12 @@ use plonky2::field::types::Field; use super::error::ProcessorError; use crate::{ - backends::plonky2::primitives::ec::curve::Point, + backends::plonky2::{ + deserialize_bytes, + primitives::ec::{curve::Point, schnorr::SecretKey}, + }, frontend::{BuilderArg, CustomPredicateBatchBuilder, StatementTmplBuilder}, - lang::parser::Rule, + lang::Rule, middleware::{ self, CustomPredicateBatch, CustomPredicateRef, Key, NativePredicate, Params, Predicate, StatementTmpl, StatementTmplArg, Value, Wildcard, F, VALUE_SIZE, @@ -795,6 +798,25 @@ fn process_literal_value( })?; Ok(Value::from(middleware_dict)) } + Rule::literal_secret_key => { + let sk_str_pair = inner_lit.clone().into_inner().next().unwrap(); + let sk_base64 = sk_str_pair.as_str(); + let bytes = deserialize_bytes(sk_base64).map_err(|_e| { + ProcessorError::InvalidLiteralFormat { + kind: "SecretKey".to_string(), + value: sk_base64.to_string(), + span: Some(get_span(&inner_lit)), + } + })?; + let secret_key = SecretKey::from_bytes(&bytes).map_err(|_e| { + ProcessorError::InvalidLiteralFormat { + kind: "SecretKey".to_string(), + value: sk_base64.to_string(), + span: Some(get_span(&inner_lit)), + } + })?; + Ok(Value::from(secret_key)) + } _ => unreachable!("Unexpected rule: {:?}", inner_lit.as_rule()), } }