ootp/basiccard/HOTPC.BAS

833 lines
20 KiB
QBasic

'
' Copyright (c) 2005,2006 Mark Fullmer
' Copyright (c) 2009 Mark Fullmer and the Ohio State University
' All rights reserved.
'
' Redistribution and use in source and binary forms, with or without
' modification, are permitted provided that the following conditions
' are met:
' 1. Redistributions of source code must retain the above copyright
' notice, this list of conditions and the following disclaimer.
' 2. Redistributions in binary form must reproduce the above copyright
' notice, this list of conditions and the following disclaimer in the
' documentation and/or other materials provided with the distribution.
'
' THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
' ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
' IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
' ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
' FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
' DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
' OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
' HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
' LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
' OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
' SUCH DAMAGE.
'
' $Id: HOTPC.BAS 92 2009-12-28 02:45:54Z maf $
'
#include "sha-1.def"
#include "preader.def"
#include "AlgID.DEF"
#include "Commands.def"
#include "hotp.def"
' Disable filesystem
#files 0
Option Explicit
' number of keys/hosts - max 254. 255 is reserved. Index is 8 bits.
Const HOTPNum = 50
' HOTPCodeVersion notes:
' Rev 1 - first production version - feb 2006
' Rev 2 - disable AdminMode on first usage - safety net
' if left on by mistake.
' Rev 3 - Count32 Commands
' - conditional compile Commands
' - Get/Set/Test naming standardize
' Rev 4 - release of Rev3
' Rev 5 - renumber Commands, readerKey, getCapabilities
' - ClearALL, checkReaderKey
' Rev 6 - release Rev5
' Rev 7 - dynamic truncate, decimal HOTP, readerKeyFailCount
' reset in ClearAll, HOTPCommon, move stack vars
' to global, reorg balance reader code
' naming consistency, default 50 systems
' Rev 8 - release Rev7
' 20 byte scratch area addressable as 5 32bit vars
public str20 as String*20
public tmpb1 as Long at str20
public tmpb2 as Long at str20+4
public tmpb3 as Long at str20+8
public tmpb4 as Long at str20+12
public tmpb5 as Long at str20+16
' 32 bit HOTPCount stored 64 bits. Top 32 bits always 0
public Count64 as String*8
public Count64low as Long at Count64+4
' Truncated 4 or 5 byte HOTP
public HOTPTruncated as String*5
' HOTPTruncated aliases for 31 bit decimal formats
public u32 as Long at HOTPTruncated
public u32b0 as Byte at u32
public u32b1 as Byte at u32+1
public u32b2 as Byte at u32+2
public u32b3 as Byte at u32+3
' HOTP Format
public HOTPfmt as Byte
' Code version
Const HOTPCodeVersion = 8
' Capabilities (conditionally compiled in functions)
eeprom Capabilities as Long = CAPSETHOST + CAPGETHOST + CAPGETHOSTNAME + _
CAPGETHOTP + CAPSETADMINMODE + _
CAPSETBALANCECARDINDEX + CAPSETPIN + _
CAPTESTPIN + CAPGETVERSION + _
CAPSETADMINKEY + CAPSETHOST32 + _
CAPGETHOST32 + CAPGETHOTPCOUNT32 + _
CAPGETHOTPHOST + CAPGETHOTPHOSTCOUNT32 + _
CAPPRDISPLAY + CAPCLEARALL + CAPSETREADERKEY
' Default PIN
Const DefaultPIN = "28165"
' Default Admin Key
eeprom AdminKey as String*20 = "00000000000000000000"
' HOTPK and its derivatives
' default Key "00000000000000000000"
' KeyI is Key XOR input pad (0x36)
' IPAD44 is 0x36 repeated 44 times
' KeyO is Key XOR output pad (0x5c)
' OPAD44 is 0x5c repeated 44 times.
' Note that only the I and O versions of the keys
' are stored -- which is all that's needed.
' An alternative implementation could choose to store
' the key and compute the I and O versions at run time
' this would use less EEPROM space at the cost of more
' CPU cycles (and less battery life) to compute a HOTP
eeprom HOTPKeyI(HOTPNum) as String*20 = &H06,&H06,&H06,&H06,&H06,&H06,&H06, _
&H06,&H06,&H06,&H06,&H06,&H06,&H06, _
&H06,&H06,&H06,&H06,&H06,&H06
eeprom HOTPKeyO(HOTPNum) as String*20 = &H6C,&H6C,&H6C,&H6C,&H6C,&H6C,&H6C, _
&H6C,&H6C,&H6C,&H6C,&H6C,&H6C,&H6C, _
&H6C,&H6C,&H6C,&H6C,&H6C,&H6C
' system hostname
eeprom HOTPHost(HOTPNum) as String*12
' system count
eeprom HOTPCount32(HOTPNum) as Long
' Input pad
eeprom IPAD44 as String*44 = &H36,&H36,&H36,&H36,&H36,&H36,&H36,&H36,&H36, _
&H36,&H36,&H36,&H36,&H36,&H36,&H36,&H36,&H36, _
&H36,&H36,&H36,&H36,&H36,&H36,&H36,&H36,&H36, _
&H36,&H36,&H36,&H36,&H36,&H36,&H36,&H36,&H36, _
&H36,&H36,&H36,&H36,&H36,&H36,&H36,&H36
' Output pad
eeprom OPAD44 as String*44 = &H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C, _
&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C, _
&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C, _
&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C, _
&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C,&H5C
' Temporary message digest (inner)
eeprom eestr20 as String*20
' PIN - used to deter unauthorized use
eeprom PIN as String*5 = DefaultPIN
' Reader Key. Weak authentication for reader
eeprom readerKey as String*5 = "00000"
' The balance card can only use one of the host definitions.
eeprom BalanceCardIndex = 255 ' Disabled
' if AdminMode is not set some Commands require a valid PIN to work.
eeprom AdminMode as Byte = 1
' Keep track of PIN failures
eeprom PINFailCount as Byte = 0
eeprom readerKeyFailCount as Byte = 0
Const MaxPINFail = 10
Const MaxReaderKeyFail = 2
declare Sub Truncate(Idx as Byte)
declare Sub HOTPCommon(ReadOnly Idx as Byte, ReadOnly Count as Long, _
ReadOnly myPIN as String*5, ReadOnly readerKey as String*5)
declare Function CheckReaderKey(ReadOnly Idx as Byte, _
ReadOnly myKey as String*5) as Byte
declare Function CheckPIN(ReadOnly myPIN as String*5) as Byte
declare Function CheckAdmin() as Byte
declare Function CheckIndex(Idx as Byte) as Byte
' Common HOTP generation code
Sub HOTPCommon(ReadOnly Idx as Byte, ReadOnly Count32 as Long, _
ReadOnly myPIN as String*5, ReadOnly readerKey as String*5)
' disable admin mode on first use.
if (AdminMode = 1) then
AdminMode = 0
end if
' don't allow operations with default pin
if myPIN = DefaultPIN then
SW1SW2 = swAccessDenied
Exit
end if
' Check reader access
if CheckReaderKey(Idx, readerKey) <> 0 then
SW1SW2 = swAccessDenied
Exit
end if
if CheckIndex(Idx) <> 0 then
SW1SW2 = swDataNotFound
Exit
end if
' when Count32 == 0, use stored count
if Count32 <> 0 then
Count64low = Count32
else
Count64low = HOTPCount32(Idx)
end if
Call ShaStart()
Call ShaAppend(HOTPKeyI(Idx))
Call ShaAppend(IPAD44)
Call ShaAppend(Count64)
str20 = ShaEnd()
Call ShaStart()
Call ShaAppend(HOTPKeyO(Idx))
Call ShaAppend(OPAD44)
Call ShaAppend(str20)
str20 = ShaEnd()
Disable OverflowCheck
HOTPCount32(Idx) = Count64low + 1
Enable OverflowCheck
' Set HOTPTruncated
Call Truncate(Idx)
End Sub ' HOTPCommon
' Convert 160 bit String to 4 or 5 Bytes and format for reader
Sub Truncate(Idx as Byte)
private offset as Byte
HOTPfmt = 0
if (asc(HOTPHost(Idx)(12)) AND &H80) then
HOTPfmt = 1
end if
if (asc(HOTPHost(Idx)(11)) AND &H80) then
HOTPfmt = HOTPfmt or 2
end if
if (asc(HOTPHost(Idx)(10)) AND &H80) then
HOTPfmt = HOTPfmt or 4
end if
' future?
' if (asc(HOTPHost(Idx)(9)) AND &H80) then
' HOTPfmt = HOTPfmt or 8
' end if
' not suport on ZC3.9 card
' HOTPfmt = (asc(HOTPHost(Idx)(12)) AND &H80) SHRL 7
' HOTPfmt = fmt OR ((asc(HOTPHost(Idx)(11)) AND &H80) SHRL 6)
' HOTPfmt = fmt OR ((asc(HOTPHost(Idx)(10)) AND &H80) SHRL 5)
' HOTPfmt = fmt OR ((asc(HOTPHost(Idx)(9)) AND &H80) SHRL 4)
if (HOTPfmt = FMTHEX40) or (HOTPfmt = 0) then
HOTPTruncated = Left$(str20,5)
Exit Sub
end if
offset = asc(str20(20)) and &H0F
if (HOTPfmt = FMTDHEX40) then
HOTPTruncated = Mid$(str20,offset+1,5)
Exit Sub
end if
' u32b0..3 are bytes of u32 alias for HOTPtruncated
u32b0 = asc(str20(offset+1)) and &H7F
u32b1 = asc(str20(offset+2))
u32b2 = asc(str20(offset+3))
u32b3 = asc(str20(offset+4))
if (HOTPfmt = FMTDEC316) then
u32 = u32 mod 1000000
elseif (HOTPfmt = FMTDEC317) then
u32 = u32 mod 10000000
elseif (HOTPfmt = FMTDEC318) then
u32 = u32 mod 100000000
elseif (HOTPfmt = FMTDEC319) then
u32 = u32 mod 1000000000
' FMTDEC3110 does not require mod
end if
End Sub ' Truncate
Function CheckPIN(ReadOnly myPIN as String*5) as Byte
if AdminMode = 1 then
CheckPIN = 0 ' success
PINFailCount = 0 ' reset
else
if PINFailCount >= MaxPINFail then
CheckPIN = 2 ' fail
else
if myPIN <> PIN then
CheckPIN = 1 ' fail
PINFailCount = PINFailCount + 1
else
CheckPIN = 0 ' Success
PINFailCount = 0
end if
end if
end if
End Function ' CheckPIN
Function CheckReaderKey(ReadOnly Idx as Byte, _
ReadOnly myKey as String*5) as Byte
if (Asc(HOTPHost(Idx)(2)) AND &H80) then
if myKey <> readerKey then
readerKeyFailCount = readerKeyFailCount + 1
if (readerKeyFailCount >= MAXReaderKeyFail) then
PINFailCount = MaxPINFail ' Lock card
end if
CheckReaderKey = 1 ' Fail
else
readerKeyFailCount = 0
CheckReaderKey = 0 ' Success
end if
else
CheckReaderKey = 0 'Success
end if
End Function ' CheckReaderKey
Function CheckAdmin() as Byte
if AdminMode <> 1 then
CheckAdmin = 1 ' fail
else
CheckAdmin = 0 ' success
end if
End Function ' CheckAdmin
Function CheckIndex(Idx as Byte) as Byte
if Idx > HOTPNum then
CheckIndex = 1
else
CheckIndex = 0
end if
End Function ' CheckIndex
#ifdef ENABLECSETHOST
Command &H80 &H40 SetHost(Idx as Byte, Count as Integer, _
HostName as String*12, HOTPKey as String*20)
if CheckAdmin() <> 0 then
SW1SW2 = swAccessDenied
Exit
end if
if CheckIndex(Idx) <> 0 then
SW1SW2 = swDataNotFound
Exit
End if
' store K
' HOTPKey(n) = K
' store K XOR IPAD
str20 = HOTPKey
tmpb1 = tmpb1 xor &H36363636
tmpb2 = tmpb2 xor &H36363636
tmpb3 = tmpb3 xor &H36363636
tmpb4 = tmpb4 xor &H36363636
tmpb5 = tmpb5 xor &H36363636
HOTPKeyI(Idx) = str20
' store K XOR OPAD
str20 = HOTPKey
tmpb1 = tmpb1 xor &H5C5C5C5C
tmpb2 = tmpb2 xor &H5C5C5C5C
tmpb3 = tmpb3 xor &H5C5C5C5C
tmpb4 = tmpb4 xor &H5C5C5C5C
tmpb5 = tmpb5 xor &H5C5C5C5C
HOTPKeyO(Idx) = str20
HOTPCount32(Idx) = Count
HOTPHost(Idx) = HostName
End Command ' SetHost
#endif 'ENABLECSETHOST
#ifdef ENABLECGETHOST
Command &H80 &H42 GetHost(Idx as Byte, Count as Integer, _
HostName as String*12, HOTPKey as String*20)
if CheckAdmin() <> 0 then
sw1sw2 = swAccessDenied
Exit
end if
if CheckIndex(Idx) <> 0 then
sw1sw2 = swDataNotFound
Exit
end if
Count = HOTPCount32(Idx)
HostName = HOTPHost(Idx)
' load K XOR IPAD
' could also do this with OPAD...
str20 = HOTPKeyI(Idx)
tmpb1 = tmpb1 xor &H36363636
tmpb2 = tmpb2 xor &H36363636
tmpb3 = tmpb3 xor &H36363636
tmpb4 = tmpb4 xor &H36363636
tmpb5 = tmpb5 xor &H36363636
HOTPKey = str20
End Command ' GetHost
#endif 'ENABLECGETHOST
#ifdef ENABLECGETHOSTNAME
Command &H80 &H44 GetHostName(Idx as Byte, myPIN as String*5,_
HostName as String*12)
if CheckPIN(myPIN) <> 0 then
SW1SW2 = swAccessDenied
Exit
end if
if CheckIndex(Idx) <> 0 then
SW1SW2 = swDataNotFound
Exit
end if
HostName = HOTPHost(Idx)
End Command ' GetHostName
#endif 'ENABLECGETHOSTNAME
#ifdef ENABLECGETHOTP
Command &H80 &H46 GetHOTP(Idx as Byte, myPIN as String*5, HOTP as String*5)
Call HOTPCommon(Idx, 0, myPIN, HOTP)
HOTP = HOTPTruncated
End Command ' GetHOTP
#endif 'ENABLECGETHOTP
#ifdef ENABLECSETADMINMODE
Command &H80 &H48 SetAdminMode(Mode as Byte, K as String*20)
if K <> AdminKey then
SW1SW2 = swAccessDenied
else
AdminMode = Mode
end if
End Command ' SetAdminMode
#endif 'ENABLECSETADMINMODE
#ifdef ENABLECSETBALANCECARDINDEX
Command &H80 &H4A SetBalanceCardIndex(Idx as Byte)
if CheckAdmin() <> 0 then
SW1SW2 = swAccessDenied
Exit
else
BalanceCardIndex = Idx
end if
End Command ' SetBalanceCardIndex
#endif 'ENABLECSETBALANCECARDINDEX
#ifdef ENABLECSETPIN
Command &H80 &H4C SetPIN(myPIN as String*5, newPIN as String*5)
if CheckPIN(myPIN) <> 0 then
SW1SW2 = swAccessDenied
else
PIN = newPIN
end if
End Command ' SetPIN
#endif 'ENABLECSETPIN
#ifdef ENABLECTESTPIN
Command &H80 &H4E TestPIN(myPIN as String*5)
private t as Byte
t = CheckPIN(myPIN)
if t <> 0 then
if t = 2 then
SW1SW2 = swBadAuthenticate ' too many tries
else
SW1SW2 = swAccessDenied
end if
end if
End Command ' TestPIN
#endif 'ENABLECTESTPIN
#ifdef ENABLECGETVERSION
Command &H80 &H50 GetVersion(V as Byte)
V = HOTPCodeVersion
End Command ' GetVersion
#endif 'ENABLECGETVERSION
#ifdef ENABLECSETADMINKEY
Command &H80 &H52 SetAdminKey(K as String*20)
if CheckAdmin() <> 0 then
sw1sw2 = swAccessDenied
Exit
end if
AdminKey = K
End Command ' SetAdminKey
#endif 'ENABLECSETADMINKEY
#ifdef ENABLECSETHOST32
Command &H80 &H54 SetHost32(Idx as Byte, Count32 as Long,_
HostName as String*12, HOTPKey as String*20)
if CheckAdmin() <> 0 then
SW1SW2 = swAccessDenied
Exit
end if
if CheckIndex(Idx) <> 0 then
SW1SW2 = swDataNotFound
Exit
End if
HOTPCount32(Idx) = Count32
HOTPHost(Idx) = HostName
' store K
' HOTPKey(n) = K
' store K XOR IPAD
str20 = HOTPKey
tmpb1 = tmpb1 xor &H36363636
tmpb2 = tmpb2 xor &H36363636
tmpb3 = tmpb3 xor &H36363636
tmpb4 = tmpb4 xor &H36363636
tmpb5 = tmpb5 xor &H36363636
HOTPKeyI(Idx) = str20
' store K XOR OPAD
str20 = HOTPKey
tmpb1 = tmpb1 xor &H5C5C5C5C
tmpb2 = tmpb2 xor &H5C5C5C5C
tmpb3 = tmpb3 xor &H5C5C5C5C
tmpb4 = tmpb4 xor &H5C5C5C5C
tmpb5 = tmpb5 xor &H5C5C5C5C
HOTPKeyO(Idx) = str20
End Command ' SetHost32
#endif 'ENABLECSETHOST32
#ifdef ENABLECGETHOST32
Command &H80 &H56 GetHost32(Idx as Byte, Count32 as Long,_
HostName as String*12, HOTPKey as String*20)
if CheckAdmin() <> 0 then
sw1sw2 = swAccessDenied
Exit
end if
if CheckIndex(Idx) <> 0 then
sw1sw2 = swDataNotFound
Exit
end if
Count32 = HOTPCount32(Idx)
HostName = HOTPHost(Idx)
' load K XOR IPAD
' could also do this with OPAD...
str20 = HOTPKeyI(Idx)
tmpb1 = tmpb1 xor &H36363636
tmpb2 = tmpb2 xor &H36363636
tmpb3 = tmpb3 xor &H36363636
tmpb4 = tmpb4 xor &H36363636
tmpb5 = tmpb5 xor &H36363636
HOTPKey = str20
End Command ' GetHost32
#endif 'ENABLECGETHOST32
#ifdef ENABLECGETHOTPCOUNT32
Command &H80 &H58 GetHOTPCount32(Idx as Byte, myPIN as String*5,_
Count32 as Long, HOTP as String*5)
Call HOTPCommon(Idx, Count32, myPIN, HOTP)
HOTP = HOTPTruncated
End Command ' GetHOTPCount32
#endif 'ENABE_GETHOTPCOUNT32
#ifdef ENABLECGETHOTPHOST
Command &H80 &H5A GetHOTPHost(Idx as Byte, myPIN as String*5,_
HOTP as String*5, HostName as String*12)
Call HOTPCommon(Idx, 0, myPIN, HOTP)
HOTP = HOTPTruncated
HostName = HOTPHost(Idx)
End Command ' GetHOTPHost
#endif 'ENABLECGETHOTPHOST
#ifdef ENABLECGETHOTPHOSTCOUNT32
Command &H80 &H5C GetHOTPHostCount32(Idx as Byte, myPIN as String*5,_
Count32 as Long, HOTP as String*5, HostName as String*12)
Call HOTPCommon(Idx, Count32, myPIN, readerKey)
HOTP = HOTPTruncated
HostName = HOTPHost(Idx)
End Command ' GetHOTPHostCount32
#endif 'ENABLECGETHOTPHOSTCOUNT32
#ifdef ENABLECCLEARALL
Command &H80 &H5E ClearAll()
private i,j as Integer
if CheckAdmin() <> 0 then
SW1SW2 = swAccessDenied
Exit
end if
for i = 0 to HOTPNum
HOTPKeyI(i) = Chr$(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
HOTPKeyO(i) = Chr$(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
HOTPCount32(i) = 0
HOTPHost(i) = Chr$(0,0,0,0,0,0,0,0,0,0,0,0)
next i
BalanceCardIndex = 255
PINFailCount = 0
readerKeyFailCount = 0
AdminMode = 1
AdminKey = "00000000000000000000"
PIN = DefaultPIN
End Command ' ClearAll
#endif 'ENABLECCLEARALL
#ifdef ENABLESETREADERKEY
Command &H80 &H60 SetReaderKey(myKey as String*5)
if CheckAdmin() <> 0 then
SW1SW2 = swAccessDenied
Exit
end if
readerKey = myKey
End Command ' SetReaderKey
#endif 'ENABLECSETREADERKEY
Command &H80 &H90 GetCapabilities(C as Long)
C = Capabilities
End Command ' GetCapabilities
'
' The balance reader doesn't wait Long for a reponse so the HMAC must be
' computed in steps
'
#ifdef ENABLECPRDISPLAY
Command &HC8 &H00 PRDisplay(RecordNumber as Byte, DataFormat as Byte, _
DigitCount as Byte,DecimalPoint as Byte, _
Delay as Byte, MoreData as Byte, _
Data as String)
select case RecordNumber
case 0
DataFormat = PRAlpha
DigitCount = 0
DecimalPoint = 0
Delay = 1
MoreData = PRMoreData
' disable admin mode on first use.
if (AdminMode = 1) then
AdminMode = 0
end if
if BalanceCardIndex = 255 then
data = "Not Enabled"
dataFormat = PRAlpha
Exit Command
end if
Count64low = HOTPCount32(BalanceCardIndex)
' Display VER8-count
data = "VER8-" + hex$(Count64low)
' start inner hash of HMAC SHA-160(K XOR ipad,text)
Call ShaStart()
Call ShaAppend(HOTPKeyI(BalanceCardIndex))
case 1
DataFormat = PRAlpha
DigitCount = 0
DecimalPoint = 6
Delay = 1
MoreData = PRMoreData
data = "HOTP"
Count64low = HOTPcount32(BalanceCardIndex)
' inner hash still working
Call ShaAppend(IPAD44)
Call ShaAppend(Count64)
case 2
DataFormat = PRAlpha
DigitCount = 0
DecimalPoint = 5
Delay = 1
MoreData = PRMoreData
Data = "HOTP"
' done with inner hash. Store temp result in eestr20 while
' balance reader resets
eestr20 = ShaEnd()
case 3
DataFormat = PRAlpha
DigitCount = 0
DecimalPoint = 4
Delay = 1
MoreData = PRMoreData
Data = "HOTP"
' start outer hash H(K XOR opad, inner)
Call ShaStart()
Call ShaAppend(HOTPKeyO(BalanceCardIndex))
case 4
DataFormat = PRAlpha
DigitCount = 0
DecimalPoint = 3
Delay = 1
MoreData = PRMoreData
data = "HOTP"
' outer still working.
Call ShaAppend(OPAD44)
case 5
DataFormat = PRAlpha
DigitCount = 0
DecimalPoint = 2
Delay = 1
MoreData = PRMoreData
data = "HOTP"
' outer still working...
Call ShaAppend(eestr20)
' Increment Count just before displaying result.
Disable OverflowCheck
HOTPCount32(BalanceCardIndex) = HOTPCount32(BalanceCardIndex) + 1
Enable OverflowCheck
case 6
DataFormat = PRAlpha
DigitCount = 0
DecimalPoint = 1
Delay = 1
MoreData = PRMoreData
data = "HOTP"
' finish with outer
eestr20 = ShaEnd()
case 7
Delay = 10000 / PRDelayUnits ' display 10 seconds
DecimalPoint = 0
MoreData = PRNoMoreData
str20 = eestr20
' sets HOTPfmt, HOTPTruncated
Call Truncate(BalanceCardIndex)
if (HOTPfmt = 0) or (HOTPfmt = FMTHEX40) or (HOTPfmt = FMTDHEX40) then
DataFormat = PRHex
DigitCount = 10
' data must be 8 Bytes Long. 3 high order Bytes ignored by reader
data = "000" + HOTPTruncated
else
DataFormat = PRNum
DigitCount = 10
' data must be 4 Bytes Long.
data = Left$(HOTPTruncated,4)
end if
case else
' should not happen
SW1SW2=swDataNotFound
end select
End Command ' PRDisplay
#endif ' ENABLECPRDISPLAY