diff options
-rw-r--r-- | docs/development/custom-vectors/arc4.rst | 30 | ||||
-rw-r--r-- | docs/development/custom-vectors/arc4/generate_arc4.py | 98 | ||||
-rw-r--r-- | docs/development/custom-vectors/arc4/verify_arc4.go | 111 | ||||
-rw-r--r-- | docs/development/test-vectors.rst | 5 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/ciphers/algorithms.py | 2 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_arc4.py | 1 | ||||
-rw-r--r-- | vectors/cryptography_vectors/ciphers/ARC4/arc4.txt | 216 |
7 files changed, 461 insertions, 2 deletions
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 +</development/custom-vectors/arc4/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 +</development/custom-vectors/arc4/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..3dee44a3 --- /dev/null +++ b/docs/development/custom-vectors/arc4/generate_arc4.py @@ -0,0 +1,98 @@ +# 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.") +} diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 0b249ccb..2f49047d 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -346,7 +346,9 @@ Symmetric ciphers * AES (CBC, CFB, ECB, GCM, OFB) from `NIST CAVP`_. * AES CTR from :rfc:`3686`. * 3DES (CBC, CFB, ECB, OFB) from `NIST CAVP`_. -* ARC4 from :rfc:`6229`. +* ARC4 (KEY-LENGTH: 40, 56, 64, 80, 128, 192, 256) from :rfc:`6229`. +* ARC4 (KEY-LENGTH: 160) generated by this project. + See: :doc:`/development/custom-vectors/arc4` * Blowfish (CBC, CFB, ECB, OFB) from `Bruce Schneier's vectors`_. * Camellia (ECB) from NTT's `Camellia page`_ as linked by `CRYPTREC`_. * Camellia (CBC, CFB, OFB) from `OpenSSL's test vectors`_. @@ -385,6 +387,7 @@ Custom Symmetric Vectors .. toctree:: :maxdepth: 1 + custom-vectors/arc4 custom-vectors/cast5 custom-vectors/idea custom-vectors/seed diff --git a/src/cryptography/hazmat/primitives/ciphers/algorithms.py b/src/cryptography/hazmat/primitives/ciphers/algorithms.py index b71dddbb..c193f797 100644 --- a/src/cryptography/hazmat/primitives/ciphers/algorithms.py +++ b/src/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -101,7 +101,7 @@ class CAST5(object): @utils.register_interface(CipherAlgorithm) class ARC4(object): name = "RC4" - key_sizes = frozenset([40, 56, 64, 80, 128, 192, 256]) + key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256]) def __init__(self, key): self.key = _verify_key_size(self, key) diff --git a/tests/hazmat/primitives/test_arc4.py b/tests/hazmat/primitives/test_arc4.py index acd306b2..00fc95b0 100644 --- a/tests/hazmat/primitives/test_arc4.py +++ b/tests/hazmat/primitives/test_arc4.py @@ -35,6 +35,7 @@ class TestARC4(object): "rfc-6229-128.txt", "rfc-6229-192.txt", "rfc-6229-256.txt", + "arc4.txt" ], lambda key, **kwargs: algorithms.ARC4(binascii.unhexlify(key)), ) diff --git a/vectors/cryptography_vectors/ciphers/ARC4/arc4.txt b/vectors/cryptography_vectors/ciphers/ARC4/arc4.txt new file mode 100644 index 00000000..4c586d63 --- /dev/null +++ b/vectors/cryptography_vectors/ciphers/ARC4/arc4.txt @@ -0,0 +1,216 @@ + +COUNT = 0 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 0 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = f644d3aa1f242c35f51b71d4faf55383 + +COUNT = 1 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 16 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = b4ec4b071c79cfe214b460d84d0d4342 + +COUNT = 2 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 240 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 93b095fdb3da6e88465fd3b195e7ae5a + +COUNT = 3 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 256 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 80f27f1cf1702bd27c4b3e995ceb95d8 + +COUNT = 4 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 496 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = a5ec0488c40c6f94d9b453fe664ba9e1 + +COUNT = 5 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 512 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = aa0371431d55c9e48c3da3a251933f1a + +COUNT = 6 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 752 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = d7b37fe82ce43d0edf46152b40e947cf + +COUNT = 7 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 768 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = fad08d586e5906c806881e0f3cf2057d + +COUNT = 8 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 1008 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 61cfa6a67577bf0bc11daeb9a4080a95 + +COUNT = 9 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 1024 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = d0ef0c6b23f128219c352c15881d52c1 + +COUNT = 10 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 1520 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 12e4aed2445a40b013823ef92a1537e7 + +COUNT = 11 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 1536 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 3c99522399fc2b4e3d10965b954d2c05 + +COUNT = 12 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 2032 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = bdf85291b809850b3ff94f28547630cd + +COUNT = 13 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 2048 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = ed9e6ebd97f43d39b69035d04baf9dde + +COUNT = 14 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 3056 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 63bca8ef13303eb912c3acd243968fe6 + +COUNT = 15 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 3072 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 91648dd1ccb08511f990af8b7555e8a0 + +COUNT = 16 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 4080 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = e2bd078d5e578d90e50d53b65364776c + +COUNT = 17 +KEY = 0102030405060708090a0b0c0d0e0f1011121314 +OFFSET = 4096 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 9c894f815caa4a9e60452082158faa14 + +COUNT = 18 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 0 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 40b068b70cbe9b5115f57c86361c6f20 + +COUNT = 19 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 16 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 534c0249ce55beadb24e7c16c4eab1ae + +COUNT = 20 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 240 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 5856b3366925e231ef602328b07cf3b7 + +COUNT = 21 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 256 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 280a60d06c151953db8e45fb7b0df11c + +COUNT = 22 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 496 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 40fc3ea8dc5512a774bcce590319ca1b + +COUNT = 23 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 512 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 604e10c7c490d9bdfb4c69bdfff18852 + +COUNT = 24 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 752 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 46de8d697170633913c1625f96776aed + +COUNT = 25 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 768 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 67b1c2de2dd328d66c78d341b879aee7 + +COUNT = 26 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 1008 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 0e03b8cfc8683da0a802bfe5dba7d8bc + +COUNT = 27 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 1024 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = d4bb24de2be6559ab2b9fb5eee38d555 + +COUNT = 28 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 1520 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = bcdbbcb75b18472371753daa15843046 + +COUNT = 29 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 1536 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 51b4a22fef8c33d27de9265855a670d4 + +COUNT = 30 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 2032 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 9464c6af04a56b35c80058a9d68dae99 + +COUNT = 31 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 2048 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 3c91eaf71e94ced21d093ab8a08b98ef + +COUNT = 32 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 3056 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 537f5e714a9d141a9bd5d19dfee2e19f + +COUNT = 33 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 3072 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = dfc61cece2570fa52dbda2e52b4c98b0 + +COUNT = 34 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 4080 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 5a5ad75eb1bb615d6e2382eabe95e782 + +COUNT = 35 +KEY = 08ebe51debb46227c6cc8b37641910833222772a +OFFSET = 4096 +PLAINTEXT = 00000000000000000000000000000000 +CIPHERTEXT = 11ebf8e2c10017575976a94b44407f3e
\ No newline at end of file |