From e03eb8abea9b6b2ad980014204e1b07cdc24d161 Mon Sep 17 00:00:00 2001 From: Scott Phillips Date: Tue, 17 May 2022 13:35:05 -0400 Subject: [PATCH] native support for oberon token verification (#418) --- include/okapi.h | 36 +++++++----- native/build.rs | 1 - native/src/ffi/oberon.rs | 5 ++ native/src/oberon/mod.rs | 75 ++++++++++++++++-------- native/src/proto/okapi/okapi_security.rs | 20 +++++++ native/src/proto/pbmse.rs | 2 - native/src/tests/oberon.rs | 32 ++++++++++ proto/okapi/security/v1/security.proto | 12 ++++ python/tests/test_oberon.py | 1 - 9 files changed, 138 insertions(+), 46 deletions(-) diff --git a/include/okapi.h b/include/okapi.h index 537d058c..8d58373e 100644 --- a/include/okapi.h +++ b/include/okapi.h @@ -39,6 +39,22 @@ int32_t didkey_resolve(struct ByteBuffer request, struct ByteBuffer *response, struct ExternError *err); +int32_t blake3_hash(struct ByteBuffer request, + struct ByteBuffer *response, + struct ExternError *err); + +int32_t blake3_keyed_hash(struct ByteBuffer request, + struct ByteBuffer *response, + struct ExternError *err); + +int32_t blake3_derive_key(struct ByteBuffer request, + struct ByteBuffer *response, + struct ExternError *err); + +int32_t sha256_hash(struct ByteBuffer request, + struct ByteBuffer *response, + struct ExternError *err); + int32_t ldproofs_create_proof(struct ByteBuffer request, struct ByteBuffer *response, struct ExternError *err); @@ -63,6 +79,10 @@ int32_t oberon_unblind_token(struct ByteBuffer request, struct ByteBuffer *response, struct ExternError *err); +int32_t oberon_verify_token(struct ByteBuffer request, + struct ByteBuffer *response, + struct ExternError *err); + int32_t oberon_create_proof(struct ByteBuffer request, struct ByteBuffer *response, struct ExternError *err); @@ -78,19 +98,3 @@ void didcomm_string_free(char *s); void okapi_bytebuffer_free(struct ByteBuffer v); void okapi_string_free(char *s); - -int32_t blake3_hash(struct ByteBuffer request, - struct ByteBuffer *response, - struct ExternError *err); - -int32_t blake3_keyed_hash(struct ByteBuffer request, - struct ByteBuffer *response, - struct ExternError *err); - -int32_t blake3_derive_key(struct ByteBuffer request, - struct ByteBuffer *response, - struct ExternError *err); - -int32_t sha256_hash(struct ByteBuffer request, - struct ByteBuffer *response, - struct ExternError *err); diff --git a/native/build.rs b/native/build.rs index abf8d059..58c7c9cc 100644 --- a/native/build.rs +++ b/native/build.rs @@ -48,7 +48,6 @@ fn compile_protobuf_files() { ], &["../proto", "../proto/pbmse/v1/"], ) - .format(true) .unwrap(); copy("okapi.examples.v1.rs", "./src/proto/okapi/okapi_examples.rs").unwrap(); diff --git a/native/src/ffi/oberon.rs b/native/src/ffi/oberon.rs index 78c37a09..53a4060e 100644 --- a/native/src/ffi/oberon.rs +++ b/native/src/ffi/oberon.rs @@ -21,6 +21,11 @@ pub extern "C" fn oberon_unblind_token(request: ByteBuffer, response: &mut ByteB c_impl!(UnBlindOberonTokenRequest, Oberon, unblind, request, response, err) } +#[no_mangle] +pub extern "C" fn oberon_verify_token(request: ByteBuffer, response: &mut ByteBuffer, err: &mut ExternError) -> i32 { + c_impl!(VerifyOberonTokenRequest, Oberon, verify_token, request, response, err) +} + #[no_mangle] pub extern "C" fn oberon_create_proof(request: ByteBuffer, response: &mut ByteBuffer, err: &mut ExternError) -> i32 { c_impl!(CreateOberonProofRequest, Oberon, proof, request, response, err) diff --git a/native/src/oberon/mod.rs b/native/src/oberon/mod.rs index 691ff280..d4afb2f7 100644 --- a/native/src/oberon/mod.rs +++ b/native/src/oberon/mod.rs @@ -1,7 +1,9 @@ -use crate::{didcomm::Error, proto::security::*}; +use std::convert::TryInto; + use oberon; use rand::prelude::*; -use std::convert::TryInto; + +use crate::{didcomm::Error, proto::security::*}; impl crate::Oberon { pub fn key<'a>(request: &CreateOberonKeyRequest) -> Result> { @@ -24,10 +26,11 @@ impl crate::Oberon { return Err(Error::InvalidField("must provide data")); } - let skbytes: [u8; oberon::SecretKey::BYTES] = match request.sk.as_slice().try_into() { - Ok(skbytes) => skbytes, - Err(_) => return Err(Error::InvalidField("invalid secret key provided")), - }; + let skbytes: [u8; oberon::SecretKey::BYTES] = request + .sk + .as_slice() + .try_into() + .map_err(|_| Error::InvalidField("invalid secret key provided"))?; let sk = oberon::SecretKey::from(&skbytes); let mut token = match oberon::Token::new(&sk, &request.data) { @@ -51,10 +54,7 @@ impl crate::Oberon { return Err(Error::InvalidField("must provide data")); } - let tokenbytes: [u8; oberon::Token::BYTES] = match request.token.as_slice().try_into() { - Ok(tokenbytes) => tokenbytes, - Err(_) => return Err(Error::InvalidField("invalid token provided")), - }; + let tokenbytes: [u8; oberon::Token::BYTES] = request.token.as_slice().try_into().map_err(|_| Error::InvalidField("invalid token provided"))?; let tkn = oberon::Token::from_bytes(&tokenbytes); if tkn.is_none().into() { @@ -80,20 +80,18 @@ impl crate::Oberon { return Err(Error::InvalidField("must provide data")); } - let pkbytes: [u8; oberon::PublicKey::BYTES] = match request.pk.as_slice().try_into() { - Ok(pkbytes) => pkbytes, - Err(_) => return Err(Error::InvalidField("invalid public key provided")), - }; + let pkbytes: [u8; oberon::PublicKey::BYTES] = request + .pk + .as_slice() + .try_into() + .map_err(|_| Error::InvalidField("invalid public key provided"))?; let pk = oberon::PublicKey::from_bytes(&pkbytes); if pk.is_none().into() { return Err(Error::InvalidField("invalid public key provided")); } - let proofbytes: [u8; oberon::Proof::BYTES] = match request.proof.as_slice().try_into() { - Ok(proofbytes) => proofbytes, - Err(_) => return Err(Error::InvalidField("invalid proof provided")), - }; + let proofbytes: [u8; oberon::Proof::BYTES] = request.proof.as_slice().try_into().map_err(|_| Error::InvalidField("invalid proof provided"))?; let proof = oberon::Proof::from_bytes(&proofbytes); if proof.is_none().into() { @@ -106,10 +104,7 @@ impl crate::Oberon { } pub fn blind<'a>(request: &BlindOberonTokenRequest) -> Result> { - let tokenbytes: [u8; oberon::Token::BYTES] = match request.token.as_slice().try_into() { - Ok(tokenbytes) => tokenbytes, - Err(_) => return Err(Error::InvalidField("invalid token provided")), - }; + let tokenbytes: [u8; oberon::Token::BYTES] = request.token.as_slice().try_into().map_err(|_| Error::InvalidField("invalid token provided"))?; let tkn = oberon::Token::from_bytes(&tokenbytes); if tkn.is_none().into() { @@ -130,10 +125,7 @@ impl crate::Oberon { } pub fn unblind<'a>(request: &UnBlindOberonTokenRequest) -> Result> { - let tokenbytes: [u8; oberon::Token::BYTES] = match request.token.as_slice().try_into() { - Ok(tokenbytes) => tokenbytes, - Err(_) => return Err(Error::InvalidField("invalid token provided")), - }; + let tokenbytes: [u8; oberon::Token::BYTES] = request.token.as_slice().try_into().map_err(|_| Error::InvalidField("invalid token provided"))?; let tkn = oberon::Token::from_bytes(&tokenbytes); if tkn.is_none().into() { @@ -152,4 +144,35 @@ impl crate::Oberon { token: tkn.to_bytes().to_vec(), }) } + + pub fn verify_token<'a>(request: &VerifyOberonTokenRequest) -> Result> { + if request.data.len() == 0 { + return Err(Error::InvalidField("must provide data")); + } + + let pkbytes: [u8; oberon::PublicKey::BYTES] = request + .pk + .as_slice() + .try_into() + .map_err(|_| Error::InvalidField("invalid public key provided"))?; + + let pk = oberon::PublicKey::from_bytes(&pkbytes); + if pk.is_none().into() { + return Err(Error::InvalidField("invalid public key provided")); + } + + let tokenbytes: [u8; oberon::Token::BYTES] = request.token.as_slice().try_into().map_err(|_| Error::InvalidField("invalid token provided"))?; + + let tkn = oberon::Token::from_bytes(&tokenbytes); + if tkn.is_none().into() { + return Err(Error::InvalidField("invalid token provided")); + } + let tkn = tkn.unwrap(); + let pk = pk.unwrap(); + + let verify_result = tkn.verify(pk, &request.data); + let is_valid = bool::from(verify_result); + + Ok(VerifyOberonTokenResponse { valid: is_valid }) + } } diff --git a/native/src/proto/okapi/okapi_security.rs b/native/src/proto/okapi/okapi_security.rs index b9c023b8..ebdcf03e 100644 --- a/native/src/proto/okapi/okapi_security.rs +++ b/native/src/proto/okapi/okapi_security.rs @@ -115,3 +115,23 @@ pub struct UnBlindOberonTokenResponse { #[prost(bytes = "vec", tag = "1")] pub token: ::prost::alloc::vec::Vec, } +/// Verify that an oberon token comes from the desired issuer +#[derive(::serde::Serialize, ::serde::Deserialize, Clone, PartialEq, ::prost::Message)] +pub struct VerifyOberonTokenRequest { + /// raw token bytes + #[prost(bytes = "vec", tag = "1")] + pub token: ::prost::alloc::vec::Vec, + /// token is valid to this public key? + #[prost(bytes = "vec", tag = "2")] + pub pk: ::prost::alloc::vec::Vec, + /// public part of oberon protocol - can be any data + #[prost(bytes = "vec", tag = "3")] + pub data: ::prost::alloc::vec::Vec, +} +/// Contains the verification result for the oberon token +#[derive(::serde::Serialize, ::serde::Deserialize, Clone, PartialEq, ::prost::Message)] +pub struct VerifyOberonTokenResponse { + /// token is valid to the public key + #[prost(bool, tag = "1")] + pub valid: bool, +} diff --git a/native/src/proto/pbmse.rs b/native/src/proto/pbmse.rs index 7907e4f7..8bb5ee3a 100644 --- a/native/src/proto/pbmse.rs +++ b/native/src/proto/pbmse.rs @@ -1,5 +1,3 @@ -mod pbmse; - /// JWS /// Protocol buffer message signing and encryption #[derive(::serde::Serialize, ::serde::Deserialize, Clone, PartialEq, ::prost::Message)] diff --git a/native/src/tests/oberon.rs b/native/src/tests/oberon.rs index b9c102fc..dcb3d11b 100644 --- a/native/src/tests/oberon.rs +++ b/native/src/tests/oberon.rs @@ -334,6 +334,38 @@ fn test_unblind_token() { ); } +#[test] +fn test_verify_token() { + let data = vec![4, 1, 1, 3]; + let req = CreateOberonKeyRequest { seed: vec![1, 2, 3] }; + let key_resp = crate::Oberon::key(&req).unwrap(); + let token = crate::Oberon::token(&CreateOberonTokenRequest { + sk: key_resp.sk, + data: data.clone(), + blinding: vec![], + }) + .unwrap(); + + // Verify with proper public key + let key_verify_good = crate::Oberon::verify_token(&VerifyOberonTokenRequest { + token: token.token.clone(), + pk: key_resp.pk.clone(), + data: data.clone(), + }) + .unwrap(); + assert_eq!(key_verify_good.valid, true); + + // Verify with improper public key + let wrong_key = crate::Oberon::key(&CreateOberonKeyRequest { seed: vec![0, 1, 2] }).unwrap(); + let key_verify_bad = crate::Oberon::verify_token(&VerifyOberonTokenRequest { + token: token.token.clone(), + pk: wrong_key.pk.clone(), + data: data.clone(), + }) + .unwrap(); + assert_eq!(key_verify_bad.valid, false); +} + #[test] fn test_create_key() { let req = CreateOberonKeyRequest { seed: vec![] }; diff --git a/proto/okapi/security/v1/security.proto b/proto/okapi/security/v1/security.proto index 7bd89513..38ea53f2 100644 --- a/proto/okapi/security/v1/security.proto +++ b/proto/okapi/security/v1/security.proto @@ -78,3 +78,15 @@ message UnBlindOberonTokenRequest { message UnBlindOberonTokenResponse { bytes token = 1; // raw unblinded token bytes } + +// Verify that an oberon token comes from the desired issuer +message VerifyOberonTokenRequest { + bytes token = 1; // raw token bytes + bytes pk = 2; // token is valid to this public key? + bytes data = 3; // public part of oberon protocol - can be any data +} + +// Contains the verification result for the oberon token +message VerifyOberonTokenResponse { + bool valid = 1; // token is valid to the public key +} \ No newline at end of file diff --git a/python/tests/test_oberon.py b/python/tests/test_oberon.py index 5427b853..6fbedf18 100644 --- a/python/tests/test_oberon.py +++ b/python/tests/test_oberon.py @@ -1,4 +1,3 @@ -import base64 import unittest from trinsicokapi import oberon