ARC4 vector creation¶
This page documents the code that was used to generate the ARC4 test vectors for key lengths not available in RFC 6229. All the vectors were generated using OpenSSL and verified with Go.
Creation¶
cryptography
was modified to support ARC4 key lengths not listed
in RFC 6229. Then the following Python script was run to generate the
vector files.
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import binascii
from cryptography.hazmat.primitives import ciphers
from cryptography.hazmat.primitives.ciphers import algorithms
_RFC6229_KEY_MATERIALS = [
(
True,
8 * "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
),
(
False,
8 * "1ada31d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a",
),
]
_RFC6229_OFFSETS = [
0,
16,
240,
256,
496,
512,
752,
768,
1008,
1024,
1520,
1536,
2032,
2048,
3056,
3072,
4080,
4096,
]
_SIZES_TO_GENERATE = [160]
def _key_for_size(size, keyinfo):
msb, key = keyinfo
if msb:
return key[: size // 4]
else:
return key[-size // 4 :]
def _build_vectors():
count = 0
output = []
key = None
plaintext = binascii.unhexlify(32 * "0")
for size in _SIZES_TO_GENERATE:
for keyinfo in _RFC6229_KEY_MATERIALS:
key = _key_for_size(size, keyinfo)
cipher = ciphers.Cipher(
algorithms.ARC4(binascii.unhexlify(key)),
None,
)
encryptor = cipher.encryptor()
current_offset = 0
for offset in _RFC6229_OFFSETS:
if offset % 16 != 0:
raise ValueError(
"Offset {} is not evenly divisible by 16".format(
offset
)
)
while current_offset < offset:
encryptor.update(plaintext)
current_offset += len(plaintext)
output.append("\nCOUNT = {}".format(count))
count += 1
output.append("KEY = {}".format(key))
output.append("OFFSET = {}".format(offset))
output.append(
"PLAINTEXT = {}".format(binascii.hexlify(plaintext))
)
output.append(
"CIPHERTEXT = {}".format(
binascii.hexlify(encryptor.update(plaintext))
)
)
current_offset += len(plaintext)
assert not encryptor.finalize()
return "\n".join(output)
def _write_file(data, filename):
with open(filename, "w") as f:
f.write(data)
if __name__ == "__main__":
_write_file(_build_vectors(), "arc4.txt")
Download link: generate_arc4.py
Verification¶
The following Go code was used to verify the vectors.
package main
import (
"bufio"
"bytes"
"crypto/rc4"
"encoding/hex"
"fmt"
"os"
"strconv"
"strings"
)
func unhexlify(s string) []byte {
bytes, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return bytes
}
type vectorArgs struct {
count string
offset uint64
key string
plaintext string
ciphertext string
}
type vectorVerifier interface {
validate(count string, offset uint64, key, plaintext, expectedCiphertext []byte)
}
type arc4Verifier struct{}
func (o arc4Verifier) validate(count string, offset uint64, key, plaintext, expectedCiphertext []byte) {
if offset%16 != 0 || len(plaintext) != 16 || len(expectedCiphertext) != 16 {
panic(fmt.Errorf("Unexpected input value encountered: offset=%v; len(plaintext)=%v; len(expectedCiphertext)=%v",
offset,
len(plaintext),
len(expectedCiphertext)))
}
stream, err := rc4.NewCipher(key)
if err != nil {
panic(err)
}
var currentOffset uint64 = 0
ciphertext := make([]byte, len(plaintext))
for currentOffset <= offset {
stream.XORKeyStream(ciphertext, plaintext)
currentOffset += uint64(len(plaintext))
}
if !bytes.Equal(ciphertext, expectedCiphertext) {
panic(fmt.Errorf("vector mismatch @ COUNT = %s:\n %s != %s\n",
count,
hex.EncodeToString(expectedCiphertext),
hex.EncodeToString(ciphertext)))
}
}
func validateVectors(verifier vectorVerifier, filename string) {
vectors, err := os.Open(filename)
if err != nil {
panic(err)
}
defer vectors.Close()
var segments []string
var vector *vectorArgs
scanner := bufio.NewScanner(vectors)
for scanner.Scan() {
segments = strings.Split(scanner.Text(), " = ")
switch {
case strings.ToUpper(segments[0]) == "COUNT":
if vector != nil {
verifier.validate(vector.count,
vector.offset,
unhexlify(vector.key),
unhexlify(vector.plaintext),
unhexlify(vector.ciphertext))
}
vector = &vectorArgs{count: segments[1]}
case strings.ToUpper(segments[0]) == "OFFSET":
vector.offset, err = strconv.ParseUint(segments[1], 10, 64)
if err != nil {
panic(err)
}
case strings.ToUpper(segments[0]) == "KEY":
vector.key = segments[1]
case strings.ToUpper(segments[0]) == "PLAINTEXT":
vector.plaintext = segments[1]
case strings.ToUpper(segments[0]) == "CIPHERTEXT":
vector.ciphertext = segments[1]
}
}
if vector != nil {
verifier.validate(vector.count,
vector.offset,
unhexlify(vector.key),
unhexlify(vector.plaintext),
unhexlify(vector.ciphertext))
}
}
func main() {
validateVectors(arc4Verifier{}, "vectors/cryptography_vectors/ciphers/ARC4/arc4.txt")
fmt.Println("ARC4 OK.")
}
Download link: verify_arc4.go