1package main
2
3import (
4	"bufio"
5	"bytes"
6	"crypto/rc4"
7	"encoding/hex"
8	"fmt"
9	"os"
10	"strconv"
11	"strings"
12)
13
14func unhexlify(s string) []byte {
15	bytes, err := hex.DecodeString(s)
16	if err != nil {
17		panic(err)
18	}
19	return bytes
20}
21
22type vectorArgs struct {
23	count      string
24	offset     uint64
25	key        string
26	plaintext  string
27	ciphertext string
28}
29
30type vectorVerifier interface {
31	validate(count string, offset uint64, key, plaintext, expectedCiphertext []byte)
32}
33
34type arc4Verifier struct{}
35
36func (o arc4Verifier) validate(count string, offset uint64, key, plaintext, expectedCiphertext []byte) {
37	if offset%16 != 0 || len(plaintext) != 16 || len(expectedCiphertext) != 16 {
38		panic(fmt.Errorf("Unexpected input value encountered: offset=%v; len(plaintext)=%v; len(expectedCiphertext)=%v",
39			offset,
40			len(plaintext),
41			len(expectedCiphertext)))
42	}
43	stream, err := rc4.NewCipher(key)
44	if err != nil {
45		panic(err)
46	}
47
48	var currentOffset uint64 = 0
49	ciphertext := make([]byte, len(plaintext))
50	for currentOffset <= offset {
51		stream.XORKeyStream(ciphertext, plaintext)
52		currentOffset += uint64(len(plaintext))
53	}
54	if !bytes.Equal(ciphertext, expectedCiphertext) {
55		panic(fmt.Errorf("vector mismatch @ COUNT = %s:\n  %s != %s\n",
56			count,
57			hex.EncodeToString(expectedCiphertext),
58			hex.EncodeToString(ciphertext)))
59	}
60}
61
62func validateVectors(verifier vectorVerifier, filename string) {
63	vectors, err := os.Open(filename)
64	if err != nil {
65		panic(err)
66	}
67	defer vectors.Close()
68
69	var segments []string
70	var vector *vectorArgs
71
72	scanner := bufio.NewScanner(vectors)
73	for scanner.Scan() {
74		segments = strings.Split(scanner.Text(), " = ")
75
76		switch {
77		case strings.ToUpper(segments[0]) == "COUNT":
78			if vector != nil {
79				verifier.validate(vector.count,
80					vector.offset,
81					unhexlify(vector.key),
82					unhexlify(vector.plaintext),
83					unhexlify(vector.ciphertext))
84			}
85			vector = &vectorArgs{count: segments[1]}
86		case strings.ToUpper(segments[0]) == "OFFSET":
87			vector.offset, err = strconv.ParseUint(segments[1], 10, 64)
88			if err != nil {
89				panic(err)
90			}
91		case strings.ToUpper(segments[0]) == "KEY":
92			vector.key = segments[1]
93		case strings.ToUpper(segments[0]) == "PLAINTEXT":
94			vector.plaintext = segments[1]
95		case strings.ToUpper(segments[0]) == "CIPHERTEXT":
96			vector.ciphertext = segments[1]
97		}
98	}
99	if vector != nil {
100		verifier.validate(vector.count,
101			vector.offset,
102			unhexlify(vector.key),
103			unhexlify(vector.plaintext),
104			unhexlify(vector.ciphertext))
105	}
106}
107
108func main() {
109	validateVectors(arc4Verifier{}, "vectors/cryptography_vectors/ciphers/ARC4/arc4.txt")
110	fmt.Println("ARC4 OK.")
111}
112