NEM ChaCha20 is a stream cipher designed by Daniel J. Bernstein . It is relatively widely used as an alternative to AES and has been included in various IETF standards, notably in TLS since 1.2.
ChaCha is itself a variant on the earlier Salsa20 stream cipher by the same author. Compared to AES it is relatively easy to implement efficiently in software and make robust against timing side-channel attacks.
Here is a pure-Tcl implementation of ChaCha20, along with the XChaCha20 extended nonce variant that is implemented in libsodium and has recently been proposed for standardisation. ChaCha20 takes a 12-byte (96-bit) nonce, so is most suitable for a message counter or other deterministic construction. XChaCha20 takes a 24-byte (192-bit) nonce, and so can safely be generated randomly to encrypt an effectively unlimited number of messages with a single key. I would recommend using XChaCha20 in most cases unless you need to interoperate, as random nonces are much simpler to implement securely.
NOTE: This is the raw stream cipher, which is completely unauthenticated. It is trivial to make undetectable changes to an encrypted ciphertext. I would recommend that you apply HMAC-SHA256 or similar to the encrypted ciphertext (with a separate key) to prevent against tampering. I will try and implement the Poly1305 authenticator shortly.
NOTE 2: The cipher is designed to work with 32-bit unsigned integers. As Tcl’s integers are unlimited precision, there may be timing side-channels still possible in this code (in particular add32 may not be constant time, I need to do some checking there). As such I would recommend only using this code in offline/batch applications for now.
The code is followed by some test vectors, demonstrating usage. Only the ensemble commands are intended to be public API.
# chacha.tcl -- # # Implementation of the ChaCha20 and XChaCha20 stream ciphers. # # Copyright (c) 2018 Neil Madden. # License: Tcl-style package require Tcl 8.6 package provide chacha20 1.0.0 # chacha20 encrypt key plaintext ?options...? # chacha20 decrypt key ciphertext ?options...? # # Encrypts (resp. decrypts) a message using the ChaCha20 cipher as described in # RFC 8439. # namespace eval ::chacha20 { namespace export encrypt decrypt namespace ensemble create variable INT32 [expr {2**32 - 1}] variable RAND_IN "" # Bitwise left rotation proc rotl {i distance} { variable INT32 expr {(($i << $distance) & $INT32) | (($i & $INT32) >> (32-$distance))} } # Addition mod 2^32 proc add32 {a b} { variable INT32 expr {($a + $b) & $INT32} } # Bitwise exclusive or proc xor {a b} { expr {$a ^ $b} } # quarterRound stateRef a b c d -- # # The chacha20 quarter-round (i.e., 4 of these make a full round). # stateRef is the name of a variable containing the chacha20 state (16 32-bit integers) # a b c d are the 4 indices to operate on # The state is mutated in-place proc quarterRound {stateRef a b c d} { variable INT32 upvar 1 $stateRef state lset state $a [add32 [lindex $state $a] [lindex $state $b]] lset state $d [rotl [xor [lindex $state $d] [lindex $state $a]] 16] lset state $c [add32 [lindex $state $c] [lindex $state $d]] lset state $b [rotl [xor [lindex $state $b] [lindex $state $c]] 12] lset state $a [add32 [lindex $state $a] [lindex $state $b]] lset state $d [rotl [xor [lindex $state $d] [lindex $state $a]] 8] lset state $c [add32 [lindex $state $c] [lindex $state $d]] lset state $b [rotl [xor [lindex $state $b] [lindex $state $c]] 7] } # rounds state ?numRounds? -- # # Applies the given number of rounds (default: 20) of the chacha20 block function # to the given state, returning the new state. # proc rounds {state {numRounds 20}} { for {set i 0} {$i < ($numRounds / 2)} {incr i} { quarterRound state 0 4 8 12 quarterRound state 1 5 9 13 quarterRound state 2 6 10 14 quarterRound state 3 7 11 15 quarterRound state 0 5 10 15 quarterRound state 1 6 11 12 quarterRound state 2 7 8 13 quarterRound state 3 4 9 14 } return $state } # Computes the chacha20 initial state for the given key, nonce and block counter proc initialState {key blockCounter nonce} { binary scan $key i8 keyInts binary scan $nonce i3 nonceInts # Magic constants set state [list 0x61707865 0x3320646e 0x79622d32 0x6b206574] lappend state {*}$keyInts lappend state $blockCounter lappend state {*}$nonceInts lmap x $state { expr $x } } # The chacha20 block function. This applies $numRounds rounds of the chacha20 round # function to the initial state and then adds back in the initial state using 32-bit # modular addition. proc block {initialState counter {numRounds 20}} { lset initialState 12 $counter #puts "initial state: [hex $initialState]" set state [rounds $initialState $numRounds] #puts "After 20 rounds: [hex $state]" lmap x $initialState y $state { add32 $x $y } } # rand numBytes -- # # Reads $numBytes from /dev/urandom. NB: this will fail on Windows. # proc rand {numBytes} { variable RAND_IN if {$RAND_IN eq "" || $RAND_IN ni [chan names]} { set RAND_IN [open /dev/urandom rb] } set data [read $RAND_IN $numBytes] return $data } # encrypt key plaintext ?options...? -- # # Encrypts the plaintext under the given key and nonce, returning the ciphertext. # # Parameters: # key - the 32-byte binary key. # plaintext - the plaintext message to encrypt. # Options: # -counter counter - the initial block counter. Defaults to 0. # -nonce - the 12-byte binary nonce. MUST be unique for every invocation for the same key. # Defaults to a random value (note: this places a safe limit of around 2^32 invocations with # the same key). # -rounds numRounds - the number of rounds to apply. Defaults to 20. The eStream project # recommended 12 rounds for speed, and you could go as low as 8, but everybody # uses 20 rounds for security reasons. ChaCha20 is very fast anyway. # Returns: # The encrypted ciphertext, of exactly the same length as the plaintext. # proc encrypt {key plaintext args} { array set options { -counter 0 -rounds 20 -nonce "" } array set options $args set counter $options(-counter) unset options(-counter) set rounds $options(-rounds) unset options(-rounds) set nonce $options(-nonce) unset options(-nonce) if {[llength [array names options]] != 0} { error "unknown option(s): [array names options]" } if {$nonce eq ""} { set nonce [rand 12] } if {[string length $key] != 32} { error "key must be exactly 32 bytes (binary format)" } if {[string length $nonce] != 12} { error "nonce must be exactly 12 bytes (binary format)" } if {$counter < 0 || $counter > (2**32-1)} { error "counter must be in range 0..2^32-1" } set len [string length $plaintext] set numBlocks [expr {$len/64 + 1}] set state [initialState $key $counter $nonce] set ciphertext "" for {set i 0} {$i < $numBlocks} {incr i} { set start [expr {$i * 64}] set end [expr {min($start + 63, $len)}] #puts "$start..$end" binary scan [string range $plaintext $start $end] c* pt #puts "Block $i plaintext: [hex $pt]" binary scan [binary format i* [block $state [expr {$counter + $i}] $rounds]] c* keyStream #puts "Block $i keystream: [hex $keyStream]" set xored [list] for {set j 0} {$j < [llength $pt]} {incr j} { lappend xored [expr {[lindex $keyStream $j] ^ [lindex $pt $j]}] } #puts "Block $i ciphered : [hex $xored]" append ciphertext [binary format c* $xored] } return $ciphertext } # decrypt key nonce ciphertext ?options...? -- # # Decrypts the ciphertext using the given key and nonce, returning the original plaintext. # NB: As ChaCha20 is a stream cipher, this is the same operation as encryption. # # Parameters: # key - the 32-byte binary key. # ciphertext - the encrypted ciphertext message to decrypt. # Options: # -counter counter - the initial block counter. Defaults to 0. # -nonce - the 12-byte binary nonce. MUST be unique for every invocation for the same key. # Defaults to a random value (note: this places a safe limit of around 2^32 invocations with # the same key). # -rounds numRounds - the number of rounds to perform. Defaults to 20. # Returns: # The decrypted plaintext message. # proc decrypt {key ciphertext args} { # chacha20 is a stream cipher so encryption == decryption encrypt $key $ciphertext {*}$args } # hex-dump a list of bytes, for debugging proc hex values { lmap x $values { format "%02x" [expr {$x & 0xFF}] } } } # hchacha20 core key input -- # # Hashes the given input under the given key. This is an extremely fast pseudorandom function (PRF) # built on top of the chacha20 core round function. It is suitable as a key deriviation function (KDF), # as used in XChaCha. Other uses are not recommended (use HMAC if you need a general purpose keyed hash). # namespace eval ::hchacha20 { namespace export core namespace ensemble create proc core {key input} { if {[string length $key] != 32} { error "key must be exactly 32 bytes (binary format)" } if {[string length $input] != 16} { error "input must be exactly 16 bytes" } binary scan $input i counter set state [chacha20::initialState $key $counter [string range $input 4 end]] #puts [lmap x $state { format %08x [expr {$x & 0xFFFFFFFF}] }] set state [chacha20::rounds $state][unset state] #puts [lmap x $state { format %08x [expr {$x & 0xFFFFFFFF}] }] binary format i8 [lreplace $state 4 11] } } # xchacha20 encrypt key plaintext ?options...? # xchacha20 decrypt key ciphertext ?options...? # # The Xchacha20 "extended nonce" stream cipher. This is a variant of chacha20 that accepts # a 24-byte nonce rather than 12 bytes. It is therefore suitable for using randomly generated # nonce values, rather than a simple counter or other deterministic nonce. This is useful in # situations where it is hard to maintain state for the nonce, for instance when a cluster of # servers are sharing the same key. The arguments and options are the same as for ChaCha, except # that a 24-byte nonce is used instead of a 12-byte one. # namespace eval ::xchacha20 { namespace export encrypt decrypt namespace ensemble create proc encrypt {key plaintext args} { array set options { -nonce "" } array set options $args set nonce $options(-nonce) unset options(-nonce) if {$nonce eq ""} { set nonce [rand 24] } if {[string length $key] != 32} { error "key must be 32 bytes" } if {[string length $nonce] != 24} { error "nonce must be 24 bytes" } set subKey [hchacha20 core $key [string range $nonce 0 15]] set subNonce \x00\x00\x00\x00[string range $nonce 16 end] chacha20 encrypt $subKey $plaintext -nonce $subNonce {*}[array get options] } proc decrypt {key ciphertext args} { encrypt $key $ciphertext {*}$args } } ##### TESTS ##### if {![info exists argv0] || [file tail [info script]] ne [file tail $argv0]} { # Not running as main script so return to avoid running tests below return } proc fromHex hex { regsub -all {\s+} $hex {} hex regsub -all {:} $hex {} hex regsub -all {..} $hex {\x\0} hex subst $hex } proc assertEqual {a b {msg ""}} { if {$a ne $b} { puts "FAIL - assertion failed: expecting '[binary encode hex $a]' to equal '[binary encode hex $b]' $msg" } else { puts "PASS - $msg" } } # Test vectors from RFC 8439 set key [fromHex {00:01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f}] set nonce [fromHex {00:00:00:09:00:00:00:4a:00:00:00:00}] set counter 1 set initialState [chacha20::initialState $key $counter $nonce] assertEqual $initialState [lmap x { 61707865 3320646e 79622d32 6b206574 03020100 07060504 0b0a0908 0f0e0d0c 13121110 17161514 1b1a1918 1f1e1d1c 00000001 09000000 4a000000 00000000 } { expr 0x$x }] "initial state" assertEqual [chacha20::rounds $initialState] [lmap x { 837778ab e238d763 a67ae21e 5950bb2f c4f2d0c7 fc62bb2f 8fa018fc 3f5ec7b7 335271c2 f29489f3 eabda8fc 82e46ebd d19c12b4 b04e16de 9e83d0cb 4e3c50a2 } { expr 0x$x }] "after 20 rounds" assertEqual [chacha20::block [chacha20::initialState $key $counter $nonce] $counter] [lmap x { e4e7f110 15593bd1 1fdd0f50 c47120a3 c7f4d1c7 0368c033 9aaa2204 4e6cd4c3 466482d2 09aa9f07 05d7c214 a2028bd9 d19c12b5 b94e16de e883d0cb 4e3c50a2 } { expr 0x$x }] "block function" set message "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it." set key [fromHex {00:01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f}] set nonce [fromHex {00:00:00:00:00:00:00:4a:00:00:00:00}] set counter 1 assertEqual [chacha20 encrypt $key $message -nonce $nonce -counter $counter] [fromHex { 6e 2e 35 9a 25 68 f9 80 41 ba 07 28 dd 0d 69 81 e9 7e 7a ec 1d 43 60 c2 0a 27 af cc fd 9f ae 0b f9 1b 65 c5 52 47 33 ab 8f 59 3d ab cd 62 b3 57 16 39 d6 24 e6 51 52 ab 8f 53 0c 35 9f 08 61 d8 07 ca 0d bf 50 0d 6a 61 56 a3 8e 08 8a 22 b6 5e 52 bc 51 4d 16 cc f8 06 81 8c e9 1a b7 79 37 36 5a f9 0b bf 74 a3 5b e6 b4 0b 8e ed f2 78 5e 42 87 4d }] "encrypt" assertEqual [chacha20 decrypt $key [chacha20 encrypt $key $message -nonce $nonce -counter $counter] -nonce $nonce -counter $counter] $message "roundtrip" # Additional test vectors from RFC 8439 Appendix A set key [string repeat \x00 32] set nonce [string repeat \x00 12] assertEqual [chacha20::block [chacha20::initialState $key 0 $nonce] 0] [lmap x { ade0b876 903df1a0 e56a5d40 28bd8653 b819d2bd 1aed8da0 ccef36a8 c70d778b 7c5941da 8d485751 3fe02477 374ad8b8 f4b8436a 1ca11815 69b687c3 8665eeb2 } { expr 0x$x }] "block function - additional test vector 1" assertEqual [chacha20::block [chacha20::initialState $key 1 $nonce] 1] [lmap x { bee7079f 7a385155 7c97ba98 0d082d73 a0290fcb 6965e348 3e53c612 ed7aee32 7621b729 434ee69c b03371d5 d539d874 281fed31 45fb0a51 1f0ae1ac 6f4d794b } { expr 0x$x }] "block function - additional test vector 2" set key [string repeat \x00 31]\x01 assertEqual [chacha20::block [chacha20::initialState $key 1 $nonce] 1] [lmap x { 2452eb3a 9249f8ec 8d829d9b ddd4ceb1 e8252083 60818b01 f38422b8 5aaa49c9 bb00ca8e da3ba7b4 c4b592d1 fdf2732f 4436274e 2561b3c8 ebdd4aa6 a0136c00 } { expr 0x$x }] "block function - additional test vector 3" set key \x00\xff[string repeat \x00 30] assertEqual [chacha20::block [chacha20::initialState $key 2 $nonce] 2] [lmap x { fb4dd572 4bc42ef1 df922636 327f1394 a78dea8f 5e269039 a1bebbc1 caf09aae a25ab213 48a6b46c 1b9d9bcb 092c5be6 546ca624 1bec45d5 87f47473 96f0992e } { expr 0x$x }] "block function - additional test vector 4" set key [string repeat \x00 32] set nonce [string repeat \x00 11]\x02 assertEqual [chacha20::block [chacha20::initialState $key 0 $nonce] 0] [lmap x { 374dc6c2 3736d58c b904e24a cd3f93ef 88228b1a 96a4dfb3 5b76ab72 c727ee54 0e0e978a f3145c95 1b748ea8 f786c297 99c28f5f 628314e8 398a19fa 6ded1b53 } { expr 0x$x }] "block function - additional test vector 5" set key [string repeat \x00 32] set nonce [string repeat \x00 12] set plaintext [string repeat \x00 64] assertEqual [chacha20 encrypt $key $plaintext -nonce $nonce] [fromHex { 76 b8 e0 ad a0 f1 3d 90 40 5d 6a e5 53 86 bd 28 bd d2 19 b8 a0 8d ed 1a a8 36 ef cc 8b 77 0d c7 da 41 59 7c 51 57 48 8d 77 24 e0 3f b8 d8 4a 37 6a 43 b8 f4 15 18 a1 1c c3 87 b6 69 b2 ee 65 86 }] "encryption - additional test vector 1" set key [string repeat \x00 31]\x01 set nonce [string repeat \x00 11]\x02 set plaintext [string trim [regsub -all {\s+} { Any submission to the IETF intended by the Contributor for publication as all or part of an IETF Internet-Draft or RFC and any statement made within the context of an IETF activity is considered an "IETF Contribution". Such statements include oral statements in IETF sessions, as well as written and electronic communications made at any time or place, which are addressed to} { }]] assertEqual [chacha20 encrypt $key $plaintext -nonce $nonce -counter 1] [fromHex { a3 fb f0 7d f3 fa 2f de 4f 37 6c a2 3e 82 73 70 41 60 5d 9f 4f 4f 57 bd 8c ff 2c 1d 4b 79 55 ec 2a 97 94 8b d3 72 29 15 c8 f3 d3 37 f7 d3 70 05 0e 9e 96 d6 47 b7 c3 9f 56 e0 31 ca 5e b6 25 0d 40 42 e0 27 85 ec ec fa 4b 4b b5 e8 ea d0 44 0e 20 b6 e8 db 09 d8 81 a7 c6 13 2f 42 0e 52 79 50 42 bd fa 77 73 d8 a9 05 14 47 b3 29 1c e1 41 1c 68 04 65 55 2a a6 c4 05 b7 76 4d 5e 87 be a8 5a d0 0f 84 49 ed 8f 72 d0 d6 62 ab 05 26 91 ca 66 42 4b c8 6d 2d f8 0e a4 1f 43 ab f9 37 d3 25 9d c4 b2 d0 df b4 8a 6c 91 39 dd d7 f7 69 66 e9 28 e6 35 55 3b a7 6c 5c 87 9d 7b 35 d4 9e b2 e6 2b 08 71 cd ac 63 89 39 e2 5e 8a 1e 0e f9 d5 28 0f a8 ca 32 8b 35 1c 3c 76 59 89 cb cf 3d aa 8b 6c cc 3a af 9f 39 79 c9 2b 37 20 fc 88 dc 95 ed 84 a1 be 05 9c 64 99 b9 fd a2 36 e7 e8 18 b0 4b 0b c3 9c 1e 87 6b 19 3b fe 55 69 75 3f 88 12 8c c0 8a aa 9b 63 d1 a1 6f 80 ef 25 54 d7 18 9c 41 1f 58 69 ca 52 c5 b8 3f a3 6f f2 16 b9 c1 d3 00 62 be bc fd 2d c5 bc e0 91 19 34 fd a7 9a 86 f6 e6 98 ce d7 59 c3 ff 9b 64 77 33 8f 3d a4 f9 cd 85 14 ea 99 82 cc af b3 41 b2 38 4d d9 02 f3 d1 ab 7a c6 1d d2 9c 6f 21 ba 5b 86 2f 37 30 e3 7c fd c4 fd 80 6c 22 f2 21 }] "encryption - additional test vector 2" set key [fromHex {1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0 47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0}] set nonce [string repeat \x00 11]\x02 set plaintext [fromHex { 27 54 77 61 73 20 62 72 69 6c 6c 69 67 2c 20 61 6e 64 20 74 68 65 20 73 6c 69 74 68 79 20 74 6f 76 65 73 0a 44 69 64 20 67 79 72 65 20 61 6e 64 20 67 69 6d 62 6c 65 20 69 6e 20 74 68 65 20 77 61 62 65 3a 0a 41 6c 6c 20 6d 69 6d 73 79 20 77 65 72 65 20 74 68 65 20 62 6f 72 6f 67 6f 76 65 73 2c 0a 41 6e 64 20 74 68 65 20 6d 6f 6d 65 20 72 61 74 68 73 20 6f 75 74 67 72 61 62 65 2e }] assertEqual [chacha20 encrypt $key $plaintext -nonce $nonce -counter 42] [fromHex { 62 e6 34 7f 95 ed 87 a4 5f fa e7 42 6f 27 a1 df 5f b6 91 10 04 4c 0d 73 11 8e ff a9 5b 01 e5 cf 16 6d 3d f2 d7 21 ca f9 b2 1e 5f b1 4c 61 68 71 fd 84 c5 4f 9d 65 b2 83 19 6c 7f e4 f6 05 53 eb f3 9c 64 02 c4 22 34 e3 2a 35 6b 3e 76 43 12 a6 1a 55 32 05 57 16 ea d6 96 25 68 f8 7d 3f 3f 77 04 c6 a8 d1 bc d1 bf 4d 50 d6 15 4b 6d a7 31 b1 87 b5 8d fd 72 8a fa 36 75 7a 79 7a c1 88 d1 }] "encryption - additional test vector 3" # Hchacha20 core - libsodium test vectors foreach {key input expected} { 24f11cce8a1b3d61e441561a696c1c1b7e173d084fd4812425435a8896a013dc d9660c5900ae19ddad28d6e06e45fe5e 5966b3eec3bff1189f831f06afe4d4e3be97fa9235ec8c20d08acfbbb4e851e3 80a5f6272031e18bb9bcd84f3385da65e7731b7039f13f5e3d475364cd4d42f7 c0eccc384b44c88e92c57eb2d5ca4dfa 6ed11741f724009a640a44fce7320954c46e18e0d7ae063bdbc8d7cf372709df cb1fc686c0eec11a89438b6f4013bf110e7171dace3297f3a657a309b3199629 fcd49b93e5f8f299227e64d40dc864a3 84b7e96937a1a0a406bb7162eeaad34308d49de60fd2f7ec9dc6a79cbab2ca34 6640f4d80af5496ca1bc2cfff1fefbe99638dbceaabd7d0ade118999d45f053d 31f59ceeeafdbfe8cae7914caeba90d6 9af4697d2f5574a44834a2c2ae1a0505af9f5d869dbe381a994a18eb374c36a0 0693ff36d971225a44ac92c092c60b399e672e4cc5aafd5e31426f123787ac27 3a6293da061da405db45be1731d5fc4d f87b38609142c01095bfc425573bb3c698f9ae866b7e4216840b9c4caf3b0865 809539bd2639a23bf83578700f055f313561c7785a4a19fc9114086915eee551 780c65d6a3318e479c02141d3f0b3918 902ea8ce4680c09395ce71874d242f84274243a156938aaa2dd37ac5be382b42 1a170ddf25a4fd69b648926e6d794e73408805835c64b2c70efddd8cd1c56ce0 05dbee10de87eb0c5acb2b66ebbe67d3 a4e20b634c77d7db908d387b48ec2b370059db916e8ea7716dc07238532d5981 3b354e4bb69b5b4a1126f509e84cad49f18c9f5f29f0be0c821316a6986e15a6 d8a89af02f4b8b2901d8321796388b6c 9816cb1a5b61993735a4b161b51ed2265b696e7ded5309c229a5a99f53534fbc 4b9a818892e15a530db50dd2832e95ee192e5ed6afffb408bd624a0c4e12a081 a9079c551de70501be0286d1bc78b045 ebc5224cf41ea97473683b6c2f38a084bf6e1feaaeff62676db59d5b719d999b c49758f00003714c38f1d4972bde57ee8271f543b91e07ebce56b554eb7fa6a7 31f0204e10cf4f2035f9e62bb5ba7303 0dd8cc400f702d2c06ed920be52048a287076b86480ae273c6d568a2e9e7518c } { assertEqual $expected [binary encode hex [hchacha20 core [binary decode hex $key] [binary decode hex $input]]] "hchacha20 core" } # XChaCha20 - libsodium test vectors foreach {key nonce expected} { 79c99798ac67300bbb2704c95c341e3245f3dcb21761b98e52ff45b24f304fc4 b33ffd3096479bcfbc9aee49417688a0a2554f8d95389419 c6e9758160083ac604ef90e712ce6e75d7797590744e0cf060f013739c ddf7784fee099612c40700862189d0397fcc4cc4b3cc02b5456b3a97d1186173 a9a04491e7bf00c3ca91ac7c2d38a777d88993a7047dfcc4 2f289d371f6f0abc3cb60d11d9b7b29adf6bc5ad843e8493e928448d 3d12800e7b014e88d68a73f0a95b04b435719936feba60473f02a9e61ae60682 56bed2599eac99fb27ebf4ffcb770a64772dec4d5849ea2d a2c3c1406f33c054a92760a8e0666b84f84fa3a618f0 5f5763ff9a30c95da5c9f2a8dfd7cc6efd9dfb431812c075aa3e4f32e04f53e4 a5fa890efa3b9a034d377926ce0e08ee6d7faccaee41b771 8a1a5ba898bdbcff602b1036e469a18a5e45789d0e8d9837d81a2388a52b0b6a0f51891528f424c4a7f492a8dd7bce8bac19fbdbe1fb379ac0 eadc0e27f77113b5241f8ca9d6f9a5e7f09eee68d8a5cf30700563bf01060b4e a171a4ef3fde7c4794c5b86170dc5a099b478f1b852f7b64 23839f61795c3cdbcee2c749a92543baeeea3cbb721402aa42e6cae140447575f2916c5d71108e3b13357eaf86f060cb 91319c9545c7c804ba6b712e22294c386fe31c4ff3d278827637b959d3dbaab2 410e854b2a911f174aaf1a56540fc3855851f41c65967a4e cbe7d24177119b7fdfa8b06ee04dade4256ba7d35ffda6b89f014e479faef6 6a6d3f412fc86c4450fc31f89f64ed46baa3256ffcf8616e8c23a06c422842b6 6b7773fce3c2546a5db4829f53a9165f41b08faae2fb72d5 8b23e35b3cdd5f3f75525fc37960ec2b68918e8c046d8a832b9838f1546be662e54feb1203e2 d45e56368ebc7ba9be7c55cfd2da0feb633c1d86cab67cd5627514fd20c2b391 fd37da2db31e0c738754463edadc7dafb0833bd45da497fc 47950efa8217e3dec437454bd6b6a80a287e2570f0a48b3fa1ea3eb868be3d486f6516606d85e5643becc473b370871ab9ef8e2a728f73b92bd98e6e26ea7c8ff96ec5a9e8de95e1eee9300c aface41a64a9a40cbc604d42bd363523bd762eb717f3e08fe2e0b4611eb4dcf3 6906e0383b895ab9f1cf3803f42f27c79ad47b681c552c63 a5fa7c0190792ee17675d52ad7570f1fb0892239c76d6e802c26b5b3544d13151e67513b8aaa1ac5af2d7fd0d5e4216964324838 9d23bd4149cb979ccf3c5c94dd217e9808cb0e50cd0f67812235eaaf601d6232 c047548266b7c370d33566a2425cbf30d82d1eaf5294109e a21209096594de8c5667b1d13ad93f744106d054df210e4782cd396fec692d3515a20bf351eec011a92c367888bc464c32f0807acd6c203a247e0db854148468e9f96bee4cf718d68d5f637cbd5a376457788e6fae90fc31097cfc } { set plaintext [binary decode hex [string repeat 0 [string length $expected]]] assertEqual $expected [binary encode hex [xchacha20 encrypt [binary decode hex $key] $plaintext -nonce [binary decode hex $nonce]]] "xchacha20" } # vim: ft=tcl