]> gitweb.factorcode.org Git - factor.git/commitdiff
totp: add new vocab
authorAlexander Iljin <ajsoft@yandex.ru>
Sat, 29 Dec 2018 17:07:35 +0000 (18:07 +0100)
committerJohn Benediktsson <mrjbq7@gmail.com>
Sun, 30 Dec 2018 20:23:17 +0000 (12:23 -0800)
extra/totp/authors.txt [new file with mode: 0644]
extra/totp/summary.txt [new file with mode: 0644]
extra/totp/totp-docs.factor [new file with mode: 0644]
extra/totp/totp-tests.factor [new file with mode: 0644]
extra/totp/totp.factor [new file with mode: 0644]

diff --git a/extra/totp/authors.txt b/extra/totp/authors.txt
new file mode 100644 (file)
index 0000000..8e1955f
--- /dev/null
@@ -0,0 +1 @@
+Alexander Ilin
diff --git a/extra/totp/summary.txt b/extra/totp/summary.txt
new file mode 100644 (file)
index 0000000..3e09424
--- /dev/null
@@ -0,0 +1 @@
+RFC 6238 Time-Based One-Time Passwords
diff --git a/extra/totp/totp-docs.factor b/extra/totp/totp-docs.factor
new file mode 100644 (file)
index 0000000..44c01f6
--- /dev/null
@@ -0,0 +1,71 @@
+! Copyright (C) 2018 Alexander Ilin.
+! See http://factorcode.org/license.txt for BSD license.
+USING: byte-arrays calendar checksums checksums.sha help.markup
+help.syntax kernel math strings ;
+IN: totp
+
+ABOUT: "totp"
+
+ARTICLE: "totp" "Time-Based One-Time Passwords"
+"The " { $vocab-link "totp" } " vocab implements time-based one-time password generation as described in RFC 6238 (" { $url "https://tools.ietf.org/html/rfc6238" } ")."
+$nl
+"The idea is that a client is able to prove its identity to a server by sumbmitting a password that is only valid for a short period of time. The password needs to be sent via a secure channel inside that time period, and client and server must have a shared secret established in advance. The TOTP protocol uses the number of whole 30-second intervals passed in Unix time as a counter value, which it authenticates with an HMAC and converts into a string of " { $link digits } ". Client and server must use the same secret key, the same hash for the HMAC, the same time reference point (not necessarily Unix time) and the same time interval length for the counter. The string of digits used as the password should be long enough to balance convenience and brute-force attack resistance. For 30-second intervals 6 or more digits are typically used."
+$nl
+"Both client and server are able to generate exactly the same digits from the shared secret using their current time as the counter. Server can be programmed to accept values from the adjacent time slots, so that time drift and network delays are compensated for, though that somewhat weakens the system."
+$nl
+"Simple high-level interface:"
+{ $subsections totp-hash totp-digits totp }
+"Customizable implementation:"
+{ $subsections timestamp>count* totp* digits }
+;
+
+HELP: totp-hash
+{ $var-description "A cryptographically secure " { $link checksum } " to be used by " { $link totp } " for the HMAC. See " { $url "https://en.wikipedia.org/wiki/HMAC" } " for more information."
+$nl
+"Default value is " { $link sha-256 } "." } ;
+
+HELP: totp-digits
+{ $var-description "The number of digits returned by " { $link totp } "."
+$nl
+"Default value is 6." } ;
+
+HELP: totp
+{ $values
+    { "key" byte-array }
+    { "string" string }
+}
+{ $description "Generate a one-time password for the " { $snippet "key" } " based on the current system time. The " { $snippet "string" } " length is " { $link totp-digits } ", and the hash used for HMAC is " { $link totp-hash } "." } ;
+
+{ totp totp* } related-words
+
+HELP: timestamp>count
+{ $values
+    { "timestamp" timestamp }
+    { "count" byte-array }
+}
+{ $description "Return the number of 30-second intervals between the Unix time and the " { $snippet "timestamp" } " as an 8-byte " { $link byte-array } "." } ;
+
+HELP: timestamp>count*
+{ $values
+    { "timestamp" timestamp } { "secs/count" number }
+    { "count" byte-array }
+}
+{ $description "Return the number of " { $snippet "secs/count" } "-second intervals between the Unix time and the " { $snippet "timestamp" } " as an 8-byte " { $link byte-array } "." } ;
+
+{ timestamp>count timestamp>count* } related-words
+
+HELP: totp*
+{ $values
+    { "count" "a number of time intervals elapsed since a fixed time point" }
+    { "key" "a secret key shared between the client and the server" }
+    { "hash" checksum }
+    { "n" fixnum }
+}
+{ $description "This is a fully customizable version of " { $link totp } ". To convert a " { $link timestamp } " into the " { $snippet "count" } " value, use " { $link timestamp>count } ". " { $snippet "n" } " is a positive 31-bit integer. To convert the returned value into a string of a predetermined length, use " { $link digits } "." } ;
+
+HELP: digits
+{ $values
+    { "n" number } { "digits" number }
+    { "string" string }
+}
+{ $description "Convert integer " { $snippet "n" } " into a decimal string of length " { $snippet "digits" } ", padding with zeroes on the left if necessary. If the string representation of " { $snippet "n" } " is longer than " { $snippet "digits" } ", return the rightmost part of the requested length." } ;
diff --git a/extra/totp/totp-tests.factor b/extra/totp/totp-tests.factor
new file mode 100644 (file)
index 0000000..cde32f9
--- /dev/null
@@ -0,0 +1,45 @@
+! Copyright (C) 2018 Alexander Ilin.
+! See http://factorcode.org/license.txt for BSD license.
+USING: calendar checksums.sha tools.test totp ;
+IN: totp.tests
+
+CONSTANT: sha1-seed B{
+    49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57 48
+}
+CONSTANT: sha256-seed B{
+    49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57 48
+    49 50 51 52 53 54 55 56 57 48 49 50
+}
+CONSTANT: sha512-seed B{
+    49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57 48
+    49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57 48
+    49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57 48
+    49 50 51 52
+}
+
+: test-time ( n -- totp-time )
+    seconds unix-1970 time+ timestamp>count ;
+
+{ "94287082" } [ 59 test-time sha1-seed   sha1    totp* 8 digits ] unit-test
+{ "46119246" } [ 59 test-time sha256-seed sha-256 totp* 8 digits ] unit-test
+! { "90693936" } [ 59 test-time sha512-seed sha-512 totp* 8 digits ] unit-test
+
+{ "07081804" } [ 1111111109 test-time sha1-seed   sha1    totp* 8 digits ] unit-test
+{ "68084774" } [ 1111111109 test-time sha256-seed sha-256 totp* 8 digits ] unit-test
+! { "25091201" } [ 1111111109 test-time sha512-seed sha-512 totp* 8 digits ] unit-test
+
+{ "14050471" } [ 1111111111 test-time sha1-seed   sha1    totp* 8 digits ] unit-test
+{ "67062674" } [ 1111111111 test-time sha256-seed sha-256 totp* 8 digits ] unit-test
+! { "99943326" } [ 1111111111 test-time sha512-seed sha-512 totp* 8 digits ] unit-test
+
+{ "89005924" } [ 1234567890 test-time sha1-seed   sha1    totp* 8 digits ] unit-test
+{ "91819424" } [ 1234567890 test-time sha256-seed sha-256 totp* 8 digits ] unit-test
+! { "93441116" } [ 1234567890 test-time sha512-seed sha-512 totp* 8 digits ] unit-test
+
+{ "69279037" } [ 2000000000 test-time sha1-seed   sha1    totp* 8 digits ] unit-test
+{ "90698825" } [ 2000000000 test-time sha256-seed sha-256 totp* 8 digits ] unit-test
+! { "38618901" } [ 2000000000 test-time sha512-seed sha-512 totp* 8 digits ] unit-test
+
+{ "65353130" } [ 20000000000 test-time sha1-seed   sha1    totp* 8 digits ] unit-test
+{ "77737706" } [ 20000000000 test-time sha256-seed sha-256 totp* 8 digits ] unit-test
+! { "47863826" } [ 20000000000 test-time sha512-seed sha-512 totp* 8 digits ] unit-test
diff --git a/extra/totp/totp.factor b/extra/totp/totp.factor
new file mode 100644 (file)
index 0000000..6e00a2d
--- /dev/null
@@ -0,0 +1,31 @@
+! Copyright (C) 2018 Alexander Ilin.
+! See http://factorcode.org/license.txt for BSD license.
+USING: calendar checksums.hmac checksums.sha io.binary kernel
+math math.bitwise math.parser namespaces sequences ;
+IN: totp
+
+SYMBOLS: totp-hash totp-digits ;
+totp-hash [ sha-256 ] initialize
+totp-digits [ 6 ] initialize
+
+<PRIVATE
+
+: totp-value ( hash-bytes -- n )
+    [ last 4 bits dup 4 + ] keep <slice> be> 31 clear-bit ;
+
+PRIVATE>
+
+: timestamp>count* ( timestamp secs/count -- count )
+    [ timestamp>unix-time ] dip /i 8 >be ; inline
+
+: timestamp>count ( timestamp -- count )
+    30 timestamp>count* ;
+
+: totp* ( count key hash -- n )
+    hmac-bytes totp-value ;
+
+: digits ( n digits -- string )
+    [ number>string ] dip [ CHAR: 0 pad-head ] keep tail* ;
+
+: totp ( key -- string )
+    now timestamp>count swap totp-hash get totp* totp-digits get digits ;