aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2014-02-12 16:17:04 -0600
committerPaul Kehrer <paul.l.kehrer@gmail.com>2014-02-12 16:17:04 -0600
commitcf6ffb5ef3b50fb6485f3e669c28156c03a5420c (patch)
tree17e5a8e9bcd70db1e692912113febe5d9ae09ff2
parent493efbb64095dca073926a07fdc654418490d8be (diff)
downloadcryptography-cf6ffb5ef3b50fb6485f3e669c28156c03a5420c.tar.gz
cryptography-cf6ffb5ef3b50fb6485f3e669c28156c03a5420c.tar.bz2
cryptography-cf6ffb5ef3b50fb6485f3e669c28156c03a5420c.zip
add cast5 (cbc, cfb, ofb) vector source info to docs
-rw-r--r--docs/development/custom-vectors/cast5.rst31
-rw-r--r--docs/development/custom-vectors/cast5/generate_cast5.py58
-rw-r--r--docs/development/custom-vectors/cast5/verify_cast5.go141
-rw-r--r--docs/development/test-vectors.rst26
4 files changed, 256 insertions, 0 deletions
diff --git a/docs/development/custom-vectors/cast5.rst b/docs/development/custom-vectors/cast5.rst
new file mode 100644
index 00000000..bbe155c8
--- /dev/null
+++ b/docs/development/custom-vectors/cast5.rst
@@ -0,0 +1,31 @@
+CAST5 Vector Creation
+=====================
+
+This page documents the code that was used to generate the CAST5 CBC, CFB, and
+OFB test vectors as well as the code used to verify them against another
+implementation. For CAST5 the vectors were generated using OpenSSL and verified
+with Go.
+
+Creation
+--------
+
+``cryptography`` was modified to support CAST5 in CBC, CFB, and OFB modes. Then
+the following python script was run to generate the vector files.
+
+.. literalinclude:: /development/custom-vectors/cast5/generate_cast5.py
+ :linenos:
+ :lines: 7-16
+
+Full code: :download:`generate_cast5.py </development/custom-vectors/cast5/generate_cast5.py>`
+
+
+Verification
+------------
+
+The following go code was used to verify the vectors.
+
+.. literalinclude:: /development/custom-vectors/cast5/verify_cast5.go
+ :linenos:
+ :lines: 36-52
+
+Full code: :download:`verify_cast5.go </development/custom-vectors/cast5/verify_cast5.go>`
diff --git a/docs/development/custom-vectors/cast5/generate_cast5.py b/docs/development/custom-vectors/cast5/generate_cast5.py
new file mode 100644
index 00000000..f038825a
--- /dev/null
+++ b/docs/development/custom-vectors/cast5/generate_cast5.py
@@ -0,0 +1,58 @@
+import binascii
+
+from cryptography.hazmat.backends.openssl.backend import backend
+from cryptography.hazmat.primitives.ciphers import base, algorithms, modes
+
+
+def encrypt(mode, key, iv, plaintext):
+ cipher = base.Cipher(
+ algorithms.CAST5(binascii.unhexlify(key)),
+ mode(binascii.unhexlify(iv)),
+ backend
+ )
+ encryptor = cipher.encryptor()
+ ct = encryptor.update(binascii.unhexlify(plaintext))
+ ct += encryptor.finalize()
+ return binascii.hexlify(ct)
+
+
+def build_vectors(mode, filename):
+ vector_file = open(filename, "r")
+
+ count = 0
+ output = []
+ key = None
+ iv = None
+ plaintext = None
+ ct = None
+ for line in vector_file:
+ line = line.strip()
+ if line.startswith("KEY"):
+ if count != 0:
+ output.append("CIPHERTEXT = {}".format(encrypt(mode, key, iv, plaintext)))
+ output.append("\nCOUNT = {}".format(count))
+ count += 1
+ name, key = line.split(" = ")
+ output.append("KEY = {}".format(key))
+ elif line.startswith("IV"):
+ name, iv = line.split(" = ")
+ iv = iv[0:16]
+ output.append("IV = {}".format(iv))
+ elif line.startswith("PLAINTEXT"):
+ name, plaintext = line.split(" = ")
+ output.append("PLAINTEXT = {}".format(plaintext))
+
+ output.append("CIPHERTEXT = {}".format(encrypt(mode, key, iv, plaintext)))
+ return "\n".join(output)
+
+
+def write_file(data, filename):
+ with open(filename, "w") as f:
+ f.write(data)
+
+cbc_path = "tests/hazmat/primitives/vectors/ciphers/AES/CBC/CBCMMT128.rsp"
+write_file(build_vectors(modes.CBC, cbc_path), "cast5-cbc.txt")
+ofb_path = "tests/hazmat/primitives/vectors/ciphers/AES/OFB/OFBMMT128.rsp"
+write_file(build_vectors(modes.OFB, ofb_path), "cast5-ofb.txt")
+cfb_path = "tests/hazmat/primitives/vectors/ciphers/AES/CFB/CFB128MMT128.rsp"
+write_file(build_vectors(modes.CFB, cfb_path), "cast5-cfb.txt")
diff --git a/docs/development/custom-vectors/cast5/verify_cast5.go b/docs/development/custom-vectors/cast5/verify_cast5.go
new file mode 100644
index 00000000..1ea10538
--- /dev/null
+++ b/docs/development/custom-vectors/cast5/verify_cast5.go
@@ -0,0 +1,141 @@
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "code.google.com/p/go.crypto/cast5"
+ "crypto/cipher"
+ "encoding/hex"
+ "fmt"
+ "os"
+ "strings"
+)
+
+func unhexlify(s string) []byte {
+ bytes, err := hex.DecodeString(s)
+ if err != nil {
+ panic(err)
+ }
+ return bytes
+}
+
+type VectorArgs struct {
+ count string
+ key string
+ iv string
+ plaintext string
+ ciphertext string
+}
+
+type VectorVerifier interface {
+ validate(count string, key, iv, plaintext, expected_ciphertext []byte)
+}
+
+type ofbVerifier struct{}
+
+func (o ofbVerifier) validate(count string, key, iv, plaintext, expected_ciphertext []byte) {
+ block, err := cast5.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ ciphertext := make([]byte, len(plaintext))
+ stream := cipher.NewOFB(block, iv)
+ stream.XORKeyStream(ciphertext, plaintext)
+
+ if !bytes.Equal(ciphertext, expected_ciphertext) {
+ panic(fmt.Errorf("vector mismatch @ COUNT = %s:\n %s != %s\n",
+ count,
+ hex.EncodeToString(expected_ciphertext),
+ hex.EncodeToString(ciphertext)))
+ }
+}
+
+type cbcVerifier struct{}
+
+func (o cbcVerifier) validate(count string, key, iv, plaintext, expected_ciphertext []byte) {
+ block, err := cast5.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ ciphertext := make([]byte, len(plaintext))
+ mode := cipher.NewCBCEncrypter(block, iv)
+ mode.CryptBlocks(ciphertext, plaintext)
+
+ if !bytes.Equal(ciphertext, expected_ciphertext) {
+ panic(fmt.Errorf("vector mismatch @ COUNT = %s:\n %s != %s\n",
+ count,
+ hex.EncodeToString(expected_ciphertext),
+ hex.EncodeToString(ciphertext)))
+ }
+}
+
+type cfbVerifier struct{}
+
+func (o cfbVerifier) validate(count string, key, iv, plaintext, expected_ciphertext []byte) {
+ block, err := cast5.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ ciphertext := make([]byte, len(plaintext))
+ stream := cipher.NewCFBEncrypter(block, iv)
+ stream.XORKeyStream(ciphertext, plaintext)
+
+ if !bytes.Equal(ciphertext, expected_ciphertext) {
+ panic(fmt.Errorf("vector mismatch @ COUNT = %s:\n %s != %s\n",
+ count,
+ hex.EncodeToString(expected_ciphertext),
+ 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,
+ unhexlify(vector.key),
+ unhexlify(vector.iv),
+ unhexlify(vector.plaintext),
+ unhexlify(vector.ciphertext))
+ }
+ vector = &VectorArgs{count: segments[1]}
+ case strings.ToUpper(segments[0]) == "IV":
+ vector.iv = segments[1][:16]
+ 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]
+ }
+ }
+
+}
+
+func main() {
+ validateVectors(ofbVerifier{},
+ "tests/hazmat/primitives/vectors/ciphers/CAST5/cast5-ofb.txt")
+ fmt.Println("OFB OK.")
+ validateVectors(cfbVerifier{},
+ "tests/hazmat/primitives/vectors/ciphers/CAST5/cast5-cfb.txt")
+ fmt.Println("CFB OK.")
+ validateVectors(cbcVerifier{},
+ "tests/hazmat/primitives/vectors/ciphers/CAST5/cast5-cbc.txt")
+ fmt.Println("CBC OK.")
+}
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index 2a071d7c..3b8632e6 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -54,8 +54,34 @@ Symmetric Ciphers
* Camellia (ECB) from NTT's `Camellia page`_ as linked by `CRYPTREC`_.
* Camellia (CBC, CFB, OFB) from `OpenSSL's test vectors`_.
* CAST5 (ECB) from :rfc:`2144`.
+* CAST5 (CBC, CFB, OFB) generated by this project.
+ See: :doc:`/development/custom-vectors/cast5`
+Creating Test Vectors
+---------------------
+
+When official vectors are unavailable ``cryptography`` may choose to build
+its own using existing vectors as source material. Current custom vectors:
+
+.. toctree::
+ :maxdepth: 1
+
+ custom-vectors/cast5
+
+If official test vectors appear in the future the custom generated vectors
+should be discarded.
+
+Any vectors generated by this method must also be prefixed with the following
+header format (substituting the correct information):
+
+.. code-block:: python
+
+ # CAST5 CBC vectors built for https://github.com/pyca/cryptography
+ # Derived from the AESVS MMT test data for CBC
+ # Verified against the CommonCrypto and Go crypto packages
+ # Key Length : 128
+
.. _`NIST`: http://www.nist.gov/
.. _`IETF`: https://www.ietf.org/
.. _`NIST CAVP`: http://csrc.nist.gov/groups/STM/cavp/