Skip to content

Commit

Permalink
native support for oberon token verification (#418)
Browse files Browse the repository at this point in the history
  • Loading branch information
fundthmcalculus authored May 17, 2022
1 parent da3ecea commit e03eb8a
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 46 deletions.
36 changes: 20 additions & 16 deletions include/okapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
1 change: 0 additions & 1 deletion native/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
5 changes: 5 additions & 0 deletions native/src/ffi/oberon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
75 changes: 49 additions & 26 deletions native/src/oberon/mod.rs
Original file line number Diff line number Diff line change
@@ -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<CreateOberonKeyResponse, Error<'a>> {
Expand All @@ -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) {
Expand All @@ -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() {
Expand All @@ -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() {
Expand All @@ -106,10 +104,7 @@ impl crate::Oberon {
}

pub fn blind<'a>(request: &BlindOberonTokenRequest) -> Result<BlindOberonTokenResponse, Error<'a>> {
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() {
Expand All @@ -130,10 +125,7 @@ impl crate::Oberon {
}

pub fn unblind<'a>(request: &UnBlindOberonTokenRequest) -> Result<BlindOberonTokenResponse, Error<'a>> {
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() {
Expand All @@ -152,4 +144,35 @@ impl crate::Oberon {
token: tkn.to_bytes().to_vec(),
})
}

pub fn verify_token<'a>(request: &VerifyOberonTokenRequest) -> Result<VerifyOberonTokenResponse, Error<'a>> {
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 })
}
}
20 changes: 20 additions & 0 deletions native/src/proto/okapi/okapi_security.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,23 @@ pub struct UnBlindOberonTokenResponse {
#[prost(bytes = "vec", tag = "1")]
pub token: ::prost::alloc::vec::Vec<u8>,
}
/// 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<u8>,
/// token is valid to this public key?
#[prost(bytes = "vec", tag = "2")]
pub pk: ::prost::alloc::vec::Vec<u8>,
/// public part of oberon protocol - can be any data
#[prost(bytes = "vec", tag = "3")]
pub data: ::prost::alloc::vec::Vec<u8>,
}
/// 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,
}
2 changes: 0 additions & 2 deletions native/src/proto/pbmse.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
mod pbmse;

/// JWS
/// Protocol buffer message signing and encryption
#[derive(::serde::Serialize, ::serde::Deserialize, Clone, PartialEq, ::prost::Message)]
Expand Down
32 changes: 32 additions & 0 deletions native/src/tests/oberon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![] };
Expand Down
12 changes: 12 additions & 0 deletions proto/okapi/security/v1/security.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
1 change: 0 additions & 1 deletion python/tests/test_oberon.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import base64
import unittest

from trinsicokapi import oberon
Expand Down

0 comments on commit e03eb8a

Please sign in to comment.