feat(08-license-integration): wire RSA license check, remove -chiave arg
Replace AES checkLicense with MachineFingerprint+LicenseFile+LicenseValidator. License.lic hardcoded next to EXE; -chiave= CLI arg and InputArgs.chiave removed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
39a9566858
commit
3312a57757
7 changed files with 322 additions and 75 deletions
|
|
@ -86,13 +86,13 @@ Completed: 2026-05-13
|
||||||
|
|
||||||
**v0.2 NcProgramManager** (in progress)
|
**v0.2 NcProgramManager** (in progress)
|
||||||
Status: In Progress
|
Status: In Progress
|
||||||
Phases: 2 of 4 complete
|
Phases: 3 of 4 complete
|
||||||
|
|
||||||
| Phase | Name | Plans | Status |
|
| Phase | Name | Plans | Status |
|
||||||
|-------|------|-------|--------|
|
|-------|------|-------|--------|
|
||||||
| 06 | rename-cleanup | 1 | ✅ Complete |
|
| 06 | rename-cleanup | 1 | ✅ Complete |
|
||||||
| 07 | license-core | 1 | ✅ Complete |
|
| 07 | license-core | 1 | ✅ Complete |
|
||||||
| 08 | license-integration | 1 | Not started |
|
| 08 | license-integration | 1 | ✅ Complete | 2026-05-17 |
|
||||||
| 09 | license-generator | 1 | Not started |
|
| 09 | license-generator | 1 | Not started |
|
||||||
|
|
||||||
### Phase 06: rename-cleanup ✅
|
### Phase 06: rename-cleanup ✅
|
||||||
|
|
@ -105,13 +105,14 @@ Phases: 2 of 4 complete
|
||||||
**Completed:** 2026-05-17
|
**Completed:** 2026-05-17
|
||||||
**Plans:** [x] 07-01: RSA core + interfaces + 6 unit tests
|
**Plans:** [x] 07-01: RSA core + interfaces + 6 unit tests
|
||||||
|
|
||||||
### Phase 08: license-integration (proposed)
|
### Phase 08: license-integration ✅
|
||||||
**Goal:** Wire license system into Program.cs, replace `checkLicense`, replace `-chiave=` with `-licfile=`
|
**Goal:** Wire license system into Program.cs, delete `checkLicense`, remove `-chiave=`, hardcode `license.lic` next to EXE
|
||||||
**Depends on:** Phase 07
|
**Completed:** 2026-05-17
|
||||||
|
**Plans:** [x] 08-01: Program.cs wiring
|
||||||
|
|
||||||
### Phase 09: license-generator (proposed)
|
### Phase 09: license-generator (proposed)
|
||||||
**Goal:** Separate generator console app — reads machine fingerprint, signs, writes `.lic` file
|
**Goal:** Separate generator console app — reads machine fingerprint, signs, writes `.lic` file
|
||||||
**Depends on:** Phase 07
|
**Depends on:** Phase 07
|
||||||
|
|
||||||
---
|
---
|
||||||
*Roadmap updated: 2026-05-17 — Phase 07 license-core complete*
|
*Roadmap updated: 2026-05-17 — Phase 08 license-integration complete*
|
||||||
|
|
|
||||||
|
|
@ -5,27 +5,25 @@
|
||||||
See: .paul/PROJECT.md (updated 2026-05-17)
|
See: .paul/PROJECT.md (updated 2026-05-17)
|
||||||
|
|
||||||
**Core value:** Machinists transfer CNC programs to/from any controller without manual steps or internet
|
**Core value:** Machinists transfer CNC programs to/from any controller without manual steps or internet
|
||||||
**Current focus:** v0.1 complete — ready for next milestone planning
|
**Current focus:** v0.2 — Phase 08 complete, Phase 09 (license-generator) next
|
||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Milestone: v0.2 NcProgramManager
|
Milestone: v0.2 NcProgramManager
|
||||||
Phase: 3 of 4 (08-license-integration) — Not started
|
Phase: 4 of 4 (09-license-generator) — Not started
|
||||||
Plan: Not started
|
Status: Phase 08 complete, ready to plan Phase 09
|
||||||
Status: Ready to plan Phase 08
|
Last activity: 2026-05-17 — Phase 08 license-integration complete (UNIFY done)
|
||||||
Last activity: 2026-05-17 — Phase 07 complete, transitioned to Phase 08
|
|
||||||
|
|
||||||
Progress:
|
Progress:
|
||||||
- Milestone v0.1: [██████████] 100% (complete)
|
- Milestone v0.1: [██████████] 100% (complete)
|
||||||
- Milestone v0.2: [█████░░░░░] 50%
|
- Milestone v0.2: [███████░░░] 75%
|
||||||
- Phase 08: [░░░░░░░░░░] 0%
|
- Phase 09: [░░░░░░░░░░] 0%
|
||||||
|
|
||||||
## Loop Position
|
## Loop Position
|
||||||
|
|
||||||
Current loop state:
|
|
||||||
```
|
```
|
||||||
PLAN ──▶ APPLY ──▶ UNIFY
|
PLAN ──▶ APPLY ──▶ UNIFY
|
||||||
✓ ✓ ✓ [Loop complete — ready for next PLAN]
|
✓ ✓ ✓ [Phase 08 closed]
|
||||||
```
|
```
|
||||||
|
|
||||||
## What Was Built (v0.1 Multi-CN Release)
|
## What Was Built (v0.1 Multi-CN Release)
|
||||||
|
|
@ -58,44 +56,72 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||||
- `NcProgramValidatorTests` — 30+ NUnit tests
|
- `NcProgramValidatorTests` — 30+ NUnit tests
|
||||||
|
|
||||||
### Test Infrastructure
|
### Test Infrastructure
|
||||||
- `FanucProgramManager.Tests/` — NUnit 3.13.3 + Moq 4.18.4, .NET 4.7.2
|
- `NcProgramManager.Tests/` — NUnit 3.13.3 + Moq 4.18.4, .NET 4.7.2
|
||||||
- `FtpServerStub` + `Lsv2ServerStub` — in-process TCP stubs
|
- `FtpServerStub` + `Lsv2ServerStub` — in-process TCP stubs
|
||||||
- Unit + integration tests; Fanuc FOCAS tests `[Ignore]`d (Windows-only DLL)
|
- Unit + integration tests; Fanuc FOCAS tests `[Ignore]`d (Windows-only DLL)
|
||||||
|
|
||||||
## CLI Args (v0.1)
|
## What Was Built (v0.2 NcProgramManager — in progress)
|
||||||
|
|
||||||
|
### Phase 06 — Rename Cleanup
|
||||||
|
- Namespace: `FanucProgramManager.*` → `NcProgramManager.*` (all files)
|
||||||
|
- Project renamed, test folder renamed, dead `FANUCMachine.cs` deleted
|
||||||
|
|
||||||
|
### Phase 07 — License Core
|
||||||
|
- `IMachineFingerprint` / `ILicenseValidator` interfaces
|
||||||
|
- `MachineFingerprint` — WMI CPU/BIOS fingerprint, SHA256 hash
|
||||||
|
- `LicenseFile.TryRead` — reads `.lic` file, returns base64 signature
|
||||||
|
- `LicenseValidator.Validate` — RSA-SHA256 verify; PLACEHOLDER public key until Phase 09
|
||||||
|
- `LicenseValidatorTests` — 6 unit tests (testable via internal ctor injection)
|
||||||
|
|
||||||
|
### Phase 08 — License Integration
|
||||||
|
- `Program.cs` — `checkLicense` deleted; RSA check wired at startup:
|
||||||
|
```csharp
|
||||||
|
string _licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic");
|
||||||
|
if (!LicenseFile.TryRead(_licPath, out _licBase64)
|
||||||
|
|| !new LicenseValidator().Validate(_licFingerprint, _licBase64)) { ... }
|
||||||
|
```
|
||||||
|
- `InputArgs.cs` — `chiave` field removed
|
||||||
|
- `-chiave=` CLI arg removed; license file hardcoded next to EXE as `license.lic`
|
||||||
|
- `MostraAiuto` updated: instructs user to place `license.lic` next to executable
|
||||||
|
|
||||||
|
## CLI Args (current)
|
||||||
- `-manufacturer=fanuc|heidenhain|siemens|mitsubishi` (default: fanuc)
|
- `-manufacturer=fanuc|heidenhain|siemens|mitsubishi` (default: fanuc)
|
||||||
|
- `-azione=` — action (Scarica/Invia/etc.)
|
||||||
|
- `-ip=` — CNC IP
|
||||||
|
- `-porta=` — port
|
||||||
- `-username=` (Heidenhain/Siemens/Mitsubishi)
|
- `-username=` (Heidenhain/Siemens/Mitsubishi)
|
||||||
- `-password=` (Heidenhain/Siemens/Mitsubishi)
|
- `-password=` (Heidenhain/Siemens/Mitsubishi)
|
||||||
|
- License: place `license.lic` next to the EXE (no CLI arg)
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
### Decisions
|
### Decisions
|
||||||
- .NET 4.7.2 locked — max compatibility
|
- .NET 4.7.2 locked — max compatibility
|
||||||
- RSA asymmetric offline licensing: private key vendor-only, public key embedded in binary
|
- RSA asymmetric offline licensing: private key vendor-only, public key embedded in binary
|
||||||
- PublicKeyXml = PLACEHOLDER until Phase 09 generates real key pair
|
- `license.lic` always at `AppDomain.CurrentDomain.BaseDirectory` — no CLI override
|
||||||
- `RSACryptoServiceProvider` + `SHA256CryptoServiceProvider` (not HashAlgorithmName) — .NET 4.7.2 compat
|
- `RSACryptoServiceProvider` + `SHA256CryptoServiceProvider` (not HashAlgorithmName) — .NET 4.7.2 compat
|
||||||
- Constructor injection pattern: internal ctor accepts key XML — testable without Windows WMI
|
- Constructor injection pattern: internal ctor accepts key XML — testable without Windows WMI
|
||||||
|
- PublicKeyXml = PLACEHOLDER until Phase 09 generates real key pair
|
||||||
- Heidenhain: LSV2 port 19000, needs real hardware testing
|
- Heidenhain: LSV2 port 19000, needs real hardware testing
|
||||||
- Siemens: FTP `/_N_MPF_DIR/`, needs Sinumerik FTP option enabled on machine
|
- Siemens: FTP `/_N_MPF_DIR/`, needs Sinumerik FTP option enabled on machine
|
||||||
- `#` = Warning in Fanuc/Mitsubishi — macro variable indicator
|
- `#` = Warning in Fanuc/Mitsubishi — macro variable indicator
|
||||||
|
|
||||||
### Deferred Issues
|
### Deferred Issues
|
||||||
- License wiring into Program.cs — Phase 08 target
|
|
||||||
- License generator tool — Phase 09 target
|
|
||||||
- Real RSA key pair — Phase 09 output; copy PublicKeyXml into LicenseValidator.cs
|
- Real RSA key pair — Phase 09 output; copy PublicKeyXml into LicenseValidator.cs
|
||||||
|
- `AesCrypt.cs` dead code — remove in future cleanup phase
|
||||||
- Heidenhain: `ReadActiveProgramAsync` / `SelectMainProgram` not implemented
|
- Heidenhain: `ReadActiveProgramAsync` / `SelectMainProgram` not implemented
|
||||||
- Siemens: same as Heidenhain
|
- Siemens: same as Heidenhain
|
||||||
- Build + integration not verifiable on Linux (fwlib32 Windows-only)
|
- Build + integration not verifiable on Linux (fwlib32 Windows-only)
|
||||||
- MachineFingerprint WMI — code-review verified, needs real Windows hardware test
|
- MachineFingerprint WMI — code-review verified, needs real Windows hardware test
|
||||||
|
|
||||||
### Blockers/Concerns
|
### Blockers/Concerns
|
||||||
None — Phase 08 can start.
|
None — Phase 09 can start.
|
||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-05-17
|
Last session: 2026-05-17
|
||||||
Stopped at: Phase 07 complete (UNIFY done)
|
Stopped at: Phase 08 UNIFY complete
|
||||||
Next action: /paul:plan for Phase 08 (license-integration)
|
Next action: /paul:plan phase 09 license-generator
|
||||||
Resume file: .paul/ROADMAP.md
|
Resume file: .paul/ROADMAP.md
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
"status": "in_progress"
|
"status": "in_progress"
|
||||||
},
|
},
|
||||||
"phase": {
|
"phase": {
|
||||||
"number": 7,
|
"number": 8,
|
||||||
"name": "license-core",
|
"name": "license-integration",
|
||||||
"status": "complete"
|
"status": "complete"
|
||||||
},
|
},
|
||||||
"loop": {
|
"loop": {
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
},
|
},
|
||||||
"timestamps": {
|
"timestamps": {
|
||||||
"created_at": "2026-05-13T00:00:00Z",
|
"created_at": "2026-05-13T00:00:00Z",
|
||||||
"updated_at": "2026-05-17T21:00:00Z"
|
"updated_at": "2026-05-17T22:00:00Z"
|
||||||
},
|
},
|
||||||
"satellite": {
|
"satellite": {
|
||||||
"groom": true
|
"groom": true
|
||||||
|
|
|
||||||
199
.paul/phases/08-license-integration/08-01-PLAN.md
Normal file
199
.paul/phases/08-license-integration/08-01-PLAN.md
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
---
|
||||||
|
phase: 08-license-integration
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: ["07-01"]
|
||||||
|
files_modified:
|
||||||
|
- Program.cs
|
||||||
|
autonomous: true
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Wire the Phase 07 RSA license core into Program.cs: replace the hardcoded AES `checkLicense` call with `MachineFingerprint` + `LicenseFile` + `LicenseValidator`. Keep `-chiave=` as the CLI arg — it now carries a path to a `.lic` file instead of the AES key string.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
The current `checkLicense` embeds a hardcoded AES key/IV in source — anyone with the source or a decompiler can generate licenses for any machine. The Phase 07 RSA core is built but not yet wired in. This phase closes the gap: the app will reject any run where the `.lic` file (passed via `-chiave=`) doesn't contain a valid RSA-SHA256 signature matching the current machine's fingerprint.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
- `Program.cs` only — `checkLicense` method deleted; license check replaced with `MachineFingerprint` + `LicenseFile.TryRead` + `LicenseValidator.Validate`; `inputArgs.chiave` now treated as `.lic` file path; help text for `-chiave=` updated to describe the file path; dead `using System.Management` and `using System.Security.Cryptography` removed; `using NcProgramManager.Licensing` added
|
||||||
|
- `InputArgs.cs` — NO CHANGE (`chiave` field stays as-is)
|
||||||
|
- Docker build + all 118 tests still pass
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
## Project Context
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/STATE.md
|
||||||
|
|
||||||
|
## Phase 07 Output (direct inputs)
|
||||||
|
@Licensing/IMachineFingerprint.cs
|
||||||
|
@Licensing/ILicenseValidator.cs
|
||||||
|
@Licensing/LicenseFile.cs
|
||||||
|
@Licensing/MachineFingerprint.cs
|
||||||
|
@Licensing/LicenseValidator.cs
|
||||||
|
|
||||||
|
## File Being Modified
|
||||||
|
@Program.cs
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: -chiave= arg still parsed, value treated as .lic file path
|
||||||
|
```gherkin
|
||||||
|
Given Program.cs parseArgomenti is called with args containing "-chiave=C:\license.lic"
|
||||||
|
When the arg is parsed
|
||||||
|
Then inputArgs.chiave equals "C:\license.lic"
|
||||||
|
And the parsing code is unchanged from the current implementation
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: RSA license check used at startup
|
||||||
|
```gherkin
|
||||||
|
Given Program.cs Main is called
|
||||||
|
When the license check runs
|
||||||
|
Then it calls MachineFingerprint().GetFingerprint(), LicenseFile.TryRead(inputArgs.chiave), and LicenseValidator.Validate(fingerprint, base64)
|
||||||
|
And the old checkLicense method no longer exists
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: License failure exits with -100
|
||||||
|
```gherkin
|
||||||
|
Given the .lic file is missing OR the signature does not match the machine fingerprint
|
||||||
|
When Main runs
|
||||||
|
Then it prints "Licenza ERRATA", calls vediErrore(-100), and returns -1
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-4: Help text updated for -chiave=
|
||||||
|
```gherkin
|
||||||
|
Given MostraAiuto is called
|
||||||
|
When its output is inspected
|
||||||
|
Then -chiave= is documented as the path to the .lic license file
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Wire RSA license check into Program.cs</name>
|
||||||
|
<files>Program.cs</files>
|
||||||
|
<action>
|
||||||
|
**1. Replace usings at top of file:**
|
||||||
|
|
||||||
|
Remove these two lines:
|
||||||
|
```csharp
|
||||||
|
using System.Management;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
```
|
||||||
|
Add:
|
||||||
|
```csharp
|
||||||
|
using NcProgramManager.Licensing;
|
||||||
|
```
|
||||||
|
Keep all other existing usings unchanged.
|
||||||
|
|
||||||
|
**2. Replace the license check call in Main:**
|
||||||
|
|
||||||
|
Remove:
|
||||||
|
```csharp
|
||||||
|
if (!checkLicense(inputArgs.chiave))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Licenza ERRATA");
|
||||||
|
vediErrore(-100);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Replace with:
|
||||||
|
```csharp
|
||||||
|
string _licFingerprint = new MachineFingerprint().GetFingerprint();
|
||||||
|
string _licBase64;
|
||||||
|
if (!LicenseFile.TryRead(inputArgs.chiave, out _licBase64)
|
||||||
|
|| !new LicenseValidator().Validate(_licFingerprint, _licBase64))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Licenza ERRATA");
|
||||||
|
vediErrore(-100);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
`inputArgs.chiave` carries the `.lic` file path — parsing in `parseArgomenti` is unchanged.
|
||||||
|
|
||||||
|
**3. Delete the entire `checkLicense` static method:**
|
||||||
|
```csharp
|
||||||
|
static Boolean checkLicense(string chiave)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Remove from first line to closing brace entirely.
|
||||||
|
|
||||||
|
**4. Update MostraAiuto — update the description for `-chiave=`:**
|
||||||
|
|
||||||
|
Remove:
|
||||||
|
```csharp
|
||||||
|
Console.WriteLine("-chiave=");
|
||||||
|
Console.WriteLine(" la stringa chiave per eseguire il software.");
|
||||||
|
```
|
||||||
|
Replace with:
|
||||||
|
```csharp
|
||||||
|
Console.WriteLine("-chiave=");
|
||||||
|
Console.WriteLine(" Path del file di licenza (.lic) per autenticare il software.");
|
||||||
|
```
|
||||||
|
|
||||||
|
Avoid: touching `parseArgomenti` chiave parsing, `Scarica`, `Invia`, `vediErrore`, or any other part of the file.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
```bash
|
||||||
|
grep -n "checkLicense\|System\.Management\|System\.Security\.Cryptography" Program.cs
|
||||||
|
grep -n "MachineFingerprint\|LicenseFile\|LicenseValidator\|NcProgramManager\.Licensing" Program.cs
|
||||||
|
grep -n "chiave" Program.cs InputArgs.cs
|
||||||
|
```
|
||||||
|
First grep: zero hits.
|
||||||
|
Second grep: 4+ hits.
|
||||||
|
Third grep: `chiave` present in both files (parsing in Program.cs unchanged, field in InputArgs.cs unchanged).
|
||||||
|
</verify>
|
||||||
|
<done>AC-2 satisfied: RSA check wired. AC-3 satisfied: failure path unchanged (-1 return). AC-4 satisfied: help text updated. AC-1 satisfied: -chiave= parsing untouched.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- `InputArgs.cs` — `chiave` field stays as-is; no rename
|
||||||
|
- `-chiave=` parsing in `parseArgomenti` — keep exactly as-is
|
||||||
|
- `AesCrypt.cs` — now dead code but removal is out of scope; leave for a cleanup phase
|
||||||
|
- `Licensing/*.cs` — Phase 07 output, must not be modified
|
||||||
|
- All `Cnc/` files
|
||||||
|
- All test files
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- No changes to `Scarica`, `Invia`, `vediErrore` logic
|
||||||
|
- No changes to any other arg parsing in `parseArgomenti`
|
||||||
|
- No new test files (license core already tested in Phase 07)
|
||||||
|
- `AesCrypt.cs` stays in csproj and on disk
|
||||||
|
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Before declaring plan complete:
|
||||||
|
- [ ] `grep "checkLicense" Program.cs` — zero hits
|
||||||
|
- [ ] `grep "System.Management\|System.Security.Cryptography" Program.cs` — zero hits
|
||||||
|
- [ ] `grep "MachineFingerprint\|LicenseFile\|LicenseValidator" Program.cs` — 3 hits
|
||||||
|
- [ ] `grep "NcProgramManager.Licensing" Program.cs` — 1 hit
|
||||||
|
- [ ] `grep "chiave" InputArgs.cs` — still present (field unchanged)
|
||||||
|
- [ ] `grep "\-chiave=" Program.cs` — still present in parseArgomenti AND in MostraAiuto
|
||||||
|
- [ ] Docker build passes, all 118 tests pass
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- All tasks completed
|
||||||
|
- All verification checks pass
|
||||||
|
- `checkLicense` method gone
|
||||||
|
- `-chiave=` CLI arg and `inputArgs.chiave` field unchanged
|
||||||
|
- RSA license check wired at startup using `inputArgs.chiave` as .lic path
|
||||||
|
- Help text describes `-chiave=` as the .lic file path
|
||||||
|
- No test regressions
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.paul/phases/08-license-integration/08-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
63
.paul/phases/08-license-integration/08-01-SUMMARY.md
Normal file
63
.paul/phases/08-license-integration/08-01-SUMMARY.md
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
---
|
||||||
|
phase: 08-license-integration
|
||||||
|
plan: 01
|
||||||
|
type: summary
|
||||||
|
status: complete
|
||||||
|
completed: 2026-05-17
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 08 Summary: license-integration
|
||||||
|
|
||||||
|
## What Was Built
|
||||||
|
|
||||||
|
RSA license check wired into `Program.cs`. Old AES-based `checkLicense` deleted. License now validated at startup against `license.lic` in the EXE directory.
|
||||||
|
|
||||||
|
**Files modified:**
|
||||||
|
- `Program.cs` — license check replaced, `checkLicense` deleted, `-chiave=` parsing removed, `MostraAiuto` updated
|
||||||
|
- `InputArgs.cs` — `chiave` field removed
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| AC | Result | Notes |
|
||||||
|
|----|--------|-------|
|
||||||
|
| AC-1: -chiave= arg still parsed | **SUPERSEDED** | User directed removal mid-APPLY (see Deviations) |
|
||||||
|
| AC-2: RSA license check at startup | PASS | `MachineFingerprint` + `LicenseFile.TryRead` + `LicenseValidator.Validate` wired |
|
||||||
|
| AC-3: License failure exits -100 | PASS | `Console.WriteLine("Licenza ERRATA")` + `vediErrore(-100)` + `return -1` |
|
||||||
|
| AC-4: Help text updated | PASS | `-chiave=` block replaced with `license.lic` location note |
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
|
||||||
|
**Plan AC-1 superseded by user decision.**
|
||||||
|
Plan stated: keep `-chiave=` as the CLI arg carrying the `.lic` file path.
|
||||||
|
User directed mid-APPLY: remove `-chiave=` entirely; hardcode `license.lic` next to the EXE.
|
||||||
|
|
||||||
|
Actual license path:
|
||||||
|
```csharp
|
||||||
|
string _licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic");
|
||||||
|
```
|
||||||
|
|
||||||
|
`inputArgs.chiave` field removed from `InputArgs.cs`. All `-chiave=` parsing removed from `parseArgomenti`.
|
||||||
|
|
||||||
|
## Verification (post-apply grep)
|
||||||
|
|
||||||
|
```
|
||||||
|
grep "checkLicense|chiave|System.Management|System.Security.Cryptography" Program.cs → 0 hits
|
||||||
|
grep "MachineFingerprint|LicenseFile|LicenseValidator|NcProgramManager.Licensing" Program.cs → 5 hits
|
||||||
|
grep "chiave" InputArgs.cs → 0 hits
|
||||||
|
```
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- License file always at `AppDomain.CurrentDomain.BaseDirectory + "license.lic"` — no CLI override
|
||||||
|
- `InputArgs.chiave` field deleted (not just unused) — cleaner than leaving dead field
|
||||||
|
- `AesCrypt.cs` left on disk (still in csproj, no cleanup in scope)
|
||||||
|
|
||||||
|
## Deferred Issues
|
||||||
|
|
||||||
|
- Real RSA key pair — Phase 09 generates; copy `PublicKeyXml` into `LicenseValidator.cs`
|
||||||
|
- Phase 09: separate `LicenseGenerator` console app — reads WMI fingerprint, signs, writes `.lic`
|
||||||
|
- `AesCrypt.cs` dead code — remove in future cleanup phase
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
No new tests (license core tested in Phase 07). Docker build + 116 tests pass (2 Fanuc FOCAS ignored, Windows-only DLL).
|
||||||
|
|
@ -11,7 +11,6 @@ namespace NcProgramManager
|
||||||
public string porta = "";
|
public string porta = "";
|
||||||
public short pathCNC = 1;
|
public short pathCNC = 1;
|
||||||
public int nodoHssb = -1;
|
public int nodoHssb = -1;
|
||||||
public string chiave = "";
|
|
||||||
public string username = "";
|
public string username = "";
|
||||||
public string password = "";
|
public string password = "";
|
||||||
public string commentoProgramma = "";
|
public string commentoProgramma = "";
|
||||||
|
|
|
||||||
55
Program.cs
55
Program.cs
|
|
@ -1,11 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Management;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using NcProgramManager.Cnc;
|
using NcProgramManager.Cnc;
|
||||||
using NcProgramManager.Cnc.Models;
|
using NcProgramManager.Cnc.Models;
|
||||||
|
using NcProgramManager.Licensing;
|
||||||
|
|
||||||
namespace NcProgramManager
|
namespace NcProgramManager
|
||||||
{
|
{
|
||||||
|
|
@ -35,7 +34,11 @@ namespace NcProgramManager
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkLicense(inputArgs.chiave))
|
string _licFingerprint = new MachineFingerprint().GetFingerprint();
|
||||||
|
string _licBase64;
|
||||||
|
string _licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic");
|
||||||
|
if (!LicenseFile.TryRead(_licPath, out _licBase64)
|
||||||
|
|| !new LicenseValidator().Validate(_licFingerprint, _licBase64))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Licenza ERRATA");
|
Console.WriteLine("Licenza ERRATA");
|
||||||
vediErrore(-100);
|
vediErrore(-100);
|
||||||
|
|
@ -275,47 +278,6 @@ namespace NcProgramManager
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Boolean checkLicense(string chiave)
|
|
||||||
{
|
|
||||||
string pcInfo = string.Empty;
|
|
||||||
ManagementClass mc = new ManagementClass("win32_processor");
|
|
||||||
ManagementObjectCollection moc = mc.GetInstances();
|
|
||||||
|
|
||||||
foreach (ManagementObject mo in moc)
|
|
||||||
{
|
|
||||||
//Get only the first CPU's ID
|
|
||||||
pcInfo = mo.Properties["processorID"].Value.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
ManagementObjectSearcher baseboardSearcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_BaseBoard");
|
|
||||||
foreach (ManagementObject queryObj in baseboardSearcher.Get())
|
|
||||||
{
|
|
||||||
//Get motherboardSerialNumber
|
|
||||||
pcInfo += queryObj["SerialNumber"].ToString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (Aes myAes = Aes.Create())
|
|
||||||
{
|
|
||||||
if (chiave == "")
|
|
||||||
return false;
|
|
||||||
myAes.IV = Convert.FromBase64String("qqxNM8Li99Cwx61VX/QoWA==");
|
|
||||||
myAes.Key = Convert.FromBase64String("7/12mP3dFLGqPGjujlwBCVblURxBaBcGGQ2c2FatfP4=");
|
|
||||||
byte[] encrypted = AesCrypt.EncryptStringToBytes_Aes(pcInfo, myAes.Key, myAes.IV);
|
|
||||||
string roundtrip = AesCrypt.DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);
|
|
||||||
string decoded = "";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
decoded = AesCrypt.DecryptStringFromBytes_Aes(Convert.FromBase64String(chiave), myAes.Key, myAes.IV);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex.ToString());
|
|
||||||
}
|
|
||||||
return pcInfo == decoded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MostraAiuto()
|
static void MostraAiuto()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Utilizzo del Software gestore Programmi FANUC");
|
Console.WriteLine("Utilizzo del Software gestore Programmi FANUC");
|
||||||
|
|
@ -326,8 +288,7 @@ namespace NcProgramManager
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("Lista dei parametri da fornire all'eseguibile\nDevono essere separati da uno spazio tra l'uno e l'altro,\nnon andare a capo e non lasciare spazi dopo l'= ");
|
Console.WriteLine("Lista dei parametri da fornire all'eseguibile\nDevono essere separati da uno spazio tra l'uno e l'altro,\nnon andare a capo e non lasciare spazi dopo l'= ");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("-chiave=");
|
Console.WriteLine("Il file 'license.lic' deve essere posizionato nella stessa cartella dell'eseguibile.");
|
||||||
Console.WriteLine(" la stringa chiave per eseguire il software.");
|
|
||||||
Console.WriteLine("-azione=");
|
Console.WriteLine("-azione=");
|
||||||
Console.WriteLine(" l'{AZIONE} da eseguire.");
|
Console.WriteLine(" l'{AZIONE} da eseguire.");
|
||||||
Console.WriteLine("-manufacturer=");
|
Console.WriteLine("-manufacturer=");
|
||||||
|
|
@ -385,8 +346,6 @@ namespace NcProgramManager
|
||||||
inputArgs.mostraHelp = true;
|
inputArgs.mostraHelp = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var chiave = arguments.Find(it => new Regex("-chiave=.*", RegexOptions.IgnoreCase).IsMatch(it));
|
|
||||||
inputArgs.chiave = chiave.Substring(8);
|
|
||||||
var azione = arguments.Find(it => new Regex("-azione=.*", RegexOptions.IgnoreCase).IsMatch(it));
|
var azione = arguments.Find(it => new Regex("-azione=.*", RegexOptions.IgnoreCase).IsMatch(it));
|
||||||
inputArgs.azione = azione.Substring(8);
|
inputArgs.azione = azione.Substring(8);
|
||||||
var manufacturer = arguments.Find(it => new Regex("-manufacturer=.*", RegexOptions.IgnoreCase).IsMatch(it));
|
var manufacturer = arguments.Find(it => new Regex("-manufacturer=.*", RegexOptions.IgnoreCase).IsMatch(it));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue