365Architect

CipherShift365 — Questionnaire Engine

Component: CipherShift365.Core.Analysis (assessment engine) + REST API endpoints + Blazor portal UI
Model: Versioned assessment templates with transparent scoring
Default template: PQC Readiness Assessment (6 sections, 18 questions)
Target framework: .NET 10.0


What It Does

The Questionnaire Engine is a self-contained assessment system that measures an organization's readiness for post-quantum cryptography migration. It supports versioned templates, four question types, transparent scoring with section-level breakdowns, and actionable recommendations.

Key features:

  • Versioned templates — each assessment template has an Id, Version, and CreatedAt timestamp. Responses record the template version they were submitted against.
  • Four question typesSingleChoice (radio), MultiChoice (check-all-that-apply), FreeText (open-ended), Numeric (quantitative).
  • Transparent scoring — every question has a MaxScore. Section scores, total scores, and readiness percentages are calculated with a deterministic algorithm.
  • Recommendations engine — sections scoring below 40% generate "needs significant improvement" items; below 70% generate "moderate — further action recommended."
  • API integration — full CRUD via /v1/questionnaire/* endpoints.
  • Portal UI — interactive form with radio groups and text inputs, client-side score preview, and post-submission results display.
  • Air-gapped — no external survey service, no SaaS dependency, no telemetry.

Why It's Needed (Value Proposition)

Need How the questionnaire engine addresses it
Standardized readiness assessment The default pqc-readiness-v1 template covers 6 domains: Regulatory, Estate, Inventory, Data/HNDL, Migration, Governance. Every engagement starts from the same baseline.
Objective scoring Algorithm-based scoring removes subjectivity. Two assessors evaluating the same answers always produce the same score.
Audit evidence Each response is timestamped, attributed to an actor, and retrievable via API. An auditor can trace a readiness percentage back to individual answers.
CISO-facing output Scores, readiness bands (Critical/Early/Moderate/Advanced), and specific recommendations are immediately actionable.
Customizable Organizations can add versioned templates for their own domains. The engine does not hard-code the PQC template — it's a generic assessment system.
No vendor lock-in Templates and responses are plain .NET records. Export them as JSON for any external system.

Installation

Prerequisites

Requirement Minimum
.NET SDK 10.0 (build); runtime: 8.0+
Dependencies CipherShift365.Core.Runtime (types), CipherShift365.Core.Analysis (engine)

Package Reference

The assessment engine is part of CipherShift365.Core.Analysis. The types are in CipherShift365.Core.Runtime:

XML
<ProjectReference Include="..\..\Core\CipherShift365.Core.Analysis\CipherShift365.Core.Analysis.csproj" />

No external NuGet packages are required beyond the .NET runtime.

Standalone Usage

BASH
# Create a console app referencing the analysis engine
dotnet new console -n QuestionnaireDemo
cd QuestionnaireDemo
dotnet add reference ..\CipherShift365.Core.Analysis\CipherShift365.Core.Analysis.csproj

How to Run / Use

Quick Start — Score the Default Assessment

CSHARP
using CipherShift365.Core.Analysis;
using CipherShift365.Core.Runtime;

var service = new AssessmentService();

// List available templates
foreach (var t in service.GetTemplates())
    Console.WriteLine($"{t.Id} v{t.Version}: {t.Title} ({t.Sections.Sum(s => s.Questions.Count)} questions)");

// Submit a response
var response = service.Submit("pqc-readiness-v1", "Alice (Security Architect)",
    new[]
    {
        new QuestionAnswer("q1.1", "Yes, fully applicable", 15),
        new QuestionAnswer("q1.2", "CNSA 2.0 compliance mandate", 10),
        new QuestionAnswer("q1.3", "2030 (key establishment deadline)", 10),
        // ... answers for all 18 questions
    }.AsReadOnly());

// Score
var score = service.Score(response);
Console.WriteLine($"Readiness: {score.ReadinessPercent:F1}%");
Console.WriteLine($"Summary: {score.Summary}");
foreach (var rec in score.Recommendations ?? Array.Empty<string>())
    Console.WriteLine($"  → {rec}");

Via the REST API

BASH
TOKEN=$(curl -s -X POST http://localhost:5000/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"clientId":"admin-001","secret":"any"}' | jq -r .token)

# List templates
curl -s http://localhost:5000/v1/questionnaire/templates \
  -H "Authorization: Bearer $TOKEN" | jq .

# Get full template
curl -s http://localhost:5000/v1/questionnaire/templates/pqc-readiness-v1 \
  -H "Authorization: Bearer $TOKEN" | jq .

# Submit
curl -s -X POST http://localhost:5000/v1/questionnaire/submit \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "pqc-readiness-v1",
    "answers": [
      {"questionId":"q1.1","value":"Yes, fully applicable","score":15},
      {"questionId":"q1.2","value":"CNSA 2.0 compliance mandate","score":10}
    ]
  }' | jq .

Via the Blazor Portal

Navigate to /questionnaire (requires ConsultantAdmin or Auditor role). The page:

  1. Loads the pqc-readiness-v1 template.
  2. Renders each section as a titled card with question inputs.
  3. Single-choice questions display as radio button groups.
  4. On submit, POSTs answers to the API and displays the scored result.

User Manual

Question Types

Type Input Scoring Example
SingleChoice Radio button group Positional: first option = highest score, last option = lowest. Score = (1 - idx/total) * MaxScore, minimum 1. q1.1: CNSA applicability (4 options, max 15 pts)
MultiChoice Text input (free-form) Manual: user provides text; basic heuristic (length > 10 chars = 3 pts, else 1 pt) q2.3: .NET versions deployed
FreeText Text input Manual: same basic heuristic q3.3: Automated discovery tool
Numeric Number input Direct numeric value (Not used in default template, available for custom templates)

Scoring for SingleChoice (positional):

The portal computes this client-side before submission:

idx = index of selected option (0-based)
total = total number of options
score = max(1, (1 - idx/total) * MaxScore)

Example for q1.1 (4 options, MaxScore=15):

  • Option 0: "Yes, fully applicable" → (1 - 0/4) * 15 = 15
  • Option 1: "Yes, partially applicable" → (1 - 1/4) * 15 = 11.25 → 11
  • Option 2: "Under evaluation" → (1 - 2/4) * 15 = 7.5 → 7
  • Option 3: "No" → (1 - 3/4) * 15 = 3.75 → 3

The max(1, ...) floor ensures every answered question contributes at least 1 point.

Default Template: PQC Readiness Assessment

Template ID: pqc-readiness-v1
Version: 1
Created: 2025-01-01
Total questions: 18
Maximum possible score: 180

Section 1: Organizational & Regulatory Context (35 pts)

ID Question Type Options Max Score
q1.1 Is your organization subject to CNSA 2.0 / NSM-10 requirements? SingleChoice Yes fully / Yes partially / Evaluating / No 15
q1.2 What is your organization's primary regulatory driver for PQC migration? SingleChoice CNSA 2.0 / NIST PQC / Best practice / Board directive / No driver 10
q1.3 When must your organization demonstrate PQC compliance? SingleChoice 2027 / 2030 / 2033 / 2035 / No deadline 10

Section 2: Estate & .NET/Azure Profile (20 pts)

ID Question Type Options Max Score
q2.1 How many .NET applications are in your cryptographic estate? SingleChoice 1-10 / 11-50 / 51-200 / 200-500 / 500+ / Unknown 8
q2.2 What percentage uses Azure services (Key Vault, Managed HSM)? SingleChoice 0-25% / 26-50% / 51-75% / 76-100% / Unknown 7
q2.3 What .NET versions are deployed in your estate? MultiChoice .NET FX 4.x / .NET 5/6 / .NET 7/8 / .NET 9 / .NET 10 5

Section 3: Cryptographic Inventory Maturity (35 pts)

ID Question Type Options Max Score
q3.1 Have you completed a full cryptographic asset inventory? SingleChoice Yes / Partially / In progress / Not started 15
q3.2 Are quantum-vulnerable algorithms identified and classified? SingleChoice All identified / Most / Some / None 10
q3.3 Do you have an automated cryptographic discovery tool in place? SingleChoice CI/CD scanning / Periodic manual / Evaluating CipherShift365 / No tool 10

Section 4: Data Sensitivity & Longevity / HNDL (35 pts)

ID Question Type Options Max Score
q4.1 What is the longest required data confidentiality period? SingleChoice <1yr / 1-5yr / 5-10yr / 10-25yr / 25+yr / Unknown 12
q4.2 Do you process data classified as HNDL sensitive? SingleChoice Yes — confirmed / Possibly / No / Unknown 13
q4.3 What data categories require quantum-resistant protection? MultiChoice PII / Financial / IP / NSS/Classified / Healthcare / None 10

Section 5: Migration Readiness (30 pts)

ID Question Type Options Max Score
q5.1 Do you have a PQC migration budget allocated? SingleChoice Fully funded / Partially / Budgeting / Not budgeted 12
q5.2 What is your target migration completion timeline? SingleChoice 2025-26 / 2027-28 / 2029-30 / 2031-33 / After 2033 / No timeline 10
q5.3 Have you identified PQC target algorithms for your estate? SingleChoice Mapped to assets / General targets / Evaluating / Not started 8

Section 6: Governance & Timeline (25 pts)

ID Question Type Options Max Score
q6.1 Is there an executive sponsor for PQC migration? SingleChoice C-level / Director/VP / Discussion / No sponsor 10
q6.2 How frequently is PQC readiness reported to leadership? SingleChoice Monthly / Quarterly / Annually / Ad-hoc 8
q6.3 Do you maintain a PQC policy document aligned with CNSA 2.0? SingleChoice Published / Draft / Planned / No policy 7

Readiness Bands

Score % Band Interpretation
≥ 85% Advanced readiness Organization is well-prepared for PQC migration.
60–84% Moderate readiness Key gaps identified; prioritized plan recommended.
30–59% Early readiness Significant gaps; comprehensive migration plan required.
< 30% Critical Immediate action required across multiple domains.

Response Lifecycle

1. GET /v1/questionnaire/templates  →  Browse available assessments
2. GET /v1/questionnaire/templates/{id}  →  View full template with questions
3. Fill out answers (portal UI or programmatic)
4. POST /v1/questionnaire/submit  →  Submit response, receive score
5. GET /v1/questionnaire/responses  →  List prior responses
6. GET /v1/questionnaire/responses/{id}  →  View detailed response with score

Developer Manual

Architecture

Rendering diagram...

Type Model

All types are defined in CipherShift365.Core.Runtime.Types.AssessmentTypes.cs:

CSHARP
public sealed record AssessmentTemplate(
    string Id,                              // e.g., "pqc-readiness-v1"
    string Title,                           // e.g., "PQC Readiness Assessment"
    int Version,                            // e.g., 1
    string Description,
    IReadOnlyList<TemplateSection> Sections,
    DateTimeOffset CreatedAt);

public sealed record TemplateSection(
    string Id,                              // e.g., "s1"
    string Title,                           // e.g., "Organizational & Regulatory Context"
    string? Description,
    IReadOnlyList<TemplateQuestion> Questions);

public sealed record TemplateQuestion(
    string Id,                              // e.g., "q1.1"
    string Text,                            // Full question text
    QuestionType Type,                      // SingleChoice | MultiChoice | FreeText | Numeric
    IReadOnlyList<string>? Options,         // For SingleChoice/MultiChoice
    int MaxScore,                           // Points achievable for this question
    string? WeightCategory);                // e.g., "Regulatory", "Estate", for grouping

public enum QuestionType { SingleChoice, MultiChoice, FreeText, Numeric }

public sealed record AssessmentResponse(
    string Id,                              // Auto-generated (12-char hex)
    string TemplateId,
    int TemplateVersion,                    // Version of template when submitted
    string Actor,                           // JWT Name claim
    DateTimeOffset SubmittedAt,
    IReadOnlyList<QuestionAnswer> Answers);

public sealed record QuestionAnswer(
    string QuestionId,                      // Must match a TemplateQuestion.Id
    string? Value,                          // Selected option text or free-text
    int Score);                             // 1 to MaxScore

public sealed record AssessmentScore(
    int TotalScore,                         // Sum of all answer scores
    int MaxPossibleScore,                   // Sum of all question MaxScores
    double ReadinessPercent,                // TotalScore / MaxPossibleScore * 100
    IReadOnlyDictionary<string, int> SectionScores,  // Section ID → section total
    string Summary,                         // Readiness band text
    IReadOnlyList<string>? Recommendations); // Generated recommendations

Scoring Algorithm

The scoring algorithm in AssessmentService.Score():

CSHARP
public AssessmentScore Score(AssessmentResponse response)
{
    var template = GetTemplate(response.TemplateId);
    var questionMap = template.Sections
        .SelectMany(s => s.Questions)
        .ToDictionary(q => q.Id);

    var sectionScores = new Dictionary<string, int>();
    var recommendations = new List<string>();
    int totalScore = 0, maxScore = 0;

    foreach (var section in template.Sections)
    {
        int sectionScore = 0, sectionMax = 0;
        foreach (var q in section.Questions)
        {
            var answer = response.Answers.FirstOrDefault(a => a.QuestionId == q.Id);
            sectionScore += answer?.Score ?? 0;   // Unanswered = 0
            sectionMax += q.MaxScore;
            maxScore += q.MaxScore;
        }
        sectionScores[section.Id] = sectionScore;
        totalScore += sectionScore;

        // Recommendation thresholds
        var pct = sectionMax > 0 ? (double)sectionScore / sectionMax : 0;
        if (pct < 0.4)
            recommendations.Add($"{section.Title}: needs significant improvement ({pct:P0})");
        else if (pct < 0.7)
            recommendations.Add($"{section.Title}: moderate — further action recommended ({pct:P0})");
    }

    var readiness = maxScore > 0 ? (double)totalScore / maxScore * 100 : 0;
    var summary = readiness switch
    {
        >= 85 => "Advanced readiness — organization is well-prepared for PQC migration.",
        >= 60 => "Moderate readiness — key gaps identified; prioritized plan recommended.",
        >= 30 => "Early readiness — significant gaps; comprehensive migration plan required.",
        _     => "Critical — immediate action required across multiple domains.",
    };

    return new AssessmentScore(totalScore, maxScore, Math.Round(readiness, 1),
        sectionScores.AsReadOnly(), summary,
        recommendations.Count > 0 ? recommendations.AsReadOnly() : null);
}

Adding a Custom Template

CSHARP
var service = new AssessmentService();

// This would typically be called in Program.cs or a database seeder
var customTemplate = new AssessmentTemplate(
    "financial-compliance-v1",
    "Financial Services PQC Compliance",
    1,
    "Assesses PQC readiness for PCI DSS and SOX regulated environments.",
    new[]
    {
        new TemplateSection("s1", "PCI DSS Cryptographic Requirements",
            "Review of payment card data encryption.",
            new[]
            {
                new TemplateQuestion("q1.1", "Is cardholder data encrypted at rest with quantum-resistant algorithms?",
                    QuestionType.SingleChoice,
                    new[] { "Yes — PQC algorithms used", "Yes — AES-256 only", "No" }, 10, "PCI"),
            }.AsReadOnly()),
        // ... more sections
    }.AsReadOnly(),
    DateTimeOffset.UtcNow);

// For the AssessmentService to pick it up, add to the internal _templates list.
// The current implementation only seeds the default template.
// To make templates extensible:
// 1. Add a public RegisterTemplate(AssessmentTemplate template) method to AssessmentService
// 2. Call it in Program.cs before any request

API Endpoints Reference

Endpoint Method Roles Request Body Response
/v1/questionnaire/templates GET ConsultantAdmin, Auditor [{id, title, version, description, sectionCount, questionCount}]
/v1/questionnaire/templates/{id} GET ConsultantAdmin, Auditor Full template with sections, questions, options, maxScore
/v1/questionnaire/submit POST ConsultantAdmin, Auditor {templateId, answers[{questionId, value, score}]} Response with full score object
/v1/questionnaire/responses GET ConsultantAdmin, Auditor [{id, templateId, version, actor, submittedAt, readinessPercent, summary}]
/v1/questionnaire/responses/{id} GET ConsultantAdmin, Auditor Response with answers array + full score breakdown

Code Examples

Submit All 18 Questions (Complete Assessment)

CSHARP
var answers = new[]
{
    // Section 1: Regulatory (35 pts max)
    new QuestionAnswer("q1.1", "Yes, fully applicable", 15),
    new QuestionAnswer("q1.2", "CNSA 2.0 compliance mandate", 10),
    new QuestionAnswer("q1.3", "2030 (key establishment deadline)", 10),

    // Section 2: Estate (20 pts max)
    new QuestionAnswer("q2.1", "51-200", 5),
    new QuestionAnswer("q2.2", "51-75%", 5),
    new QuestionAnswer("q2.3", ".NET 6, .NET 7/8 deployed", 3),

    // Section 3: Inventory (35 pts max)
    new QuestionAnswer("q3.1", "Partially — some assets identified", 10),
    new QuestionAnswer("q3.2", "Most identified", 7),
    new QuestionAnswer("q3.3", "Evaluating/trialing CipherShift365", 5),

    // Section 4: Data / HNDL (35 pts max)
    new QuestionAnswer("q4.1", "10-25 years", 9),
    new QuestionAnswer("q4.2", "Possibly — under evaluation", 8),
    new QuestionAnswer("q4.3", "PII, Financial, IP data requires protection", 3),

    // Section 5: Migration (30 pts max)
    new QuestionAnswer("q5.1", "Partially funded", 9),
    new QuestionAnswer("q5.2", "2029-2030", 7),
    new QuestionAnswer("q5.3", "Under evaluation with CipherShift365", 5),

    // Section 6: Governance (25 pts max)
    new QuestionAnswer("q6.1", "Yes — Director/VP level", 7),
    new QuestionAnswer("q6.2", "Quarterly", 6),
    new QuestionAnswer("q6.3", "Draft in progress", 5),
}.AsReadOnly();

var response = service.Submit("pqc-readiness-v1", "Alice", answers);
var score = service.Score(response);
// TotalScore: ~142, MaxScore: 180
// ReadinessPercent: ~78.9% → "Moderate readiness"
// Recommendations: ["Estate Profile: moderate — further action recommended (55%)", ...]

PowerShell — Query Responses via API

POWERSHELL
$token = (Invoke-RestMethod -Uri "http://localhost:5000/v1/auth/token" -Method Post -Body '{"clientId":"admin-001","secret":"any"}' -ContentType "application/json").token
headers = @{ Authorization = "Bearertoken" }

# Get all responses
responses=InvokeRestMethodUri"http://localhost:5000/v1/questionnaire/responses"Headersresponses = Invoke-RestMethod -Uri "http://localhost:5000/v1/questionnaire/responses" -Headersheaders
foreach (rinr inresponses) {
    Write-Host "((r.actor): ((r.readinessPercent)% — ((r.summary)"
}

# Get detailed response
detail=InvokeRestMethodUri"http://localhost:5000/v1/questionnaire/responses/detail = Invoke-RestMethod -Uri "http://localhost:5000/v1/questionnaire/responses/(responses[0].id)"Headersresponses[0].id)" -Headersheaders
foreach (aina indetail.answers) {
    Write-Host "  ((a.questionId): ((a.value) (((a.score) pts)"
}

Programmatically Compare Two Responses

CSHARP
var resp1 = service.GetResponse("abc123");
var resp2 = service.GetResponse("def456");

var score1 = service.Score(resp1!);
var score2 = service.Score(resp2!);

Console.WriteLine($"Response 1 ({resp1!.SubmittedAt:yyyy-MM-dd}): {score1.ReadinessPercent:F1}%");
Console.WriteLine($"Response 2 ({resp2!.SubmittedAt:yyyy-MM-dd}): {score2.ReadinessPercent:F1}%");
Console.WriteLine($"Delta: {score2.ReadinessPercent - score1.ReadinessPercent:+#.#;-#.#}%");

// Section-level comparison
foreach (var (sectionId, sectionScore1) in score1.SectionScores)
{
    var sectionScore2 = score2.SectionScores[sectionId];
    var delta = sectionScore2 - sectionScore1;
    Console.WriteLine($"  {sectionId}: {sectionScore1} → {sectionScore2} ({delta:+#;-#})");
}

Configuration Options

Setting Location Default Description
Default template DefaultAssessmentTemplate.Create() pqc-readiness-v1 The only template seeded at startup
Recommendation threshold — critical AssessmentService.Score() < 40% of section max Generates "needs significant improvement"
Recommendation threshold — moderate AssessmentService.Score() 40–69% of section max Generates "moderate — further action"
Advanced readiness band AssessmentService.Score() ≥ 85% "Advanced readiness" summary
Moderate readiness band AssessmentService.Score() 60–84% "Moderate readiness" summary
Early readiness band AssessmentService.Score() 30–59% "Early readiness" summary
Critical band AssessmentService.Score() < 30% "Critical" summary
Minimum score per answer Portal SelectOption() max(1, computed) Floor to ensure answered questions contribute
FreeText minimal score Portal SetText() 1 (≤10 chars) or 3 (>10 chars) Heuristic for unvalidated text

Design Decisions & Rationale

Decision Rationale
Records over classes All types are C# record types — value equality, immutability, with-expressions for templates.
Score stored with answer The QuestionAnswer record carries its own Score. This means scores are determined at submission time and the response is self-contained. The Score() method recalculates independently for verification but uses the stored scores as primary.
Positional scoring for SingleChoice Simple, transparent, no hidden weights. The assumption is that options are ordered from best to worst. Organizations can adjust by reordering options in their custom template.
In-memory storage Templates and responses are held in List<T>. No database, no file I/O. Suitable for engagement-tier use. Production deployments should replace with persistent storage.
Section-level recommendations Threshold-based rather than absolute. Avoids overwhelming the user — only sections with clear gaps get recommendations.
Version-stamped responses Each AssessmentResponse records TemplateVersion. If a template is updated, older responses remain interpretable against their original version.
Share

Keyboard Shortcuts

⌘ K
Open search
/
Focus search
?
Show shortcuts
b
Toggle bookmark
Alt+←
Previous page
Alt+→
Next page
Esc
Close overlay