Skip to content

Commit

Permalink
Added support to decode the raw QR Code data to a SMART Health Card J…
Browse files Browse the repository at this point in the history
…WS token, see SmartHealthCardQRCodeDecoder
  • Loading branch information
angusmillar committed May 28, 2021
1 parent 55fa209 commit 15a3c1b
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 5 deletions.
10 changes: 10 additions & 0 deletions SmartHealthCard.QRCode/Encoder/INumericalModeDecoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using SmartHealthCard.QRCode.Model;
using System.Collections.Generic;

namespace SmartHealthCard.QRCode.Encoder
{
public interface INumericalModeDecoder
{
string Decode(IEnumerable<Chunk> ChunkList);
}
}
10 changes: 10 additions & 0 deletions SmartHealthCard.QRCode/Encoder/IQRCodeDecoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using SmartHealthCard.QRCode.Model;
using System.Collections.Generic;

namespace SmartHealthCard.QRCode.Encoder
{
public interface IQRCodeDecoder
{
IEnumerable<Chunk> GetQRCodeChunkList(IEnumerable<string> QRCodeRawDataList);
}
}
34 changes: 34 additions & 0 deletions SmartHealthCard.QRCode/Encoder/NumericalModeDecoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartHealthCard.QRCode.Model;

namespace SmartHealthCard.QRCode.Encoder
{
public class NumericalModeDecoder : INumericalModeDecoder
{
public string Decode(IEnumerable<Chunk> ChunkList)
{
StringBuilder StringBuilder = new StringBuilder();
foreach (Chunk Chunk in ChunkList)
{
string Numeric = Chunk.NumericSegment;
foreach (string Number in Spliter(Numeric, 2))
{
if (int.TryParse(Number, out int IntNumber))
{
StringBuilder.Append(Convert.ToChar(IntNumber + 45));
}
}
}
return StringBuilder.ToString();
}

private IEnumerable<string> Spliter(string str, int chunkSize)
{
return Enumerable.Range(0, str.Length / chunkSize)
.Select(i => str.Substring(i * chunkSize, chunkSize));
}
}
}
41 changes: 41 additions & 0 deletions SmartHealthCard.QRCode/Encoder/QRCodeDecoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Net.Codecrete.QrCodeGenerator;
using SmartHealthCard.QRCode.Exceptions;
using SmartHealthCard.QRCode.Model;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SmartHealthCard.QRCode.Encoder
{
public class QRCodeDecoder : IQRCodeDecoder
{
public IEnumerable<Chunk> GetQRCodeChunkList(IEnumerable<string> QRCodeRawDataList)
{
//shc:/2/3/56762909524320603460292437404460<snipped for brevity>
//shc:/56762909524320603460292437404460<snipped for brevity>
List<Chunk> ChunkList = new List<Chunk>();
Chunk? Chunk = null;
foreach (string QRCodeRawData in QRCodeRawDataList)
{
string[] Split = QRCodeRawData.Split('/');
if (Split.Length == 2)
{
Chunk = new Chunk($"{Split[0]}/", Split[1]);
}
else if (Split.Length == 4)
{
Chunk = new Chunk($"{Split[0]}/{Split[1]}/{Split[2]}/", Split[3]);
}
else
{
throw new QRCodeChunkFormatException($"The raw QR Code data was incorrectly formated, found {Split.Length} chunks where only 2 or 4 are allowed.");
}
ChunkList.Add(Chunk);
}
return ChunkList;
}
}
}
15 changes: 15 additions & 0 deletions SmartHealthCard.QRCode/Exceptions/QRCodeChunkFormatException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SmartHealthCard.QRCode.Exceptions
{
public class QRCodeChunkFormatException : FormatException
{
public QRCodeChunkFormatException(string message) : base(message)
{
}
}
}
4 changes: 2 additions & 2 deletions SmartHealthCard.QRCode/SmartHealthCard.QRCode.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
<Version>0.1.0-alpha.1</Version>
<Version>0.1.0-alpha.2</Version>
<Authors>Angus Millar</Authors>
<Description>FHIR SMART Health Card JWS token QR Code encoder libaray</Description>
<Company>PyroHealth</Company>
Expand All @@ -12,7 +12,7 @@
<RepositoryUrl>https://github.com/angusmillar/SmartHealthCard</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>SmartHealthCard JWS JWT FHIR covid19 immunization laboratory VerifiableCredential</PackageTags>
<PackageReleaseNotes>This is the first alpha.1 release of the library for alpha development use</PackageReleaseNotes>
<PackageReleaseNotes>Added support to decode the raw QR Code data to a SMART Health Card JWS token</PackageReleaseNotes>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

Expand Down
53 changes: 53 additions & 0 deletions SmartHealthCard.QRCode/SmartHealthCardQRCodeDecoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using SmartHealthCard.QRCode.Encoder;
using SmartHealthCard.QRCode.Model;
using System.Collections.Generic;

namespace SmartHealthCard.QRCode
{
/// <summary>
/// A decoder to decode from the raw QR Code data to a SMART Health Card JWS token
/// Sorry but this does not accept a QR image directly, it requires a scanner to
/// have gotten the raw QR Code data from the QR Code image
/// </summary>
public class SmartHealthCardQRCodeDecoder
{
private readonly INumericalModeDecoder NumericalModeDecoder;
private readonly IQRCodeDecoder QRCodeDecoder;

/// <summary>
/// Default Constructor
/// </summary>
public SmartHealthCardQRCodeDecoder()
{
this.NumericalModeDecoder = new NumericalModeDecoder();
this.QRCodeDecoder = new QRCodeDecoder();
}

/// <summary>
/// Provide any implementation of the following interfaces to override their default implementation
/// </summary>
/// <param name="NumericalModeDecoder">Provides an implementation of the Numerical decoder used for SMART Health Card QR Codes, see: https://smarthealth.cards/#encoding-chunks-as-qr-codes </param>
/// <param name="QRCodeDecoder">Provides an implementation of the chuck decoder used for SMART Health Card QR Codes, see: https://smarthealth.cards/#encoding-chunks-as-qr-codes</param>
public SmartHealthCardQRCodeDecoder(
INumericalModeDecoder? NumericalModeDecoder = null,
IQRCodeDecoder? QRCodeDecoder = null)
{
this.NumericalModeDecoder = NumericalModeDecoder ?? new NumericalModeDecoder();
this.QRCodeDecoder = QRCodeDecoder ?? new QRCodeDecoder();
}

/// <summary>
/// Provided SMART Health Card QR Code raw data and it will return a SMART Health Card JWS Token
/// </summary>
/// <param name="QRCodeRawDataList"></param>
/// <returns></returns>
public string GetToken(List<string> QRCodeRawDataList)
{
IQRCodeDecoder QRCodeDecoder = new QRCodeDecoder();
IEnumerable<Chunk> ChunkList = QRCodeDecoder.GetQRCodeChunkList(QRCodeRawDataList);
INumericalModeDecoder NumericalModeDecoder = new NumericalModeDecoder();
string test = NumericalModeDecoder.Decode(ChunkList);
return test;
}
}
}
3 changes: 2 additions & 1 deletion SmartHealthCard.QRCode/SmartHealthCardQRCodeEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class SmartHealthCardQRCodeEncoder


/// <summary>
/// An encoder to generate SMART Health Card QR Code Images or there raw payloads
/// Default Constructor
/// </summary>
public SmartHealthCardQRCodeEncoder()
: this(new QRCodeEncoderSettings())
Expand Down Expand Up @@ -83,5 +83,6 @@ public List<string> GetQRCodeRawDataList(string SmartHealthCardJWSToken)
Chunk[] ChunkArray = SmartHealthCardJwsChunker.Chunk(SmartHealthCardJWSToken);
return QRCodeEncoder.GetQRCodeRawDataList(ChunkArray);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@

namespace SmartHealthCard.Test
{
public class SmartHealthCardQRCodeFactoryTest
public class SmartHealthCardQRCodeEncoderTest
{
[Fact]
public async void Create_QRCode()
public async void Encode_QRCode()
{
//### Prepare ######################################################

Expand Down Expand Up @@ -77,5 +77,59 @@ public async void Create_QRCode()

}

[Fact]
public async void Decode_QRCodeRawData()
{
//### Prepare ######################################################

//Get the ECC certificate from the Windows Certificate Store by Thumb-print
X509Certificate2 Certificate = CertificateSupport.GetCertificate(Thumbprint: CertificateSupport.TestingThumbprint);

//The Version of FHIR in use
string FhirVersion = "4.0.1";

//Get FHIR bundle
Bundle FhirBundleResource = FhirDataSupport.GetCovid19FhirBundleExample1();
string FhirBundleJson = FhirSerializer.SerializeToJson(FhirBundleResource);

//The base of the URL where a validator will retie the public keys from (e.g : [Issuer]/.well-known/jwks.json)
Uri Issuer = new Uri("https://sonichealthcare.com/something");

//When the Smart Health Card became valid, the from date.
DateTimeOffset IssuanceDateTimeOffset = DateTimeOffset.Now.AddMinutes(-1);

//The Uri for the type of VerifiableCredentials
// Uri VerifiableCredentialType = new Uri("https://smarthealth.cards#covid19");
List<VerifiableCredentialType> VerifiableCredentialTypeList = new List<VerifiableCredentialType>() { VerifiableCredentialType.Covid19 };

//Create the SmartHealthCardModel
SmartHealthCardModel SmartHealthCardToEncode = new SmartHealthCardModel(Issuer, IssuanceDateTimeOffset,
new VerifiableCredential(VerifiableCredentialTypeList,
new CredentialSubject(FhirVersion, FhirBundleJson)));

//Instantiate the SmartHealthCard Encoder
SmartHealthCardEncoder SmartHealthCardEncoder = new SmartHealthCardEncoder();
X509Certificate2[] CertArray = new X509Certificate2[] { Certificate };

//### Act ##########################################################

//Get the Smart Health Card retrieve Token
string SmartHealthCardJwsToken = await SmartHealthCardEncoder.GetTokenAsync(Certificate, SmartHealthCardToEncode);

//Create list of QR Codes
SmartHealthCardQRCodeEncoder SmartHealthCardQRCodeEncoder = new SmartHealthCardQRCodeEncoder();
List<string> QRCodeRawDataList = SmartHealthCardQRCodeEncoder.GetQRCodeRawDataList(SmartHealthCardJwsToken);


SmartHealthCardQRCodeDecoder SmartHealthCardQRCodeDecoder = new SmartHealthCardQRCodeDecoder();
string JWS = SmartHealthCardQRCodeDecoder.GetToken(QRCodeRawDataList);

//### Assert #######################################################

Assert.True(!string.IsNullOrWhiteSpace(JWS));
Assert.Equal(3, JWS.Split('.').Length);

}

}
}

0 comments on commit 15a3c1b

Please sign in to comment.