DEV Community

Cover image for Module 2 — Derived Identities and Secure Export
Antonio Jose Socorro Marin
Antonio Jose Socorro Marin

Posted on

Module 2 — Derived Identities and Secure Export

Executive Summary

This module establishes the methodology to generate derived identities from the identity container defined in Module 1. A derived identity is a secure and verifiable credential that:

Is cryptographically bound to the original container.

Is generated through robust derivation functions (SHA3/SHAKE256).

Is exported in encrypted and signed form to destination devices (smartphones, tokens, federated systems).

Maintains traceability, governance, and compliance with international standards on digital identity, biometrics, and cryptography.

Technical Requirements

Algorithms: ECDSA P-256 for signatures, AES-256-GCM for encryption, SHA3-256/SHAKE256 for hashing and derivation, HMAC-SHA256 for OTP or biometric-derived PINs.

Key custody: Master and signing keys stored in HSM or PKCS#11 modules.

Integrity: SHA3-256 hashes and ECDSA signatures encapsulated in CMS/PKCS#7.

Interoperability: Alignment with ISO/IEC 19794 for biometrics and NIST SP 800-63 for assurance levels (IAL/AAL/FAL).

Privacy: Minimal data exposure; derived credentials include only references and strictly required attributes.

Logical Model of the Derived Identity
{
"derived_id": "",
"source_container_ref": {
"container_uuid": "",
"container_hash": ""
},
"purpose": "mobile-id" | "federation" | "temporary-token",
"assurance": { "IAL": 2, "AAL": 2, "FAL": 1 },
"derived_keys": {
"signing_pub": "",
"enc_pub": ""
},
"validity": {
"not_before": "...",
"not_after": "..."
},
"policies": { "export_restrictions": "...", "usage": "authn,sign" },
"audit": [ ... ],
"integrity": {
"hash": "",
"signature": { "alg": "ECDSA-P256-SHA256", "value": "" }
}
}

Derivation Flow

Authorization: verification of permissions and policies.

Parameters: definition of purpose, validity, and assurance level.

Cryptographic derivation: use SHAKE256 on the container hash + context (purpose, device_id, nonce).

Key generation: in HSM or secure element; issue associated certificates.

Secure packaging: canonicalized JSON structure, signed with ECDSA and encrypted with AES-GCM.

Delivery and audit: secure channel, record of hash and signature stored in evidence repositories.

Cryptographic Design

Seed: SHA3-256(container).

KDF: SHAKE256(seed || context || nonce) → yields K_sign and K_enc.

Encryption: AES-256-GCM with unique IV and AAD including derived_id and container_hash.

Signature: ECDSA P-256 with SHA-256 over canonicalized payload.

Encapsulation: CMS/PKCS#7 optional for export.

Example Code 1 — derive_identity.py

Key derivation and creation of the derived identity.

derive_identity.py

Author: Antonio José Socorro Marín © 2025

import os, json, base64, hashlib
from datetime import datetime, timedelta
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.hashes import SHAKE256
from cryptography.hazmat.primitives.asymmetric import ec

def canonical_json_bytes(obj):
return json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode('utf-8')

def shake256_kdf(seed_bytes, context_bytes, output_len=64):
shake = hashlib.shake_256()
shake.update(seed_bytes + b'|' + context_bytes)
return shake.digest(output_len)

def generate_ecdsa_p256_from_seed(seed_bytes):
digest = hashlib.sha256(seed_bytes).digest()
int_val = int.from_bytes(digest, 'big')
order = int("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16)
priv_int = (int_val % (order - 1)) + 1
return ec.derive_private_key(priv_int, ec.SECP256R1())

def derive_identity(container_hash_hex, purpose, dest_id, validity_days=90):
seed = bytes.fromhex(container_hash_hex)
nonce = os.urandom(16)
context = purpose.encode() + b'|' + dest_id.encode() + b'|' + nonce
km = shake256_kdf(seed, context, 64)
k_sign, k_enc = km[:32], km[32:]
priv_key = generate_ecdsa_p256_from_seed(k_sign)
pub_key = priv_key.public_key()
pub_pem = pub_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode()

derived_id = {
    "derived_id": base64.urlsafe_b64encode(os.urandom(12)).decode().rstrip('='),
    "source_container_ref": {"container_hash": container_hash_hex},
    "purpose": purpose,
    "assurance": {"IAL": 2, "AAL": 2, "FAL": 1},
    "derived_keys": {"signing_pub": pub_pem},
    "validity": {
        "not_before": datetime.utcnow().isoformat() + "Z",
        "not_after": (datetime.utcnow() + timedelta(days=validity_days)).isoformat() + "Z"
    },
    "policies": {"export_restrictions": "no_share", "usage": "authn,sign"},
    "audit": []
}
canonical = canonical_json_bytes(derived_id)
integrity_hash = hashlib.sha3_256(canonical).hexdigest()
derived_id["integrity"] = {"hash": integrity_hash, "alg": "SHA3-256"}
sig = priv_key.sign(canonical, ec.ECDSA(hashes.SHA256()))
derived_id["integrity"]["signature"] = base64.b64encode(sig).decode()
return derived_id, k_enc, nonce
Enter fullscreen mode Exit fullscreen mode

if name == "main":
container_hash = "d2f3a4b5c6d7e8f90123456789abcdef0123456789abcdef0123456789abcdef"
derived, k_enc, nonce = derive_identity(container_hash, "mobile-id", "device-01")
print(json.dumps(derived, indent=2, ensure_ascii=False))

Example Code 2 — export_derived_bundle.py

Packaging and encryption of the derived identity.

export_derived_bundle.py

Author: Antonio José Socorro Marín © 2025

import os, json, base64
from datetime import datetime
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec

def canonical_json_bytes(obj):
return json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode()

def aes_gcm_encrypt(key, plaintext, aad):
iv = os.urandom(12)
aesgcm = AESGCM(key)
ct = aesgcm.encrypt(iv, plaintext, aad)
return iv, ct

def sign_with_ecdsa_p256(priv, data):
return priv.sign(data, ec.ECDSA(hashes.SHA256()))

def pack_and_protect(derived_identity, k_enc, ca_priv):
canonical = canonical_json_bytes(derived_identity)
aad = json.dumps({
"derived_id": derived_identity["derived_id"],
"source_container_hash": derived_identity["source_container_ref"]["container_hash"],
"purpose": derived_identity["purpose"],
"timestamp": datetime.utcnow().isoformat()+"Z"
}, sort_keys=True).encode()
iv, ciphertext = aes_gcm_encrypt(k_enc, canonical, aad)
envelope = {
"version": "1.0",
"metadata": {
"aad": base64.b64encode(aad).decode(),
"iv": base64.b64encode(iv).decode(),
"ciphertext": base64.b64encode(ciphertext).decode(),
"cipher": "AES-256-GCM"
}
}
env_canon = canonical_json_bytes(envelope)
sig = base64.b64encode(sign_with_ecdsa_p256(ca_priv, env_canon)).decode()
return {"envelope": envelope, "signature": {"alg": "ECDSA-P256-SHA256", "value": sig}}

if name == "main":
ca_key = ec.generate_private_key(ec.SECP256R1())
derived_identity = {
"derived_id": "abc123",
"source_container_ref": {"container_hash": "d2f3..."},
"purpose": "mobile-id",
"derived_keys": {"signing_pub": "-----BEGIN PUBLIC KEY-----"},
"validity": {"not_before":"2025-09-28T00:00:00Z","not_after":"2025-12-27T00:00:00Z"}
}
k_enc = os.urandom(32)
package = pack_and_protect(derived_identity, k_enc, ca_key)
print(json.dumps(package, indent=2, ensure_ascii=False))

Operational Checklist

HSM/PKCS#11: generate keys and signatures only inside secure hardware.

CMS/IDMS: define APIs for derivation requests and authorization.

Consent: store proof of subject authorization.

Testbed: reproducible environment with CA, issuer, destination, and evidence repository.

Audit: record container_hash, derived_id, signed bundle, and timestamp.

Revocation: CRL/OCSP specific to derived credentials.

Conclusion

Module 2 defines the architecture, algorithms, and flows required for the creation and secure export of derived identities. This model consolidates traceability, privacy, and compliance, serving as the foundation for later phases such as federation, mobile integration, and temporary credentials.

Top comments (0)