We had yesterday a small video session using whereby and some Smalltalkers diskussed the topic “totp” as second authentification method after an successful login (we were VASmalltalk, VisualWorks and Gemstone related).
To get all work done under Gemstone you need the MSKLibQREncode, the Multibase-Code and one class from the MSK-ModelBaseRuntime package: MSKTOTPSupport.
The MSKTOTPSupport class has three class methods with all stuff needed. Perhaps some methods are defined elsewhere, but you should at least get the ideaa …
totpCurrentValueForSecret: keyArray forTime: aDateAndTime digits: numberOfDigits period: seconds algorithm: aString
” Returns a new value or nil in error case
totpCurrentValueForSecret: keyArray
forTime: aDateAndTime
digits: numberOfDigits
period: seconds
algorithm: aString
"^Integer | nil Returns a new value or nil in error case
MSKTOTPSupport
totpCurrentValueForSecret: 'joachim' asByteArray
forTime: DateAndTime now
digits: 6
period: 30
algorithm: 'sha256'
"
| hmac key offset binaryValue currentTimeIntervalStep byteArray |
currentTimeIntervalStep := ((aDateAndTime asSeconds floor -
(DateAndTimeANSI posixSeconds: 0 offset: (Duration zero) ) asSeconds floor) / seconds) floor .
byteArray := currentTimeIntervalStep asByteArrayOfSize: 8 .
aString = 'md5' ifTrue:[ hmac := byteArray asMd5HmacWithKey: keyArray ].
aString = 'sha1' ifTrue:[ hmac := byteArray asSha1HmacWithKey: keyArray ].
aString = 'sha256' ifTrue:[ hmac := byteArray asSha256HmacWithKey: keyArray ].
aString = 'sha512' ifTrue:[ hmac := byteArray asSha512HmacWithKey: keyArray ].
hmac isNil ifTrue:[ ^nil ].
hmac := hmac asByteArray.
"letztes byte nehmen"
offset := ((hmac at: hmac size) asInteger bitAnd:16r0f) +1.
binaryValue := (((hmac at: offset) bitAnd: 16r7f) bitShift: 24)
+ (((hmac at: offset + 1) bitAnd: 16rff) bitShift: 16)
+ (((hmac at: offset + 2) bitAnd: 16rff) bitShift: 8)
+ (((hmac at: offset + 3) bitAnd: 16rff) ).
^binaryValue \\ (10 raisedTo: numberOfDigits)
totpQRStringForSecret: keyByteArray
label: identification
period: seconds
issuer: issuerString
digits: digits
algorithm: algName
icon: iconURL
color: colorValueStringRRGGBB
"
MSKTOTPSupport
totpQRStringForSecret: 'joachim' asByteArray
label: 'cati'
period: 30
issuer: 'GESSCati'
digits: nil
algorithm: 'sha256'
icon: nil
https://github.com/npmccallum/freeotp-android/blob/master/URI.md
"
| writeStream |
writeStream := WriteStream on: String new.
writeStream
nextPutAll: 'otpauth://totp/' ;
"URI-encoded "
nextPutAll: identification encodeForHTTP ;
nextPutAll: '?secret=' ;
nextPutAll: (MultibaseBase32upper new encode: keyByteArray).
(digits notNil and:[ digits ~= 6 ])
ifTrue:[ writeStream nextPutAll: '&digits=' ; nextPutAll: digits asString ].
(seconds notNil and:[ seconds ~= 30 ])
ifTrue:[ writeStream nextPutAll: '&period=' ; nextPutAll: seconds asString ].
(issuerString pumIsStringAndNotEmpty)
ifTrue:[ writeStream nextPutAll: '&issuer=' ; nextPutAll: issuerString encodeForHTTP ].
(algName pumIsStringAndNotEmpty and:[ algName ~= 'sha1' ])
ifTrue:[ writeStream nextPutAll: '&algorithm=' ; nextPutAll: algName ].
(colorValueStringRRGGBB pumIsStringAndNotEmpty)
ifTrue:[ writeStream nextPutAll: '&color=' ; nextPutAll: colorValueStringRRGGBB ].
(iconURL pumIsStringAndNotEmpty)
ifTrue:[ writeStream nextPutAll: '&image=' ; nextPutAll: iconURL ].
^writeStream contents
totpSecretAsBased32: keyByteArray
"
MSKTOTPSupport totpSecureKeyBased32: 'joachim' asByteArray
"
^MultibaseBase32upper new encode: keyByteArray
So by writing the following code, we could generate a png showing a QR code, which FreeOTP could use:
(MSKLibQRToolOptions newPNGFor: (MSKTOTPSupport totpQRStringForSecret: 'joachim' asByteArray label: 'GessCATI:M.Feldtmann' period: 30 issuer: 'Anonymous' digits: 6 algorithm: 'sha1' icon: 'https://feldtmann.ddns.net/test.png' color: '808080') path: '/var/www/html/Sebastian.png') moduleSize: 15 ; foreground: Color black ; background: Color white ; qrToolCreateQRCode
The system uses the local qrencode tool available under Linux and we got the following QR png file – and you may try your FreeOTP with that result.
Some question still remain when other algorithm have to be used, but this will be solved – and how the remaining other algorithm could be inserted.

Working Model – Server Side
Here is a description of a working model (on the server side) and a description of a potential API to manage TOTP systemwide. Sorry, its in German.
Pingback: TOTP in Smalltalk – a little experiment – Joachims Small World