Skip to content

Commit

Permalink
Merge pull request #33 from HenrikWM/f-combine-sample-server-apis
Browse files Browse the repository at this point in the history
Combine sample apis into single Token Api
  • Loading branch information
HenrikWM committed Dec 1, 2020
2 parents 305cb0e + ad465ff commit 7edc134
Show file tree
Hide file tree
Showing 26 changed files with 149 additions and 255 deletions.
9 changes: 1 addition & 8 deletions AnonymousTokens.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4DDE2573-894
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnonymousTokens.Client", "src\AnonymousTokens.Client\AnonymousTokens.Client.csproj", "{D98EB88A-9922-4AA5-8C97-6206C7196CD5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.TokenGeneration.Api", "samples\ClientServer\Server\Server.TokenGeneration.Api\Server.TokenGeneration.Api.csproj", "{E691A98B-3EBC-4AE7-8858-67601E805909}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.TokenVerification.Api", "samples\ClientServer\Server\Server.TokenVerification.Api\Server.TokenVerification.Api.csproj", "{A4143A14-789E-4AD0-BAD9-BF5B32E24EC7}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Token.Api", "samples\ClientServer\Server\Server.Token.Api\Server.Token.Api.csproj", "{E691A98B-3EBC-4AE7-8858-67601E805909}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{4FB09123-E5E6-4367-BEDD-FF3D9906A2BA}"
EndProject
Expand Down Expand Up @@ -56,10 +54,6 @@ Global
{E691A98B-3EBC-4AE7-8858-67601E805909}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E691A98B-3EBC-4AE7-8858-67601E805909}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E691A98B-3EBC-4AE7-8858-67601E805909}.Release|Any CPU.Build.0 = Release|Any CPU
{A4143A14-789E-4AD0-BAD9-BF5B32E24EC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4143A14-789E-4AD0-BAD9-BF5B32E24EC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4143A14-789E-4AD0-BAD9-BF5B32E24EC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4143A14-789E-4AD0-BAD9-BF5B32E24EC7}.Release|Any CPU.Build.0 = Release|Any CPU
{E24CF8EE-6D9B-49AC-92A5-EB1F81FDAAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E24CF8EE-6D9B-49AC-92A5-EB1F81FDAAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E24CF8EE-6D9B-49AC-92A5-EB1F81FDAAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -84,7 +78,6 @@ Global
{CCB7635D-7D0F-4371-AC14-828E208790AF} = {017DB04E-CA83-4400-90FD-4CA10428EC51}
{D98EB88A-9922-4AA5-8C97-6206C7196CD5} = {4DDE2573-8947-4348-95B4-688102A8294D}
{E691A98B-3EBC-4AE7-8858-67601E805909} = {017DB04E-CA83-4400-90FD-4CA10428EC51}
{A4143A14-789E-4AD0-BAD9-BF5B32E24EC7} = {017DB04E-CA83-4400-90FD-4CA10428EC51}
{E24CF8EE-6D9B-49AC-92A5-EB1F81FDAAA2} = {4FB09123-E5E6-4367-BEDD-FF3D9906A2BA}
{017DB04E-CA83-4400-90FD-4CA10428EC51} = {645EAD64-C3E7-41AA-B7E4-E423DFFCEEDF}
{EF827382-7F6E-4230-A917-9D11415ECAD6} = {4DDE2573-8947-4348-95B4-688102A8294D}
Expand Down
84 changes: 74 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,80 @@

A C#-implementation of <https://github.com/tjesi/anonymous-tokens>.

## Scope
## Getting started

This Proof-of-Concept contains:
A typical scenario consists of a client and a server. A complete sample of this scenario is available in the `samples\ClientServer` folder, where the client is a console application and the server is an ASP.NET MVC API.

- Generating the key pair
- Initiating the communication with random numbers
- Generating the token and creating the proof of correctness
- Randomizing the token and verifies proof of correctness
- Verification of token
1. Create a cryptographic key pair.

Example using the elliptic curve `prime256v1`:

```bash
# generate a private key for a curve
openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem

# generate corresponding public key
openssl ec -in private-key.pem -pubout -out public-key.pem
```

### Server

2. The server instantiates `TokenGenerator` and `TokenVerifier` so that they are available to the client.

If your `TokenGenerator` and `TokenVerifier` are hosted in a REST API you will need to create API-endpoints for token generation and verification. See the sample `Server.Token.Api` project for examples on request/response models, API-endpoint contract etc.

3. Create an implementation for `IPrivateKeyStore`.

We provide an `InMemoryPrivateKeyStore` which loads a dummy-key to use for demo purposes and quickstarts. Your **Private key** should be loaded from a database, embedded resource, from device storage etc.

4. Create an implementation for `ISeedStore`.

We provide an `InMemorySeedStore` which contains an in-memory list to use for demo purposes and quickstarts. Your seed store should be a database or some persistent storage.

### Client

5. Create an implementation for `IPublicKeyStore`.

We provide an `InMemoryPublicKeyStore` which loads a dummy-key to use for demo purposes and quickstarts. Your **Public key** should be loaded from a database, embedded resource, from device storage, loaded from an API etc.

6. Instantiate the `Initiate` class and perform token generation and token verification.

Here's an example from the sample `Console.Client` project on how this might look:

```csharp
// Import parameters for the elliptic curve prime256v1
var ecParameters = CustomNamedCurves.GetByOid(X9ObjectIdentifiers.Prime256v1);

var publicKeyStore = new InMemoryPublicKeyStore();
var publicKey = await publicKeyStore.GetAsync();

_initiator = new Initiator();

// 1. Initiate communication with a masked point P = r*T = r*Hash(t)
var init = _initiator.Initiate(ecParameters.Curve);
var t = init.t;
var r = init.r;
var P = init.P;

// 2. Generate token Q = k*P and proof (c,z) of correctness
var (Q, proofC, proofZ) = await _tokenApiClient.GenerateTokenAsync(ecParameters.Curve, P);

// 3. Randomise the token Q, by removing the mask r: W = (1/r)*Q = k*T. Also checks that proof (c,z) is correct.
var W = _initiator.RandomiseToken(ecParameters, publicKey, P, Q, proofC, proofZ, r);

// 4. Verify that the token (t,W) is correct.
var isVerified = await _tokenApiClient.VerifyTokenAsync(t, W);
if (isVerified)
{
Console.WriteLine("Token is valid.");
}
else
{
Console.WriteLine("Token is invalid.");
}
```

Your client should now be able to perform the protocol flow.

## How to build

Expand All @@ -32,9 +97,8 @@ This Proof-of-Concept contains:

- In the root of the cloned repo open 3 terminal windows.
- Run each in a separate terminal:
- `dotnet run --project .\samples\ClientServer\Client\Client.Console\Client.Console.csproj`
- `dotnet run --project .\samples\ClientServer\Server\Server.TokenGeneration.Api\Server.TokenGeneration.Api.csproj`
- `dotnet run --project .\samples\ClientServer\Server\Server.TokenVerification.Api\Server.TokenVerification.Api.csproj`
- `dotnet run --project .\samples\ClientServer\Client.Console\Client.Console.csproj`
- `dotnet run --project .\samples\ClientServer\Server.Token.Api\Server.Token.Api.csproj`

### Build and run benchmarks

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Encoders;

namespace AnonymousTokensConsole.ApiClients.TokenGeneration.Models
namespace AnonymousTokensConsole.ApiClients.TokenApi.Models
{
public class GenerateTokenRequestModel
{
Expand All @@ -12,4 +12,4 @@ public GenerateTokenRequestModel(ECPoint P)
PAsHex = Hex.ToHexString(P.GetEncoded());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;

namespace AnonymousTokensConsole.ApiClients.TokenGeneration.Models
namespace AnonymousTokensConsole.ApiClients.TokenApi.Models
{
public class GenerateTokenResponseModel
{
Expand All @@ -13,4 +13,4 @@ public class GenerateTokenResponseModel
[JsonPropertyName("proofZAsHex")]
public string ProofZAsHex { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Encoders;

namespace Client.Console.ApiClients.TokenVerification.Models
namespace Client.Console.ApiClients.TokenApi.Models
{
public class VerifyTokenRequestModel
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@

using AnonymousTokensConsole.ApiClients.TokenGeneration.Models;

using AnonymousTokensConsole.ApiClients.TokenApi.Models;

using Client.Console.ApiClients.TokenApi.Models;

using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
Expand All @@ -11,17 +13,17 @@
using System.Text.Json;
using System.Threading.Tasks;

namespace AnonymousTokensConsole.ApiClients.TokenGeneration
namespace AnonymousTokensConsole.ApiClients.TokenApi
{
public class TokenGenerationApiClient
public class TokenApiClient
{
private static readonly HttpClient _client = new HttpClient();

private const string TokenGenerationApiUrl = "https://localhost:5001";
private const string TokenApiUrl = "https://localhost:5001";

public TokenGenerationApiClient()
public TokenApiClient()
{
_client.BaseAddress = new Uri(TokenGenerationApiUrl);
_client.BaseAddress = new Uri(TokenApiUrl);
_client.DefaultRequestHeaders.Add("Accept", "application/json");
}

Expand Down Expand Up @@ -50,5 +52,27 @@ public async Task<(ECPoint Q, BigInteger proofC, BigInteger proofZ)> GenerateTok

throw new Exception($"Failed to generate token: {result.ReasonPhrase}.");
}

public async Task<bool> VerifyTokenAsync(byte[] t, ECPoint W)
{
var requestUri = new Uri($"/token/verify", UriKind.Relative);

var request = new HttpRequestMessage(HttpMethod.Post, requestUri);

var jsonPayload = JsonSerializer.Serialize(new VerifyTokenRequestModel(t, W));

request.Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");

var result = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
if (result.IsSuccessStatusCode)
{
using var contentStream = await result.Content.ReadAsStreamAsync();
var response = await JsonSerializer.DeserializeAsync<bool>(contentStream, null);

return response;
}

throw new Exception($"Failed to verify token: {result.ReasonPhrase}.");
}
}
}
}

This file was deleted.

11 changes: 4 additions & 7 deletions samples/ClientServer/Client/Client.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
using AnonymousTokens.Client.Protocol;
using AnonymousTokens.Services.InMemory;

using AnonymousTokensConsole.ApiClients.TokenGeneration;

using Client.Console.ApiClients.TokenVerification;
using AnonymousTokensConsole.ApiClients.TokenApi;

using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.EC;
Expand All @@ -19,8 +17,7 @@ class Program
{
private static Initiator _initiator;

private static readonly TokenGenerationApiClient _tokenGenerationClient = new TokenGenerationApiClient();
private static readonly TokenVerificationApiClient _tokenVerificationClient = new TokenVerificationApiClient();
private static readonly TokenApiClient _tokenApiClient = new TokenApiClient();

static async Task Main(string[] args)
{
Expand All @@ -39,13 +36,13 @@ static async Task Main(string[] args)
var P = init.P;

// 2. Generate token Q = k*P and proof (c,z) of correctness
var (Q, proofC, proofZ) = await _tokenGenerationClient.GenerateTokenAsync(ecParameters.Curve, P);
var (Q, proofC, proofZ) = await _tokenApiClient.GenerateTokenAsync(ecParameters.Curve, P);

// 3. Randomise the token Q, by removing the mask r: W = (1/r)*Q = k*T. Also checks that proof (c,z) is correct.
var W = _initiator.RandomiseToken(ecParameters, publicKey, P, Q, proofC, proofZ, r);

// 4. Verify that the token (t,W) is correct.
var isVerified = await _tokenVerificationClient.VerifyTokenAsync(t, W);
var isVerified = await _tokenApiClient.VerifyTokenAsync(t, W);
if (isVerified)
{
Console.WriteLine("Token is valid.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,34 @@
using Org.BouncyCastle.Crypto.EC;
using Org.BouncyCastle.Utilities.Encoders;

using Server.TokenGeneration.Api.Models;
using Server.Token.Api.Models;

using System.Threading.Tasks;

namespace Server.TokenGeneration.Api.Controllers
namespace Server.Token.Api.Controllers
{
[ApiController]
[Route("[controller]")]
public class TokenController : ControllerBase
{
private readonly X9ECParameters _ecParameters;
private readonly IPrivateKeyStore _privateKeyStore;
private readonly IPublicKeyStore _publicKeyStore;
private readonly ITokenGenerator _tokenGenerator;
private readonly ITokenVerifier _tokenVerifier;

private readonly X9ECParameters _ecParameters;

public TokenController(
IPrivateKeyStore privateKeyStore,
IPublicKeyStore publicKeyStore,
ITokenGenerator tokenGenerator)
ITokenGenerator tokenGenerator,
ITokenVerifier tokenVerifier)
{
_privateKeyStore = privateKeyStore;
_publicKeyStore = publicKeyStore;
_tokenGenerator = tokenGenerator;
_tokenVerifier = tokenVerifier;

_ecParameters = CustomNamedCurves.GetByOid(X9ObjectIdentifiers.Prime256v1);
}

Expand All @@ -48,5 +53,18 @@ public async Task<GenerateTokenResponseModel> Generate(GenerateTokenRequestModel

return new GenerateTokenResponseModel(Q, c, z);
}

[Route("verify")]
[HttpPost]
public async Task<bool> Verify(VerifyTokenRequestModel model)
{
var k = await _privateKeyStore.GetAsync();
var t = Hex.Decode(model.tAsHex);
var W = _ecParameters.Curve.DecodePoint(Hex.Decode(model.WAsHex));

var isValid = await _tokenVerifier.VerifyTokenAsync(k, _ecParameters.Curve, t, W);

return isValid;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Server.TokenGeneration.Api.Models
namespace Server.Token.Api.Models
{

public class GenerateTokenRequestModel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Encoders;

namespace Server.TokenGeneration.Api.Models
namespace Server.Token.Api.Models
{
public class GenerateTokenResponseModel
{
Expand Down

0 comments on commit 7edc134

Please sign in to comment.