Differenze tra le versioni di "Il formato OpenPass"
| [versione verificata] | [versione verificata] |
m (→Significato dei campi e area di memoria di appartenenza: :modificato l'allineamento della tabella da center a left) |
(→Formato 4: : commentato in quando è una bozza) |
||
| Riga 357: | Riga 357: | ||
|} | |} | ||
</div> | </div> | ||
| − | + | <! | |
=== Formato 4 === | === Formato 4 === | ||
A seguito della variazione dell’intestazione del settore e della necessità di introdurre i nuovi token : Root.DaysLimit e Root.Insurance si rende necessario variare la struttura del formato. | A seguito della variazione dell’intestazione del settore e della necessità di introdurre i nuovi token : Root.DaysLimit e Root.Insurance si rende necessario variare la struttura del formato. | ||
| Riga 647: | Riga 647: | ||
Assegnando quindi 2<sup>(20)</sup> (1.048.576) possibili tipi di biglietto (TicketID) a ciascun dominio anziché i precedenti 223 (8.388.608). | Assegnando quindi 2<sup>(20)</sup> (1.048.576) possibili tipi di biglietto (TicketID) a ciascun dominio anziché i precedenti 223 (8.388.608). | ||
| − | + | --> | |
== La formattazione di memoria == | == La formattazione di memoria == | ||
Ogni tag ISO possiede uno o più blocchi di memoria riscrivibile. Ogni blocco dati è composto da 32 bit. I blocchi sono numerati da 0 a N, dove N varia a seconda della capacità di memoria a disposizione. Ogni tag possiede un preambolo, l’identificatore OpenPass, posizionato al blocco 0. La memoria del tag è la seguente: | Ogni tag ISO possiede uno o più blocchi di memoria riscrivibile. Ogni blocco dati è composto da 32 bit. I blocchi sono numerati da 0 a N, dove N varia a seconda della capacità di memoria a disposizione. Ogni tag possiede un preambolo, l’identificatore OpenPass, posizionato al blocco 0. La memoria del tag è la seguente: | ||
Versione delle 14:22, 14 set 2016
Indice
Struttura del formato
Di seguito si riportano tutti i formati OpenPass utilizzati negli anni.
Formato 1
Il Formato 1 è stato il primo formato redatto ed è stato usato solo come versione di prova, quindi non è mai stato utilizzato sul mercato. Per questo motivo il formato 1 non viene riportato.
Formato 2
Anche il Formato 2 di OpenPass è stato utilizzato solo in fase di test e non è attualmente usato, quindi non viene riportato.
Formato 3
Il Formato 3 è il formato attualmente attivo, strutturato come segue.
Struttura del formato
Clicca su Espandi per visualizzare la struttura del formato.
<?xml version="1.0"?>
<FormatDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Identifier>3</Identifier>
<Description>Common compatible format for season passes, day/time pass and point passes - V0.25 (DRAFT)</Description>
<Creator>Axess Alfi SkiData</Creator>
<LastTouch>2010-09-15T13:23:00</LastTouch>
<!-- Change history -->
<!-- V0.10 Apr 15th, 2010: initial version (Axess AG) -->
<!-- V0.11 Apr 26th, 2010: changes from first review (Skidata, Axess AG) -->
<!-- V0.12 May 10th, 2010: remove reloading of point tickets (Skidata, Axess AG) -->
<!-- V0.13 May 11th, 2010: Group renamed to Region (Skidata AG) -->
<!-- V0.14 May 26th, 2010: changes from review with Alfi: constant value for Root.Group, changed Root.TicketType, renamed Root.SerialNumberEx, minor changes (Axess AG, Alfi) -->
<!-- V0.15 June 11th, 2010: decrease size of Root.Region, change syntax of variable area (add new tokens Root.BlockCRC, ...), use Root.Season+Root.SerialNumberEx in Lombardia (Axess AG, Alfi)-->
<!-- V0.16 June 15th, 2010: removed Root.PriceType, correct bitaddress in variable area, added comments (Axess AG, Skidata AG)-->
<!-- V0.17 June 16th, 2010: added Root.TargetRegion, modified Root.UserID, defined enumerations for Root.Region (Axess AG)-->
<!-- V0.18 June 18th, 2010: renamed Root.Region to Root.IssuerRegion (Axess AG)-->
<!-- V0.19 July 13th, 2010: introduced format 4 (point pass) - identical to format 3 but increased width for Root.RemainingDuration to 20 bits + skipped Root.CurrentDay (SkiData, Axess AG)-->
<!-- V0.20 July 19th, 2010: splitted Root.UserID in Root.UserIDRegion, Root.UserIDWorkstation, Root.UserID, splitted VariableArea parts to support automatic checksum calculations, removed format 4 (SkiData)-->
<!-- V0.21 July 19th, 2010: changed Root.RemainingDuration to 14, Root.CurrentDay to 12 and Root.CRC to 6 bits (Axess AG, SkiData)-->
<!-- V0.22 July 21th, 2010: changed Root.Duration to 14 bits, reverted some changes (Root.UserID + VariableArea definition) from V0.20 and V0.21 to stay compatible with OpenPass (Axess AG)-->
<!-- V0.23 August 17th, 2010: added Root.DateIssued, Root.TimeIssued (Axess AG, SkiData)-->
<!-- V0.24 August 23rd, 2010: renamed Root.BlockCRC tokens to have unique names (Axess AG, SkiData)-->
<!-- V0.25 September 15th, 2010: added currency code (Axess AG)-->
<FixedArea>
<TokenAllocation>
<Name>Root.CRC</Name>
<BitAddress>0</BitAddress>
<BitWidth>16</BitWidth>
</TokenAllocation>
<TokenAllocation>
<Name>Root.IssuerRegion</Name>
<BitAddress>16</BitAddress>
<BitWidth>13</BitWidth>
<!-- Note: in SkiData/Axess compatibilty replaces 'Root.Region' the earlier field 'Root.Group': worldwide unique number for region, also stored in header -->
<!-- (0=Test, 1=Lombardia, ...) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Group</Name>
<ConstantValue>1</ConstantValue>
<!-- Note: no usage for coding Root.Group, therefor define constant value for it -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.UsageDomain</Name>
<BitAddress>29</BitAddress>
<BitWidth>9</BitWidth>
<!-- pool (group of companies having gates) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.SubDomain</Name>
<BitAddress>38</BitAddress>
<BitWidth>7</BitWidth>
<!-- 0 = NotUsed (All gates within usage domain) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Workstation</Name>
<BitAddress>45</BitAddress>
<BitWidth>11</BitWidth>
<!-- unique ID of the workstation (0-2047) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.StartDate</Name>
<BitAddress>56</BitAddress>
<BitWidth>14</BitWidth>
<!-- ticket is valid starting from this date (days since 1/1/2008) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.ExpiryDate</Name>
<BitAddress>70</BitAddress>
<BitWidth>14</BitWidth>
<!-- ticket is valid until this date (days since 1/1/2008), also stored in header (SkiData/Axess compatibilty only) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.StartTime</Name>
<BitAddress>84</BitAddress>
<BitWidth>11</BitWidth>
<!-- ticket is valid from this time (minutes since midnight), only applicable if Root.Unit = Hours/Minutes -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.StartMode</Name>
<BitAddress>95</BitAddress>
<BitWidth>4</BitWidth>
<!-- 1 = ValidFromIssue (directly use Root.StartDate, Root.StartTime, Root.ExpiryDate) -->
<!-- 2 = ValidFromFirstUsage (day pass depot ticket, use Root.DateOfFirstEntry + Root.StartTime/Root.TimeOfFirstEntry [dependent on Root.Unit]; Root.StartDate and Root.ExpiryDate specify absolute limits only) -->
<!-- only applicable if Root.Unit = Days -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Duration</Name>
<BitAddress>99</BitAddress>
<BitWidth>14</BitWidth>
<!-- duration (in days/hours/points) - only used if countable (Days, Hours, Minutes, Years, Month, Points, SingleRides), otherwise set to 1 (Seasons, ...) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TicketType</Name>
<BitAddress>113</BitAddress>
<BitWidth>4</BitWidth>
<!-- 0 = time pass, 1 = consumable (point pass, non-consecutive time pass) -->
<!-- Note: if multiple formats are used, this token can be ommited -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Unit</Name>
<BitAddress>117</BitAddress>
<BitWidth>4</BitWidth>
<!-- unit for Root.Duration (0 = Days, 1 = Reserved, 2 = Reserved, 3 = Points, 4 = SingleRides, 5 = Seasons, 6 = Reserved, 7 = Hours, 8 = Minutes, 9 = Year, 10 = Month) -->
<!-- currently only supported: 0 = Days, 3 = Points, 5 = Seasons, 7 = Hours, 8 = Minutes -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.SerialNumberEx2</Name>
<BitAddress>121</BitAddress>
<BitWidth>32</BitWidth>
<!-- unique serial number of ticket (based on used workstation ID) -->
<!-- Note: no longer dependent on season! (SkiData/Axess compatibilty only) -->
<!-- TODO: in Lombardia replaced by Root.Season (4 bit) + Root.SerialNumberEx (28 bit); needs final confirmation from Alfi -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Restrictions</Name>
<BitAddress>153</BitAddress>
<BitWidth>16</BitWidth>
<!-- 0 = No Restrictions; restriction ID defined on central server (valid season, days, time, ...) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TicketID</Name>
<BitAddress>169</BitAddress>
<BitWidth>32</BitWidth>
<!-- unique ticket/product identifier -->
<!-- has to match with Region, UsageDomain, Duration, Unit, Restriction and UserType -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Price</Name>
<BitAddress>201</BitAddress>
<BitWidth>23</BitWidth>
<!-- tariff in 1/100 Root.Currency units -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.UserID</Name>
<BitAddress>224</BitAddress>
<BitWidth>56</BitWidth>
<!-- unique user ID (this is a bitfield, upper 13 bits contain the region, middle 11 bits contain workstation ID, lower 32 bit contain a local ID) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.UserType</Name>
<BitAddress>280</BitAddress>
<BitWidth>8</BitWidth>
<!-- only used for visualization on gate, e.g. Adult, child, senior, ... -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TargetRegion</Name>
<BitAddress>288</BitAddress>
<BitWidth>13</BitWidth>
<!-- region for interpretation of UsageDomain, TicketID, Restrictions (typically same as Root.Region) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.DateIssued</Name>
<BitAddress>301</BitAddress>
<BitWidth>14</BitWidth>
<!-- date when the ticket has been sold; used for identification of e.g. pre-selling of season passes ... -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TimeIssued</Name>
<BitAddress>315</BitAddress>
<BitWidth>11</BitWidth>
<!-- time when the ticket has been sold; used for identification of e.g. 'gestaffelten Tageskarten' -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.CurrencyCode</Name>
<BitAddress>326</BitAddress>
<BitWidth>15</BitWidth>
<!-- ISO 4217 alphanumeric currency code converted to 3*5 bit numeric value ('A'=0x00, 'B'=0x01, ...) -->
<!-- e.g. 'EUR': 'E' (0x04) + 'U' (0x14) + 'R' (0x11) -> Root.CurrencyCode = 001001010010001b = 0x1291) -->
</TokenAllocation>
</FixedArea>
<VariableArea>
<!-- ********* START of VARIABLE AREA ********** -->
<TokenAllocation>
<Name>Root.BlockCRC</Name>
<BitAddress>0</BitAddress>
<BitWidth>16</BitWidth>
<!-- CRC calculated from this token until the next Root.BlockCRC -->
<!-- (CRC calculated from BitAddress 0-63) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Initialized</Name>
<BitAddress>16</BitAddress>
<BitWidth>1</BitWidth>
</TokenAllocation>
<TokenAllocation>
<Name>Root.TicketState</Name>
<BitAddress>17</BitAddress>
<BitWidth>2</BitWidth>
<!-- 0 = Valid, 1 = Cancelled, 2 = Refunded -->
<!-- Note: this value needs to be evaluated by each vendor, even if it is not set by all vendors; gate must only be opened if ticket state is 0! -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.DateOfFirstEntry</Name>
<BitAddress>19</BitAddress>
<BitWidth>14</BitWidth>
<!-- date of first usage (days since 1/1/2008) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TimeOfFirstEntry</Name>
<BitAddress>33</BitAddress>
<BitWidth>11</BitWidth>
<!-- time of first usage (minutes since midnight) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Reserved</Name>
<BitAddress>44</BitAddress>
<BitWidth>20</BitWidth>
<!-- padding to match chip block boundary -->
</TokenAllocation>
<!-- ********* START of SHADOW A ********** -->
<TokenAllocation>
<Name>Root.BlockCRCA</Name>
<BitAddress>64</BitAddress>
<BitWidth>6</BitWidth>
<!-- (CRC calculated from BitAddress 64-95) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.CurrentDayA</Name>
<BitAddress>70</BitAddress>
<BitWidth>12</BitWidth>
<!-- last usage of the ticket (days since Root.DateOfFirstEntry) -->
<!-- required field -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.RemainingDurationA</Name>
<BitAddress>82</BitAddress>
<BitWidth>14</BitWidth>
<!-- remaing duration -->
</TokenAllocation>
<!-- ********* PADDING ********** -->
<TokenAllocation>
<Name>Root.BlockCRCP</Name>
<BitAddress>96</BitAddress>
<BitWidth>32</BitWidth>
<!-- dummy entry: used for padding to match chip block boundary (on 64bit chips) -->
<!-- (CRC calculated from BitAddress 96-127) -->
</TokenAllocation>
<!-- ********* START of SHADOW B ********** -->
<TokenAllocation>
<Name>Root.BlockCRCB</Name>
<BitAddress>128</BitAddress>
<BitWidth>6</BitWidth>
<!-- (CRC calculated from BitAddress 128-159) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.CurrentDayB</Name>
<BitAddress>134</BitAddress>
<BitWidth>12</BitWidth>
<!-- last usage of the ticket (days since Root.DateOfFirstEntry) -->
<!-- required field -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.RemainingDurationB</Name>
<BitAddress>146</BitAddress>
<BitWidth>14</BitWidth>
<!-- remaing duration -->
</TokenAllocation>
</VariableArea>
</FormatDefinition>
Significato dei campi e area di memoria di appartenenza
Clicca su Espandi per visualizzare il significato dei campi e l'area di memoria a cui appartengono.
| Nome | Significato | Area |
|---|---|---|
| Root.CRC | CRC dell'area fissa | Fissa |
| Root.IssuerRegion | Codice numerico del gruppo (es. RegioneLombardia = 1) | Fissa |
| Root.Group | Valore costante parti a 1 | Fissa |
| Root.UsageDomain | Il codice numerico identificativo del dominio di abilitazione del biglietto (es. 0=solo stazione, 4=Skipass Lombardia) | Fissa |
| Root.Subdomain | Il codice che indica per ogni dominio di abilitazione quale sottoinsieme di impianti/servizi utilizzare all’interno di quelli a disposizione. | Fissa |
| Root.Workstation | Codice numerico della postazione emittente | Fissa |
| Root.StartDate | Data di inizio del biglietto espressa in giorni trascorsi dal 1.1.2008 | Fissa |
| Root.ExpiryDate | Data di scadenza del biglietto espressa in giorni trascorsi dal 1.1.2008 | Fissa |
| Root.StartTime | Biglietto valido a partire da questo tempo (minuti dalla mezzanotte), applicabile solo se Root.Unit = Hours/Minutes | Fissa |
| Root.StartMode | 1 = ValidFromIssue (usare Root.StartDate, Root.StartTime, Root.ExpiryDate) 2 = ValidFromFirstUsage (biglietto di deposito pass giornaliero, usare Root.DateOfFirstEntry + Root.StartTime/Root.TimeOfFirstEntry [dipende da Root.Unit]; Root.StartDate e Root.ExpiryDate specificano solo limiti assoluti). Applicabile solo se Root.Unit = Days |
Fissa |
| Root.Duration | Durata del biglietto (in giorni/ore/punti), espressa in unità di Root.Unit. | Fissa |
| Root.TicketType | 0 = time pass, 1 = consumable (point pass, non-consecutive time pass). Nota: se sono usati formati multipli, questo token può essere omesso. | Fissa |
| Root.Unit | Unità di durata del biglietto, 0=giorni, 1=mattine, 2=pomeriggi, 3=punti, 4=corse, 5=stagioni, 6=anni,.... Attualmente only supported: 0 = giorni, 3 = punti, 5 = stagioni, 7 = ore, 8 = minuti. |
Fissa |
| Root.SerialNumberEx2 | Il codice univoco di identificazione del biglietto (lo identifica relativamente a Root.Workstation e all’anno/stagione di emissione) | Fissa |
| Root.Restrictions | 0 = No restrizioni; restriction ID definito dal server centrale (stagione valida, giorni, tempo, ...) | Fissa |
| Root.TicketID | Identificativo univoco del ticket | Fissa |
| Root.Price | Il prezzo di acquisto, in centesimi di euro | Fissa |
| Root.UserID | L’identificativo numerico dell’utente nel database anagrafico. Composto da 3 campi: regione (13 bit), workstation (11 bit) e local id (32 bit). | Fissa |
| Root.UserType | Il tipo di utente, es. adulti, bambini | Fissa |
| Root.TargetRegion | Regione per interpretazione di UsageDomain, TicketID, Restrictions (solitamente uguale a Root.IssuerRegion) | Fissa |
| Root.DateIssued | Data di vendita del biglietto | Fissa |
| Root.TimeIssued | Orario di vendita del biglietto | Fissa |
| Root.CurrencyCode | Valuta utilizzata (es: EUR) | Fissa |
| Root.BlockCRC | CRC calcolato dal bit 0 al bit 63 | Variabile |
| Root.Initialized | I dati nell’area variabile sono stati compilati utilizzando per la prima volta un biglietto | Variabile |
| Root.TicketState | 0 = Valido, 1 = Cancellato, 2 = Rimborsato | Variabile |
| Root.DateOfFirstEntry | Data di primo utilizzo (dal 1/1/2008) | Variabile |
| Root.TimeOfFirstEntry | Orario di primo utilizzo (minuti dalla mezzanotte) | Variabile |
| Root.Reserved | Padding | Variabile |
| Root.BlockCRCA | CRC dal bit 64 al bit 95 | Variabile |
| Root.CurrentDayA | Ultimo utilizzo del biglietto (giorni da Root.DateOfFirstEntry) | Variabile |
| Root.RemainingDurationA | Durata rimasta | Variabile |
| Root.BlockCRCP | CRC dal bit 96 al bit 127 | Variabile |
| Root.BlockCRCB | CRC dal bit 128 al bit 159 | Variabile |
| Root.CurrentDayB | Ultimo utilizzo del biglietto (giorni da Root.DateOfFirstEntry) | Variabile |
| Root.RemainingDurationB | Durata rimasta | Variabile |
<!
Formato 4
A seguito della variazione dell’intestazione del settore e della necessità di introdurre i nuovi token : Root.DaysLimit e Root.Insurance si rende necessario variare la struttura del formato.
Clicca su Espandi per visualizzare la struttura del formato.
<?xml version="1.0"?>
<FormatDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Identifier>4</Identifier>
<Description>Common compatible format for season passes, day/time pass and point passes - V4.1 (DRAFT)</Description>
<Creator>Anef Ski Lombardia</Creator>
<LastTouch>2016-07-01T12:00:00</LastTouch>
<!-- Change history -->
<!-- V0.10 Apr 15th, 2010: initial version (Axess AG) -->
<!-- V0.11 Apr 26th, 2010: changes from first review (Skidata, Axess AG) -->
<!-- V0.12 May 10th, 2010: remove reloading of point tickets (Skidata, Axess AG) -->
<!-- V0.13 May 11th, 2010: Group renamed to Region (Skidata AG) -->
<!-- V0.14 May 26th, 2010: changes from review with Alfi: constant value for Root.Group, changed Root.TicketType, renamed Root.SerialNumberEx, minor changes (Axess AG, Alfi) -->
<!-- V0.15 June 11th, 2010: decrease size of Root.Region, change syntax of variable area (add new tokens Root.BlockCRC, ...), use Root.Season+Root.SerialNumberEx in Lombardia (Axess AG, Alfi)-->
<!-- V0.16 June 15th, 2010: removed Root.PriceType, correct bitaddress in variable area, added comments (Axess AG, Skidata AG)-->
<!-- V0.17 June 16th, 2010: added Root.TargetRegion, modified Root.UserID, defined enumerations for Root.Region (Axess AG)-->
<!-- V0.18 June 18th, 2010: renamed Root.Region to Root.IssuerRegion (Axess AG)-->
<!-- V0.19 July 13th, 2010: introduced format 4 (point pass) - identical to format 3 but increased width for Root.RemainingDuration to 20 bits + skipped Root.CurrentDay (SkiData, Axess AG)-->
<!-- V0.20 July 19th, 2010: splitted Root.UserID in Root.UserIDRegion, Root.UserIDWorkstation, Root.UserID, splitted VariableArea parts to support automatic checksum calculations, removed format 4 (SkiData)-->
<!-- V0.21 July 19th, 2010: changed Root.RemainingDuration to 14, Root.CurrentDay to 12 and Root.CRC to 6 bits (Axess AG, SkiData)-->
<!-- V0.22 July 21th, 2010: changed Root.Duration to 14 bits, reverted some changes (Root.UserID + VariableArea definition) from V0.20 and V0.21 to stay compatible with OpenPass (Axess AG)-->
<!-- V0.23 August 17th, 2010: added Root.DateIssued, Root.TimeIssued (Axess AG, SkiData)-->
<!-- V0.24 August 23rd, 2010: renamed Root.BlockCRC tokens to have unique names (Axess AG, SkiData)-->
<!-- V0.25 September 15th, 2010: added currency code (Axess AG)-->
<!-- V4.1 July 27th, 2016: changed Root.IssuerRegion from 13 to 5 bits, changed Root.TargetRegion from 13 to 5 bits, changed Root.UsageDomain from 9 to 12 bits, changed Root.Workstation from 11 to 14 bits, renamed Root.IssuerRegion to Root.Group, removed constant Root.Group, introduced Root.DaysLimit and Root.Insurance (Anef Ski Lombardia) -->
<FixedArea>
<TokenAllocation>
<Name>Root.CRC</Name>
<BitAddress>0</BitAddress>
<BitWidth>16</BitWidth>
</TokenAllocation>
<TokenAllocation>
<Name>Root.Group</Name>
<BitAddress>16</BitAddress>
<BitWidth>5</BitWidth>
<!-- Note: in SkiData/Axess compatibilty replaces 'Root.Region' the earlier field 'Root.Group': worldwide unique number for region, also stored in header -->
<!-- (0=Test, 1=Lombardia, ...) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.UsageDomain</Name>
<BitAddress>21</BitAddress>
<BitWidth>12</BitWidth>
<!-- pool (group of companies having gates) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.SubDomain</Name>
<BitAddress>33</BitAddress>
<BitWidth>7</BitWidth>
<!-- 0 = NotUsed (All gates within usage domain) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Workstation</Name>
<BitAddress>40</BitAddress>
<BitWidth>14</BitWidth>
<!-- unique ID of the workstation (0-2047) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.StartDate</Name>
<BitAddress>54</BitAddress>
<BitWidth>14</BitWidth>
<!-- ticket is valid starting from this date (days since 1/1/2008) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.ExpiryDate</Name>
<BitAddress>68</BitAddress>
<BitWidth>14</BitWidth>
<!-- ticket is valid until this date (days since 1/1/2008), also stored in header (SkiData/Axess compatibilty only) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.StartTime</Name>
<BitAddress>82</BitAddress>
<BitWidth>11</BitWidth>
<!-- ticket is valid from this time (minutes since midnight), only applicable if Root.Unit = Hours/Minutes -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.StartMode</Name>
<BitAddress>93</BitAddress>
<BitWidth>4</BitWidth>
<!-- 1 = ValidFromIssue (directly use Root.StartDate, Root.StartTime, Root.ExpiryDate) -->
<!-- 2 = ValidFromFirstUsage (day pass depot ticket, use Root.DateOfFirstEntry + Root.StartTime/Root.TimeOfFirstEntry [dependent on Root.Unit]; Root.StartDate and Root.ExpiryDate specify absolute limits only) -->
<!-- only applicable if Root.Unit = Days -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Duration</Name>
<BitAddress>97</BitAddress>
<BitWidth>14</BitWidth>
<!-- duration (in days/hours/points) - only used if countable (Days, Hours, Minutes, Years, Month, Points, SingleRides), otherwise set to 1 (Seasons, ...) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TicketType</Name>
<BitAddress>111</BitAddress>
<BitWidth>4</BitWidth>
<!-- 0 = time pass, 1 = consumable (point pass, non-consecutive time pass) -->
<!-- Note: if multiple formats are used, this token can be ommited -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Unit</Name>
<BitAddress>115</BitAddress>
<BitWidth>4</BitWidth>
<!-- unit for Root.Duration (0 = Days, 1 = Reserved, 2 = Reserved, 3 = Points, 4 = SingleRides, 5 = Seasons, 6 = Reserved, 7 = Hours, 8 = Minutes, 9 = Year, 10 = Month) -->
<!-- currently only supported: 0 = Days, 3 = Points, 5 = Seasons, 7 = Hours, 8 = Minutes -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.SerialNumberEx2</Name>
<BitAddress>119</BitAddress>
<BitWidth>32</BitWidth>
<!-- unique serial number of ticket (based on used workstation ID) -->
<!-- Note: no longer dependent on season! (SkiData/Axess compatibilty only) -->
<!-- TODO: in Lombardia replaced by Root.Season (4 bit) + Root.SerialNumberEx (28 bit); needs final confirmation from Alfi -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Restrictions</Name>
<BitAddress>151</BitAddress>
<BitWidth>16</BitWidth>
<!-- 0 = No Restrictions; restriction ID defined on central server (valid season, days, time, ...) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TicketID</Name>
<BitAddress>167</BitAddress>
<BitWidth>32</BitWidth>
<!-- unique ticket/product identifier -->
<!-- has to match with Region, UsageDomain, Duration, Unit, Restriction and UserType -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Price</Name>
<BitAddress>199</BitAddress>
<BitWidth>23</BitWidth>
<!-- tariff in 1/100 Root.Currency units -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.UserID</Name>
<BitAddress>222</BitAddress>
<BitWidth>56</BitWidth>
<!-- unique user ID (this is a bitfield, upper 13 bits contain the region, middle 11 bits contain workstation ID, lower 32 bit contain a local ID) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.UserType</Name>
<BitAddress>278</BitAddress>
<BitWidth>8</BitWidth>
<!-- only used for visualization on gate, e.g. Adult, child, senior, ... -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TargetRegion</Name>
<BitAddress>286</BitAddress>
<BitWidth>5</BitWidth>
<!-- region for interpretation of UsageDomain, TicketID, Restrictions (typically same as Root.Region) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.DateIssued</Name>
<BitAddress>291</BitAddress>
<BitWidth>14</BitWidth>
<!-- date when the ticket has been sold; used for identification of e.g. pre-selling of season passes ... -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TimeIssued</Name>
<BitAddress>305</BitAddress>
<BitWidth>11</BitWidth>
<!-- time when the ticket has been sold; used for identification of e.g. 'gestaffelten Tageskarten' -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.CurrencyCode</Name>
<BitAddress>316</BitAddress>
<BitWidth>15</BitWidth>
<!-- ISO 4217 alphanumeric currency code converted to 3*5 bit numeric value ('A'=0x00, 'B'=0x01, ...) -->
<!-- e.g. 'EUR': 'E' (0x04) + 'U' (0x14) + 'R' (0x11) -> Root.CurrencyCode = 001001010010001b = 0x1291) -->
</TokenAllocation>
<TokenAllocation>
<Name> Root.DaysLimit </Name>
<BitAddress>331</BitAddress>
<BitWidth>9</BitWidth>
<!-- giorni di validità a partire dal primo utilizzo, se 0 nessun limite -->
</TokenAllocation>
<TokenAllocation>
<Name> Root.Insurance </Name>
<BitAddress>340</BitAddress>
<BitWidth>1</BitWidth>
<!-- 0 =non assicurato , 1 assicurato -->
</TokenAllocation>
</FixedArea>
<VariableArea>
<!-- ********* START of VARIABLE AREA ********** -->
<TokenAllocation>
<Name>Root.BlockCRC</Name>
<BitAddress>0</BitAddress>
<BitWidth>16</BitWidth>
<!-- CRC calculated from this token until the next Root.BlockCRC -->
<!-- (CRC calculated from BitAddress 0-63) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Initialized</Name>
<BitAddress>16</BitAddress>
<BitWidth>1</BitWidth>
</TokenAllocation>
<TokenAllocation>
<Name>Root.TicketState</Name>
<BitAddress>17</BitAddress>
<BitWidth>2</BitWidth>
<!-- 0 = Valid, 1 = Cancelled, 2 = Refunded -->
<!-- Note: this value needs to be evaluated by each vendor, even if it is not set by all vendors; gate must only be opened if ticket state is 0! -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.DateOfFirstEntry</Name>
<BitAddress>19</BitAddress>
<BitWidth>14</BitWidth>
<!-- date of first usage (days since 1/1/2008) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.TimeOfFirstEntry</Name>
<BitAddress>33</BitAddress>
<BitWidth>11</BitWidth>
<!-- time of first usage (minutes since midnight) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.Reserved</Name>
<BitAddress>44</BitAddress>
<BitWidth>20</BitWidth>
<!-- padding to match chip block boundary -->
</TokenAllocation>
<!-- ********* START of SHADOW A ********** -->
<TokenAllocation>
<Name>Root.BlockCRCA</Name>
<BitAddress>64</BitAddress>
<BitWidth>6</BitWidth>
<!-- (CRC calculated from BitAddress 64-95) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.CurrentDayA</Name>
<BitAddress>70</BitAddress>
<BitWidth>12</BitWidth>
<!-- last usage of the ticket (days since Root.DateOfFirstEntry) -->
<!-- required field -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.RemainingDurationA</Name>
<BitAddress>82</BitAddress>
<BitWidth>14</BitWidth>
<!-- remaing duration -->
</TokenAllocation>
<!-- ********* PADDING ********** -->
<TokenAllocation>
<Name>Root.BlockCRCP</Name>
<BitAddress>96</BitAddress>
<BitWidth>32</BitWidth>
<!-- dummy entry: used for padding to match chip block boundary (on 64bit chips) -->
<!-- (CRC calculated from BitAddress 96-127) -->
</TokenAllocation>
<!-- ********* START of SHADOW B ********** -->
<TokenAllocation>
<Name>Root.BlockCRCB</Name>
<BitAddress>128</BitAddress>
<BitWidth>6</BitWidth>
<!-- (CRC calculated from BitAddress 128-159) -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.CurrentDayB</Name>
<BitAddress>134</BitAddress>
<BitWidth>12</BitWidth>
<!-- last usage of the ticket (days since Root.DateOfFirstEntry) -->
<!-- required field -->
</TokenAllocation>
<TokenAllocation>
<Name>Root.RemainingDurationB</Name>
<BitAddress>146</BitAddress>
<BitWidth>14</BitWidth>
<!-- remaing duration -->
</TokenAllocation>
</VariableArea>
</FormatDefinition>
Nuovo metodo di calcolo per ticketid:
A seguito della variazione di dimensioni del token Root.UsageDomain si rende necessario ridefinire l’algoritmo di calcolo per la generazione dei blocchi di TicketId preassegnati a ciascun dominio (Root.UsageDomain).
Il metodo di calcolo è il seguente: Root.UsageDomain * 2(20) + IDD
NB: Massimo valore di IDD = 2(20)-1 = 1.048.575
Assegnando quindi 2(20) (1.048.576) possibili tipi di biglietto (TicketID) a ciascun dominio anziché i precedenti 223 (8.388.608). -->
La formattazione di memoria
Ogni tag ISO possiede uno o più blocchi di memoria riscrivibile. Ogni blocco dati è composto da 32 bit. I blocchi sono numerati da 0 a N, dove N varia a seconda della capacità di memoria a disposizione. Ogni tag possiede un preambolo, l’identificatore OpenPass, posizionato al blocco 0. La memoria del tag è la seguente:
| Blocco | Contenuto | Commenti |
|---|---|---|
| 0 | Identificatore OpenPass | Valore costante, indica la chiave fisica da utilizzare |
| 1..A | Primo settore/biglietto | |
| A+1..B | Secondo settore/biglietto | |
| B+1..N | Terzo settore/biglietto |
Lo stesso tag può contenere più biglietti (o settori), a seconda delle esigenze. I biglietti nello stesso tag possono essere aggiunti da più emittenti anche di differenti società, fino al limite di riempimento della memoria a disposizione. Un indice dei settori e biglietti non esiste. I terminali periferici, ai fini di valutare se l’utente ha diritto o meno di usufruire del servizio, cominciano a leggere il tag dall’inizio e valutano i biglietti uno dopo l’altro, finché non ne trovano uno valido. Un settore contiene uno ed un solo biglietto. La dimensione di ogni settore è variabile e la memoria viene allocata secondo le esigenze. Un qualunque terminale può decidere di spostare i settori registrati all’interno del tag, secondo le proprie necessità.
Settore
Il settore dati è la minima unità di allocazione. La sua effettiva dimensione dipende dalle esigenze di gestione e dalla lunghezza delle chiavi scelte. Visto che ogni emittente possiede una propria chiave segreta, il settore deve indicare in chiaro un codice numerico abbinato all’emittente. Il codice numerico, univoco per ogni emittente, identifica quindi in modo univoco la chiave da utilizzare per la decrittazione del settore. Un settore dati inizia sempre con un nuovo blocco di un tag.
Il settore dati possiede un header da 32 bit che descrive il contenuto successivo.
Il contenuto del settore è così strutturato:
| Bit | Contenuto | Commenti |
|---|---|---|
| 0,1 | Codice identificativo formato | Serve per decodificare il resto del contenuto |
| 2..F | Dati dell’area fissa | |
| F+1..G | Dati dell’area variabile (opzionale) | |
| G+1..H | Dati dell’area variabile (opzionale) |
Header
Come detto il settore dati possiede un header da 32 bit che descrive il contenuto successivo. Di seguito si riporta il contenuto dell'header per i formati attualmente validi.
Formato 3
I 32 bits sono divisi in questo modo:
- 11 bit per il token Root.Workstation
- 9 bit per la versione del codice della chiave relativa alla Root.Workstation (valore zero 0 per tutti, non avendo mai variato le chiavi, prima versione per tutti quindi)
- 5 bit per la lunghezza in blocchi del settore criptato
- 7 bit non utilizzati e sempre valorizzati a 0000000
| Bit | N° bit | Contenuto | Commenti |
|---|---|---|---|
| 0..10 | 11 | Codice cassa emittente (0..2047) Token: Root.Workstation |
Un massimo di 2048 emittenti (workstation) possono far parte dello stesso gruppo. |
| 11..19 | 9 | Codice chiave di decriptazione Token | 0 = Prima versione, 1 = seconda versione, … |
| 20..24 | 5 | Lunghezza dei dati a seguire | In blocchi da 32bits (massimo 32 blocchi) |
| 25..31 | 7 | Riservato a sviluppi futuri |
Formato 4
Al fine di aumentare il numero di emittenti (workstation) gestibili e introdurre l’utilizzo dei gruppi di emittenti (workstation) per sistemi diversi interagenti OpenPass, vengono apportate le seguenti modifiche:
- aumento della dimensione in bits per il token Root.Workstation portandola da 11 bits a 14 bits, quindi aumentando il possibile numero massimo delle emittenti (workstation) gestibili per ogni singolo gruppo, da 2048 disponibile oggi a 16384
- introduzione del token Root.Group nel header del settore OpenPass con dimensione 5 bits, introducendo quindi 32 possibili raggruppamenti di emittenti (workstation) per gestire diversi sistemi OpenPass interagenti
- introduzione della versione del formato OpenPass nel header del settore OpenPass con dimensione 2 bits (00=prima versione, 01=seconda versione (quella descritta in questo documento), 10 e 11 sviluppi futuri
- riduzione della dimensione in bits per la versione della chiave presente in header, portandola da 9 bits a 6 bits, per far posto all’aumento della dimensione di Root.Workstation e all’introduzione di Root.Group e della versione del formato OpenPass
| Bit | N° bit | Contenuto | Commenti |
|---|---|---|---|
| 0..13 | 14 | Codice cassa emittente (0.. 16383) Token: Root.Workstation |
Un massimo di 16384 emittenti (workstation) possono far parte dello stesso gruppo. |
| 14..19 | 6 | Codice chiave di decriptazione Token | 0 = Prima versione, 1 = seconda versione, … |
| 20..24 | 5 | Lunghezza dei dati a seguire | In blocchi da 32bits (massimo 32 blocchi) |
| 25..29 | 5 | Codice gruppo delle cassa emettitrici (0.. 31) Token: Root.Group |
Un massimo di 32 gruppi di emittenti (workstation). |
| 30..31 | 2 | Versione di formattazione del settore OpenPass | 00=prima versione | 01=seconda versione |
Fissa
L’area fissa contiene tutte le informazioni che sono prestabilite al momento dell’emissione del biglietto e che non subiranno mai cambiamenti durante l’utilizzo del biglietto (es. stazione emittente, durata nominale, progressivo di vendita, data di emissione).
L’area fissa va necessariamente protetta con tecniche crittografiche a chiave asimmetrica, per certificarne la autenticità. Questa protezione rappresenta la sicurezza logica del sistema. Come già detto, nessuno che non sia in possesso della chiave segreta di un emittente sarà mai in grado di scrivere un messaggio che sia considerato valido dagli utilizzatori.
Variabili
L’area variabile, facoltativa, contiene tutte le informazioni che sono destinate a mutare con l’utilizzo del biglietto, come ad esempio le unità restanti, il giorno dell’ultimo utilizzo, l’ora minima del passaggio successivo etc.etc.
L’area variabile, per sua natura, deve potere essere letta ed aggiornata dai terminali periferici, ed è potenzialmente la più vulnerabile. Non è infatti possibile implementare un meccanismo come quello stabilito per l’area fissa, se non utilizzando tecniche crittografiche a chiave simmetrica, visto che la stessa chiave deve essere nota e uguale per tutti. A meno di utilizzi esclusivi di biglietti che non richiedano riscritture, è fortemente consigliato l’utilizzo di tags rfid dotati di sicurezza a bordo. Il solo utilizzo infatti di tecniche di sicurezza logica non protegge contro il più semplice degli attacchi, cioè la riscrittura del contenuto della stessa card a partire dai dati del biglietto prima del suo utilizzo. La crittografia di questa area è pertanto inutile.
Ancillare
L’area ancillare, facoltativa, contiene informazioni statiche, utili al momento dell’emissione del biglietto, ma che sono ininfluenti ai fini della valutazione della validità del medesimo, come ad esempio il prezzo di vendita. L’area ancillare, facoltativa, non è protetta da crittografia. Infatti, viste le esigenze pressoché nulle di sicurezza per i dati contenuti in quest’area, se ne sconsiglia l’utilizzo (nessuno trae beneficio dalla loro alterazione). I dati ancillari, qualora fossero sensibili, possono risiedere nell’area fissa.
Lettura del formato
Come già detto nei capitoli precedenti il formato OpenPass è libero in lettura, quindi chiunque può leggere il contenuto. La lettura avviene un settore alla volta, che ricordiamo è così composto:
- header del settore: in chiaro;
- area fissa: cifrata. Per decifrarla è necessaria la chiave pubblica relativa alla workstation che ha scritto il biglietto;
- area variabile: in chiaro;
- area ancillare: in chiaro.
Il fatto che l'area fissa sia cifrata garantisce la sicurezza logica del sistema. L'algoritmo che viene utilizzato per cifrare l'area fissa è l'RSA: RSA è un algoritmo di cifratura asimmetrica che si basa su due chiavi distinte, la chiave pubblica e la chiave privata. La chiave privata è utilizzata dalle workstation per scrivere i dati, mentre quella pubblica è utilizzata per leggere i dati cifrati. Quindi per leggere l'area fissa, è necessario recuperare la corrispondente chiave pubblica.
Infine esiste un ulteriore livello di sicurezza, che consiste nella verifica dei CRC presenti nell'area fissa e nell'area variabile. Se i CRC sono validi, allora siamo sicuri che il contenuto del biglietto non è stato alterato.
Il CRC è calcolato su tutti i dati dell’area che lo contiene (preimpostando a 0 lo spazio dove dovrà essere inserito il risultato) e sull’UID del tag. L’inclusione del token Root.CRC protegge l’area fissa da qualunque tentativo di creazione di biglietti, nonché dalla duplicazione dei dati di biglietti validi su di un altro tag. Così come i CRC dell'area di dati variabili. Un settore contenente un’area che non soddisfa il controllo del CRC non deve essere considerato valido, in quanto danneggiato o non autentico.
Di seguito si riporta uno schema di lettura dei dati:
e lo schema di verifica del CRC:
L'algoritmo per il calcolo del CRC CCITT-16, è il seguente:
unsigned short CrcCCITT(BYTE *Data, long Size)
{
unsigned short crc=0xffff;
for(long i=0; i<Size; i++) {
crc^=Data[i];
for(long j=0; j<8; j++) {
if(crc & 0x0001)
crc=(crc>>1)^0x8408;
else
crc>>=1;
}
}
return crc;
}
Scrittura del formato
Un identificatore di OpenPass è presente nel primo blocco utile (scrivibile) del tag. È una costante numerica a 32 bit randomica che vale per un determinato gruppo di emittenti, ed indica la chiave utilizzata per la scrittura fisica del tag. Ogni emittente e punto periferico gestisce una lista, in ordine cronologico, di identificatori e chiavi, che viene fornita dal gestore. La lista va memorizzata in modo sicuro sui terminali, per evitare fughe di informazioni. Ad ogni periodo prestabilito, ad esempio annualmente, il gestore del sistema rilascia un nuovo codice di identificazione e una nuova chiave fisica, unitamente alla data di primo utilizzo della stessa. Ogni postazione emittente e ogni terminale periferico che si trovi ad elaborare un tag con un codice di identificazione scaduto deve aggiornare l’identificatore OpenPass, quindi sostituire la chiave di accesso al chip con quella nuova. Questa procedura consente un rinnovo rapido delle chiavi delle cards in circolazione, impedendo di fatto ogni tentativo di frode dovuta a perdita di sicurezza delle chiavi sul nascere, se questo non è volontario da parte di un associato.
La chiave di sicurezza non viene impiegata tale e quale sul tag, ma passa attraverso un processo di crittografia a chiave simmetrica, per diversificarla. Per diversificazione (key diversification) si intende la creazione di una chiave di accesso personalizzata per ogni singolo chip. Questo passo è essenziale per evitare intercettazioni della chiave di sicurezza nella trasmissione in chiaro tra lettore e chip, tecnica che è alla portata di molti. Un sistema crittografico (es. triplo DES o meglio, AES) viene predisposto allo scopo in ogni punto di lettura. Le chiavi del motore crittografico sono precaricate e prestabilite. Non serve modificarle, se non eccezionalmente (chiave fisica master). Per ottenere la chiave fisica da utilizzare nel chip, si prende la chiave fisica distribuita dal gestore, si concatena l’UID del chip e si crittografa tale messaggio con il motore crittografico, eseguendo un padding del messaggio con una costante fino ad ottenere la dimensione del blocco crittografico. Il messaggio così ottenuto si tronca ai 32 o ai 64 bit, a seconda del modello di tag impiegato.
La personalizzazione delle cards viene effettuata al primo utilizzo (normalmente l’emissione del primo biglietto), inizializzando l’identificatore e caricando la più recente chiave a disposizione. Nel circuito di utilizzo delle cards si può introdurre il concetto di trusted level, ovvero l’insieme degli associati che possono avere accesso alla chiave fisica. Gli utenti al di fuori di questo gruppo hanno il solo accesso in lettura alle informazioni contenute nel tag. Non si dà alcuna prescrizione riguardo la conservazione delle chiavi private. La conservazione delle stesse e la protezione contro i furti di informazioni è responsabilità degli utenti finali nei confronti degli altri utenti consorziati.
L'algoritmo di cifratura che viene utilizzato è AES (Advanced Encryption Standard), ovvero un algoritmo di cifratura a blocchi. Esso riceve in ingresso l'UID della tessera concatenato con la chiave fisica distribuita del gestore, e utilizza una chiave che viene letta in chiaro dal primo blocco di memoria (ad esempio: OPL1). In uscita l'algoritmo ritorna la chiave di scrittura a 32 bit, che mi permette quindi di scrivere sulla tessera.
Di seguito viene schematizzato il funzionamento:
Quando verranno scritti i dati l'area fissa dovrà essere scritta cifrata utilizzando l'algoritmo RSA con la chiave privata della workstation, e sarà necessario calcolare e scrivere i vari CRC presenti nell'area fissa e nell'area variabile, calcolati sui contenuti della memoria. L'algoritmo per il calcolo del CRC è lo stesso del paragrafo precedente (Lettura del formato).