Skip to content

Commit

Permalink
Merge pull request #2 from ABouveron/test_api_keygen
Browse files Browse the repository at this point in the history
API implementation
  • Loading branch information
ABouveron authored Jul 18, 2023
2 parents 23749a0 + 339bf2b commit 1ccd933
Show file tree
Hide file tree
Showing 43 changed files with 1,033 additions and 64 deletions.
12 changes: 12 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
KEYGEN_ACCOUNT_ID=
KEYGEN_ACCOUNT_ID_LOCALHOST=
KEYGEN_ACCOUNT_ID_OFFICIAL=
KEYGEN_ACCOUNT_EMAIL=
KEYGEN_ACCOUNT_EMAIL_LOCALHOST=
KEYGEN_ACCOUNT_EMAIL_OFFICIAL=
KEYGEN_ACCOUNT_PASSWORD=
KEYGEN_ACCOUNT_PASSWORD_LOCALHOST=
KEYGEN_ACCOUNT_PASSWORD_OFFICIAL=
KEYGEN_ADMIN_TOKEN=
KEYGEN_ADMIN_TOKEN_LOCALHOST=
KEYGEN_ADMIN_TOKEN_OFFICIAL=
4 changes: 2 additions & 2 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Docker Image CI

on:
push:
branches: [ "master" ]
branches: [ "master", "test_api_keygen" ]
pull_request:
branches: [ "master" ]
branches: [ "master", "test_api_keygen" ]

jobs:

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ example-csharp-licensing-Docker.sln
machine.lic
license.lic
!examples/*
*.env
/typescript
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ RUN dotnet build

FROM alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70 as runner
RUN apk add dotnet7-runtime
ENV COMPlus_EnableDiagnostics=0
WORKDIR /src
COPY --from=builder /src .
RUN apk --no-cache add curl
ENTRYPOINT ["dotnet", "bin/Debug/net7.0/example-csharp-licensing-Docker.dll", "examples/license.lic", "examples/machine.lic", "198e9fe586114844f6a4eaca5069b41a7ed43fb5a2df84892b69826d64573e39"]
77 changes: 54 additions & 23 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace example_csharp_licensing_Docker;

public abstract partial class Program
{
// Definition of all constant and variables needed to verify and decrypt the license file
private static string _publicKey = "e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788";

private static string _publicKey =
"e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788";

// Method to get EUID on Linux
[DllImport("libc")]
private static extern uint geteuid();
Expand All @@ -19,14 +22,15 @@ public abstract partial class Program
.Get()
.Cast<ManagementObject>()
.First()
.Properties["SerialNumber"]
.Value
.ToString();
.Properties["SerialNumber"].Value.ToString();

if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return null;
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return null;
if (geteuid() != 0)
{
Console.WriteLine("You must be root to get the serial number. Execute again with \"sudo dotnet run\".");
Console.WriteLine(
"You must be root to get the serial number. Execute again with \"sudo dotnet run\"."
);
return null;
}

Expand All @@ -41,30 +45,29 @@ public abstract partial class Program
}
}

public static void Main(string[] args)
public static void Main_aux(string[]? args)
{
try
{
string pathLicenseFile;
string pathMachineFile;
string fingerprint;
if (args.Length == 0)

if (args?.Length == 0)
{
pathLicenseFile = "license.lic";
pathMachineFile = "machine.lic";

var serialNumber = GetSerialNumber();
if (serialNumber is null)
{
Console.WriteLine(
"Unable to get serial number. Is your system compatible? Compatible systems list: [Windows, Linux]");
"Unable to get serial number. Is your system compatible? Compatible systems list: [Windows, Linux]"
);
return;
}
else
{
Console.WriteLine("Serial number : " + serialNumber);
}

Console.WriteLine("Serial number : " + serialNumber);

// Compute machine file fingerprint
var hashAlgorithm = new Sha3Digest(512);
Expand All @@ -81,20 +84,23 @@ public static void Main(string[] args)
}
else
{
Debug.Assert(args != null, nameof(args) + " != null");
pathLicenseFile = args[0];
pathMachineFile = args[1];
fingerprint = args[2];
}

var licenseKey = File.ReadAllText(pathLicenseFile);
var machineFileRaw = File.ReadAllText(pathMachineFile);

// Parse signed license file (removing cert header, newlines and footer)
string? encodedPayload;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
encodedPayload = WindowsRegex().Replace(machineFileRaw, "");
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
else if (
RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
|| RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
)
encodedPayload = UnixRegex().Replace(machineFileRaw, "");
else
encodedPayload = null;
Expand All @@ -116,9 +122,9 @@ public static void Main(string[] args)
encodedSignature = lic.sig;
algorithm = lic.alg;
}
catch (JsonException e)
catch (Exception e)
{
Console.WriteLine($"Failed to parse machine file: {e.Message}");
Console.WriteLine((object?)$"Failed to parse machine file: {e.Message}");

return;
}
Expand All @@ -144,6 +150,7 @@ public static void Main(string[] args)
Console.WriteLine("Machine file is valid! Decrypting...");

// Decrypt license file dataset
// ReSharper disable once NotAccessedVariable
string plaintext;
try
{
Expand All @@ -156,7 +163,7 @@ public static void Main(string[] args)
var tag = Convert.FromBase64String(encodedTag);
byte[] secret;

// Hash license key to get decryption secret
// Hash license key to get decryption secret
try
{
var licenseKeyBytes = Encoding.UTF8.GetBytes(licenseKey);
Expand Down Expand Up @@ -189,6 +196,7 @@ public static void Main(string[] args)
cipher.DoFinal(output, len);

// Convert decrypted bytes to string
// ReSharper disable once RedundantAssignment
plaintext = Encoding.UTF8.GetString(output);
}
catch (Exception e)
Expand Down Expand Up @@ -220,9 +228,32 @@ public static void Main(string[] args)
private static partial Regex UnixRegex();

// Regex used for Windows systems
[GeneratedRegex("(^-----BEGIN MACHINE FILE-----\n|^-----BEGIN MACHINE FILE-----\r\n|\r\n|-----END MACHINE FILE-----\n$|-----END MACHINE FILE-----\r\n$)")]
[GeneratedRegex(
"(^-----BEGIN MACHINE FILE-----\n|^-----BEGIN MACHINE FILE-----\r\n|\r\n|-----END MACHINE FILE-----\n$|-----END MACHINE FILE-----\r\n$)"
)]
private static partial Regex WindowsRegex();

public static async Task Main(string[]? args)
{
if (args == null || args.Length == 0)
{
Main_aux(args);
}
else
{
if (args[0] == "api")
{
Env.Load();
await CheckInternet.CheckInternetAsync();
TestApi.Main_aux();
return;
}

Main_aux(args);
}
}

[SuppressMessage("ReSharper", "InconsistentNaming")]
public class LicenseFile
{
public string? enc { get; set; }
Expand Down
45 changes: 43 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,73 @@

Original repo : https://github.com/ABouveron/example-csharp-cryptographic-machine-files

# How to run
# Test official and localhost API

The goal of this project is to be able to contact the API while online & offline. The official API can be contacted while online, and the localhost one will be used when you are offline.
You can only access the dashboard when online.

*Not functional on Windows.*

**Requirements:**
- Docker
- scripts working on Ubuntu (other distributions not tested)
- filling `install.env` with the credentials you want to use in localhost mode
- filling `.env` *\*_OFFICIAL* and *\*_LOCALHOST* variables with the appropriate values

OFFICIAL values can be found on [keygen.sh](https://app.keygen.sh/settings).
A token for the API will be generated on the first run. Execute the run.sh script two times to get both tokens: one while having Internet access and one without.

**Linux:**

```shell
source install.sh
```

```shell
source run.sh
```
In another terminal:
```shell
sudo dotnet run api
```

(You need to run it as root because sending an async ping is limited to root. If not running as root, the official API
cannot be contacted.)

# Run basic offline file verification

## Example config:

**Linux / Windows:**

```shell
dotnet run examples/license.lic examples/machine.lic 198e9fe586114844f6a4eaca5069b41a7ed43fb5a2df84892b69826d64573e39
```

## Normal config:
* Your fingerprint should be the hash of the serial number of your machine (you can execute the program to see it) computed with **SHA3_512** ([Online Converter](https://emn178.github.io/online-tools/sha3_512.html)).

* Your fingerprint should be the hash of the serial number of your machine (you can execute the program to see it)
computed with **SHA3_512** ([Online Converter](https://emn178.github.io/online-tools/sha3_512.html)).
* Replace the public key from [keygen.sh](keygen.sh) line 96 of `Program.cs`.
* Get your machine file on [keygen.sh](keygen.sh) and put the raw license key in a new file named `license.lic`.
* Put your `machine.lic` & `license.lic` in the same folder as `Program.cs` and run:

**Linux:**

```shell
sudo dotnet run
```

(You need to run it as root because it needs to access `/dev/sda` to get the serial number of your machine.)

**Windows:**

```shell
dotnet run
```

## Docker:

```shell
docker build . -t license-example-csharp
docker run -it license-example-csharp
Expand Down
8 changes: 3 additions & 5 deletions Test.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using Xunit.Sdk;

namespace example_csharp_licensing_Docker;

public class Test
Expand All @@ -19,8 +17,8 @@ public void UnitTest()
Console.SetOut(stringWriter);

// Run the application
Program.Main(
new string[]
Program.Main_aux(
new[]
{
"examples/license.lic",
"examples/machine.lic",
Expand All @@ -34,4 +32,4 @@ public void UnitTest()
const string expectedOutput = "Hello, World!";
Assert.Contains(expectedOutput, output);
}
}
}
8 changes: 7 additions & 1 deletion Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
global using System.Management;
global using System.Runtime.InteropServices;
global using System.Text;
global using System.Text.Json;
global using System.Text.RegularExpressions;
global using NSec.Cryptography;
global using Org.BouncyCastle.Crypto.Digests;
Expand All @@ -11,3 +10,10 @@
global using Org.BouncyCastle.Crypto.Parameters;
global using Xunit;
global using Xunit.Abstractions;
global using RestSharp;
global using System.Diagnostics.CodeAnalysis;
global using DotNetEnv;
global using Newtonsoft.Json;
global using example_csharp_licensing_Docker.utilities;
global using example_csharp_licensing_Docker.api;
global using System.Net.NetworkInformation;
51 changes: 51 additions & 0 deletions api/Authentication.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace example_csharp_licensing_Docker.api;

public abstract class Authentication
{
public static void LicenseAuth(
string licenseId,
string licenseKey,
string name,
string platform,
string fingerprint
)
{
var jsonLicenseKeyAuth = new JsonKeygen
{
data = new Data
{
type = "machines",
attributes = new Attributes
{
fingerprint = fingerprint,
platform = platform,
name = name
},
relationships = new Relationships
{
license = new License
{
data = new LicenseData { type = "licenses", id = licenseId }
}
}
}
};
Console.WriteLine(
BashCmd.Execute(
"curl -X POST https://"
+ CheckInternet.Api
+ "/v1/accounts/"
+ System.Environment.GetEnvironmentVariable("KEYGEN_ACCOUNT_ID")
+ "/machines "
+ "-H 'Content-Type: application/vnd.api+json' "
+ "-H 'Accept: application/vnd.api+json' "
+ "-H 'Authorization: License "
+ licenseKey
+ "' "
+ "-d '"
+ JsonConvert.SerializeObject(jsonLicenseKeyAuth)
+ "'"
)
);
}
}
Loading

0 comments on commit 1ccd933

Please sign in to comment.