diff options
Diffstat (limited to 'src/main/java/org/uic/barcode/ssbFrame')
13 files changed, 1734 insertions, 0 deletions
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbClass.java b/src/main/java/org/uic/barcode/ssbFrame/SsbClass.java new file mode 100644 index 0000000..cf8d9aa --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbClass.java @@ -0,0 +1,8 @@ +package org.uic.barcode.ssbFrame; + +public enum SsbClass { + + FIRST, + Second; + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbCommonTicketPart.java b/src/main/java/org/uic/barcode/ssbFrame/SsbCommonTicketPart.java new file mode 100644 index 0000000..8eef552 --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbCommonTicketPart.java @@ -0,0 +1,151 @@ +package org.uic.barcode.ssbFrame; + +import org.uic.barcode.asn1.uper.BitBuffer; +import org.uic.barcode.asn1.uper.ByteBitBuffer; +import org.uic.barcode.ticket.EncodingFormatException; + +public abstract class SsbCommonTicketPart extends SsbTicketPart { + + /* + Number of adult passengers Num (<100) 7,000 + Number of child passengers Num (<100) 7,000 + "specimen" code Bit Flag 1,000 + Class of travel Lookup of 64 options 6,000 + TCN: Issuing unique Ticket number 14 AlphaNum 84,000 + Year of issue Num (0..9) 4,000 + Issuing day, from first of January Num (<512) 9,000 + */ + + protected int numberOfAdults = 0; + protected int numberOfChildren = 0; + protected boolean specimen = true; + protected SsbClass classCode = null; + protected String ticketNumber = null; + protected int year = 0; + protected int day = 0; + + protected int decodeCommonPart(byte[] bytes) { + + BitBuffer bits = new ByteBitBuffer(bytes); + + int offset = 27; // header offset + numberOfAdults = bits.getInteger(offset, 7); + offset = offset + 7; + numberOfChildren = bits.getInteger(offset, 7); + offset = offset + 7; + specimen = bits.get(offset); + offset++; + int classIndex = bits.getInteger(offset, 6); + classCode = SsbClass.values()[classIndex]; + offset = offset + 6; + ticketNumber = bits.getChar6String(offset, 84); + offset = offset + 84; + year = bits.getInteger(offset, 4); + offset = offset + 4; + day = bits.getInteger(offset, 9); + offset = offset + 9; + return offset; + } + + protected int encodeCommonPart(byte[] bytes, int offset) throws EncodingFormatException { + + BitBuffer bits = new ByteBitBuffer(bytes); + + if (numberOfAdults < 0 || numberOfAdults > 99) { + throw new EncodingFormatException("SSB number of adults too big"); + } + bits.putInteger(offset,7, numberOfAdults); + offset = offset + 7; + + if (numberOfChildren < 0 || numberOfChildren > 99) { + throw new EncodingFormatException("SSB number of children too big"); + } + bits.putInteger(offset, 7, numberOfChildren); + offset = offset + 7; + + bits.put(offset,specimen); + offset++; + + bits.putInteger(offset, 6,classCode.ordinal()); + offset = offset + 6; + + if (ticketNumber.length() > 14) { + throw new EncodingFormatException("SSB Ticket Number too long"); + } + bits.putChar6String(offset, 84, ticketNumber); + offset = offset + 84; + + + bits.putInteger(offset, 4, (year % 10)); + offset = offset + 4; + + if (day > 512) { + throw new EncodingFormatException("SSB day too long"); + } + bits.putInteger(offset, 9, day); + offset = offset + 9; + + return offset; + + + } + + public int getNumberOfAdults() { + return numberOfAdults; + } + + public void setNumberOfAdults(int numberOfAdults) { + this.numberOfAdults = numberOfAdults; + } + + public int getNumberOfChildren() { + return numberOfChildren; + } + + public void setNumberOfChildren(int numberOfChildren) { + this.numberOfChildren = numberOfChildren; + } + + public boolean isSpecimen() { + return specimen; + } + + public void setSpecimen(boolean specimen) { + this.specimen = specimen; + } + + public SsbClass getClassCode() { + return classCode; + } + + public void setClassCode(SsbClass classCode) { + this.classCode = classCode; + } + + public String getTicketNumber() { + return ticketNumber; + } + + public void setTicketNumber(String ticketNumber) { + this.ticketNumber = ticketNumber; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } + + public int getDay() { + return day; + } + + public void setDay(int day) { + this.day = day; + } + + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbFrame.java b/src/main/java/org/uic/barcode/ssbFrame/SsbFrame.java new file mode 100644 index 0000000..b473c1e --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbFrame.java @@ -0,0 +1,371 @@ +package org.uic.barcode.ssbFrame; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.Signature; +import java.security.SignatureException; +import java.security.Provider.Service; +import java.util.Arrays; + + +import org.uic.barcode.ticket.EncodingFormatException; +import org.uic.barcode.utils.AlgorithmNameResolver; +import org.uic.barcode.utils.SecurityUtils; + +public class SsbFrame { + + private SsbHeader header = new SsbHeader(); + + private byte[] signaturePart1 = null; + + private byte[] signaturePart2 = null; + + private SsbNonUic nonUicData = null; + + private SsbNonReservation nonReservationData = null; + + private SsbReservation reservationData = null; + + private SsbGroup groupData = null; + + private SsbPass passData = null; + + public void decode(byte[] bytes) throws EncodingFormatException { + + if (bytes.length != 114) { + throw new EncodingFormatException("Data size does not fit to SSB"); + } + + if (header == null) { + header = new SsbHeader(); + } + + int offset = 0; + + offset = offset + header.decodeContent(bytes,0); + + if (header.getTicketType().equals(SsbTicketType.UIC_1_IRT_RES_BOA)) { + + reservationData = new SsbReservation(); + offset = offset + reservationData.decodeContent(bytes,offset); + + } else if (header.getTicketType().equals(SsbTicketType.UIC_2_NRT)) { + + nonReservationData = new SsbNonReservation(); + offset = offset + nonReservationData.decodeContent(bytes,offset); + + } else if (header.getTicketType().equals(SsbTicketType.UIC_3_GRP)) { + + groupData = new SsbGroup(); + offset = offset + groupData.decodeContent(bytes, offset); + + } else if (header.getTicketType().equals(SsbTicketType.UIC_4_RPT)) { + + passData = new SsbPass(); + offset = offset + passData.decodeContent(bytes,offset); + + } else { + + nonUicData = new SsbNonUic(); + offset = offset + nonUicData.decodeContent(bytes,offset); + + } + + byte[] signatureBytes = new byte[56]; + + try { + //check for non-standard signature encoding + BigInteger[] bInts = SecurityUtils.decodeSignatureIntegerSequence(signatureBytes); + byte[] sig = SecurityUtils.encodeSignatureIntegerSequence(bInts[0],bInts[1]); + signaturePart1 = bInts[0].toByteArray(); + signaturePart2 = bInts[1].toByteArray(); + //decoding the entire signature was ok, so there was no split + } catch (Exception e) { + //the signature is correctly implemented, continue with recombination + signaturePart1 = new byte[28]; + signaturePart2 = new byte[28]; + + for (int i = 0 ; i < 28;i++) { + signaturePart1[i] = bytes[58 + i]; + signaturePart2[i] = bytes[58 + 28 + i]; + } + } + + + } + + + + + public byte[] encode() throws EncodingFormatException { + + byte[] bytes = new byte[114]; + + int offset = header.encodeContent(bytes,0); + + + + if (nonUicData != null) { + offset = nonUicData.encodeContent(bytes, offset); + } else if (nonReservationData != null) { + offset = nonReservationData.encodeContent(bytes, offset); + } else if (reservationData != null) { + offset = reservationData.encodeContent(bytes, offset); + } else if (groupData != null) { + offset = groupData.encodeContent(bytes, offset); + } else if (passData != null) { + offset = passData.encodeContent(bytes, offset); + } else { + throw new EncodingFormatException("Data Content for SSB missing"); + }; + + + if (signaturePart1.length > 28) { + throw new EncodingFormatException("Signature too large"); + } + if (signaturePart2.length > 28) { + throw new EncodingFormatException("Signature too large"); + } + + for (int i = 1 ; i < 29; i++) { + int sigInd = signaturePart1.length - i; + if (sigInd < signaturePart1.length && sigInd >= 0) { + bytes[58 + 28 - i] = signaturePart1[sigInd]; + } else { + bytes[58 + 28 - i] = '\0'; + } + sigInd = signaturePart2.length - i; + if (sigInd < signaturePart2.length && sigInd >= 0) { + bytes[58 + 28 + 28 - i] = signaturePart2[sigInd]; + } else { + bytes[58 + 28 + 28 - i] = '\0'; + } + } + + + return bytes; + + } + + public byte[] getDataForSignature() throws EncodingFormatException { + + byte[] bytes = new byte[58]; + + int offset = header.encodeContent(bytes,0); + + + if (nonUicData != null) { + offset = nonUicData.encodeContent(bytes, offset); + } else if (nonReservationData != null) { + offset = nonReservationData.encodeContent(bytes, offset); + } else if (reservationData != null) { + offset = reservationData.encodeContent(bytes, offset); + } else if (groupData != null) { + offset = groupData.encodeContent(bytes, offset); + } else if (passData != null) { + offset = passData.encodeContent(bytes, offset); + } else { + throw new EncodingFormatException("Data Content for SSB missing"); + }; + + return bytes; + + } + + public SsbHeader getHeader() { + return header; + } + + public void setHeader(SsbHeader header) { + this.header = header; + } + + public byte[] getSignaturePart1() { + return signaturePart1; + } + + public void setSignaturePart1(byte[] signaturePart1) { + this.signaturePart1 = signaturePart1; + } + + public byte[] getSignaturePart2() { + return signaturePart2; + } + + public void setSignaturePart2(byte[] signaturePart2) { + this.signaturePart2 = signaturePart2; + } + + public SsbNonUic getNonUicData() { + return nonUicData; + } + + public void setNonUicData(SsbNonUic nonUicData) { + this.nonUicData = nonUicData; + this.nonReservationData = null; + this.reservationData = null; + this.groupData = null; + this.passData = null; + } + + public SsbNonReservation getNonReservationData() { + return nonReservationData; + } + + public void setNonReservationData(SsbNonReservation nonReservationData) { + this.nonReservationData = nonReservationData; + header.setTicketType(SsbTicketType.UIC_2_NRT); + this.reservationData = null; + this.nonUicData = null; + this.groupData = null; + this.passData = null; + } + + public SsbReservation getReservationData() { + return reservationData; + } + + public void setReservationData(SsbReservation reservationData) { + header.setTicketType(SsbTicketType.UIC_1_IRT_RES_BOA); + this.nonReservationData = null; + this.nonUicData = null; + this.groupData = null; + this.passData = null; + this.reservationData = reservationData; + } + + public SsbGroup getGroupData() { + return groupData; + } + + public void setGroupData(SsbGroup groupData) { + this.groupData = groupData; + header.setTicketType(SsbTicketType.UIC_3_GRP); + this.nonReservationData = null; + this.nonUicData = null; + this.reservationData = null; + this.passData = null; + + } + + public SsbPass getPassData() { + return passData; + } + + public void setPassData(SsbPass passData) { + this.passData = passData; + header.setTicketType(SsbTicketType.UIC_4_RPT); + this.nonReservationData = null; + this.nonUicData = null; + this.groupData = null; + this.reservationData = null; + } + + public void signLevel1(PrivateKey key, Provider prov, String keyId, String algorithmOid) throws Exception { + + + this.header.setKeyId(Integer.parseInt(keyId)); + + byte[] data = getDataForSignature(); + + if (prov == null) { + //check for a provider supporting the key + prov = SecurityUtils.findPrivateKeyProvider(key); + } + + //find the algorithm name for the signature OID + String algo = AlgorithmNameResolver.getSignatureAlgorithmName(algorithmOid, prov); + Signature sig = null; + + if (prov != null) { + sig = Signature.getInstance(algo, prov); + } else { + sig = Signature.getInstance(algo); + } + sig.initSign(key); + + sig.update(data); + + byte[] signature = sig.sign(); + + BigInteger[] bInts = SecurityUtils.decodeSignatureIntegerSequence(signature); + + signaturePart1 = toUnsignedBytes(bInts[0]); + + signaturePart2 = toUnsignedBytes(bInts[1]); + + + } + + private static byte[] toUnsignedBytes(BigInteger i) { + byte[] b = i.abs().toByteArray(); + //remove top sign bit + if (b[0] == 0) { + b = Arrays.copyOfRange(b, 1, b.length); + } + return b; + } + + /** + * Verify the signature + * + * Note: an appropriate security provider (e.g. BC) must be registered before + * + * @param key the key + * @param singningAlg the Object ID of the signing algorithm + * @param a dedicated security provider to validate the signature + * @return true, if successful + * @throws InvalidKeyException the invalid key exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws SignatureException the signature exception + * @throws IllegalArgumentException the illegal argument exception + * @throws UnsupportedOperationException the unsupported operating exception + * @throws EncodingFormatException + * @throws IOException + */ + public boolean verifyByAlgorithmOid(PublicKey key, String signingAlg, Provider prov) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException { + //find the algorithm name for the signature OID + String algo = null; + + if (prov != null) { + Service service = prov.getService("Signature",signingAlg); + if (service != null) { + algo = service.getAlgorithm(); + } + } else { + Provider[] provs = Security.getProviders(); + for (Provider p : provs) { + if (algo == null) { + Service service = p.getService("Signature",signingAlg); + if (service != null) { + algo = service.getAlgorithm(); + } + } + } + + } + + if (algo == null) { + throw new NoSuchAlgorithmException("No service for algorithm found: " + signingAlg); + } + Signature sig = Signature.getInstance(algo); + sig.initVerify(key); + sig.update(getDataForSignature()); + + BigInteger r = new BigInteger(1,signaturePart1); + BigInteger s = new BigInteger(1,signaturePart2); + + byte[] signature = SecurityUtils.encodeSignatureIntegerSequence(r,s); + + return sig.verify(signature); + } + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbGroup.java b/src/main/java/org/uic/barcode/ssbFrame/SsbGroup.java new file mode 100644 index 0000000..7751ef6 --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbGroup.java @@ -0,0 +1,168 @@ +package org.uic.barcode.ssbFrame; + +import org.uic.barcode.asn1.uper.BitBuffer; +import org.uic.barcode.asn1.uper.ByteBitBuffer; +import org.uic.barcode.ticket.EncodingFormatException; + +public class SsbGroup extends SsbCommonTicketPart { + + protected int firstDayOfValidity = 0; + protected int lastDayOfValidity = 0; + protected boolean isReturnJourney = false; + private int infoCode = 0; + private String text = null; + private SsbStations stations = new SsbStations(); + + private String groupName = null; + + private int counterMarkNumber = 0; + + + @Override + protected int decodeContent(byte[] bytes, int offset) { + + offset = offset + decodeCommonPart(bytes); + + BitBuffer bits = new ByteBitBuffer(bytes); + + isReturnJourney = bits.get(offset); + offset = offset++; + + firstDayOfValidity = bits.getInteger(offset, 9); + offset = offset + 9; + + lastDayOfValidity = bits.getInteger(offset, 9); + offset = offset + 9; + + offset = stations.decode(offset, bytes); + + groupName = bits.getChar6String(offset, 72); + offset = offset + 72; + + counterMarkNumber = bits.getInteger(offset, 9); + offset = offset + 9; + + infoCode = bits.getInteger(offset, 14); + offset = offset + 14; + + text = bits.getChar6String(offset, 144); + offset = offset + 144; + + return offset; + + } + + @Override + protected int encodeContent(byte[] bytes, int offset) throws EncodingFormatException { + + offset = offset + encodeCommonPart(bytes, offset); + + BitBuffer bits = new ByteBitBuffer(bytes); + + bits.put(offset, isReturnJourney); + offset = offset++; + + if (firstDayOfValidity < 0 || firstDayOfValidity > 511) { + throw new EncodingFormatException("SSB first day of validity too big"); + } + bits.putInteger(offset, 9, firstDayOfValidity); + offset = offset + 9; + + if (lastDayOfValidity < 0 || lastDayOfValidity > 511) { + throw new EncodingFormatException("SSB last day of validity too big"); + } + bits.putInteger(offset, 9, lastDayOfValidity); + offset = offset + 9; + + offset = stations.encode(offset, bytes); + + if (groupName.length() > 12) { + throw new EncodingFormatException("SSB group name too big"); + } + bits.putChar6String(offset, 72,groupName); + offset = offset + 72; + + if (counterMarkNumber < 0 || counterMarkNumber > 246) { + throw new EncodingFormatException("SSB number of countermark too big"); + } + bits.putInteger(offset, 9,counterMarkNumber); + offset = offset + 9; + + if (infoCode < 0 || infoCode > 9999) { + throw new EncodingFormatException("SSB info code too big"); + } + bits.putInteger(offset, 14, infoCode); + offset = offset + 14; + + if (text.length() > 24) { + throw new EncodingFormatException("SSB text too big"); + } + bits.putChar6String(offset, 144, text); + offset = offset + 144; + + return offset; + } + + public int getFirstDayOfValidity() { + return firstDayOfValidity; + } + + public void setFirstDayOfValidity(int firstDayOfValidity) { + this.firstDayOfValidity = firstDayOfValidity; + } + + public int getLastDayOfValidity() { + return lastDayOfValidity; + } + + public void setLastDayOfValidity(int lastDayOfValidity) { + this.lastDayOfValidity = lastDayOfValidity; + } + + public boolean isReturnJourney() { + return isReturnJourney; + } + + public void setReturnJourney(boolean isReturnJourney) { + this.isReturnJourney = isReturnJourney; + } + + public int getInfoCode() { + return infoCode; + } + + public void setInfoCode(int infoCode) { + this.infoCode = infoCode; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public SsbStations getStations() { + return stations; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public int getCounterMarkNumber() { + return counterMarkNumber; + } + + public void setCounterMarkNumber(int counterMarkNumber) { + this.counterMarkNumber = counterMarkNumber; + } + + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbHeader.java b/src/main/java/org/uic/barcode/ssbFrame/SsbHeader.java new file mode 100644 index 0000000..48c8eaf --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbHeader.java @@ -0,0 +1,126 @@ +package org.uic.barcode.ssbFrame; + +import org.uic.barcode.asn1.uper.BitBuffer; +import org.uic.barcode.asn1.uper.ByteBitBuffer; +import org.uic.barcode.ticket.EncodingFormatException; + +public class SsbHeader extends SsbTicketPart { + + private int version = 3; + private SsbTicketType ticketType = null; + private int keyId = 0; + private int issuer = 0; + + /* + Version Num 0-4 Bits + Issuer code Num 14 Bits + ID Num 4 Bits + Ticket type code Num 5 Bits + */ + + public SsbHeader(int version, SsbTicketType type, int keyId, int issuer) { + this.issuer = issuer; + this.keyId = keyId; + this.ticketType = type; + this.version = version; + } + + public SsbHeader() { + } + + public int decodeContent(byte[] headerData, int offset) { + + BitBuffer bits = new ByteBitBuffer(headerData); + + version = bits.getInteger(0, 4); + issuer = bits.getInteger(4, 14); + keyId = bits.getInteger(18, 4); + ticketType = SsbTicketType.values()[bits.getInteger(22, 5)]; + + return 4 + 14 + 4 + 5; + + } + + public int encodeContent(byte[] bytes, int offset) throws EncodingFormatException { + + BitBuffer bits = new ByteBitBuffer(bytes); + + if (version < 0 || version > 15) { + throw new EncodingFormatException("SSB Version too big"); + } + + bits.putInteger(0, 4, version); + + if (issuer < 0 || issuer > 9999) { + throw new EncodingFormatException("SSB Issuer code too big"); + } + + bits.putInteger(4, 14, issuer); + + if (keyId < 0 || keyId > 15) { + throw new EncodingFormatException("SSB Key Id too big"); + } + + bits.putInteger(18, 4, keyId); + + bits.putInteger(22, 5, ticketType.ordinal()); + + return 4 + 14 + 4 + 5; + + } + + + + public int getVersion() { + return version; + } + + + + public void setVersion(int version) { + this.version = version; + } + + + + public SsbTicketType getTicketType() { + return ticketType; + } + + + + public void setTicketType(SsbTicketType ticketType) { + this.ticketType = ticketType; + } + + + + public int getKeyId() { + return keyId; + } + + + + public void setKeyId(int keyId) { + this.keyId = keyId; + } + + + + public int getIssuer() { + return issuer; + } + + + + public void setIssuer(int issuer) { + this.issuer = issuer; + } + + + + + + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbNonReservation.java b/src/main/java/org/uic/barcode/ssbFrame/SsbNonReservation.java new file mode 100644 index 0000000..80fc2bc --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbNonReservation.java @@ -0,0 +1,131 @@ +package org.uic.barcode.ssbFrame; + +import org.uic.barcode.asn1.uper.BitBuffer; +import org.uic.barcode.asn1.uper.ByteBitBuffer; +import org.uic.barcode.ticket.EncodingFormatException; + +public class SsbNonReservation extends SsbCommonTicketPart { + + protected int firstDayOfValidity = 0; + protected int lastDayOfValidity = 0; + protected boolean isReturnJourney = false; + private int infoCode = 0; + private String text = null; + private SsbStations stations = new SsbStations(); + + + @Override + protected int decodeContent(byte[] bytes, int offset) { + + offset = offset + decodeCommonPart(bytes); + + BitBuffer bits = new ByteBitBuffer(bytes); + + isReturnJourney = bits.get(offset); + offset = offset++; + + firstDayOfValidity = bits.getInteger(offset, 9); + offset = offset + 9; + + lastDayOfValidity = bits.getInteger(offset, 9); + offset = offset + 9; + + offset = stations.decode(offset, bytes); + + infoCode = bits.getInteger(offset, 14); + offset = offset + 14; + + text = bits.getChar6String(offset, 222); + offset = offset + 222; + + return offset; + + } + + @Override + protected int encodeContent(byte[] bytes, int offset) throws EncodingFormatException { + + offset = offset + encodeCommonPart(bytes, offset); + + BitBuffer bits = new ByteBitBuffer(bytes); + + bits.put(offset, isReturnJourney); + offset = offset++; + + if (firstDayOfValidity < 0 || firstDayOfValidity > 511) { + throw new EncodingFormatException("SSB first day of validity too big"); + } + bits.putInteger(offset, 9, firstDayOfValidity); + offset = offset + 9; + + if (lastDayOfValidity < 0 || lastDayOfValidity > 511) { + throw new EncodingFormatException("SSB last day of validity too big"); + } + bits.putInteger(offset, 9, lastDayOfValidity); + offset = offset + 9; + + offset = stations.encode(offset, bytes); + + if (infoCode < 0 || infoCode > 9999) { + throw new EncodingFormatException("SSB info code too big"); + } + bits.putInteger(offset, 14, infoCode); + offset = offset + 14; + + if (text.length() > 37) { + throw new EncodingFormatException("SSB text too big"); + } + bits.putChar6String(offset, 222, text); + offset = offset + 222; + + return offset; + + } + + public int getFirstDayOfValidity() { + return firstDayOfValidity; + } + + public void setFirstDayOfValidity(int firstDayOfValidity) { + this.firstDayOfValidity = firstDayOfValidity; + } + + public int getLastDayOfValidity() { + return lastDayOfValidity; + } + + public void setLastDayOfValidity(int lastDayOfValidity) { + this.lastDayOfValidity = lastDayOfValidity; + } + + public boolean isReturnJourney() { + return isReturnJourney; + } + + public void setReturnJourney(boolean isReturnJourney) { + this.isReturnJourney = isReturnJourney; + } + + public int getInfoCode() { + return infoCode; + } + + public void setInfoCode(int infoCode) { + this.infoCode = infoCode; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public SsbStations getStations() { + return stations; + } + + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbNonUic.java b/src/main/java/org/uic/barcode/ssbFrame/SsbNonUic.java new file mode 100644 index 0000000..1f0049e --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbNonUic.java @@ -0,0 +1,69 @@ +package org.uic.barcode.ssbFrame; + +import org.uic.barcode.asn1.uper.AsnUtils; +import org.uic.barcode.asn1.uper.BitBuffer; +import org.uic.barcode.asn1.uper.ByteBitBuffer; + +public class SsbNonUic extends SsbTicketPart { + + + + + byte[] openData = null; + + @Override + protected int decodeContent(byte[] bytes, int offset) { + + BitBuffer bits = new ByteBitBuffer(bytes); + + StringBuffer sb = new StringBuffer(); + + + for (int i = offset; i < openDataLength; i++) { + if (bits.get(i) == false) { + sb.append("1"); + } else { + sb.append("0"); + } + } + + for (int i = openDataLength; i < 440; i++) { + sb.append("0"); + } + + openData = AsnUtils.fromBooleanString(sb.toString()); + + return offset + openDataLength ; + + } + + @Override + protected int encodeContent(byte[] bytes, int offset) { + + BitBuffer bits = new ByteBitBuffer(bytes); + + String bitString = AsnUtils.toBooleanString(openData); + + + for (int i = 0; i< openDataLength ; i++) { + if (i < bitString.length() && bitString.charAt(i) == '0') { + bits.put(offset + i, true); + } else { + bits.put(offset + i, false); + } + } + + return offset + openDataLength; + } + + public byte[] getOpenData() { + return openData; + } + + public void setOpenData(byte[] openData) { + this.openData = openData; + } + + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbPass.java b/src/main/java/org/uic/barcode/ssbFrame/SsbPass.java new file mode 100644 index 0000000..a26fb61 --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbPass.java @@ -0,0 +1,262 @@ +package org.uic.barcode.ssbFrame; + +import org.uic.barcode.asn1.uper.BitBuffer; +import org.uic.barcode.asn1.uper.ByteBitBuffer; +import org.uic.barcode.ticket.EncodingFormatException; + +public class SsbPass extends SsbCommonTicketPart { + + /* + * RPT sub ticket type 3 values 2 2 bit 1 = INTERRAIL, 2 = EURAIL EUROPE, 3 = EURAIL OVERSEAS + First day of validity from the issuing date Num (<367) 9 bit 000 = open date for regular Eurail pass to be activated + Maximum duration from the issuing date for OVERSEAS; otherwise, last day of validity Num (<278) 9 bit 9 months max. validity + Number of days of travel allowed Num (<93) 7 bit + Country code 1 Num (<100) 7 0.875 100 = all countries + Country code 2 Num (<99) 7 0.875 If country code 1 is 100, then 00 + Country code 3 Num (<99) 7 0.875 If country code 1 is 100, then 00 + Country code 4 Num (<99) 7 0.875 If country code 1 is 100, then 00 + Country code 5 Num (<99) 7 0.875 If country code 1 is 100, then 00 + Second page Bit flag 1 0.125 For a two-page pass + Information messages Num (<9999) 14 1.75 + Open text 6-bit ASCII (40 Char) 240 30 + */ + + + private int passSubType = 0; + private int firstDayOfValidity = 0; + private int maximumValidityDuration = 0; + private int numberOfTravels = 0; + private int country_1 = 0; + private int country_2 = 0; + private int country_3 = 0; + private int country_4 = 0; + private int country_5 = 0; + private boolean hasSecondPage = false; + private int infoCode = 0; + private String text = null; + + @Override + protected int decodeContent(byte[] bytes, int offset) { + + offset = offset + decodeCommonPart(bytes); + + BitBuffer bits = new ByteBitBuffer(bytes); + + passSubType = bits.getInteger(offset, 2); + offset = offset + 2; + + firstDayOfValidity = bits.getInteger(offset, 9); + offset = offset + 9; + + maximumValidityDuration = bits.getInteger(offset, 9); + offset = offset + 9; + + numberOfTravels = bits.getInteger(offset, 7); + offset = offset + 7; + + country_1 = bits.getInteger(offset, 7); + offset = offset + 7; + + country_2 = bits.getInteger(offset, 7); + offset = offset + 7; + + country_3 = bits.getInteger(offset, 7); + offset = offset + 7; + + country_4 = bits.getInteger(offset, 7); + offset = offset + 7; + + country_5 = bits.getInteger(offset, 7); + offset = offset + 7; + + hasSecondPage = bits.get(offset); + offset++; + + infoCode = bits.getInteger(offset, 14); + offset = offset + 14; + + text = bits.getChar6String(offset, 240); + offset = offset + 240; + + return offset; + } + + @Override + protected int encodeContent(byte[] bytes, int offset) throws EncodingFormatException { + + offset = offset + encodeCommonPart(bytes, offset); + + BitBuffer bits = new ByteBitBuffer(bytes); + + if (passSubType < 0 || passSubType > 3) { + throw new EncodingFormatException("SSB pass type too big"); + } + bits.putInteger(offset, 2,passSubType); + offset = offset + 2; + + if (firstDayOfValidity < 0 || firstDayOfValidity > 511) { + throw new EncodingFormatException("SSB first day of validity too big"); + } + bits.putInteger(offset, 9,firstDayOfValidity); + offset = offset + 9; + + if (maximumValidityDuration < 0 || maximumValidityDuration > 511) { + throw new EncodingFormatException("SSB validity duration too big"); + } + bits.putInteger(offset, 9,maximumValidityDuration); + offset = offset + 9; + + if (numberOfTravels < 0 || numberOfTravels > 94) { + throw new EncodingFormatException("SSB number of travels too big"); + } + bits.putInteger(offset, 7, numberOfTravels); + offset = offset + 7; + + if (country_1 < 0 || country_1 > 100) { + throw new EncodingFormatException("SSB country 1 too big"); + } + bits.putInteger(offset, 7,country_1); + offset = offset + 7; + + if (country_2 < 0 || country_2 > 99) { + throw new EncodingFormatException("SSB country 2 too big"); + } + bits.putInteger(offset, 7,country_2); + offset = offset + 7; + + if (country_3 < 0 || country_3 > 99) { + throw new EncodingFormatException("SSB country 3 too big"); + } + bits.putInteger(offset, 7,country_3); + offset = offset + 7; + + if (country_4 < 0 || country_4 > 99) { + throw new EncodingFormatException("SSB country 4 too big"); + } + bits.putInteger(offset, 7,country_4); + offset = offset + 7; + + if (country_5 < 0 || country_5 > 99) { + throw new EncodingFormatException("SSB country 5 too big"); + } + bits.putInteger(offset, 7,country_5); + offset = offset + 7; + + bits.put(offset, hasSecondPage); + offset++; + + if (infoCode < 0 || infoCode > 9999) { + throw new EncodingFormatException("SSB info code too big"); + } + bits.putInteger(offset, 14, infoCode); + offset = offset + 14; + + if (text.length() > 40) { + throw new EncodingFormatException("SSB text too big"); + } + bits.putChar6String(offset, 240,text); + offset = offset + 240; + + return offset; + } + + public int getPassSubType() { + return passSubType; + } + + public void setPassSubType(int passSubType) { + this.passSubType = passSubType; + } + + public int getFirstDayOfValidity() { + return firstDayOfValidity; + } + + public void setFirstDayOfValidity(int firstDayOfValidity) { + this.firstDayOfValidity = firstDayOfValidity; + } + + public int getMaximumValidityDuration() { + return maximumValidityDuration; + } + + public void setMaximumValidityDuration(int maximumValidityDuration) { + this.maximumValidityDuration = maximumValidityDuration; + } + + public int getNumberOfTravels() { + return numberOfTravels; + } + + public void setNumberOfTravels(int numberOfTravels) { + this.numberOfTravels = numberOfTravels; + } + + public int getCountry_1() { + return country_1; + } + + public void setCountry_1(int country_1) { + this.country_1 = country_1; + } + + public int getCountry_2() { + return country_2; + } + + public void setCountry_2(int country_2) { + this.country_2 = country_2; + } + + public int getCountry_3() { + return country_3; + } + + public void setCountry_3(int country_3) { + this.country_3 = country_3; + } + + public int getCountry_4() { + return country_4; + } + + public void setCountry_4(int country_4) { + this.country_4 = country_4; + } + + public int getCountry_5() { + return country_5; + } + + public void setCountry_5(int country_5) { + this.country_5 = country_5; + } + + public boolean isHasSecondPage() { + return hasSecondPage; + } + + public void setHasSecondPage(boolean hasSecondPage) { + this.hasSecondPage = hasSecondPage; + } + + public int getInfoCode() { + return infoCode; + } + + public void setInfoCode(int infoCode) { + this.infoCode = infoCode; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbReservation.java b/src/main/java/org/uic/barcode/ssbFrame/SsbReservation.java new file mode 100644 index 0000000..c70c2d1 --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbReservation.java @@ -0,0 +1,237 @@ +package org.uic.barcode.ssbFrame; + +import org.uic.barcode.asn1.uper.BitBuffer; +import org.uic.barcode.asn1.uper.ByteBitBuffer; +import org.uic.barcode.ticket.EncodingFormatException; + +public class SsbReservation extends SsbCommonTicketPart { + + private SsbStations stations = new SsbStations(); + + private int ticketSubType = 0; + + private int departureDate = 0; + + private int departureTime = 0; + + private String train = null; + + private int coach = 0; + + private String place = null; + + private boolean overbooking = false; + + private int infoCode = 0; + + private String text = null; + + + + + + @Override + protected int decodeContent(byte[] bytes, int offset) { + + offset = offset + decodeCommonPart(bytes); + + BitBuffer bits = new ByteBitBuffer(bytes); + + ticketSubType = bits.getInteger(offset, 2); + offset = offset + 2; + + stations = new SsbStations(); + offset = stations.decode(offset, bytes); + + /* + * Departure date : First day of validity from the issuing date Num (<367) 9,000 + Departure Time Num (<1440) 11,000 + Train number AlphaNum + 5 Car 30,000 + Coach number Num (< 999) 10,000 + Seat/berth number 3 AlphaNum 18,000 + Overbooking indicator Bit Flag 1,000 + Information Messages Num (< 9999) 14,000 + Open Tekst 6 Bit ASCII (27 Car) 162,000 + */ + + departureDate = bits.getInteger(offset, 9); + offset = offset + 9; + + departureTime = bits.getInteger(offset, 11); + offset = offset + 11; + + train = bits.getChar6String(offset, 30); + offset = offset + 30; + + coach = bits.getInteger(offset, 10); + offset = offset + 10; + + place = bits.getChar6String(offset, 18); + offset = offset + 18; + + overbooking = bits.get(offset); + offset++; + + infoCode = bits.getInteger(offset, 14); + offset = offset + 14; + + text = bits.getChar6String(offset, 162); + offset = offset + 162; + + return offset; + } + + @Override + protected int encodeContent(byte[] bytes, int offset) throws EncodingFormatException { + + offset = offset + encodeCommonPart(bytes, offset); + + BitBuffer bits = new ByteBitBuffer(bytes); + + if (ticketSubType < 0 || ticketSubType > 3) { + throw new EncodingFormatException("SSB pass type too big"); + } + bits.putInteger(offset, 2,ticketSubType); + offset = offset + 2; + + offset = stations.encode(offset, bytes); + + /* + * Departure date : First day of validity from the issuing date Num (<367) 9,000 + Departure Time Num (<1440) 11,000 + Train number AlphaNum + 5 Car 30,000 + Coach number Num (< 999) 10,000 + Seat/berth number 3 AlphaNum 18,000 + Overbooking indicator Bit Flag 1,000 + Information Messages Num (< 9999) 14,000 + Open Tekst 6 Bit ASCII (27 Car) 162,000 + */ + + if (departureDate < 0 || departureDate > 512) { + throw new EncodingFormatException("SSB departure date too big"); + } + bits.putInteger(offset, 9, departureDate); + offset = offset + 9; + + if (departureTime < 0 || departureTime > 1440) { + throw new EncodingFormatException("SSB departure time too big"); + } + bits.putInteger(offset, 11,departureTime); + offset = offset + 11; + + if (train.length() > 5) { + throw new EncodingFormatException("SSB train too big"); + } + bits.putChar6String(offset, 30,train); + offset = offset + 30; + + if (coach < 0 || coach > 999) { + throw new EncodingFormatException("SSB coach too big"); + } + bits.putInteger(offset, 10,coach); + offset = offset + 10; + + if (place.length() > 3) { + throw new EncodingFormatException("SSB coach too big"); + } + bits.putChar6String(offset, 18,place); + offset = offset + 18; + + bits.put(offset, overbooking); + offset++; + + if (infoCode < 0 || infoCode > 9999) { + throw new EncodingFormatException("SSB info code too big"); + } + bits.putInteger(offset, 14, infoCode); + offset = offset + 14; + + if (text.length() > 27) { + throw new EncodingFormatException("SSB text too big"); + } + bits.putChar6String(offset, 162, text); + offset = offset + 162; + + return offset; + + } + + public SsbStations getStations() { + return stations; + } + + public int getTicketSubType() { + return ticketSubType; + } + + public void setTicketSubType(int ticketSubType) { + this.ticketSubType = ticketSubType; + } + + public int getDepartureDate() { + return departureDate; + } + + public void setDepartureDate(int departureDate) { + this.departureDate = departureDate; + } + + public int getDepartureTime() { + return departureTime; + } + + public void setDepartureTime(int departureTime) { + this.departureTime = departureTime; + } + + public String getTrain() { + return train; + } + + public void setTrain(String train) { + this.train = train; + } + + public int getCoach() { + return coach; + } + + public void setCoach(int coach) { + this.coach = coach; + } + + public String getPlace() { + return place; + } + + public void setPlace(String place) { + this.place = place; + } + + public boolean isOverbooking() { + return overbooking; + } + + public void setOverbooking(boolean overbooking) { + this.overbooking = overbooking; + } + + public int getInfoCode() { + return infoCode; + } + + public void setInfoCode(int infoCode) { + this.infoCode = infoCode; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbStationCodeTable.java b/src/main/java/org/uic/barcode/ssbFrame/SsbStationCodeTable.java new file mode 100644 index 0000000..8aeaf22 --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbStationCodeTable.java @@ -0,0 +1,9 @@ +package org.uic.barcode.ssbFrame; + +public enum SsbStationCodeTable { + + UNKNOWN_0, + NRT, + RESERVATION, + UNKNOWN_3; +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbStations.java b/src/main/java/org/uic/barcode/ssbFrame/SsbStations.java new file mode 100644 index 0000000..e3b7654 --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbStations.java @@ -0,0 +1,132 @@ +package org.uic.barcode.ssbFrame; + +import org.uic.barcode.asn1.uper.BitBuffer; +import org.uic.barcode.asn1.uper.ByteBitBuffer; +import org.uic.barcode.ticket.EncodingFormatException; + +public class SsbStations { + + /* + * Station code 1 bit 0 = Num; or 1=Bilateral AlphaNum 6Char + + Numeric: + Station code List 4 bit 1= NRT; 2=Reservation + Departure station Location 28 bit + Arrival Station 28 bit + + AlphaNum: + Departure: 30 bit + Arrival = 30 bit + + */ + + protected String arrivalStationCode = " "; + protected String departureStationCode = " "; + protected SsbStationCodeTable codeTable = SsbStationCodeTable.NRT; + + public int encode(int offset, byte[] bytes) throws EncodingFormatException { + + boolean isAlphaNumeric = false; + + BitBuffer bits = new ByteBitBuffer(bytes); + + try { + Integer.parseInt(arrivalStationCode); + Integer.parseInt(departureStationCode); + isAlphaNumeric = false; + } catch(NumberFormatException e) { + isAlphaNumeric = true; + } + bits.put(offset, isAlphaNumeric); + offset++; + + if (isAlphaNumeric) { + if (departureStationCode.length() > 6) { + throw new EncodingFormatException("SSB departure station too long"); + } + bits.putChar6String(offset,30, departureStationCode); + offset = offset + 30; + + if (arrivalStationCode.length() > 6) { + throw new EncodingFormatException("SSB arrival station too long"); + } + bits.putChar6String(offset,30, arrivalStationCode); + offset = offset + 30; + } else { + bits.putInteger(offset, 4, codeTable.ordinal()); + offset = offset + 4; + + int stationCode = Integer.parseInt(departureStationCode); + if (stationCode < 0 || stationCode > 9999999) { + throw new EncodingFormatException("SSB departure station code too long"); + } + bits.putInteger(offset, 28, stationCode); + offset = offset + 28; + + stationCode = Integer.parseInt(arrivalStationCode); + if (stationCode < 0 || stationCode > 9999999) { + throw new EncodingFormatException("SSB arrival station code too long"); + } + bits.putInteger(offset, 28, stationCode); + offset = offset + 28; + } + + return offset; + + } + + public int decode(int offset, byte[] bytes) { + + BitBuffer bits = new ByteBitBuffer(bytes); + + boolean isAlphaNumeric = bits.get(offset); + offset++; + + if (isAlphaNumeric) { + departureStationCode = bits.getChar6String(offset,30); + offset = offset + 30; + arrivalStationCode = bits.getChar6String(offset,30); + offset = offset + 30; + } else { + codeTable = SsbStationCodeTable.values()[bits.getInteger(offset, 4)]; + offset = offset + 4; + departureStationCode = Integer.toString(bits.getInteger(offset, 28)); + offset = offset + 28; + arrivalStationCode = Integer.toString(bits.getInteger(offset, 28)); + offset = offset + 28; + } + + + + return offset; + + } + + public String getArrivalStationCode() { + return arrivalStationCode; + } + + public void setArrivalStationCode(String arrivalStationCode) { + this.arrivalStationCode = arrivalStationCode; + } + + public String getDepartureStationCode() { + return departureStationCode; + } + + public void setDepartureStationCode(String departureStationCode) { + this.departureStationCode = departureStationCode; + } + + public SsbStationCodeTable getCodeTable() { + return codeTable; + } + + public void setCodeTable(SsbStationCodeTable codeTable) { + this.codeTable = codeTable; + } + + + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbTicketPart.java b/src/main/java/org/uic/barcode/ssbFrame/SsbTicketPart.java new file mode 100644 index 0000000..717608a --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbTicketPart.java @@ -0,0 +1,31 @@ +package org.uic.barcode.ssbFrame; + +import org.uic.barcode.ticket.EncodingFormatException; + +public abstract class SsbTicketPart { + + public static int openDataLength = 437; + + public void decode(byte[] bytes) throws EncodingFormatException { + if (bytes.length != 114) { + throw new EncodingFormatException("Data size does not fit to SSB"); + } + decodeContent(bytes, 0); + }; + + protected abstract int decodeContent(byte[] bytes , int offset); + + public void encode(byte[] bytes) throws EncodingFormatException { + if (bytes.length != 114) { + throw new EncodingFormatException("Data size does not fit to SSB"); + } + encodeContent(bytes, 0); + } + + protected abstract int encodeContent(byte[] bytes, int offset) throws EncodingFormatException; + + + + + +} diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbTicketType.java b/src/main/java/org/uic/barcode/ssbFrame/SsbTicketType.java new file mode 100644 index 0000000..cb60a26 --- /dev/null +++ b/src/main/java/org/uic/barcode/ssbFrame/SsbTicketType.java @@ -0,0 +1,39 @@ +package org.uic.barcode.ssbFrame; + +public enum SsbTicketType { + + UIC_1_IRT_RES_BOA, + UIC_2_NRT, + UIC_3_GRP, + UIC_4_RPT, + UIC_5_UNDEFINED, + UIC_6_UNDEFINED, + UIC_7_UNDEFINED, + UIC_8_UNDEFINED, + UIC_9_UNDEFINED, + UIC_10_UNDEFINED, + UIC_11_UNDEFINED, + UIC_12_UNDEFINED, + UIC_13_UNDEFINED, + UIC_14_UNDEFINED, + UIC_15_UNDEFINED, + UIC_16_UNDEFINED, + UIC_17_UNDEFINED, + UIC_18_UNDEFINED, + UIC_19_UNDEFINED, + UIC_20_UNDEFINED, + NONUIC_21_BILATERAL, + NONUIC_22_BILATERAL, + NONUIC_23_BILATERAL, + NONUIC_24_BILATERAL, + NONUIC_25_BILATERAL, + NONUIC_26_BILATERAL, + NONUIC_27_BILATERAL, + NONUIC_28_BILATERAL, + NONUIC_29_BILATERAL, + NONUIC_30_BILATERAL, + NONUIC_31_BILATERAL, + NONUIC_32_BILATERAL; + + +} |