From d01c20fb29dc3a6ba53accd3bd561c05715f62c9 Mon Sep 17 00:00:00 2001 From: Ehren Kret Date: Sat, 28 Nov 2015 02:16:15 -0800 Subject: Add support for 160 bit ARC4 keys --- docs/development/custom-vectors/arc4.rst | 30 ++++++ .../custom-vectors/arc4/generate_arc4.py | 89 +++++++++++++++++ .../development/custom-vectors/arc4/verify_arc4.go | 111 +++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 docs/development/custom-vectors/arc4.rst create mode 100644 docs/development/custom-vectors/arc4/generate_arc4.py create mode 100644 docs/development/custom-vectors/arc4/verify_arc4.go (limited to 'docs') diff --git a/docs/development/custom-vectors/arc4.rst b/docs/development/custom-vectors/arc4.rst new file mode 100644 index 00000000..ed8cd548 --- /dev/null +++ b/docs/development/custom-vectors/arc4.rst @@ -0,0 +1,30 @@ +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. + +.. literalinclude:: /development/custom-vectors/arc4/generate_arc4.py + +Download link: :download:`generate_arc4.py +` + + +Verification +------------ + +The following Go code was used to verify the vectors. + +.. literalinclude:: /development/custom-vectors/arc4/verify_arc4.go + :language: go + +Download link: :download:`verify_arc4.go +` diff --git a/docs/development/custom-vectors/arc4/generate_arc4.py b/docs/development/custom-vectors/arc4/generate_arc4.py new file mode 100644 index 00000000..093bb49d --- /dev/null +++ b/docs/development/custom-vectors/arc4/generate_arc4.py @@ -0,0 +1,89 @@ +# 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. + +from __future__ import absolute_import, division, print_function + +import binascii + +from cryptography.hazmat.backends import default_backend +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, default_backend()) + 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') diff --git a/docs/development/custom-vectors/arc4/verify_arc4.go b/docs/development/custom-vectors/arc4/verify_arc4.go new file mode 100644 index 00000000..508fe980 --- /dev/null +++ b/docs/development/custom-vectors/arc4/verify_arc4.go @@ -0,0 +1,111 @@ +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.") +} -- cgit v1.2.3