⚠️ Draft — results under revision; refer to the thesis for verified figures.
MSc Thesis · Viva Presentation · 2024–2025

Securing Edge-Based Smart Parking Video Streams using Lightweight Blockchain on NVIDIA Jetson Nano

Draft / offline
Jetson Nano
SHA-256 Hash Chain
AWS DynamoDB · us-east-1
Overview
3
Security layers
14
Experiments (E1–E10 + P1–P4)
390
Measured data points (13 × n=30; E10 n=0)
14
Parking spots monitored
Problem Statement
No integrity mechanism at the IoT edge

CCTV-based smart parking systems capture and transmit video without any cryptographic verification. An attacker with access to the local network or storage can silently inject fake frames, delete real frames, or alter metadata — and existing systems have no way to detect it. Raw video transmitted to the cloud further introduces MITM risk and violates user privacy.

Proposed Solution
Lightweight SHA-256 hash chain on Jetson Nano

Every captured frame is cryptographically hashed using SHA-256. Each hash is chained to the previous, forming a tamper-evident ledger that runs entirely on the Jetson Nano edge device. The integrity mechanism anchors only SHA-256 hashes and detection metadata to AWS DynamoDB for remote verification — data minimisation on the integrity path. (JPEG frame upload to S3 is a separate, configurable operational feature used here for the dashboard/audit; it is not required for integrity verification.)

Research Objectives
Four objectives

RO1 — Design a 3-layer security framework (edge detection, hash chain, cloud anchor) for IoT parking.

RO2 — Implement a lightweight blockchain on resource-constrained hardware without consensus overhead.

RO3 — Validate against 10 defined attack scenarios with statistical rigour (n≥30, CLT, 95% CI). Status: 9 conducted (E1–E9); E10 pending its field session.

RO4 — Quantify performance overhead and assess system sustainability over a 24-hour protocol. Status: met — 23.22 h between first and last sample, 30 samples, no memory leak or CPU drift.

Key Contribution
Edge-first integrity without consensus

Full blockchain consensus (proof-of-work, proof-of-stake) is infeasible on constrained hardware. This work implements a consensus-free SHA-256 hash chain with cloud anchoring on an ARM Cortex-A57 (Jetson Nano) and measures what it does and does not achieve, with n=30 per experiment.

What the measurements support. Tampering that leaves the chain internally inconsistent is detected without exception (E1–E5, E8, E9: 100%, 30/30 each), at a cost the platform absorbs comfortably — no statistically significant CPU overhead, 12,161 hashes/s, O(n) verification, and no memory leak over a 24-hour run.

What they do not. An attacker who recomputes hashes after tampering leaves a self-consistent chain: replay detection is 53.3% (E6) and local chain-rewrite detection is 0% by design (E7). The cloud anchor is architecturally the right answer to both, but as currently built it is not an independent witness — the device holds the credentials that write it, and its anchored_at timestamp is device-generated. Making the checkpoint store append-only and its timestamps server-derived is therefore the load-bearing next step, not an optional hardening.

System Architecture
3-LAYER SECURITY FRAMEWORK · NVIDIA JETSON NANO EDGE NODE → AWS CLOUD ANCHOR
Layer 1
Edge Detection
Video acquisition & occupancy analysis
  • CCTV RTSP stream (rstp_stream.py)
  • Flask web stream (web_stream.py)
  • ROI polygon — 14 spots (rois.json)
  • OpenCV Canny edge detection
  • Thresholds: low=100, high=200
  • Occupancy: Occupied / Empty
  • Interval: 1 frame every 5 seconds
  • Frames stored locally; S3 upload is a configurable feature (UPLOAD_FRAMES_TO_S3), enabled here
Layer 2
Hash Chain Security
Tamper-evident integrity at the edge
  • SHA-256 hash of raw frame bytes
  • Block: index, timestamp, detection_result, frame_hash, previous_hash, block_hash
  • block_hash = SHA-256(json.dumps({block_index, timestamp, frame_hash, detection_result, device_id, previous_hash}, sort_keys=True))
  • Forward chaining: break is localised
  • O(n) sequential verification
  • Genesis block: previous_hash = "0"×64
  • No network consensus required
  • psutil monitors CPU/RAM in real-time
Layer 3
Cloud Anchor
Trusted external checkpoint via AWS
  • DynamoDB checkpoint table
  • Anchor every N blocks (configurable)
  • Uploads: block_index, block_hash, frame_hash, timestamp, device_id, anchored_at
  • Only SHA-256 hashes + metadata required for integrity verification
  • Remote verification via block_hash
  • Detects replay attacks outside anchor window
  • Strong consistency reads (boto3)
  • Region: us-east-1
End-to-End Data Flow
01
CCTV Camera
RTSP://192.168.x.x
02
Frame Capture
1 frame / 5 sec
03
Canny Detection
14 ROI polygons
04
SHA-256 Hash
frame bytes → 64-char hex
05
Block Created
chained to previous_hash
06
Local Storage
frame + chain on Jetson
07
DynamoDB Anchor
hash only ↑ cloud
Hardware — Edge Node
NVIDIA Jetson Nano (4GB)

CPU: ARM Cortex-A57 quad-core @ 1.43 GHz
RAM: 4 GB LPDDR4
Storage: 128 GB eMMC
OS: Ubuntu 18.04 LTS (headless mode)
Access: SSH via VPN tunnel
Python: 3.8 · OpenCV 4.11 · hashlib · boto3 1.37 · psutil 7.2

Hardware — Cloud Anchor
AWS DynamoDB

Table: DynamoDB checkpoint table
Partition key: block_index (String)
Attributes: block_hash, block_index, timestamp, device_id
Consistency: Strong read (boto3)
Region: us-east-1 (N. Virginia)
Access: AWS CLI configured on Jetson · IAM role with DynamoDB PutItem/GetItem

System Operation Flowchart
LAYER LEGEND Layer 1 — Edge Detection Layer 2 — Hash Chain Layer 3 — Cloud Anchor START 01 INITIALIZE Load ROIs · HashChain · AWS config LAYER 1 — EDGE DETECTION 02 RTSP FRAME CAPTURE 1 frame / 5 sec · resize 1020×600 03 CANNY EDGE DETECTION low=100 · high=200 04 OCCUPANCY CLASSIFY 14 ROI polygons · Occupied / Empty LAYER 2 — HASH CHAIN SECURITY 05 SHA-256 HASH frame bytes → 64-char hex digest 06 CREATE BLOCK index · ts · frame_hash · detection · device · previous_hash 07 HASHCHAIN APPEND SQLite · block_hash = SHA-256(…) CHECKPOINT INTERVAL? NO YES LAYER 3 — CLOUD ANCHOR 08 DYNAMODB UPLOAD block_hash · block_index · ts 09 CLOUD VERIFICATION strong read · compare hashes TAMPER DETECTED? YES TAMPER ALERT ⚠ NO 10 WAIT INTERVAL 5s (CAPTURE_INTERVAL_SEC) CONTINUOUS LOOP
Methodology
Hardware Environment
NVIDIA Jetson Nano Developer Kit
CPUARM Cortex-A57 · Quad-core · 1.43 GHz
GPUNVIDIA Maxwell · 128 CUDA cores
RAM4 GB LPDDR4
Storage128 GB eMMC
OSUbuntu 18.04 LTS
Python3.8
Software Stack
Key Libraries
OpenCV 4.11Frame capture, Canny detection, ROI masking
boto3 1.37AWS S3 + DynamoDB integration
SciPy 1.10t-test, linear regression, statistics
psutil 7.2CPU and RAM monitoring
SQLite3Local blockchain persistence (chain.db)
hashlibSHA-256 cryptographic hashing

Layer 1 — Canny Edge Detection

ROI Definition

14 parking slots defined manually using roi_selector.py. Polygon coordinates saved to rois.json. Updated without modifying detection code whenever camera moves.

ROI → Binary mask → Grayscale → Canny(100, 200) → edge_count
edge_count > 500 → Occupied · else → Empty
Frame Capture Strategy

Fixed interval of 5 seconds — not status-change-only. Eliminates temporal gaps in the hash chain that would allow undetected feed substitution.

720 blocks/hour at 5s interval
1020×600 frame resolution (resized from 2560×1440)
14 parking slots monitored

Layer 2 — Edge-Only Processing (Privacy-by-Design)

Data Minimisation: The integrity mechanism only requires SHA-256 hash, detection status, timestamp, and block index to be uploaded — no visual content is needed to verify the chain. JPEG frame upload to S3 is a separate, configurable operational feature (enabled in this deployment for dashboard/audit/labelling purposes); the integrity guarantee holds identically whether it is enabled or not.
01
Capture Frame
RTSP → resize 1020×600
02
JPEG Encode
cv2.imencode(".jpg")
03
SHA-256 Hash
hashlib.sha256(bytes)
04
Store Locally
JPEG on Jetson eMMC
05
Upload Hash Only
hash + status → AWS

Layer 3 — Lightweight Blockchain

Block Structure
Field Description
block_indexSequential position in chain
timestampUTC time (ISO 8601)
frame_hashSHA-256 of JPEG frame
detection_resultParking status per slot
previous_hashHash of preceding block
block_hashSHA-256 of all above fields
Chain Construction — Per Frame
1Compute frame_hash = SHA-256(JPEG bytes)
2Retrieve previous_hash from chain tip
3Compute block_hash = SHA-256(all fields)
4Append block → persist to SQLite (chain.db)
5If block_index % 10 == 0 → checkpoint to DynamoDB
block_hash = SHA-256(
  json.dumps({block_index, timestamp, frame_hash,
    detection_result, device_id, previous_hash},
  sort_keys=True).encode())

Experimental Design

Tamper Detection (E1–E10)

10 attack scenarios (E1–E10) mapped one-to-one onto 10 attack vectors, AV-01 to AV-10. 30 trials each for E1–E9; E10 is pending a field session (n=0). Covers: frame modification, deletion, injection, metadata tampering, hash alteration, replay, chain rewrite, feed substitution, timestamp manipulation, physical spoofing.

Performance Benchmarks (P1–P4)

P1 — Security overhead (CPU/RAM/FPS) · P2 — Hash throughput (h/s) · P3 — Verification scaling O(n) · P4 — 24-hour stability. All with n≥30, 95% CI, paired t-test where applicable.

Statistical Methods

Paired t-test (P1), Wilson score CI (detection rates), linear regression with R² (P3, E7, P4), 95% confidence intervals on all metrics. A total of 390 measured data points across the 13 experiments with data (E10 has n=0, pending its field session).

Implementation

Block Structure & Chaining Formula

frame_hash = SHA-256( jpeg_frame_bytes )

block_hash = SHA-256(
  json.dumps({
    "block_index", "timestamp", "frame_hash",
    "detection_result", "device_id", "previous_hash"
  }, sort_keys=True).encode()
)

All six fields are covered. Canonicalisation is JSON with
sorted keys, not string concatenation.

Genesis block: previous_hash = "0" × 64
import hashlib, json from dataclasses import dataclass, field @dataclass class Block: block_index: int timestamp: str frame_hash: str # SHA-256 of the JPEG bytes detection_result: dict # per-slot occupancy device_id: str previous_hash: str # preceding block_hash block_hash: str = field(init=False) def __post_init__(self): self.block_hash = self.compute_hash() def compute_hash(self) -> str: payload = json.dumps({ "block_index": self.block_index, "timestamp": self.timestamp, "frame_hash": self.frame_hash, "detection_result": self.detection_result, "device_id": self.device_id, "previous_hash": self.previous_hash, }, sort_keys=True).encode() return hashlib.sha256(payload).hexdigest()

Chain Verification Logic

Tamper at block k means:
block[k].block_hash ≠ SHA-256(block[k].fields)
block[k+1].previous_hash ≠ block[k].block_hash

Both conditions checked per block → O(n) total
Break position pinpointed exactly at k
def verify_chain(chain: list[Block]) -> dict: # Verify genesis block itself if compute_block_hash(chain[0]) != chain[0].block_hash: return {"valid": False, "break_at": 0, "type": "genesis_tampered"} for i in range(1, len(chain)): b, prev = chain[i], chain[i - 1] # 1. Linkage: does this block reference prev correctly? if b.previous_hash != prev.block_hash: return {"valid": False, "break_at": i, "type": "chain_break"} # 2. Integrity: is this block's own hash correct? if b.block_hash != compute_block_hash(b): return {"valid": False, "break_at": i, "type": "hash_mismatch"} return {"valid": True, "blocks_verified": len(chain)}

Cloud Anchor Upload

import boto3 dynamodb = boto3.resource('dynamodb', region_name='us-east-1') table = dynamodb.Table('<table_name>') def upload_anchor(block: Block): # Only hash + metadata anchored — frame upload (if enabled) is a separate path table.put_item(Item={ 'block_index': str(block.block_index), # partition key 'block_hash': block.block_hash, 'frame_hash': block.frame_hash, 'timestamp': block.timestamp, 'device_id': block.device_id, 'anchored_at': datetime.utcnow().isoformat() # device-generated, NOT server-side }) def verify_against_cloud(block: Block) -> bool: resp = table.get_item( Key={'block_index': str(block.block_index)}, ConsistentRead=True ) item = resp.get('Item') if not item: return False return item['block_hash'] == block.block_hash

Detection Pipeline Integration

import cv2, time from parking_detector import detect_occupancy chain = [] ANCHOR_INTERVAL = 10 # anchor every 10 blocks FRAME_INTERVAL = 5 # capture every 5 seconds def process_frame(frame, previous_hash: str, index: int): # Layer 1: detection result = detect_occupancy(frame, rois) # Layer 2: hash chain frame_bytes = cv2.imencode('.jpg', frame)[1].tobytes() frame_hash = hash_frame(frame_bytes) block = Block( index=index, timestamp=time.time(), detection_result=result, frame_hash=frame_hash, previous_hash=previous_hash, block_hash="" ) block.block_hash = compute_block_hash(block) chain.append(block) # Layer 3: cloud anchor (every N blocks) if index % ANCHOR_INTERVAL == 0: upload_anchor(block) return block
Hash Chain Visualisation
INTACT CHAIN — each block correctly references previous block_hash
TAMPERED CHAIN — Block #2 pixel data modified → block_hash invalid → break detected immediately at verification
Key property: Even a 1-pixel change in frame data produces a completely different SHA-256 hash (avalanche effect). The attacker cannot predict or reproduce the original hash without the original data. Chain breaks are localised — only the tampered block and everything after it becomes invalid.
Threat Model & Attack Mitigations
Threat model scope: In scope: read/write access to stored frames and chain records on the Jetson (post-capture), network MITM, and physical camera substitution or obstruction (evaluated in E8 and E10). Out of scope: independent compromise of the AWS account, compromise of the running detector process, and denial of service. Note the device holds IAM PutItem credentials, so in-scope device compromise currently also confers cloud-write capability — see E7.
AV-01 Frame Modification MITIGATED
Attacker modifies pixel data in a stored JPEG frame (any number of pixels, including just 1). SHA-256 avalanche effect guarantees a completely different frame_hash. The block's block_hash then fails recomputation during verify_chain(). Attack is detected in O(1) per block.
Experiment E1 — 100% (30/30), mean latency 16.15 ms [12.11, 20.18]
AV-02 Frame Deletion MITIGATED
Attacker deletes a block from the chain. The successor block's previous_hash no longer matches the preceding block's block_hash, creating a detectable gap at the exact deletion position. Detection latency is proportional to position in chain (blocks near start 1.69 ms, mid-chain 20.31 ms, near end 38.49 ms).
Experiment E2 — 100% (30/30), mean 20.16 ms; 1.69 / 20.31 / 38.49 ms by position
AV-03 Frame Injection MITIGATED
Attacker inserts fake frames — from another camera, AI-generated, or duplicated with altered metadata. The injected block cannot produce a valid block_hash that simultaneously satisfies both its own integrity check and the linkage to adjacent blocks. Detection is content-agnostic.
Experiment E3 — 100% (30/30), mean 20.23 ms [14.76, 25.70]
AV-04 Metadata Tampering MITIGATED
Attacker alters non-pixel fields: timestamp (+/- seconds), detection_result (Occupied→Empty to hide vehicle), or device_id. All six block fields are covered by the hash — block_hash = SHA-256(json.dumps({block_index, timestamp, frame_hash, detection_result, device_id, previous_hash}, sort_keys=True)) — so changing any one of them, including detection_result, invalidates block_hash immediately. Hashed, not signed: there is no key, so the chain is tamper-evident rather than unforgeable.
Experiment E4 — 100% (30/30) across timestamp/result/device, mean 18.43 ms
AV-05 Hash Record Alteration MITIGATED
Attacker directly modifies stored hash values (single character change, full random replacement, or block swap). The recomputed block_hash will not match the stored value. Altering block_hash without modifying the underlying data is the most direct attack, but trivially detected.
Experiment E5 — 100% (30/30), incl. 1-character hex changes, mean 20.27 ms
AV-06 Replay Attack NOT MET
Attacker replays old chain segments across 4 scenarios. A (original timestamp + original hash) and B (updated timestamp + original hash) keep the stored block_hash while block_index and previous_hash change, so self-integrity fails and both are detected 8/8. C (original timestamp + recomputed hash) and D (updated timestamp + recomputed hash) recompute block_hash after replaying, leaving an internally consistent, correctly linked chain — neither is detected (0/7 each). The cloud anchor does not recover C/D as implemented: anchored_at is device-generated and checkpoints can be overwritten using the device's own IAM credentials.
Experiment E6 — 53.3% (16/30), Wilson CI [36.14, 69.77], target >95% NOT MET
AV-07 Chain Rewrite UNRESOLVED
Attacker rewrites K blocks, recomputing all block_hashes. Rewrite cost = O(K × SHA-256), measured on the Jetson Nano at 0.0393 ms/block — 39.58 ms for 1,000 blocks. Rewriting is therefore CHEAP, and computational cost is not a meaningful deterrent at this scale. The cloud anchor is architecturally the only remaining ground truth, but as currently implemented it does not close the gap either: the device holds IAM PutItem credentials and upload_checkpoint() issues an unconditional put_item, so an attacker with device write access can overwrite the checkpoints as well. Append-only checkpoint storage is required before any cloud detection claim can be made.
Experiment E7 — 0/30 local by design; O(K) confirmed R²=0.9992; cloud detection NOT measured
AV-08 Feed Substitution MITIGATED
Attacker replaces the camera feed with a loop or pre-recorded footage. Detection is a repeated-hash frequency scan: an identical frame produces an identical frame_hash, and the chain is flagged when any hash repeats more than 5 times. Measured detection was 10/10 in each of the three simulated time slots (morning, afternoon, night), with max_repeat = 20 in every trial — the mechanism is hash-pattern-driven and showed no time-of-day dependence. Scope: frame_hash is SHA-256 over JPEG bytes, so this detects byte-identical replay; a re-encoded static feed would produce differing hashes and evade it.
Experiment E8 — 100% (30/30) detected, target >90% MET
AV-09 Timestamp Manipulation MITIGATED
Attacker manipulates the system clock (forward, backward, random date) to forge temporal context. Detection is a local timestamp-gap heuristic: adjacent block timestamps differing by more than 3600 s are flagged. Because timestamp is also covered by block_hash, self-integrity fails independently — chain_valid = 0 in all 30 trials — so E9 is caught redundantly by two local mechanisms. Note the cloud anchor does NOT contribute here: its anchored_at field is generated on the device (datetime.utcnow() in cloud_anchor.py), not by DynamoDB, so it is not an independent time reference against a device-level attacker.
Experiment E9 — 100% (30/30) via local timestamp-gap heuristic; chain_valid=0 corroborates
AV-10 Physical Sensor Spoofing LAYER 1 SCOPE
Objects placed in spots to fool Canny detector: white poster, dark tape car outline, cardboard box, reflective surface, plastic bag, tree branch. This is a Layer 1 detection accuracy problem — not a security attack on the hash chain. The hash chain faithfully records whatever Canny reports, demonstrating that data integrity is independent of sensor accuracy. Edge-count threshold analysis is planned to characterise spoofing objects.
Experiment E10 — Pending field session (n=0)
Experiment Results — E1 to E10 Real data — Jetson Nano 2026-04-13
Real Data: E1–E9 measured on Jetson Nano (2026-04-13), × 30 trials each. Synthetic chains: random.seed(42) fixes which block indices are targeted (reproducible); frame payloads are os.urandom() bytes, which cannot be seeded but do not affect detection outcomes. E10 is pending a field session (n=0) — not included in this measured dataset.

Security Experiment Summary (n = 30 per experiment)

Exp Name n Detection Rate Mean Latency SD 95% CI Min Max Result
E1 Frame modification 30 100% (30/30) 16.15 ms 11.28 [12.11, 20.18] 1.08 ms 36.38 ms TARGET MET
E2 Frame deletion 30 100% (30/30) 20.16 ms 15.28 [14.70, 25.63] 1.39 ms 38.93 ms TARGET MET
E3 Frame injection 30 100% (30/30) 20.23 ms 15.29 [14.76, 25.70] 1.19 ms 38.39 ms TARGET MET
E4 Metadata tampering 30 100% (30/30) 18.43 ms 11.99 [14.14, 22.72] 0.36 ms 39.47 ms TARGET MET
E5 Hash record alteration 30 100% (30/30) 20.27 ms 12.03 [15.97, 24.58] 2.26 ms 39.40 ms TARGET MET
E6 Replay attack (4 scenarios) 30 53.3% (16/30) 40.25 ms 2.94 [39.20, 41.30] 38.98 ms 55.48 ms NOT MET
E7 Chain rewrite (K=10–1000 blocks) 30 0% local (cloud not measured) 13.40 ms 15.09 [8.00, 18.80] 0.40 ms 40.30 ms BY DESIGN
E8 Feed substitution 30 100% (30/30) 38.95 ms 0.39 [38.81, 39.09] 38.26 ms 39.83 ms TARGET MET
E9 Timestamp manipulation 30 100% (30/30) 16.14 ms 11.12 [12.16, 20.12] 1.13 ms 36.34 ms TARGET MET
E10 Physical sensor spoofing 0 PENDING FIELD SESSION

E2 — Frame Deletion: Latency by Chain Position

Sequential verification means beginning deletions are found fastest. Deletion near end requires traversing nearly the whole chain first.

E2 Combined (n=30): Mean = 20.16 ms · SD = 15.28 · [14.70, 25.63]
Beginning (idx 35, n=10): 1.69 ms ± 0.15
Middle (idx 503, n=10): 20.31 ms ± 0.29
End (idx 964, n=10): 38.49 ms ± 0.21

E6 — Replay Attack: Detection by Scenario

Scenarios A and B keep the original block_hash while block_index and previous_hash change, so self-integrity fails and they are always caught. Scenarios C and D recompute block_hash after replaying, leaving an internally consistent chain that local verification cannot distinguish from a legitimate append — neither is detected.

Scenario A (orig ts + orig hash): 8/8 100%
Scenario B (new ts + orig hash): 8/8 100%
Scenario C (orig ts + new hash): 0/7 0%
Scenario D (new ts + new hash): 0/7 0%
Combined: 16/30 = 53.3% · Wilson 95% CI [36.14, 69.77] · target >95% NOT MET
The cloud anchor does not currently recover C/D: anchored_at is device-generated and checkpoints can be overwritten with the device's own credentials.

E7 — Chain Rewrite: Rewrite Cost vs K (blocks)

Linear relationship confirms O(K) rewrite complexity at 0.0393 ms/block. Note this makes rewriting cheap, not expensive — ~40 ms for 1,000 blocks — so rewrite latency is not a deterrent. No desktop comparison was run.

K (blocks)TrialsRewrite Time (ms)Post-Rewrite Verify (ms)Detected Locally
1060.49 ± 0.0639.360/6
5062.43 ± 0.5739.190/6
10064.35 ± 0.2939.000/6
500620.17 ± 0.4638.790/6
1000639.58 ± 0.4838.720/6
Linear regression: Time = 0.03932 × K + 0.351 ms · R² = 0.9992 → confirms O(K)
Local detection is 0/30 by design — a rewritten chain is internally consistent.
Cloud detection was NOT measured in this experiment; no cloud detection rate is claimed.

E10 — Physical Spoofing: False Positive Rate by Object

Pending field session. E10 has not yet been conducted (n=0). A field session at the car park is scheduled to collect this data. No results are available to display yet — figures previously shown here were placeholders and have been removed.
Performance Benchmarks — P1 to P4

P1 — Security Layer Overhead (Paired t-test, n = 30)

30 paired measurements: 1 minute without blockchain, then 1 minute with blockchain. Same hardware, same stream, same conditions.

MetricWithout BCWith BCOverhead95% CI
CPU (%)11.6 ± 18.13.6 ± 12.7−7.99%[−16.21, +0.22]
RAM (MB)+0.06 MB[−0.05, +0.17]
FPS19.95 ± 0.00214.64 ± 1.30−5.31[−5.79, −4.82]
Paired t-test (CPU): t = 1.99, df = 29, p = 0.056
CPU overhead: −7.99%, 95% CI [−16.21,+0.22] spans zero → NO significant overhead detected
(negative sign is variance, not a CPU saving — SD of the paired differences is 22.0, larger than the mean; the 18.1/12.7 above are per-condition SDs)
ΔRAM: +0.06 MB (n.s.) · FPS: −5.30 (p<0.001, the one significant cost) → TARGET MET

CPU Overhead Distribution (30 trials)

Conclusion: No statistically significant CPU overhead was detected (−7.99%, t(29)=1.99, p=0.056; CI spans zero). The negative point estimate reflects background load variance on the Jetson, not a CPU saving. ΔRAM +0.06 MB is also not significant. The FPS reduction (19.95 → 14.64, −5.30, p<0.001) is significant and is the framework's main measured cost; it is tolerable here only because the deployed capture cadence is 1 frame / 5 s (0.2 fps), roughly 73× below even the reduced processing rate.

P2 — SHA-256 Hash Throughput (n = 30 runs)

Each run hashes 1,000 frames and measures total time; 30 runs (benchmark_hash.csv).

ConditionMean h/sSD95% CIPer-hash (ms)
All runs (n=30)12,160.7138.20[12,147.04, 12,174.38]0.082
Min run12,037.430.083
Max run12,204.760.082
Min: 12,037.4 h/s · Max: 12,204.8 h/s · SD 38.2 (CV < 0.4%)
All 30 runs exceeded the 100 h/s design target → TARGET MET
At 1 frame/5s = 720 frames/hour = 0.2 h/s demand → ~4 orders of magnitude headroom

P2 Throughput Distribution

P3 — Chain Verification Time vs Length (n = 30 runs)

6 runs each for 5 chain lengths (100, 500, 1,000, 5,000, 10,000 blocks). Linear regression proves O(n) scaling.

Chain Length (n)RunsMean Verify TimeSD95% CIPer-block (ms)RAM during (MB)
100 blocks63.995 ms0.145[3.879, 4.111]0.0400
500 blocks619.548 ms0.273[19.330, 19.766]0.0391
1,000 blocks639.352 ms0.409[39.025, 39.679]0.0394
5,000 blocks6203.491 ms1.806[202.046, 204.936]0.0407
10,000 blocks6398.003 ms1.448[396.844, 399.162]0.0398
Linear regression: Time = 0.03993 × n + 0.296 ms
R² = 0.9998 · Slope = 0.0399 ms/block
Confirms O(n) scaling — no hidden quadratic complexity → TARGET MET

P4 — Stability Test (30 sampling points)

Full 24-hour protocol: 23.22 h between first and last sample, 30 samples at 48-minute intervals, run 2026-07-03 → 2026-07-04 against an isolated test database (p4_chain_24h.db) so the production chain was untouched. An earlier 4.85 h pilot (2026-04-13, 10-minute intervals) is retained below for comparison. Trend analysis detects memory leaks and CPU drift.

SampleTime (hr)CPU (%)RAM (MB)Verify (ms)Chain LengthIntegrity
10.000.629.540.6011✓ Valid
20.800.529.721.1221✓ Valid
31.600.929.721.6431✓ Valid
42.400.429.722.2041✓ Valid
53.200.529.722.6851✓ Valid
64.000.329.723.2661✓ Valid
74.800.429.723.7871✓ Valid
85.600.629.724.2881✓ Valid
96.410.429.724.7191✓ Valid
107.210.629.985.32101✓ Valid
118.010.329.985.37111✓ Valid
128.810.429.986.12121✓ Valid
139.610.329.986.65131✓ Valid
1410.410.329.986.86141✓ Valid
1511.210.429.987.35151✓ Valid
1612.010.329.987.93161✓ Valid
1712.810.429.988.14171✓ Valid
1813.610.729.988.50181✓ Valid
1914.410.429.988.84191✓ Valid
2015.210.429.989.18201✓ Valid
2116.010.429.989.48211✓ Valid
2216.811.229.9810.20221✓ Valid
2317.610.629.9810.95231✓ Valid
2418.420.829.9810.82241✓ Valid
2519.220.929.9811.74251✓ Valid
2620.020.729.9812.13261✓ Valid
2720.820.929.9812.04271✓ Valid
2821.620.829.9813.36281✓ Valid
2922.420.829.9812.83291✓ Valid
3023.221.130.1714.16301✓ Valid
24 h run — RAM: mean=29.90 MB · SD 0.14 · drift=+0.63 MB over 23.22 h · slope=0.0133 MB/sample → no memory leak → TARGET MET
24 h run — CPU: Mean=0.58% · SD=0.25% · range 0.3–1.2% · stable (no upward trend)
24 h run — Chain integrity: 30/30 valid (100%) · Verify time: O(n) growth 0.60ms→14.16ms
4.85 h pilot — RAM mean=29.61 MB · drift=+0.20 MB · CPU 0.49% ± 0.23 · 30/30 valid
Stability conclusion: The full 24-hour protocol (23.22 h between first and last sample) confirms the pilot. RAM stayed essentially flat at 29.90 MB with total drift +0.63 MB (slope 0.0133 MB/sample — two orders of magnitude below the 1 MB/sample leak criterion). CPU averaged 0.58% (SD 0.25, max 1.2%). All 30 samples showed valid chain integrity. Verification time scaled linearly from 0.60 ms to 14.16 ms as the chain grew from 11 to 301 blocks — that growth is the protocol appending 10 synthetic blocks per sample, not live camera traffic — consistent with the P3 O(n) result.
Statistical Methodology

CLT Compliance — Why n ≥ 30

The Central Limit Theorem states that sample means approach a normal distribution as n increases, regardless of population distribution. n ≥ 30 is the accepted minimum for parametric tests to be valid.

x̄  = Σxᵢ / n                  (sample mean)
s   = √[Σ(xᵢ − x̄)² / (n−1)]  (sample SD)
SE  = s / √n                  (standard error)
CI  = x̄ ± 1.96 × SE          (95% confidence interval)
Reporting template:
"E1: n=30, detection 100% (30/30).
Mean latency = 16.15 ms
(SD=11.28, 95% CI [12.11, 20.18])."
Why n=30 for each experiment:
390 measured (13 × 30); E10 n=0 pending
Sub-groups n=10 → descriptive only
Combined n=30 → inferential tests valid

Statistical Tests by Experiment

TestUsed ForExps
Descriptive + 95% CIDetection rate, latency, throughputE1–E9, P2
Paired t-testCPU/RAM with vs without blockchainP1
Linear regression (R²)O(n) scaling, rewrite cost vs KE7, P3
Trend analysis (slope)RAM drift over 24hr; slope ≈ 0P4
Pending (n=0)Per-object FP rate — field session not yet conductedE10

Master Results Summary

ExpnPrimary ResultTargetPass?
E130100% (30/30) · 16.15 ms [12.11,20.18]100%
E230100% (30/30) · 20.16 ms [14.70,25.63]100%
E330100% (30/30) · 20.23 ms [14.76,25.70]100%
E430100% (30/30) · 18.43 ms [14.14,22.72]100%
E530100% (30/30) · 20.27 ms [15.97,24.58]100%
E63053.3% (16/30) [36.14,69.77] · 40.25 ms>95%NOT MET
E730Rewrite cost 0.49–39.6 ms, O(K) confirmed R²=0.9992 · local detection 0/30 by design · cloud detection NOT measuredCharacterise rewrite cost, confirm O(K)
E830100% (30/30) · 38.95 ms [38.81,39.09]>90%
E930100% (30/30) · 16.14 ms [12.16,20.12]>95%
E100Pending field sessionAnalysis
P130No significant CPU overhead: −7.99% [−16.21,+0.22] · t(29)=1.99, p=0.056<15%
P23012,160.7 h/s [12,147.0,12,174.4]>100 h/s
P330O(n) · R²=0.9998 · 0.0399 ms/blockO(n)
P430RAM drift +0.63 MB over 23.2 h · CPU 0.58% · chain 30/30 validNo leak
Overall conclusion: E1–E5, E8 and E9 meet their detection targets at 100% (30/30 each). Two do not, for the same structural reason: E6 (replay) reaches 53.3% because scenarios that recompute block_hash after tampering leave a locally self-consistent chain, and E7 (chain rewrite) is 0% locally by design for the same reason. Both are honest limitations of local-only verification, not implementation faults — but note that the cloud anchor does not currently compensate for them either (see E7 detail). E10 has not been conducted (n=0). P1–P4 meet their targets.
Scripts — System Codebase

Python Scripts Overview

All scripts located in ~/parking_security/ on Jetson Nano and C:\Users\ASUS\Downloads\parking_system\ on ROG laptop.

Core System

parking_detector.py Full Pipeline — Live Parking Detection

Main system script. Connects to RTSP camera → captures frame every 5s → resizes to 1020×600 → runs Canny edge detection on 14 ROI polygons → computes SHA-256 frame hash → adds block to blockchain → uploads frame to S3 → saves status to DynamoDB.

CAPTURE_INTERVAL_SEC=5 · CHECKPOINT_INTERVAL=10 · HEADLESS=True · auto-reconnect on RTSP drop
blockchain.py Blockchain Core — HashChain + ChainStorage

Defines the Block dataclass, HashChain class, and ChainStorage (SQLite backend). Each block stores: index, timestamp, frame_hash (SHA-256 of JPEG bytes), previous_hash, block_hash (SHA-256 of all fields combined), and metadata. verify_chain() traverses the full chain in O(n).

block_hash = SHA-256(json.dumps({block_index, timestamp, frame_hash, detection_result, device_id, previous_hash}, sort_keys=True))
cloud_anchor.py Cloud Anchor — DynamoDB Checkpoint

Uploads blockchain checkpoints to AWS DynamoDB every 10 blocks. verify_against_cloud() cross-checks the local chain against DynamoDB records. Intended as the independent reference for E7 (chain rewrite) and E6 Scenario D (replay), but NOT yet effective against those: the device holds PutItem credentials and upload_checkpoint() issues an unconditional put_item, so a device-level attacker can overwrite checkpoints. anchored_at is also device-generated. Append-only storage and a server-derived timestamp are required first; cloud detection has not been measured.

Region: us-east-1 · Auto-checkpoint every 10 blocks
roi_selector.py ROI Drawing Tool

Interactive OpenCV tool for drawing parking spot polygons on the camera frame. Left-click adds points, right-click closes polygon, middle-click removes last. Saves polygon coordinates to rois.json. Currently configured with 14 parking spots.

Output: rois.json · 14 polygons · Frame resized to 1020×600
performance_monitor.py Performance Monitor

Background thread that samples CPU%, RAM (MB), GPU%, FPS, and chain length every 10 seconds. Writes to timestamped CSV in data/. Used by parking_detector.py during live operation and by run_P4_stability.py for the 24-hour stability test.

Experiment Scripts

run_all.py Master Runner — E1–E10, P1–P3

Runs all experiment scripts in sequence: run_tamper_experiments.py → run_advanced_experiments.py → run_P1_overhead.py → benchmark_hash.py → benchmark_chain.py. Logs pass/fail for each. Total runtime ~135 minutes on Jetson Nano. P4 excluded (run separately).

Runtime: 135.7 min · 4/5 passed (P2 had Python 3.8 type hint issue, fixed)
run_tamper_experiments.py E1–E7 Tamper Detection Experiments

Runs 7 tamper attack experiments × 30 trials each. Each trial builds a 1000-block synthetic chain (payload bytes from os.urandom(); random.seed(42) fixes the targeted indices), applies an attack, then calls verify_chain() to measure detection. Results saved as individual CSVs with trial, detected flag, latency_ms, and attack-specific fields.

run_advanced_experiments.py E8–E10 Advanced Experiments

E8 (feed substitution) — detects repeated frame_hash patterns in chain. E9 (timestamp manipulation) — forward/backward/random clock changes; the recorded detection is a timestamp-gap heuristic (gap > 3600 s), with block_hash invalidation as independent corroboration (chain_valid = 0 in all 30 trials). E10 (physical spoofing) — template CSV for manual data collection with real objects.

tamper_simulator.py Attack Simulator Library

Library of attack functions used by the experiment scripts. Provides: build_test_chain(), attack_frame_modification(), attack_frame_deletion(), attack_frame_injection(), attack_metadata_tampering(), attack_replay(), attack_chain_rewrite(). Each returns detected flag and latency_ms.

run_P1_overhead.py P1 — Security Overhead Benchmark

30 paired measurements of CPU%, RAM (MB), and FPS with and without blockchain active. Uses paired t-test to determine statistical significance of overhead. Runs on Jetson Nano under controlled conditions.

benchmark_hash.py P2 — Hash Throughput Benchmark

Hashes 1,000 random frames × 30 iterations and measures throughput. Result: 12,160.7 hashes/sec (SD=38.2) on Jetson Nano ARM CPU. Highly consistent (CV < 0.4%). Each frame hash takes ~0.082 ms.

Result: 12,160.7 h/s · SD=38.2 · CI=[12,147, 12,174]
benchmark_chain.py P3 — Verification Scaling Benchmark

Measures verify_chain() time for chains of 100, 500, 1,000, 5,000, and 10,000 blocks (6 runs each). Confirms O(n) linear scaling. Results: 4.0ms → 19.5ms → 39.4ms → 203.5ms → 398.0ms.

10K blocks = 398 ms · 0.040 ms per block · R²≈1.00
run_P4_stability.py P4 — Long-Term Stability Test

Runs the parking system continuously and records 30 samples (CPU%, RAM MB, chain length, chain validity, verify_ms) at 48-minute intervals (23.22 h between first and last sample; an earlier pilot used 10-minute intervals over 4.85 h). Tests whether the system degrades over time — no memory leaks, no performance drift. Linear regression on RAM over time gives the drift slope (MB/hr).

Utility Scripts

generate_report.py Report Generator — Charts + Stats

Reads all experiment CSVs, computes statistics (mean, SD, 95% CI, t-test, linear regression), and generates 5 matplotlib PNG charts for the thesis. Outputs figures to reports/ directory.

generate_master_table.py Master Results Table

Aggregates all CSVs into a single master results table (CSV + plain text). Ready-to-paste into thesis Chapter 5.

statistical_analysis.py Statistical Analysis Library

Provides: analyse_experiment() for per-CSV stats, paired_ttest() for P1 paired comparisons, linear_regression() for P3/P4 trend lines, and trend_analysis() for stability drift detection.

setup_dynamodb.py AWS DynamoDB Setup

Creates and verifies DynamoDB tables for checkpoint anchoring and per-frame hash + detection status. Region: us-east-1.

test_blockchain.py Unit Tests

6 unit tests covering: genesis block creation, block addition, chain verification, tamper detection, persistence to SQLite, and cloud anchor connectivity. All 6 tests pass.