add cast5 (cbc, cfb, ofb) vector source info to docs
diff --git a/docs/development/custom-vectors/cast5.rst b/docs/development/custom-vectors/cast5.rst
new file mode 100644
index 0000000..bbe155c
--- /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 0000000..f038825
--- /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 0000000..1ea1053
--- /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 2a071d7..3b8632e 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -54,8 +54,34 @@
 * 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/