diff --git a/Cargo.lock b/Cargo.lock
index c4f3e15aeb81b584d3cda5718079ff621b768ef5..af58821b0ff9a6a179bf099eb6cab7e5f89160cb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -413,7 +413,7 @@ dependencies = [
  "clap 4.5.1",
  "config",
  "env_logger 0.10.2",
- "jsonwebtoken",
+ "jsonwebtoken 9.2.0",
  "jwt-simple",
  "kbs-types",
  "lazy_static",
@@ -742,6 +742,12 @@ version = "0.21.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
 
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
 [[package]]
 name = "base64-url"
 version = "2.0.2"
@@ -842,6 +848,12 @@ version = "2.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
 
+[[package]]
+name = "bitmask"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead"
+
 [[package]]
 name = "block-buffer"
 version = "0.10.4"
@@ -936,6 +948,28 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "ccatoken"
+version = "0.1.0"
+source = "git+https://github.com/veraison/rust-ccatoken?branch=main#cd2cac3fac8a277c4aa4e7b1abbab0d6fb447694"
+dependencies = [
+ "base64 0.21.7",
+ "bitmask",
+ "ciborium",
+ "clap 4.5.1",
+ "cose-rust",
+ "ear",
+ "hex",
+ "hex-literal",
+ "jsonwebtoken 9.2.0",
+ "multimap 0.9.1",
+ "openssl",
+ "serde",
+ "serde_json",
+ "serde_with 3.8.1",
+ "thiserror",
+]
+
 [[package]]
 name = "cexpr"
 version = "0.6.0"
@@ -1391,8 +1425,18 @@ version = "0.13.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
 dependencies = [
- "darling_core",
- "darling_macro",
+ "darling_core 0.13.4",
+ "darling_macro 0.13.4",
+]
+
+[[package]]
+name = "darling"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
+dependencies = [
+ "darling_core 0.20.8",
+ "darling_macro 0.20.8",
 ]
 
 [[package]]
@@ -1409,17 +1453,42 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "darling_core"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.10.0",
+ "syn 2.0.51",
+]
+
 [[package]]
 name = "darling_macro"
 version = "0.13.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
 dependencies = [
- "darling_core",
+ "darling_core 0.13.4",
  "quote",
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "darling_macro"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
+dependencies = [
+ "darling_core 0.20.8",
+ "quote",
+ "syn 2.0.51",
+]
+
 [[package]]
 name = "dashmap"
 version = "5.5.3"
@@ -1482,6 +1551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
 dependencies = [
  "powerfmt",
+ "serde",
 ]
 
 [[package]]
@@ -1550,13 +1620,14 @@ checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
 [[package]]
 name = "ear"
 version = "0.1.2"
-source = "git+https://github.com/veraison/rust-ear?rev=43f7f480d09ea2ebc03137af8fbcd70fe3df3468#43f7f480d09ea2ebc03137af8fbcd70fe3df3468"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a803b607ab6a71337ed90f753f7390aa706a1e1de8c5a1406420477f2ed3578"
 dependencies = [
  "base64 0.21.7",
  "ciborium",
  "cose-rust",
  "hex",
- "jsonwebtoken",
+ "jsonwebtoken 8.3.0",
  "openssl",
  "phf",
  "serde",
@@ -2053,6 +2124,15 @@ name = "hex"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "hex-literal"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
 
 [[package]]
 name = "hkdf"
@@ -2277,6 +2357,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
  "autocfg",
  "hashbrown 0.12.3",
+ "serde",
 ]
 
 [[package]]
@@ -2287,6 +2368,7 @@ checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
 dependencies = [
  "equivalent",
  "hashbrown 0.14.3",
+ "serde",
 ]
 
 [[package]]
@@ -2398,6 +2480,20 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "jsonwebtoken"
+version = "8.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378"
+dependencies = [
+ "base64 0.21.7",
+ "pem 1.1.1",
+ "ring 0.16.20",
+ "serde",
+ "serde_json",
+ "simple_asn1",
+]
+
 [[package]]
 name = "jsonwebtoken"
 version = "9.2.0"
@@ -2406,7 +2502,7 @@ checksum = "5c7ea04a7c5c055c175f189b6dc6ba036fd62306b58c66c9f6389036c503a3f4"
 dependencies = [
  "base64 0.21.7",
  "js-sys",
- "pem",
+ "pem 3.0.3",
  "ring 0.17.8",
  "serde",
  "serde_json",
@@ -2781,6 +2877,15 @@ version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
 
+[[package]]
+name = "multimap"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1a5d38b9b352dbd913288736af36af41c48d61b1a8cd34bcecd727561b7d511"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "native-tls"
 version = "0.2.11"
@@ -3207,6 +3312,15 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
 
+[[package]]
+name = "pem"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8"
+dependencies = [
+ "base64 0.13.1",
+]
+
 [[package]]
 name = "pem"
 version = "3.0.3"
@@ -3579,7 +3693,7 @@ dependencies = [
  "itertools 0.10.5",
  "lazy_static",
  "log",
- "multimap",
+ "multimap 0.8.3",
  "petgraph",
  "prettyplease",
  "prost",
@@ -4372,7 +4486,25 @@ dependencies = [
  "base64 0.13.1",
  "chrono",
  "serde",
- "serde_with_macros",
+ "serde_with_macros 1.5.2",
+]
+
+[[package]]
+name = "serde_with"
+version = "3.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20"
+dependencies = [
+ "base64 0.22.1",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.2.3",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_with_macros 3.8.1",
+ "time",
 ]
 
 [[package]]
@@ -4381,12 +4513,24 @@ version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
 dependencies = [
- "darling",
+ "darling 0.13.4",
  "proc-macro2",
  "quote",
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "serde_with_macros"
+version = "3.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2"
+dependencies = [
+ "darling 0.20.8",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.51",
+]
+
 [[package]]
 name = "serial_test"
 version = "0.9.0"
@@ -5405,14 +5549,14 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 [[package]]
 name = "veraison-apiclient"
 version = "0.0.1"
-source = "git+https://github.com/chendave/rust-apiclient?branch=token#64fb77855bf0ce14e3167deec62cb2da3e6421c0"
+source = "git+https://github.com/thomas-fossati/rust-apiclient?branch=token#67b9bd75eda5ea9b283d9140adb407fd62877f73"
 dependencies = [
  "base64 0.13.1",
  "chrono",
  "jsonwebkey",
  "reqwest",
  "serde",
- "serde_with",
+ "serde_with 1.14.0",
  "thiserror",
  "url",
 ]
@@ -5430,6 +5574,7 @@ dependencies = [
  "base64 0.21.7",
  "bincode",
  "byteorder",
+ "ccatoken",
  "cfg-if",
  "codicon",
  "csv-rs",
@@ -5437,7 +5582,7 @@ dependencies = [
  "eventlog-rs",
  "hex",
  "jsonwebkey",
- "jsonwebtoken",
+ "jsonwebtoken 9.2.0",
  "kbs-types",
  "log",
  "openssl",
diff --git a/attestation-service/verifier/Cargo.toml b/attestation-service/verifier/Cargo.toml
index ef0cd9df9c0c307364c25cbf0d7e077d4029c59f..ec3d0ddb6b42f5eb7ace47f874e48a078355176b 100644
--- a/attestation-service/verifier/Cargo.toml
+++ b/attestation-service/verifier/Cargo.toml
@@ -12,7 +12,7 @@ az-snp-vtpm-verifier = [ "az-snp-vtpm", "sev", "snp-verifier" ]
 az-tdx-vtpm-verifier = [ "az-tdx-vtpm", "openssl", "tdx-verifier" ]
 snp-verifier = [ "asn1-rs", "openssl", "sev", "x509-parser" ]
 csv-verifier = [ "openssl", "csv-rs", "codicon" ]
-cca-verifier = [ "ear", "jsonwebtoken", "veraison-apiclient" ]
+cca-verifier = [ "ear", "jsonwebtoken", "veraison-apiclient", "ccatoken" ]
 
 [dependencies]
 anyhow.workspace = true
@@ -41,8 +41,9 @@ serde_json.workspace = true
 sev = { version = "1.2.0", features = ["openssl", "snp"], optional = true }
 sgx-dcap-quoteverify-rs = { git = "https://github.com/intel/SGXDataCenterAttestationPrimitives", tag = "DCAP_1.16", optional = true }
 strum.workspace = true
-veraison-apiclient = { git = "https://github.com/chendave/rust-apiclient", branch = "token", optional = true }
-ear = { git = "https://github.com/veraison/rust-ear", rev = "43f7f480d09ea2ebc03137af8fbcd70fe3df3468", optional = true }
+veraison-apiclient = { git = "https://github.com/thomas-fossati/rust-apiclient", branch = "token", optional = true }
+ear = { version = "0.1.2", optional = true }
+ccatoken = { git = "https://github.com/veraison/rust-ccatoken", branch = "main", optional = true }
 x509-parser = { version = "0.14.0", optional = true }
 
 [build-dependencies]
diff --git a/attestation-service/verifier/src/cca/config.rs b/attestation-service/verifier/src/cca/config.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ed13341d022f1e97d63c48a5f4cd3b1193b14ed0
--- /dev/null
+++ b/attestation-service/verifier/src/cca/config.rs
@@ -0,0 +1,55 @@
+// Copyright (c) 2024 Linaro Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+use serde::{Deserialize, Serialize};
+use std::fs::File;
+use std::path::Path;
+use thiserror::Error;
+
+pub const DEFAULT_CCA_CONFIG: &str = "/etc/coco-as/cca-config.json";
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct Config {
+    pub local_verifier: Option<LocalVerifier>,
+    pub remote_verifier: Option<RemoteVerifier>,
+}
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct RemoteVerifier {
+    #[serde(alias = "origin")]
+    pub origin: String,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct LocalVerifier {
+    pub ta_store: String,
+    pub rv_store: String,
+}
+
+#[derive(Error, Debug)]
+pub enum ConfigError {
+    #[error("IO error: {0}")]
+    IO(#[from] std::io::Error),
+    #[error("failed to parse CCA config file: {0}")]
+    JsonFileParse(#[source] serde_json::Error),
+}
+
+impl Default for Config {
+    fn default() -> Config {
+        Config {
+            remote_verifier: None,
+            local_verifier: None,
+        }
+    }
+}
+
+impl TryFrom<&Path> for Config {
+    type Error = ConfigError;
+    fn try_from(config_path: &Path) -> Result<Self, ConfigError> {
+        let file = File::open(config_path)?;
+        serde_json::from_reader::<File, Config>(file).map_err(ConfigError::JsonFileParse)
+    }
+}
diff --git a/attestation-service/verifier/src/cca/local.rs b/attestation-service/verifier/src/cca/local.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3281fbfc2cc257e6787b4cd64ba461fc0d0b16dd
--- /dev/null
+++ b/attestation-service/verifier/src/cca/local.rs
@@ -0,0 +1,108 @@
+// Copyright (c) 2023 Arm Ltd.
+// Copyright (c) 2024 Linaro Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+use super::*;
+use base64::engine::general_purpose;
+use ccatoken::store::*;
+use ccatoken::token::Evidence;
+use config::Config;
+use core::result::Result::Ok;
+use ear::{Appraisal, Ear, RawValue, TrustTier, VerifierID};
+use log::debug;
+use std::collections::BTreeMap;
+use std::fs;
+
+pub fn verify(
+    config: Config,
+    token: &Vec<u8>,
+    _expected_report_data: &Vec<u8>,
+) -> Result<Ear, anyhow::Error> {
+    debug!("using config: {:?}", config);
+
+    let ta_store = config.local_verifier.clone().unwrap().ta_store;
+    let rv_store = config.local_verifier.clone().unwrap().rv_store;
+
+    let jta = fs::read_to_string(ta_store).context("loading TA store")?;
+    let jrv = fs::read_to_string(rv_store).context("loading RV store")?;
+
+    let mut tas: MemoTrustAnchorStore = Default::default();
+    tas.load_json(&jta)
+        .context("loading trust anchors from JSON store")?;
+
+    let mut rvs: MemoRefValueStore = Default::default();
+    rvs.load_json(&jrv)
+        .context("loading reference values from JSON store")?;
+
+    let mut e: Evidence = Evidence::decode(&token).context("decoding CCA evidence")?;
+
+    e.verify(&tas).context("verifying CCA evidence")?;
+    e.appraise(&rvs).context("appraising CCA evidence")?;
+
+    let (platform_tv, realm_tvec) = e.get_trust_vectors();
+
+    // Check that the Realm token was correctly signed using the RAK and that
+    // the RAK was correctly attested.
+    if realm_tvec.instance_identity.tier() != TrustTier::Affirming {
+        bail!("RAK signature or RAK attestation could not be verified");
+    }
+
+    // Synthesize TCB claims the way EAR wants to report them.
+    let annotated_evidence =
+        annotated_evidence(&e).context("syntesizing CCA Realm TCB claims-set")?;
+
+    let mut appraisal = Appraisal::new();
+    appraisal.annotated_evidence = annotated_evidence;
+    appraisal.trust_vector = platform_tv;
+    appraisal.update_status_from_trust_vector();
+
+    let ear = Ear {
+        profile: "tag:github.com,2023:veraison/ear".to_string(),
+        vid: VerifierID {
+            build: "CoCo CCA local verifier".to_string(),
+            developer: "https://veraison-project.org".to_string(),
+        },
+        submods: BTreeMap::from([("CCA_SSD_PLATFORM".to_string(), appraisal)]),
+        iat: 0,             // not relevant
+        nonce: None,        // not relevant
+        raw_evidence: None, // not relevant
+    };
+
+    Ok(ear)
+}
+
+fn annotated_evidence(e: &Evidence) -> Result<BTreeMap<String, RawValue>, anyhow::Error> {
+    let pv = general_purpose::STANDARD.encode(e.realm_claims.perso.clone());
+    let rim = general_purpose::STANDARD.encode(e.realm_claims.rim.clone());
+    let rem0 = general_purpose::STANDARD.encode(e.realm_claims.rem[0].clone());
+    let rem1 = general_purpose::STANDARD.encode(e.realm_claims.rem[1].clone());
+    let rem2 = general_purpose::STANDARD.encode(e.realm_claims.rem[2].clone());
+    let rem3 = general_purpose::STANDARD.encode(e.realm_claims.rem[3].clone());
+    let nonce = general_purpose::STANDARD.encode(e.realm_claims.challenge.clone());
+
+    // I am making the choice of reporting only the claims that are related to
+    // (currently) unvalidate pieces of the TCB.  The assumption is that CCA
+    // platform has been already fully validated (including RAK attestation),
+    // and that the only piece of TCB that remains to be validated is the Realm.
+    let j = format!(
+        r#"{{
+            "platform": {{}},
+            "realm": {{
+                "cca-realm-challenge": "{nonce}",
+                "cca-realm-extensible-measurements": [ "{rem0}", "{rem1}", "{rem2}", "{rem3}" ],
+                "cca-realm-hash-algo-id": "",
+                "cca-realm-initial-measurement": "{rim}",
+                "cca-realm-personalization-value": "{pv}",
+                "cca-realm-public-key": "",
+                "cca-realm-public-key-hash-algo-id": ""
+            }}
+        }}
+    "#
+    );
+
+    let tcb = serde_json::from_str(&j)?;
+
+    Ok(tcb)
+}
diff --git a/attestation-service/verifier/src/cca/mod.rs b/attestation-service/verifier/src/cca/mod.rs
index 78fc0ce362dbe2506702e80838ed6f104f3ee607..5c279e421b27cb93a957d6c0f309c80413455602 100644
--- a/attestation-service/verifier/src/cca/mod.rs
+++ b/attestation-service/verifier/src/cca/mod.rs
@@ -1,4 +1,5 @@
 // Copyright (c) 2023 Arm Ltd.
+// Copyright (c) 2024 Linaro Ltd.
 //
 // SPDX-License-Identifier: Apache-2.0
 //
@@ -9,15 +10,18 @@ use async_trait::async_trait;
 use base64::Engine;
 use core::result::Result::Ok;
 use ear::{Ear, RawValue};
-use jsonwebtoken::{self as jwt};
-use log::{debug, error, info};
+use log::{debug, info};
 use serde::{Deserialize, Serialize};
+use std::path::Path;
 use std::{collections::BTreeMap, str};
 use veraison_apiclient::*;
 
-const VERAISON_ADDR: &str = "VERAISON_ADDR";
-const DEFAULT_VERAISON_ADDR: &str = "localhost:8080";
-const MEDIA_TYPE: &str = "application/eat-collection; profile=http://arm.com/CCA-SSD/1.0.0";
+mod config;
+use config::{Config, DEFAULT_CCA_CONFIG};
+mod local;
+mod remote;
+
+const CCA_CONFIG_FILE: &str = "CCA_CONFIG_FILE";
 
 #[derive(Debug, Default)]
 pub struct CCA {}
@@ -35,8 +39,8 @@ pub struct SwComponent {
 #[derive(Debug, Default, Serialize, Deserialize)]
 #[serde(rename_all = "kebab-case")]
 pub struct CcaPlatformClaims {
-    pub cca_platform_challenge: String,
-    pub cca_platform_sw_components: Vec<SwComponent>,
+    pub cca_platform_challenge: Option<String>,
+    pub cca_platform_sw_components: Option<Vec<SwComponent>>,
 }
 
 #[derive(Debug, Default, Serialize, Deserialize)]
@@ -50,7 +54,7 @@ pub struct RealmClaims {
 }
 
 #[derive(Debug, Default, Serialize, Deserialize)]
-struct Evidence {
+struct EvidenceClaimsSet {
     realm: RealmClaims,
     platform: CcaPlatformClaims,
 }
@@ -61,17 +65,6 @@ struct CcaEvidence {
     token: Vec<u8>,
 }
 
-fn my_evidence_builder(
-    nonce: &[u8],
-    accept: &[String],
-    token: Vec<u8>,
-) -> Result<(Vec<u8>, String), veraison_apiclient::Error> {
-    info!("server challenge: {:?}", nonce);
-    info!("acceptable media types: {:#?}", accept);
-    // TODO: Get the CCA media type from the slice of `accept`.
-    Ok((token, MEDIA_TYPE.to_string()))
-}
-
 #[async_trait]
 impl Verifier for CCA {
     async fn evaluate(
@@ -80,6 +73,14 @@ impl Verifier for CCA {
         expected_report_data: &ReportData,
         expected_init_data_hash: &InitDataHash,
     ) -> Result<TeeEvidenceParsedClaim> {
+        let config_file =
+            std::env::var(CCA_CONFIG_FILE).unwrap_or_else(|_| DEFAULT_CCA_CONFIG.to_string());
+
+        let config = Config::try_from(Path::new(&config_file))
+            .map_err(|e| anyhow!("parsing {config_file}: {e}"))?;
+
+        debug!("remote verifier: {:?}", config.remote_verifier);
+
         let ReportData::Value(expected_report_data) = expected_report_data else {
             bail!("CCA verifier must provide report data field!");
         };
@@ -89,54 +90,21 @@ impl Verifier for CCA {
         let evidence = serde_json::from_slice::<CcaEvidence>(evidence)
             .context("Deserialize CCA Evidence failed.")?;
 
-        let host_url =
-            std::env::var(VERAISON_ADDR).unwrap_or_else(|_| DEFAULT_VERAISON_ADDR.to_string());
-
-        let discovery = Discovery::from_base_url(format!("http://{:}", host_url))?;
-
-        let verification_api = discovery.get_verification_api().await?;
+        let ear: Ear;
 
-        let relative_endpoint = verification_api
-            .get_api_endpoint("newChallengeResponseSession")
-            .context("Failed to discover the verification endpoint details.")?;
-
-        let api_endpoint = format!("http://{:}{}", host_url, relative_endpoint);
-
-        // create a ChallengeResponse object
-        let cr = ChallengeResponseBuilder::new()
-            .with_new_session_url(api_endpoint)
-            .build()?;
-
-        let token = evidence.token;
-        let n = Nonce::Value(expected_report_data.clone());
-        let result = match cr.run(n, my_evidence_builder, token.clone()).await {
-            Err(e) => {
-                error!("Error: {}", e);
-                bail!("CCA Attestation failed with error: {:?}", e);
-            }
-            Ok(attestation_result) => attestation_result,
+        if config.remote_verifier.is_some() {
+            ear = remote::verify(config, &evidence.token, &expected_report_data).await?;
+        } else {
+            ear = local::verify(config, &evidence.token, &expected_report_data)?;
         };
 
-        // Get back the pub key to decrypt the ear which holds raw evidence and the session nonce
-        let public_key_pem = verification_api.ear_verification_key_as_pem()?;
-        let dk = jwt::DecodingKey::from_ec_pem(public_key_pem.as_bytes())
-            .context("get the decoding key from the pem public key")?;
-        let plain_ear = Ear::from_jwt(result.as_str(), jwt::Algorithm::ES256, &dk)
-            .context("decrypt the ear with the decoding key")?;
+        info!("EAR: {:?}", ear);
 
-        let ear_nonce = plain_ear.nonce.context("get nonce from ear")?;
-        let nonce_byte = base64::engine::general_purpose::URL_SAFE
-            .decode(ear_nonce.to_string())
-            .context("decode nonce byte from ear")?;
-
-        if expected_report_data != nonce_byte {
-            bail!("report data is different from that in ear's session nonce");
-        }
-
-        let cca_mod = match plain_ear.submods.get("CCA_SSD_PLATFORM") {
+        let cca_mod = match ear.submods.get("CCA_SSD_PLATFORM") {
             Some(value) => value,
             None => bail!("no entry found for CCA_SSD_PLATFORM"),
         };
+
         let evidence = &cca_mod.annotated_evidence;
 
         // NOTE: CCA validation by the Verasion has some overlapping with the RVPS, the similar validation has been done by the Verasion already.
@@ -144,7 +112,7 @@ impl Verifier for CCA {
         let tcb = parse_cca_evidence(evidence)?;
 
         if let InitDataHash::Value(expected_init_data_hash) = expected_init_data_hash {
-            debug!("Check the binding of init data.");
+            debug!("Check the binding of init data");
             if *expected_init_data_hash
                 != base64::engine::general_purpose::STANDARD
                     .decode(&tcb.realm.cca_realm_personalization_value)
@@ -228,11 +196,11 @@ impl Verifier for CCA {
 ///    }
 /// }
 /// NOTE: each of the value are base64 encoded hex value.
-fn parse_cca_evidence(evidence_map: &BTreeMap<String, RawValue>) -> Result<Evidence> {
-    let mut evidence = Evidence::default();
+fn parse_cca_evidence(evidence_map: &BTreeMap<String, RawValue>) -> Result<EvidenceClaimsSet> {
+    let mut evidence = EvidenceClaimsSet::default();
     let platfrom = evidence_map
         .get("platform")
-        .context("get platform evidence from the cca evidence map")?;
+        .context("get platform evidence from the CCA evidence map")?;
     let output = serde_json::to_string(platfrom)?;
     let p: CcaPlatformClaims = serde_json::from_str(output.as_str())?;
     evidence.platform = p;
@@ -247,7 +215,7 @@ fn parse_cca_evidence(evidence_map: &BTreeMap<String, RawValue>) -> Result<Evide
     Ok(evidence)
 }
 
-fn cca_generate_parsed_claim(tcb: Evidence) -> Result<TeeEvidenceParsedClaim> {
+fn cca_generate_parsed_claim(tcb: EvidenceClaimsSet) -> Result<TeeEvidenceParsedClaim> {
     let v = serde_json::to_value(tcb).context("build json value from the cca evidence")?;
     Ok(v as TeeEvidenceParsedClaim)
 }
@@ -261,7 +229,7 @@ mod tests {
     fn test_cca_generate_parsed_claim() {
         let s = fs::read("./test_data/cca-claims.json").unwrap();
         let evidence = String::from_utf8_lossy(&s);
-        let tcb = serde_json::from_str::<Evidence>(&evidence).unwrap();
+        let tcb = serde_json::from_str::<EvidenceClaimsSet>(&evidence).unwrap();
         let parsed_claim = cca_generate_parsed_claim(tcb);
         assert!(parsed_claim.is_ok());
         let _ = fs::write(
diff --git a/attestation-service/verifier/src/cca/remote.rs b/attestation-service/verifier/src/cca/remote.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5732ee8e93a30e3709e035873e56a33d105b11d4
--- /dev/null
+++ b/attestation-service/verifier/src/cca/remote.rs
@@ -0,0 +1,85 @@
+// Copyright (c) 2023 Arm Ltd.
+// Copyright (c) 2024 Linaro Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+use super::*;
+use base64::Engine;
+use config::Config;
+use core::result::Result::Ok;
+use ear::Ear;
+use log::{debug, error};
+use std::str;
+
+const MEDIA_TYPE: &str = "application/eat-collection; profile=http://arm.com/CCA-SSD/1.0.0";
+
+fn evidence_builder(
+    nonce: &[u8],
+    accept: &[String],
+    token: Vec<u8>,
+) -> Result<(Vec<u8>, String), veraison_apiclient::Error> {
+    debug!("server challenge: {:?}", nonce);
+    debug!("acceptable media types: {:#?}", accept);
+    // TODO: Get the CCA media type from the slice of `accept`.
+    Ok((token, MEDIA_TYPE.to_string()))
+}
+
+pub async fn verify(
+    config: Config,
+    token: &Vec<u8>,
+    expected_report_data: &Vec<u8>,
+) -> Result<Ear, anyhow::Error> {
+    let host_url = config.remote_verifier.unwrap().origin;
+
+    let discovery = Discovery::from_base_url(host_url.clone())?;
+
+    let verification_api = discovery.get_verification_api().await?;
+
+    let relative_endpoint = verification_api
+        .get_api_endpoint("newChallengeResponseSession")
+        .context("discovering verification endpoint details")?;
+
+    let api_endpoint = format!("{:}{}", host_url, relative_endpoint);
+
+    let cr = ChallengeResponseBuilder::new()
+        .with_new_session_url(api_endpoint)
+        .build()?;
+
+    let n = Nonce::Value(expected_report_data.clone());
+
+    let result = match cr.run(n, evidence_builder, token.clone()).await {
+        Err(e) => {
+            error!("Error: {}", e);
+            bail!("remote verification failed with error: {:?}", e);
+        }
+        Ok(attestation_result) => attestation_result,
+    };
+
+    let verifier_pkey = verification_api.ear_verification_key_as_string();
+
+    let plain_ear = Ear::from_jwt_jwk(
+        result.as_str(),
+        ear::Algorithm::ES256,
+        verifier_pkey.as_bytes(),
+    )
+    .context("decrypting EAR with the decoding key")?;
+
+    if let Some(ref ear_nonce) = plain_ear.nonce {
+        let nonce_byte = base64::engine::general_purpose::URL_SAFE
+            .decode(ear_nonce.to_string())
+            .context("base64-decoding nonce from EAR")?;
+
+        if *expected_report_data != nonce_byte {
+            bail!(
+                "nonce verification failed: want {:02x?}, got {:02x?}",
+                *expected_report_data,
+                nonce_byte
+            );
+        }
+    } else {
+        bail!("no nonce found in EAR")
+    }
+
+    Ok(plain_ear)
+}
diff --git a/cca/NOTES.md b/cca/NOTES.md
index eee5b6ba57aee58de13b23d2ff3cce79f06b93c3..bf1ddc8f36434847af8105f2b80fff92cc58fd39 100644
--- a/cca/NOTES.md
+++ b/cca/NOTES.md
@@ -103,43 +103,32 @@ RUST_LOG=debug ${TRUSTEE_SRC}/target/release/rvps -c ${TRUSTEE_SRC}/kbs/config/r
 
 ## AS
 
-```sh
-env VERAISON_ADDR=veraison.example:8080 RUST_LOG=debug ${TRUSTEE_SRC}/target/debug/grpc-as -c ${TRUSTEE_SRC}/kbs/config/as-config.json -s 127.0.0.1:50004
-```
-
-## Test using `grpcurl(1)`
-
-Install `grpcurl`:
-```sh
-go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
-```
+local verifier:
 
 ```sh
-${TRUSTEE_SRC}/cca/scripts/request.sh
+CCA_CONFIG_FILE=${TRUSTEE_SRC}/cca/misc/cca-config-local.json \
+VERAISON_ADDR=213.146.141.101:8080 \
+RUST_LOG=debug \
+  ${TRUSTEE_SRC}/target/debug/grpc-as \
+    -c ${TRUSTEE_SRC}/cca/misc/as-config.json \
+    -s 127.0.0.1:50004
 ```
 
-# Shell Utils
-
-## Base64Url functions
-
-`base64(1)` does not handle the URL-safe alphabet.
-
-`base64url.sh` provides two functions (`base64url::encode` and `base64url::decode`) that can be used instead:
+remote verifier:
 
 ```sh
-source ${TRUSTEE_SRC}/cca/scripts/base64url.sh` 
+CCA_CONFIG_FILE=${TRUSTEE_SRC}/cca/misc/cca-config-remote.json \
+VERAISON_ADDR=213.146.141.101:8080 \
+RUST_LOG=debug \
+  ${TRUSTEE_SRC}/target/debug/grpc-as \
+    -c ${TRUSTEE_SRC}/cca/misc/as-config.json \
+    -s 127.0.0.1:50004
 ```
 
-## Tokenise Script
-
-The `${TRUSTEE_SRC}/cca/scripts/tokenise.sh` script takes a raw CCA attestation token in CBOR format and wraps it the (pretty peculiar) way the gRPC interface expects it to be consumed.
-
-The CBOR file is passed as argument, e.g.:
+## KBC simulator
 
 ```sh
-${TRUSTEE_SRC}cca/scripts/tokenise.sh \
-  ${TRUSTEE_SRC}/cca/misc/cca-example-token.cbor \
-  > cca-example-token-grpc-request.json 
+${TRUSTEE_SRC}/cca/scripts/kbs-client.sh
 ```
 
 # Veraison Services
diff --git a/cca/misc/as-config.json b/cca/misc/as-config.json
new file mode 100644
index 0000000000000000000000000000000000000000..8e42f2c8d913230ab92a2b13511b2a15e6dfcd2c
--- /dev/null
+++ b/cca/misc/as-config.json
@@ -0,0 +1,11 @@
+{
+    "work_dir": "/opt/confidential-containers/attestation-service",
+    "policy_engine": "opa",
+    "rvps_config": {
+	    "remote_addr":"http://localhost:50003"
+    },
+    "attestation_token_broker": "Simple",
+    "attestation_token_config": {
+        "duration_min": 5
+    }
+}
diff --git a/cca/misc/cca-config-local.json b/cca/misc/cca-config-local.json
new file mode 100644
index 0000000000000000000000000000000000000000..221cbaae805ada92111e4b72844897c568204592
--- /dev/null
+++ b/cca/misc/cca-config-local.json
@@ -0,0 +1,6 @@
+{
+  "local-verifier": {
+    "ta-store": "/Users/tho/Code/git.codelinaro.org/linaro/dcap/cca-demos/coco-trustee/cca/misc/tastore.json",
+    "rv-store": "/Users/tho/Code/git.codelinaro.org/linaro/dcap/cca-demos/coco-trustee/cca/misc/rvstore.json"
+  }
+}
diff --git a/cca/misc/cca-config-remote.json b/cca/misc/cca-config-remote.json
new file mode 100644
index 0000000000000000000000000000000000000000..da4c01df934d7c91b5d1ca329947583228146eaf
--- /dev/null
+++ b/cca/misc/cca-config-remote.json
@@ -0,0 +1,5 @@
+{
+  "remote-verifier": {
+    "origin": "http://213.146.141.101:8080"
+  }
+}
diff --git a/cca/misc/cca-config.json b/cca/misc/cca-config.json
new file mode 100644
index 0000000000000000000000000000000000000000..da4c01df934d7c91b5d1ca329947583228146eaf
--- /dev/null
+++ b/cca/misc/cca-config.json
@@ -0,0 +1,5 @@
+{
+  "remote-verifier": {
+    "origin": "http://213.146.141.101:8080"
+  }
+}
diff --git a/cca/misc/rvstore.json b/cca/misc/rvstore.json
new file mode 100644
index 0000000000000000000000000000000000000000..a2168486377ed31ccaf79345c4c61b62d7d675a7
--- /dev/null
+++ b/cca/misc/rvstore.json
@@ -0,0 +1,101 @@
+{
+  "platform": [
+    {
+      "implementation-id": "7f454c4602010100000000000000000003003e00010000005058000000000000",
+      "sw-components": [
+        {
+          "measurement-value": "9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "RSE_BL1_2"
+        },
+        {
+          "measurement-value": "53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "RSE_BL2"
+        },
+        {
+          "measurement-value": "1121cfccd5913f0a63fec40a6ffd44ea64f9dc135c66634ba001d10bcf4302a2",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "RSE_S"
+        },
+        {
+          "measurement-value": "1571b5ec78bd68512bf7830bb6a2a44b2047c7df57bce79eb8a1c0e5bea0a501",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "AP_BL1"
+        },
+        {
+          "measurement-value": "10159baf262b43a92d95db59dae1f72c645127301661e0a3ce4e38b295a97c58",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "AP_BL2"
+        },
+        {
+          "measurement-value": "10122e856b3fcd49f063636317476149cb730a1aa1cfaad818552b72f56d6f68",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "SCP_BL1"
+        },
+        {
+          "measurement-value": "aa67a169b0bba217aa0aa88a65346920c84c42447c36ba5f7ea65f422c1fe5d8",
+          "signer-id": "f14b4987904bcb5814e4459a057ed4d20f58a633152288a761214dcd28780b56",
+          "version": null,
+          "component-type": "SCP_BL2"
+        },
+        {
+          "measurement-value": "2e6d31a5983a91251bfae5aefa1c0a19d8ba3cf601d0e8a706b4cfa9661a6b8a",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "AP_BL31"
+        },
+        {
+          "measurement-value": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "RMM"
+        },
+        {
+          "measurement-value": "1a252402972f6057fa53cc172b52b9ffca698e18311facd0f3b06ecaaef79e17",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "HW_CONFIG"
+        },
+        {
+          "measurement-value": "9a92adbc0cee38ef658c71ce1b1bf8c65668f166bfb213644c895ccb1ad07a25",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "FW_CONFIG"
+        },
+        {
+          "measurement-value": "238903180cc104ec2c5d8b3f20c5bc61b389ec0a967df8cc208cdc7cd454174f",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "TB_FW_CONFIG"
+        },
+        {
+          "measurement-value": "e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017",
+          "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3",
+          "version": null,
+          "component-type": "SOC_FW_CONFIG"
+        }
+      ],
+      "platform-configuration": "cfcfcfcf"
+    }
+  ],
+  "realm": [
+    {
+      "initial-measurement": "ae87fbf7f105e71a7827cb6f0896889c73d2beaf4830d4f9cb05e3e96c3fc7b4faa04caf36a26bd094cf9c7b738617e4c189aaee184dedda5d7a03c39dbdac7c",
+      "rak-hash-algorithm": "sha-256",
+      "extensible-measurements": [
+        "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+        "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+        "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+        "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+      ],
+      "personalization-value": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/cca/misc/tastore.json b/cca/misc/tastore.json
new file mode 100644
index 0000000000000000000000000000000000000000..e35b3a618225733bd290334ac1ca78c2d24fb989
--- /dev/null
+++ b/cca/misc/tastore.json
@@ -0,0 +1,12 @@
+[
+  {
+    "pkey": {
+  "crv": "P-384",
+  "kty": "EC",
+  "x": "IShnxS4rlQiwpCCpBWDzlNLfqiG911FP8akBr-fh94uxHU5m-Kijivp2r2oxxN6M",
+  "y": "hM4tr8mWQli1P61xh3T0ViDREbF26DGOEYfbAjWjGNN7pZf-6A4OTHYqEryz6m7U"
+},
+    "implementation-id": "7f454c4602010100000000000000000003003e00010000005058000000000000",
+    "instance-id": "0107060504030201000f0e0d0c0b0a090817161514131211101f1e1d1c1b1a1918"
+  }
+]
\ No newline at end of file
diff --git a/cca/scripts/base64url.sh b/cca/scripts/base64url.sh
deleted file mode 100644
index 395745fb3661a6c29b30d76e104e1348eb018e43..0000000000000000000000000000000000000000
--- a/cca/scripts/base64url.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-base64url::encode() {
-	base64 -w0 \
-		| tr '+/' '-_' \
-		| tr -d '=';
-}
-
-base64url::decode() {
-	awk '{ if (length($0) % 4 == 3) print $0"="; else if (length($0) % 4 == 2) print $0"=="; else print $0; }' \
-		| tr -- '-_' '+/' \
-		| base64 -d;
-}
diff --git a/cca/scripts/endorse-rim.sh b/cca/scripts/endorse-rim.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d4e01effa4e035027c4f8b0f61b84e28162c9e6a
--- /dev/null
+++ b/cca/scripts/endorse-rim.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+set -eu
+set -o pipefail
+
+RIM=${RIM:?must be set to the expected RIM value (b64-encoded)}
+
+_d="$(dirname $(readlink -f $0))"
+
+source "${_d}/func.sh"
+
+tmp_dir="$(mktemp -d)"
+trap 'rm -rf -- "$tmp_dir"' EXIT
+
+rv="${tmp_dir}/rim-rv.json"
+
+cat << EOF > ${rv}
+{
+  "cca.realm.cca-realm-initial-measurement": [
+    "${RIM}"
+  ]
+}
+EOF
+
+blob="${tmp_dir}/blob.json"
+
+payload=$(cat ${rv} | ${_base64})
+cat << EOF > ${blob}
+{
+    "version" : "0.1.0",
+    "type": "sample",
+    "payload": "$payload"
+}
+EOF
+
+echo ">>> submitting ${blob} to RVPS"
+
+${_d}/../../target/release/rvps-tool register -p ${blob}
+
+echo ">>> done!"
diff --git a/cca/scripts/func.sh b/cca/scripts/func.sh
new file mode 100644
index 0000000000000000000000000000000000000000..22504081b5fbe0b3999e2683a252e82aaee9c34d
--- /dev/null
+++ b/cca/scripts/func.sh
@@ -0,0 +1,44 @@
+if [ $(uname) == "Darwin" ]
+then
+  _base64="base64"
+  _sed="gsed"
+  _tokenise="mac_tokenise"
+else
+  _base64="base64 -w0"
+  _sed="sed"
+  _tokenise="linux_tokenise"
+fi
+
+function mac_tokenise() {
+  local token="$1"
+
+  od -v -t u1 "${token}" \
+    | cut -c11- \
+    | tr -s '[:blank:]' '\n' \
+    | ${_sed} -e 's/$/,/' \
+    | ${_sed} -e '$s/,//' \
+    | tr -d '\n'
+}
+
+function linux_tokenise() {
+  local token="$1"
+
+  od -v -w1 -t u1 ${token} \
+    | cut -c9- \
+    | ${_sed} -e 's/$/,/' \
+    | ${_sed} '$d' \
+    | ${_sed} -e '$s/,//' \
+    | tr -d '\n'
+}
+
+base64url_encode() {
+	${_base64} \
+		| tr '+/' '-_' \
+		| tr -d '=';
+}
+
+base64url_decode() {
+	awk '{ if (length($0) % 4 == 3) print $0"="; else if (length($0) % 4 == 2) print $0"=="; else print $0; }' \
+		| tr -- '-_' '+/' \
+		| base64 -d;
+}
diff --git a/cca/scripts/request.sh b/cca/scripts/grpc-request.sh
similarity index 100%
rename from cca/scripts/request.sh
rename to cca/scripts/grpc-request.sh
diff --git a/cca/scripts/kbs-client.sh b/cca/scripts/kbs-client.sh
index 443e2be570534ddf7037b215d6197345d1d0811c..18831fa5367da4d4662d561864c0d500ca1d74dc 100755
--- a/cca/scripts/kbs-client.sh
+++ b/cca/scripts/kbs-client.sh
@@ -1,16 +1,19 @@
 #!/bin/bash
 #
-# TODO(tho)
-# * make per-run temp directory to store all the transient files
-# * make kbs-request on the fly (remove file in ../misc)
+# TODO
 # * check that evcli is in PATH as a precondition
 
 set -euo pipefail
 
 _d="$(dirname $(readlink -f $0))"
 
+source "${_d}/func.sh"
+
+tmp_dir="$(mktemp -d)"
+trap 'rm -rf -- "$tmp_dir"' EXIT
+
 kbs="kbs:8080"
-cookie_jar=cookie.txt
+cookie_jar="${tmp_dir}/cookie.txt"
 
 cp /dev/null ${cookie_jar}
 
@@ -21,7 +24,7 @@ nonce=$(curl -X POST http://${kbs}/kbs/v0/auth \
      -sS \
      -c ${cookie_jar} \
      -H 'Content-Type: application/json' \
-     -d @${_d}/../misc/kbs-request.json | jq .nonce | sed -e 's/"//g')
+     -d @${_d}/../misc/kbs-request.json | jq .nonce | ${_sed} -e 's/"//g')
 
 echo ">>> got nonce: $nonce"
 echo ">>> cookies:"
@@ -33,7 +36,7 @@ p_cpak=${_d}/../misc/cpak-pub.json
 s_cpak=${_d}/../misc/cpak-priv.json
 s_rak=${_d}/../misc/rak-priv.json
 golden=${_d}/../misc/cca-example-token.cbor
-token=/tmp/t.cbor
+token="${tmp_dir}/t.cbor"
 
 nonce_hex=$(cat << EOF | tr -d '\n' | tr -d '[:space:] ' | shasum -a 384 | cut -d' ' -f1
 {
@@ -48,25 +51,17 @@ nonce_hex=$(cat << EOF | tr -d '\n' | tr -d '[:space:] ' | shasum -a 384 | cut -
 EOF
 )"00000000000000000000000000000000"
 
-nonce_b64=$(echo $nonce_hex | xxd -p -r | base64 -w0)
+nonce_b64=$(echo $nonce_hex | xxd -p -r | ${_base64})
 
 echo ">>> computed nonce: $nonce_b64"
 
 evcli cca create -r ${s_rak} -p ${s_cpak} -t ${token} -c <(evcli cca check -k ${p_cpak} -t ${golden} \
-	| tail -1 \
-	| jq --arg n "$nonce_b64" '."cca-realm-delegated-token"."cca-realm-challenge" = $n')
-
-vectoken=$(cat << EOF
-$(od -v -w1 -t u1 ${token} \
-	| cut -c9- \
-	| sed -e 's/$/,/' \
-	| sed '$d' \
-	| sed -e '$s/,//' \
-	| tr -d '\n' )
-EOF
-)
+  | tail -1 \
+  | jq --arg n "$nonce_b64" '."cca-realm-delegated-token"."cca-realm-challenge" = $n')
+
+vectoken="$(${_tokenise} ${token})"
 
-attreq=a.json
+attreq="${tmp_dir}/a.json"
 
 cat << EOF > $attreq
 {
diff --git a/cca/scripts/make-token.sh b/cca/scripts/make-token.sh
deleted file mode 100755
index 8a04763b11bf646ad2a7e1a8376318e5fdeabf04..0000000000000000000000000000000000000000
--- a/cca/scripts/make-token.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-set -exu
-set -o pipefail
-
-_P="$(dirname $(readlink -f $0))"
-
-CPAK_PUB=${_P}/../misc/cpak-pub.json
-CPAK_PRIV=${_P}/../misc/cpak-priv.json
-RAK_PRIV=${_P}/../misc/rak-priv.json
-GOLDEN=${_P}/../misc/cca-example-token.cbor
-TOKEN=t.cbor
-
-NONCE=$1
-
-PAD="0000000000000000000000000000000000000000000000000000000000000000"
-
-NONCE_HEX="$(echo -n $NONCE | base64 -d  | xxd -p -c 256)$PAD"
-
-NONCE=$(echo $NONCE_HEX | xxd -p -r | base64 -w0)
-
-evcli cca create -r ${RAK_PRIV} -p ${CPAK_PRIV} -t ${TOKEN} -c <(evcli cca check -k ${CPAK_PUB} -t ${GOLDEN} \
-	| tail -1 \
-	| jq --arg n "$NONCE" '."cca-realm-delegated-token"."cca-realm-challenge" = $n')
diff --git a/cca/scripts/tokenise.sh b/cca/scripts/tokenise.sh
deleted file mode 100755
index f600b837a20b1c30d1d9b6ce0c5810c3e66d0922..0000000000000000000000000000000000000000
--- a/cca/scripts/tokenise.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-
-set -Eeux
-set -o pipefail
-
-TRUSTEE_SRC="$(dirname $0)/../.."
-. ${TRUSTEE_SRC}/cca/scripts/base64url.sh
-
-CBOR=$1
-
-[ -f "${CBOR}" ] || exit 1
-
-b64=$(cat << EOF
-{ "token": [
-$(od -v -w1 -t u1 ${CBOR} \
-	| cut -c9- \
-	| sed -e 's/$/,/' \
-	| sed '$d' \
-	| sed -e '$s/,//')
-] }
-EOF
-)
-
-cat << EOF
-{
-  "tee": "7",
-  "evidence": "$(echo $b64 | base64url::encode)",
-  "tee-evidence": "$b64",
-  "raw_runtime_data": "bobW2XzHE7xt1D285JGmtAMRwCeov4WjnaY-nORMEyqKEZ0pb65qaZnpvz5EcbDOASRdiJQkwx6JeTs7HWsVBA",
-  "policy_ids": []
-}
-EOF