From bdb54c653eda54b003e50460928cfd8bbc80bc44 Mon Sep 17 00:00:00 2001 From: CGantert345 <57003061+CGantert345@users.noreply.github.com> Date: Thu, 16 Dec 2021 16:13:13 +0100 Subject: extended api for dynamic content additional tests --- src/main/java/org/uic/barcode/Decoder.java | 12 ++ src/main/java/org/uic/barcode/Encoder.java | 18 ++ .../dynamicContent/api/DynamicContentCoder.java | 4 +- .../uic/barcode/dynamicContent/fdc1/TimeStamp.java | 7 +- .../org/uic/barcode/dynamicFrame/DynamicFrame.java | 94 ++++++++- .../test/DynamicFrameDynamicContentApiTest.java | 230 +++++++++++++++++++++ .../java/org/uic/barcode/test/TimeStampTest.java | 36 ++++ .../uic/barcode/test/utils/DynamicTestContent.java | 38 ++++ 8 files changed, 436 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/uic/barcode/test/DynamicFrameDynamicContentApiTest.java create mode 100644 src/test/java/org/uic/barcode/test/TimeStampTest.java create mode 100644 src/test/java/org/uic/barcode/test/utils/DynamicTestContent.java diff --git a/src/main/java/org/uic/barcode/Decoder.java b/src/main/java/org/uic/barcode/Decoder.java index 9f102d3..4c8b044 100644 --- a/src/main/java/org/uic/barcode/Decoder.java +++ b/src/main/java/org/uic/barcode/Decoder.java @@ -8,6 +8,7 @@ import java.security.PublicKey; import java.security.SignatureException; import java.util.zip.DataFormatException; +import org.uic.barcode.dynamicContent.api.IUicDynamicContent; import org.uic.barcode.dynamicFrame.Constants; import org.uic.barcode.dynamicFrame.DataType; import org.uic.barcode.dynamicFrame.DynamicFrame; @@ -237,6 +238,17 @@ public class Decoder { return dynamicFrame; } + /** + * Gets the dynamic content. + * + * @return the dynamic header + */ + public IUicDynamicContent getDynamicContent() { + if (dynamicFrame == null) return null; + + return dynamicFrame.getDynamicContent(); + } + /** * Sets the dynamic header. * diff --git a/src/main/java/org/uic/barcode/Encoder.java b/src/main/java/org/uic/barcode/Encoder.java index f0f8d14..3b30e8a 100644 --- a/src/main/java/org/uic/barcode/Encoder.java +++ b/src/main/java/org/uic/barcode/Encoder.java @@ -6,6 +6,7 @@ import java.security.Provider; import java.security.PublicKey; import org.uic.barcode.asn1.datatypesimpl.OctetString; +import org.uic.barcode.dynamicContent.api.IUicDynamicContent; import org.uic.barcode.dynamicContent.fdc1.UicDynamicContentDataFDC1; import org.uic.barcode.dynamicFrame.Constants; import org.uic.barcode.dynamicFrame.DataType; @@ -178,6 +179,15 @@ public class Encoder { } } + public void setDynamicData(IUicDynamicContent content) throws EncodingFormatException { + if (dynamicFrame != null) { + if (dynamicFrame.getLevel2SignedData() == null) { + dynamicFrame.setLevel2SignedData(new Level2DataType()); + } + dynamicFrame.addDynamicContent(content); + } + } + public void setLevel2Data(DataType level2data) { if (dynamicFrame != null) { if (dynamicFrame.getLevel2SignedData() == null) { @@ -203,6 +213,14 @@ public class Encoder { return null; } + + public IUicDynamicContent getDynamicContent() { + if (dynamicFrame != null && dynamicFrame.getLevel2SignedData() != null) { + return dynamicFrame.getDynamicContent(); + } + return null; + } + public UicDynamicContentDataFDC1 getDynamicContentDataUIC1() { if (dynamicFrame != null && dynamicFrame.getLevel2SignedData() != null) { return dynamicFrame.getDynamicDataFDC1(); diff --git a/src/main/java/org/uic/barcode/dynamicContent/api/DynamicContentCoder.java b/src/main/java/org/uic/barcode/dynamicContent/api/DynamicContentCoder.java index d8bf3b4..34406e0 100644 --- a/src/main/java/org/uic/barcode/dynamicContent/api/DynamicContentCoder.java +++ b/src/main/java/org/uic/barcode/dynamicContent/api/DynamicContentCoder.java @@ -26,9 +26,11 @@ import org.uic.barcode.ticket.api.utils.UicEncoderUtils; public class DynamicContentCoder { + public static String dynamicContentDataFDC1 = "FDC1"; + public static byte[] encode(IUicDynamicContent content, String format) throws EncodingFormatException { - if (format != null && !format.equals("FDC1")) { + if (format != null && !format.equals(dynamicContentDataFDC1)) { throw new EncodingFormatException("Format of dynamic content not supported!"); } diff --git a/src/main/java/org/uic/barcode/dynamicContent/fdc1/TimeStamp.java b/src/main/java/org/uic/barcode/dynamicContent/fdc1/TimeStamp.java index ecbb226..a1b9581 100644 --- a/src/main/java/org/uic/barcode/dynamicContent/fdc1/TimeStamp.java +++ b/src/main/java/org/uic/barcode/dynamicContent/fdc1/TimeStamp.java @@ -91,8 +91,11 @@ public class TimeStamp { this.secondOfDay = time; } + + + /** - * Gets the time. + * Gets the current date and time in UTC * * @return the date and time of content creation in UTC */ @@ -136,4 +139,6 @@ public class TimeStamp { } + + } diff --git a/src/main/java/org/uic/barcode/dynamicFrame/DynamicFrame.java b/src/main/java/org/uic/barcode/dynamicFrame/DynamicFrame.java index c74215d..eb26729 100644 --- a/src/main/java/org/uic/barcode/dynamicFrame/DynamicFrame.java +++ b/src/main/java/org/uic/barcode/dynamicFrame/DynamicFrame.java @@ -18,10 +18,14 @@ import org.uic.barcode.asn1.datatypes.RestrictedString; import org.uic.barcode.asn1.datatypes.Sequence; import org.uic.barcode.asn1.datatypesimpl.OctetString; import org.uic.barcode.asn1.uper.UperEncoder; +import org.uic.barcode.dynamicContent.api.DynamicContentCoder; +import org.uic.barcode.dynamicContent.api.IUicDynamicContent; import org.uic.barcode.dynamicContent.fdc1.UicDynamicContentDataFDC1; +import org.uic.barcode.ticket.EncodingFormatException; import org.uic.barcode.utils.AlgorithmNameResolver; +// TODO: Auto-generated Javadoc /** * The DynamicHeader for bar codes * @@ -30,6 +34,9 @@ import org.uic.barcode.utils.AlgorithmNameResolver; @Sequence public class DynamicFrame extends Object{ + /** + * Instantiates a new dynamic frame. + */ public DynamicFrame() {} /** The format. */ @@ -37,12 +44,13 @@ public class DynamicFrame extends Object{ @RestrictedString(CharacterRestriction.IA5String) public String format; + /** The level 2 signed data. */ /*level 2 data*/ @FieldOrder(order = 1) Level2DataType level2SignedData; - /** The signature of level 2 data*/ + /** The signature of level 2 data. */ @FieldOrder(order = 2) @Asn1Optional public OctetString level2Signature; @@ -64,18 +72,38 @@ public class DynamicFrame extends Object{ this.format = format; } + /** + * Gets the level 2 signed data. + * + * @return the level 2 signed data + */ public Level2DataType getLevel2SignedData() { return level2SignedData; } + /** + * Sets the level 2 signed data. + * + * @param level2SignedData the new level 2 signed data + */ public void setLevel2SignedData(Level2DataType level2SignedData) { this.level2SignedData = level2SignedData; } + /** + * Gets the level 2 signature. + * + * @return the level 2 signature + */ public OctetString getLevel2Signature() { return level2Signature; } + /** + * Sets the level 2 signature. + * + * @param level2Signature the new level 2 signature + */ public void setLevel2Signature(OctetString level2Signature) { this.level2Signature = level2Signature; } @@ -108,6 +136,7 @@ public class DynamicFrame extends Object{ * * Note: an appropriate security provider (e.g. BC) must be registered before * + * @return the int */ public int validateLevel2() { @@ -120,6 +149,8 @@ public class DynamicFrame extends Object{ * * Note: an appropriate security provider (e.g. BC) must be registered before * + * @param prov the prov + * @return the int */ public int validateLevel2(Provider prov) { @@ -213,6 +244,9 @@ public class DynamicFrame extends Object{ * * Note: an appropriate security provider (e.g. BC) must be registered before * + * @param key the key + * @param prov the prov + * @return the int */ public int validateLevel1(PublicKey key, Provider prov) { @@ -282,6 +316,8 @@ public class DynamicFrame extends Object{ * * Note: an appropriate security provider (e.g. BC) must be registered before * + * @param key the key + * @return the int */ public int validateLevel1(PublicKey key) { @@ -289,6 +325,12 @@ public class DynamicFrame extends Object{ } + /** + * Sign level 2 data without a specific security provider. + * + * @param key the key + * @throws Exception the exception + */ public void signLevel2(PrivateKey key) throws Exception { //find the algorithm name for the signature OID @@ -302,6 +344,13 @@ public class DynamicFrame extends Object{ } + /** + * Sign level 2 data. + * + * @param key the key + * @param prov the security Provider + * @throws Exception the exception + */ public void signLevel2(PrivateKey key, Provider prov) throws Exception { //find the algorithm name for the signature OID @@ -315,10 +364,53 @@ public class DynamicFrame extends Object{ } + /** + * Adds the dynamic content and encodes it. (API level) + * + * @param content the dynamic content + * @throws EncodingFormatException the encoding format exception + */ + public void addDynamicContent(IUicDynamicContent content) throws EncodingFormatException { + + + this.getLevel2SignedData().setLevel2Data(new DataType()); + + this.getLevel2SignedData().getLevel2Data().setFormat(DynamicContentCoder.dynamicContentDataFDC1); + + this.getLevel2SignedData().getLevel2Data().setByteData(DynamicContentCoder.encode(content, DynamicContentCoder.dynamicContentDataFDC1)); + + } + + /** + * Adds the level 2 dynamic data. (ASN level) + * + * @param dynamicData the dynamic data + */ public void addLevel2DynamicData(UicDynamicContentDataFDC1 dynamicData) { this.getLevel2SignedData().setLevel2Data( dynamicData.getDataType()); } + /** + * Gets the dynamic content. + * + * @return the dynamic content + */ + public IUicDynamicContent getDynamicContent() { + + if (this.getLevel2SignedData() == null || + this.getLevel2SignedData().getLevel2Data() == null){ + return null; + } + + return DynamicContentCoder.decode(this.getLevel2SignedData().getLevel2Data().getByteData()); + + } + + /** + * Gets the dynamic data FDC 1. + * + * @return the dynamic data FDC 1 + */ public UicDynamicContentDataFDC1 getDynamicDataFDC1() { if (this.getLevel2SignedData() == null || diff --git a/src/test/java/org/uic/barcode/test/DynamicFrameDynamicContentApiTest.java b/src/test/java/org/uic/barcode/test/DynamicFrameDynamicContentApiTest.java new file mode 100644 index 0000000..5a70841 --- /dev/null +++ b/src/test/java/org/uic/barcode/test/DynamicFrameDynamicContentApiTest.java @@ -0,0 +1,230 @@ +package org.uic.barcode.test; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Date; +import java.util.zip.DataFormatException; + +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.junit.Before; +import org.junit.Test; +import org.uic.barcode.Decoder; +import org.uic.barcode.Encoder; +import org.uic.barcode.dynamicContent.api.IUicDynamicContent; +import org.uic.barcode.dynamicFrame.Constants; +import org.uic.barcode.test.utils.DynamicTestContent; +import org.uic.barcode.test.utils.SimpleUICTestTicket; +import org.uic.barcode.ticket.EncodingFormatException; +import org.uic.barcode.ticket.api.spec.IUicRailTicket; + +public class DynamicFrameDynamicContentApiTest { + + public String signatureAlgorithmOID = null; + public String elipticCurve = null; + public String keyPairAlgorithmOID = null; + + public KeyPair keyPairLevel1 = null; + public KeyPair keyPairLevel2 = null; + + public byte[] passIdHash = "PassId".getBytes(); + public byte[] phoneIdHash = "myPhone".getBytes(); + + + public IUicRailTicket testFCBticket = null; + + + @Before public void initialize() { + + signatureAlgorithmOID = Constants.ECDSA_SHA256; + keyPairAlgorithmOID = Constants.KG_EC_256; + elipticCurve = "secp256k1"; + + testFCBticket = SimpleUICTestTicket.getUicTestTicket(); + + Security.addProvider(new BouncyCastleProvider()); + + try { + keyPairLevel1 = generateECKeys(keyPairAlgorithmOID, elipticCurve); + keyPairLevel2 = generateECKeys(keyPairAlgorithmOID, elipticCurve); + } catch (Exception e) { + assert(false); + } + + assert(keyPairLevel1 != null); + + assert(keyPairLevel2 != null); + + } + + + @Test public void testDynamicContentEncoding() { + + IUicRailTicket ticket = testFCBticket; + + Encoder enc = null; + + try { + enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 13); + } catch (IOException | EncodingFormatException e1) { + assert(false); + } + + assert(enc != null); + + enc.setLevel2Algs(signatureAlgorithmOID, keyPairAlgorithmOID, keyPairLevel2.getPublic()); + + try { + enc.signLevel1("1080", keyPairLevel1.getPrivate(), signatureAlgorithmOID, "1"); + } catch (Exception e) { + assert(false); + } + + try { + enc.setDynamicData(DynamicTestContent.createDynamicTestContent()); + enc.signLevel2(keyPairLevel2.getPrivate()); + + } catch (Exception e) { + assert(false); + } + + + byte[] encoded = null; + try { + encoded = enc.encode(); + } catch (Exception e) { + assert(false); + } + + assert(encoded != null); + + + + } + + @Test public void testDynamicContentDecoding() { + + IUicRailTicket ticket = testFCBticket; + + Encoder enc = null; + + try { + enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 13); + } catch (IOException | EncodingFormatException e1) { + assert(false); + } + + assert(enc != null); + + enc.setLevel2Algs(signatureAlgorithmOID, keyPairAlgorithmOID, keyPairLevel2.getPublic()); + + try { + enc.signLevel1("1080", keyPairLevel1.getPrivate(), signatureAlgorithmOID, "1"); + } catch (Exception e) { + assert(false); + } + + try { + enc.setDynamicData(DynamicTestContent.createDynamicTestContent()); + enc.signLevel2(keyPairLevel2.getPrivate()); + } catch (Exception e) { + assert(false); + } + + + byte[] encoded = null; + try { + encoded = enc.encode(); + } catch (Exception e) { + assert(false); + } + + assert(encoded != null); + + Decoder dec = null; + try { + dec = new Decoder(encoded); + } catch (IOException e) { + assert(false); + } catch (EncodingFormatException e) { + assert(false); + } catch (DataFormatException e) { + assert(false); + } + assert(dec != null); + + int signatureCheck = 0; + try { + signatureCheck = dec.validateLevel1(keyPairLevel1.getPublic(),null); + } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException | IllegalArgumentException + | UnsupportedOperationException | IOException | EncodingFormatException e) { + assert(false); + } + + assert(signatureCheck == Constants.LEVEL1_VALIDATION_OK); + + SimpleUICTestTicket.compare(ticket, dec.getUicTicket()); + + int level2check = 0; + try { + level2check = dec.validateLevel2(); + } catch (Exception e) { + assert(false); + } + + assert(level2check == Constants.LEVEL2_VALIDATION_OK); + + IUicDynamicContent dynamicData = dec.getDynamicContent(); + + assert(dynamicData.getChallengeString().equals("CHALLENGE")); + + assert(dynamicData.getAppId().equals("MyApp")); + + assert(Arrays.equals(dynamicData.getPassIdHash(),passIdHash)); + + assert(Arrays.equals(dynamicData.getPhoneIdHash(),phoneIdHash)); + + assert(dynamicData.getGeoCoordinate().getLatitude() == 123456L); + assert(dynamicData.getGeoCoordinate().getLongitude() == 23456L); + + Date timeStamp = dynamicData.getTimeStamp(); + ZonedDateTime retrievedTimeStamp = timeStamp.toInstant().atZone(ZoneId.of("UTC")); + ZonedDateTime originalTimeStamp = ZonedDateTime.now(ZoneId.of("UTC")); + long diff = ChronoUnit.SECONDS.between(originalTimeStamp, retrievedTimeStamp); + + assert(diff > -50 && diff < 50); + } + + public KeyPair generateECDSAKeys(String keyAlgorithmName, String paramName) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException{ + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(paramName); + KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC"); + g.initialize(ecSpec, new SecureRandom()); + return g.generateKeyPair(); + } + + public KeyPair generateECKeys(String keyAlgorithmOid, String curve) throws Exception{ + + String keyAlgorithmName = "ECDSA"; + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(curve); + KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC"); + g.initialize(ecSpec, new SecureRandom()); + return g.generateKeyPair(); + } + + +} diff --git a/src/test/java/org/uic/barcode/test/TimeStampTest.java b/src/test/java/org/uic/barcode/test/TimeStampTest.java new file mode 100644 index 0000000..b2e3ab1 --- /dev/null +++ b/src/test/java/org/uic/barcode/test/TimeStampTest.java @@ -0,0 +1,36 @@ +package org.uic.barcode.test; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +import org.junit.Test; +import org.uic.barcode.dynamicContent.fdc1.TimeStamp; + +public class TimeStampTest { + + + + @Test public void testDateConversion() { + + + TimeStamp ts = new TimeStamp(); + + ts.setDay(10L); + ts.setTime(6000L); + + Date date = ts.getTimeAsDate(); + + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + cal.setTime(date); + long seconds = cal.get(Calendar.SECOND); + seconds = seconds + cal.get(Calendar.MINUTE) * 60; + seconds = seconds + cal.get(Calendar.HOUR_OF_DAY) * 60 * 60; + + assert(seconds == 6000L); + + + } + + +} diff --git a/src/test/java/org/uic/barcode/test/utils/DynamicTestContent.java b/src/test/java/org/uic/barcode/test/utils/DynamicTestContent.java new file mode 100644 index 0000000..a39b270 --- /dev/null +++ b/src/test/java/org/uic/barcode/test/utils/DynamicTestContent.java @@ -0,0 +1,38 @@ +package org.uic.barcode.test.utils; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; + +import org.uic.barcode.dynamicContent.api.IUicDynamicContent; +import org.uic.barcode.dynamicContent.api.SimpleUicDynamicContent; +import org.uic.barcode.ticket.api.impl.SimpleGeoCoordinate; +import org.uic.barcode.ticket.api.spec.IGeoCoordinate; + +public class DynamicTestContent { + + public static byte[] passIdHash = "PassId".getBytes(); + public static byte[] phoneIdHash = "myPhone".getBytes(); + + + public static IUicDynamicContent createDynamicTestContent() { + + IUicDynamicContent dc = new SimpleUicDynamicContent(); + dc.setChallengeString("CHALLENGE"); + dc.setAppId("MyApp"); + dc.setPhoneIdHash(phoneIdHash); + dc.setPassIdHash(passIdHash); + + ZonedDateTime timeStamp = ZonedDateTime.now(ZoneId.of("UTC")); + + dc.setTimeStamp(Date.from(timeStamp.toInstant())); + + IGeoCoordinate geo = new SimpleGeoCoordinate(); + geo.setLatitude(123456L); + geo.setLongitude(23456L); + dc.setGeoCoordinate(geo); + + return dc; + } + +} -- cgit v1.2.3