diff options
Diffstat (limited to 'docs/development')
24 files changed, 1469 insertions, 101 deletions
diff --git a/docs/development/c-bindings.rst b/docs/development/c-bindings.rst index 993a4ab2..1b58dab6 100644 --- a/docs/development/c-bindings.rst +++ b/docs/development/c-bindings.rst @@ -3,7 +3,7 @@ C bindings C bindings are bindings to C libraries, using cffi_ whenever possible. -.. _cffi: https://cffi.readthedocs.org +.. _cffi: https://cffi.readthedocs.io Bindings live in :py:mod:`cryptography.hazmat.bindings`. @@ -88,9 +88,10 @@ Adding constant, types, functions... You can create bindings for any name that exists in some version of the library you're binding against. However, the project also has to -keep supporting older versions of the library. In order to achieve -this, binding modules have ``CUSTOMIZATIONS`` and -``CONDITIONAL_NAMES`` constants. +keep supporting older versions of the library. In order to achieve this, +binding modules have a ``CUSTOMIZATIONS`` constant, and there is a +``CONDITIONAL_NAMES`` constants in +``src/cryptography/hazmat/bindings/openssl/_conditional.py``. Let's say you want to enable quantum transmogrification. The upstream library implements this as the following API:: @@ -127,9 +128,7 @@ opaque struct:: ...; } QM_TRANSMOGRIFICATION_CTX; -Confusingly, functions that aren't always available on all supported -versions of the library, should be defined in ``MACROS`` and *not* in -``FUNCTIONS``. Fortunately, you just have to copy the signature:: +For functions just add the signature to ``FUNCTIONS``:: int QM_transmogrify(QM_TRANSMOGRIFICATION_CTX *, int); @@ -168,24 +167,31 @@ the necessarily type definitions are in place. Finally, add an entry to ``CONDITIONAL_NAMES`` with all of the things you want to conditionally export:: - CONDITIONAL_NAMES = { - ... - "Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION": [ + def cryptography_has_quantum_transmogrification(): + return [ "QM_TRANSMOGRIFICATION_ALIGNMENT_LEFT", "QM_TRANSMOGRIFICATION_ALIGNMENT_RIGHT", - "QM_transmogrify" + "QM_transmogrify", ] + + + CONDITIONAL_NAMES = { + ... + "Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION": ( + cryptography_has_quantum_transmogrification + ), } + Caveats ~~~~~~~ Sometimes, a set of loosely related features are added in the same version, and it's impractical to create ``#ifdef`` statements for each one. In that case, it may make sense to either check for a particular -version. For example, to check for OpenSSL 1.0.0 or newer:: +version. For example, to check for OpenSSL 1.1.0 or newer:: - #if OPENSSL_VERSION_NUMBER >= 0x10000000L + #if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER Sometimes, the version of a library on a particular platform will have features that you thought it wouldn't, based on its version. diff --git a/docs/development/custom-vectors/arc4.rst b/docs/development/custom-vectors/arc4.rst new file mode 100644 index 00000000..5b1b65d5 --- /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/custom-vectors/cast5/generate_cast5.py b/docs/development/custom-vectors/cast5/generate_cast5.py index 20f35179..ce046b0f 100644 --- a/docs/development/custom-vectors/cast5/generate_cast5.py +++ b/docs/development/custom-vectors/cast5/generate_cast5.py @@ -23,33 +23,34 @@ def encrypt(mode, key, iv, plaintext): def build_vectors(mode, filename): - vector_file = open(filename, "r") - count = 0 output = [] key = None iv = None plaintext = 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))) + with open(filename, "r") as vector_file: + 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) @@ -57,6 +58,7 @@ 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" diff --git a/docs/development/custom-vectors/cast5/verify_cast5.go b/docs/development/custom-vectors/cast5/verify_cast5.go index cbc89a03..b2d711a9 100644 --- a/docs/development/custom-vectors/cast5/verify_cast5.go +++ b/docs/development/custom-vectors/cast5/verify_cast5.go @@ -3,7 +3,7 @@ package main import ( "bufio" "bytes" - "code.google.com/p/go.crypto/cast5" + "golang.org/x/crypto/cast5" "crypto/cipher" "encoding/hex" "fmt" diff --git a/docs/development/custom-vectors/hkdf.rst b/docs/development/custom-vectors/hkdf.rst new file mode 100644 index 00000000..6d7e8618 --- /dev/null +++ b/docs/development/custom-vectors/hkdf.rst @@ -0,0 +1,28 @@ +HKDF vector creation +==================== + +This page documents the code that was used to generate a longer +HKDF test vector (1200 bytes) than is available in :rfc:`5869`. All +the vectors were generated using OpenSSL and verified with Go. + +Creation +-------- + +The following Python script was run to generate the vector files. + +.. literalinclude:: /development/custom-vectors/hkdf/generate_hkdf.py + +Download link: :download:`generate_hkdf.py +</development/custom-vectors/hkdf/generate_hkdf.py>` + + +Verification +------------ + +The following Go code was used to verify the vectors. + +.. literalinclude:: /development/custom-vectors/hkdf/verify_hkdf.go + :language: go + +Download link: :download:`verify_hkdf.go +</development/custom-vectors/hkdf/verify_hkdf.go>` diff --git a/docs/development/custom-vectors/hkdf/generate_hkdf.py b/docs/development/custom-vectors/hkdf/generate_hkdf.py new file mode 100644 index 00000000..8976effd --- /dev/null +++ b/docs/development/custom-vectors/hkdf/generate_hkdf.py @@ -0,0 +1,39 @@ +# 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 hashes +from cryptography.hazmat.primitives.kdf.hkdf import HKDF + +IKM = binascii.unhexlify(b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") +L = 1200 +OKM = HKDF( + algorithm=hashes.SHA256(), length=L, salt=None, info=None, + backend=default_backend() +).derive(IKM) + + +def _build_vectors(): + output = [ + "COUNT = 0", + "Hash = SHA-256", + "IKM = " + binascii.hexlify(IKM).decode("ascii"), + "salt = ", "info = ", + "L = {}".format(L), + "OKM = " + binascii.hexlify(OKM).decode("ascii"), + ] + 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(), 'hkdf.txt') diff --git a/docs/development/custom-vectors/hkdf/verify_hkdf.go b/docs/development/custom-vectors/hkdf/verify_hkdf.go new file mode 100644 index 00000000..ddeb3d8e --- /dev/null +++ b/docs/development/custom-vectors/hkdf/verify_hkdf.go @@ -0,0 +1,69 @@ +package main + +import ( + "bufio" + "bytes" + "crypto/sha256" + "encoding/hex" + "fmt" + "golang.org/x/crypto/hkdf" + "io" + "os" + "strconv" + "strings" +) + +func unhexlify(s string) []byte { + bytes, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return bytes +} + +func verifier(l uint64, ikm, okm []byte) bool { + hash := sha256.New + hkdf := hkdf.New(hash, ikm, nil, nil) + okmComputed := make([]byte, l) + io.ReadFull(hkdf, okmComputed) + return bytes.Equal(okmComputed, okm) +} + +func validateVectors(filename string) bool { + vectors, err := os.Open(filename) + if err != nil { + panic(err) + } + defer vectors.Close() + + var segments []string + var l uint64 + var ikm, okm string + + scanner := bufio.NewScanner(vectors) + for scanner.Scan() { + segments = strings.Split(scanner.Text(), " = ") + + switch { + case strings.ToUpper(segments[0]) == "L": + l, err = strconv.ParseUint(segments[1], 10, 64) + if err != nil { + panic(err) + } + case strings.ToUpper(segments[0]) == "IKM": + ikm = segments[1] + case strings.ToUpper(segments[0]) == "OKM": + okm = segments[1] + } + } + return verifier(l, unhexlify(ikm), unhexlify(okm)) +} + +func main() { + if validateVectors("vectors/cryptography_vectors/KDF/hkdf-generated.txt") { + fmt.Println("HKDF OK.") + } else { + fmt.Println("HKDF failed.") + os.Exit(1) + } +} diff --git a/docs/development/custom-vectors/idea.rst b/docs/development/custom-vectors/idea.rst index 336cdf01..758a108e 100644 --- a/docs/development/custom-vectors/idea.rst +++ b/docs/development/custom-vectors/idea.rst @@ -29,4 +29,4 @@ project's Python bindings. Download link: :download:`verify_idea.py </development/custom-vectors/idea/verify_idea.py>` -.. _`Botan`: http://botan.randombit.net +.. _`Botan`: https://botan.randombit.net diff --git a/docs/development/custom-vectors/idea/generate_idea.py b/docs/development/custom-vectors/idea/generate_idea.py index c9f94024..2eb6996e 100644 --- a/docs/development/custom-vectors/idea/generate_idea.py +++ b/docs/development/custom-vectors/idea/generate_idea.py @@ -52,6 +52,7 @@ 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), "idea-cbc.txt") OFB_PATH = "tests/hazmat/primitives/vectors/ciphers/AES/OFB/OFBMMT128.rsp" diff --git a/docs/development/custom-vectors/rsa-oaep-sha2.rst b/docs/development/custom-vectors/rsa-oaep-sha2.rst new file mode 100644 index 00000000..36f256d7 --- /dev/null +++ b/docs/development/custom-vectors/rsa-oaep-sha2.rst @@ -0,0 +1,56 @@ +RSA OAEP SHA2 vector creation +============================= + +This page documents the code that was used to generate the RSA OAEP SHA2 +test vectors as well as code used to verify them against another +implementation. + + +Creation +-------- + +``cryptography`` was modified to allow the use of SHA2 in OAEP encryption. Then +the following python script was run to generate the vector files. + +.. literalinclude:: /development/custom-vectors/rsa-oaep-sha2/generate_rsa_oaep_sha2.py + +Download link: :download:`generate_rsa_oaep_sha2.py +</development/custom-vectors/rsa-oaep-sha2/generate_rsa_oaep_sha2.py>` + + +Verification +------------ + +A Java 8 program was written using `Bouncy Castle`_ to load and verify the test +vectors. + + +.. literalinclude:: /development/custom-vectors/rsa-oaep-sha2/VerifyRSAOAEPSHA2.java + +Download link: :download:`VerifyRSAOAEPSHA2.java +</development/custom-vectors/rsa-oaep-sha2/VerifyRSAOAEPSHA2.java>` + +Using the Verifier +------------------ + +Download and install the `Java 8 SDK`_. Initial verification was performed +using ``jdk-8u77-macosx-x64.dmg``. + +Download the latest `Bouncy Castle`_ JAR. Initial verification was performed +using ``bcprov-jdk15on-154.jar``. + +Set the ``-classpath`` to include the Bouncy Castle jar and the path to +``VerifyRSAOAEPSHA2.java`` and compile the program. + +.. code-block:: console + + $ javac -classpath ~/Downloads/bcprov-jdk15on-154.jar:./ VerifyRSAOAEPSHA2.java + +Finally, run the program with the path to the SHA-2 vectors: + +.. code-block:: console + + $ java -classpath ~/Downloads/bcprov-jdk15on-154.jar:./ VerifyRSAOAEPSHA2 + +.. _`Bouncy Castle`: https://www.bouncycastle.org/ +.. _`Java 8 SDK`: https://www.oracle.com/technetwork/java/javase/downloads/index.html diff --git a/docs/development/custom-vectors/rsa-oaep-sha2/VerifyRSAOAEPSHA2.java b/docs/development/custom-vectors/rsa-oaep-sha2/VerifyRSAOAEPSHA2.java new file mode 100644 index 00000000..e1bfd3d1 --- /dev/null +++ b/docs/development/custom-vectors/rsa-oaep-sha2/VerifyRSAOAEPSHA2.java @@ -0,0 +1,416 @@ +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Security; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.RSAPrivateKeySpec; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; +import javax.xml.bind.DatatypeConverter; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +class TestVectorData { + public BigInteger pub_key_modulus; + public BigInteger pub_key_exponent; + public BigInteger priv_key_public_exponent; + public BigInteger priv_key_modulus; + public BigInteger priv_key_exponent; + public BigInteger priv_key_prime_1; + public BigInteger priv_key_prime_2; + public BigInteger priv_key_prime_exponent_1; + public BigInteger priv_key_prime_exponent_2; + public BigInteger priv_key_coefficient; + public byte[] plaintext; + public byte[] ciphertext; +} + +class TestVectorLoader { + private static final String FILE_HEADER = "# RSA OAEP SHA2 vectors built"; + private static final String EXAMPLE_HEADER = "# ====="; + private static final String EXAMPLE = "# Example"; + private static final String PUBLIC_KEY = "# Public key"; + private static final String PUB_MODULUS = "# Modulus:"; + private static final String PUB_EXPONENT = "# Exponent:"; + private static final String PRIVATE_KEY = "# Private key"; + private static final String PRIV_MODULUS = "# Modulus:"; + private static final String PRIV_PUBLIC_EXPONENT = "# Public exponent:"; + private static final String PRIV_EXPONENT = "# Exponent:"; + private static final String PRIV_PRIME_1 = "# Prime 1:"; + private static final String PRIV_PRIME_2 = "# Prime 2:"; + private static final String PRIV_PRIME_EXPONENT_1 = "# Prime exponent 1:"; + private static final String PRIV_PRIME_EXPONENT_2 = "# Prime exponent 2:"; + private static final String PRIV_COEFFICIENT = "# Coefficient:"; + private static final String OAEP_EXAMPLE_HEADER = "# OAEP Example"; + private static final String MESSAGE = "# Message:"; + private static final String ENCRYPTION = "# Encryption:"; + + private BufferedReader m_reader = null; + private FileReader m_file_reader = null; + private TestVectorData m_data = null; + + TestVectorLoader() { + + } + + protected void finalize() { + close(); + } + + public void open(String path) throws IOException { + close(); + m_file_reader = new FileReader(path); + m_reader = new BufferedReader(m_file_reader); + m_data = new TestVectorData(); + } + + public void close() { + try { + if (m_reader != null) { + m_reader.close(); + m_reader = null; + } + if (m_file_reader != null) { + m_file_reader.close(); + m_file_reader = null; + } + m_data = null; + } catch (IOException e) { + System.out.println("Exception closing files"); + e.printStackTrace(); + } + } + + public TestVectorData loadNextTest() throws IOException { + if (m_file_reader == null || m_reader == null || m_data == null) { + throw new IOException("A test vector file must be opened first"); + } + + String line = m_reader.readLine(); + + if (line == null) { + // end of file + return null; + } + + if (line.startsWith(FILE_HEADER)) { + // start of file + skipFileHeader(m_reader); + line = m_reader.readLine(); + } + + if (line.startsWith(OAEP_EXAMPLE_HEADER)) { + // Next example, keep existing keys and load next message + loadMessage(m_reader, m_data); + return m_data; + } + + // otherwise it's a new example + if (!line.startsWith(EXAMPLE_HEADER)) { + throw new IOException("Test Header Missing"); + } + startNewTest(m_reader); + m_data = new TestVectorData(); + + line = m_reader.readLine(); + if (!line.startsWith(PUBLIC_KEY)) + throw new IOException("Public Key Missing"); + loadPublicKey(m_reader, m_data); + + line = m_reader.readLine(); + if (!line.startsWith(PRIVATE_KEY)) + throw new IOException("Private Key Missing"); + loadPrivateKey(m_reader, m_data); + + line = m_reader.readLine(); + if (!line.startsWith(OAEP_EXAMPLE_HEADER)) + throw new IOException("Message Missing"); + loadMessage(m_reader, m_data); + + return m_data; + } + + private byte[] unhexlify(String line) { + byte[] bytes = DatatypeConverter.parseHexBinary(line); + return bytes; + } + + private BigInteger readBigInteger(BufferedReader br) throws IOException { + return new BigInteger(br.readLine(), 16); + } + + private void skipFileHeader(BufferedReader br) throws IOException { + br.readLine(); // # # Derived from the NIST OAEP SHA1 vectors + br.readLine(); // # # Verified against the Bouncy Castle OAEP SHA2 implementation + br.readLine(); // # + } + + private void startNewTest(BufferedReader br) throws IOException { + String line = br.readLine(); + if (!line.startsWith(EXAMPLE)) + throw new IOException("Example Header Missing"); + } + + private void loadPublicKey(BufferedReader br, TestVectorData data) throws IOException { + String line = br.readLine(); + if (!line.startsWith(PUB_MODULUS)) + throw new IOException("Public Key Modulus Missing"); + data.pub_key_modulus = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PUB_EXPONENT)) + throw new IOException("Public Key Exponent Missing"); + data.pub_key_exponent = readBigInteger(br); + } + + private void loadPrivateKey(BufferedReader br, TestVectorData data) throws IOException { + String line = br.readLine(); + if (!line.startsWith(PRIV_MODULUS)) + throw new IOException("Private Key Modulus Missing"); + data.priv_key_modulus = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PUBLIC_EXPONENT)) + throw new IOException("Private Key Public Exponent Missing"); + data.priv_key_public_exponent = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_EXPONENT)) + throw new IOException("Private Key Exponent Missing"); + data.priv_key_exponent = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PRIME_1)) + throw new IOException("Private Key Prime 1 Missing"); + data.priv_key_prime_1 = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PRIME_2)) + throw new IOException("Private Key Prime 2 Missing"); + data.priv_key_prime_2 = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PRIME_EXPONENT_1)) + throw new IOException("Private Key Prime Exponent 1 Missing"); + data.priv_key_prime_exponent_1 = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PRIME_EXPONENT_2)) + throw new IOException("Private Key Prime Exponent 2 Missing"); + data.priv_key_prime_exponent_2 = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_COEFFICIENT)) + throw new IOException("Private Key Coefficient Missing"); + data.priv_key_coefficient = readBigInteger(br); + } + + private void loadMessage(BufferedReader br, TestVectorData data) throws IOException { + String line = br.readLine(); + if (!line.startsWith(MESSAGE)) + throw new IOException("Plaintext Missing"); + data.plaintext = unhexlify(br.readLine()); + + line = br.readLine(); + if (!line.startsWith(ENCRYPTION)) + throw new IOException("Ciphertext Missing"); + data.ciphertext = unhexlify(br.readLine()); + } + +} + +public class VerifyRSAOAEPSHA2 { + + public enum SHAHash { + SHA1, SHA224, SHA256, SHA384, SHA512 + } + + private SHAHash m_mgf1_hash; + private SHAHash m_alg_hash; + private Cipher m_cipher; + private PrivateKey m_private_key; + private AlgorithmParameters m_algo_param; + + VerifyRSAOAEPSHA2(SHAHash mgf1_hash, SHAHash alg_hash, TestVectorData test_data) throws Exception { + + m_mgf1_hash = mgf1_hash; + m_alg_hash = alg_hash; + + MGF1ParameterSpec mgf1_spec = getMGF1ParameterSpec(m_mgf1_hash); + AlgorithmParameterSpec algo_param_spec = getAlgorithmParameterSpec(m_alg_hash, mgf1_spec); + + m_algo_param = AlgorithmParameters.getInstance("OAEP"); + m_algo_param.init(algo_param_spec); + + m_private_key = loadPrivateKey(test_data); + + m_cipher = getCipher(m_alg_hash); + } + + private Cipher getCipher(SHAHash alg_hash) throws GeneralSecurityException { + Cipher cipher = null; + + switch (alg_hash) { + + case SHA1: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA1andMGF1Padding", "BC"); + break; + + case SHA224: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-224andMGF1Padding", "BC"); + break; + + case SHA256: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding", "BC"); + break; + + case SHA384: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-384andMGF1Padding", "BC"); + break; + + case SHA512: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-512andMGF1Padding", "BC"); + break; + } + + return cipher; + } + + private MGF1ParameterSpec getMGF1ParameterSpec(SHAHash mgf1_hash) { + MGF1ParameterSpec mgf1 = null; + + switch (mgf1_hash) { + + case SHA1: + mgf1 = MGF1ParameterSpec.SHA1; + break; + case SHA224: + mgf1 = MGF1ParameterSpec.SHA224; + break; + + case SHA256: + mgf1 = MGF1ParameterSpec.SHA256; + break; + + case SHA384: + mgf1 = MGF1ParameterSpec.SHA384; + break; + + case SHA512: + mgf1 = MGF1ParameterSpec.SHA512; + break; + } + + return mgf1; + } + + private AlgorithmParameterSpec getAlgorithmParameterSpec(SHAHash alg_hash, MGF1ParameterSpec mgf1_spec) { + + OAEPParameterSpec oaep_spec = null; + + switch (alg_hash) { + + case SHA1: + oaep_spec = new OAEPParameterSpec("SHA1", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + + case SHA224: + oaep_spec = new OAEPParameterSpec("SHA-224", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + + case SHA256: + oaep_spec = new OAEPParameterSpec("SHA-256", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + + case SHA384: + oaep_spec = new OAEPParameterSpec("SHA-384", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + + case SHA512: + oaep_spec = new OAEPParameterSpec("SHA-512", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + } + + return oaep_spec; + } + + private PrivateKey loadPrivateKey(TestVectorData test_data) throws Exception { + KeyFactory kf = KeyFactory.getInstance("RSA"); + + RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(test_data.priv_key_modulus, test_data.priv_key_exponent); + + return kf.generatePrivate(keySpec); + } + + public void testDecrypt(byte[] plaintext, byte[] ciphertext) throws Exception { + System.out.println("Verifying OAEP with mgf1_hash: " + m_mgf1_hash + " alg_hash: " + m_alg_hash + " - " + + ciphertext.length + " bytes ciphertext - " + + plaintext.length + " bytes plaintext"); + + m_cipher.init(Cipher.DECRYPT_MODE, m_private_key, m_algo_param); + byte[] java_plaintext = m_cipher.doFinal(ciphertext); + + if (Arrays.equals(java_plaintext, plaintext) == false) { + throw new Exception("Verification failure - plaintext does not match after decryption."); + } + } + + public static void main(String[] args) { + Security.addProvider(new BouncyCastleProvider()); + + // assume current directory if no path given on command line + String vector_path = "./vectors/cryptography_vectors/asymmetric/RSA/oaep-custom"; + + if (args.length > 0) { + vector_path = args[0]; + } + + System.out.println("Vector file path: " + vector_path); + + try { + // loop over each combination of hash loading the vector file + // to verify for each + for (SHAHash mgf1_hash : SHAHash.values()) { + for (SHAHash alg_hash : SHAHash.values()) { + if (mgf1_hash.name().toLowerCase().equals("sha1") && + alg_hash.name().toLowerCase().equals("sha1")) { + continue; + } + String filename = "oaep-" + mgf1_hash.name().toLowerCase() + + "-" + alg_hash.name().toLowerCase() + ".txt"; + + System.out.println("Loading " + filename + "..."); + + TestVectorLoader loader = new TestVectorLoader(); + loader.open(vector_path + filename); + + TestVectorData test_data; + + // load each test in the file and verify + while ((test_data = loader.loadNextTest()) != null) { + VerifyRSAOAEPSHA2 verify = new VerifyRSAOAEPSHA2(mgf1_hash, alg_hash, test_data); + verify.testDecrypt(test_data.plaintext, test_data.ciphertext); + } + + System.out.println("Verifying " + filename + " completed successfully."); + } + } + + System.out.println("All verification completed successfully"); + + } catch (Exception e) { + // if any exception is thrown the verification has failed + e.printStackTrace(); + System.out.println("Verification Failed!"); + } + } +} diff --git a/docs/development/custom-vectors/rsa-oaep-sha2/generate_rsa_oaep_sha2.py b/docs/development/custom-vectors/rsa-oaep-sha2/generate_rsa_oaep_sha2.py new file mode 100644 index 00000000..bd5148f5 --- /dev/null +++ b/docs/development/custom-vectors/rsa-oaep-sha2/generate_rsa_oaep_sha2.py @@ -0,0 +1,128 @@ +# 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 +import itertools +import os + +from cryptography.hazmat.backends.openssl.backend import backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding, rsa + +from tests.utils import load_pkcs1_vectors, load_vectors_from_file + + +def build_vectors(mgf1alg, hashalg, filename): + vectors = load_vectors_from_file(filename, load_pkcs1_vectors) + + output = [] + for vector in vectors: + # RSA keys for this must be long enough to accommodate the length of + # the underlying hash function. This means we can't use the keys from + # the sha1 test vectors for sha512 tests because 1024-bit keys are too + # small. Instead we parse the vectors for the test cases, then + # generate our own 2048-bit keys for each. + private, _ = vector + skey = rsa.generate_private_key(65537, 2048, backend) + pn = skey.private_numbers() + examples = private["examples"] + output.append(b"# =============================================") + output.append(b"# Example") + output.append(b"# Public key") + output.append(b"# Modulus:") + output.append(format(pn.public_numbers.n, "x")) + output.append(b"# Exponent:") + output.append(format(pn.public_numbers.e, "x")) + output.append(b"# Private key") + output.append(b"# Modulus:") + output.append(format(pn.public_numbers.n, "x")) + output.append(b"# Public exponent:") + output.append(format(pn.public_numbers.e, "x")) + output.append(b"# Exponent:") + output.append(format(pn.d, "x")) + output.append(b"# Prime 1:") + output.append(format(pn.p, "x")) + output.append(b"# Prime 2:") + output.append(format(pn.q, "x")) + output.append(b"# Prime exponent 1:") + output.append(format(pn.dmp1, "x")) + output.append(b"# Prime exponent 2:") + output.append(format(pn.dmq1, "x")) + output.append(b"# Coefficient:") + output.append(format(pn.iqmp, "x")) + pkey = skey.public_key() + vectorkey = rsa.RSAPrivateNumbers( + p=private["p"], + q=private["q"], + d=private["private_exponent"], + dmp1=private["dmp1"], + dmq1=private["dmq1"], + iqmp=private["iqmp"], + public_numbers=rsa.RSAPublicNumbers( + e=private["public_exponent"], + n=private["modulus"] + ) + ).private_key(backend) + count = 1 + + for example in examples: + message = vectorkey.decrypt( + binascii.unhexlify(example["encryption"]), + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ) + assert message == binascii.unhexlify(example["message"]) + ct = pkey.encrypt( + message, + padding.OAEP( + mgf=padding.MGF1(algorithm=mgf1alg), + algorithm=hashalg, + label=None + ) + ) + output.append( + b"# OAEP Example {0} alg={1} mgf1={2}".format( + count, hashalg.name, mgf1alg.name + ) + ) + count += 1 + output.append(b"# Message:") + output.append(example["message"]) + output.append(b"# Encryption:") + output.append(binascii.hexlify(ct)) + + return b"\n".join(output) + + +def write_file(data, filename): + with open(filename, "w") as f: + f.write(data) + + +oaep_path = os.path.join( + "asymmetric", "RSA", "pkcs-1v2-1d2-vec", "oaep-vect.txt" +) +hashalgs = [ + hashes.SHA1(), + hashes.SHA224(), + hashes.SHA256(), + hashes.SHA384(), + hashes.SHA512(), +] +for hashtuple in itertools.product(hashalgs, hashalgs): + if ( + isinstance(hashtuple[0], hashes.SHA1) and + isinstance(hashtuple[1], hashes.SHA1) + ): + continue + + write_file( + build_vectors(hashtuple[0], hashtuple[1], oaep_path), + "oaep-{0}-{1}.txt".format(hashtuple[0].name, hashtuple[1].name) + ) diff --git a/docs/development/custom-vectors/secp256k1.rst b/docs/development/custom-vectors/secp256k1.rst index b19bf4e4..e0579b4f 100644 --- a/docs/development/custom-vectors/secp256k1.rst +++ b/docs/development/custom-vectors/secp256k1.rst @@ -29,4 +29,4 @@ the following python script was run to generate the vector files. Download link: :download:`verify_secp256k1.py </development/custom-vectors/secp256k1/verify_secp256k1.py>` -.. _`pure Python ecdsa`: https://pypi.python.org/pypi/ecdsa +.. _`pure Python ecdsa`: https://pypi.org/project/ecdsa/ diff --git a/docs/development/custom-vectors/secp256k1/generate_secp256k1.py b/docs/development/custom-vectors/secp256k1/generate_secp256k1.py index 502a3ff6..d6a2071a 100644 --- a/docs/development/custom-vectors/secp256k1/generate_secp256k1.py +++ b/docs/development/custom-vectors/secp256k1/generate_secp256k1.py @@ -74,6 +74,7 @@ def write_file(lines, dest): print(line) print(line, file=dest) + source_path = os.path.join("asymmetric", "ECDSA", "FIPS_186-3", "SigGen.txt") dest_path = os.path.join("asymmetric", "ECDSA", "SECP256K1", "SigGen.txt") diff --git a/docs/development/custom-vectors/secp256k1/verify_secp256k1.py b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py index 3d2c25b9..b236d77f 100644 --- a/docs/development/custom-vectors/secp256k1/verify_secp256k1.py +++ b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py @@ -6,7 +6,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import ( - encode_rfc6979_signature + encode_dss_signature ) from tests.utils import ( @@ -27,7 +27,7 @@ def verify_one_vector(vector): message = vector['message'] x = vector['x'] y = vector['y'] - signature = encode_rfc6979_signature(vector['r'], vector['s']) + signature = encode_dss_signature(vector['r'], vector['s']) numbers = ec.EllipticCurvePublicNumbers( x, y, diff --git a/docs/development/custom-vectors/seed.rst b/docs/development/custom-vectors/seed.rst index 290fb77a..8c4a7aab 100644 --- a/docs/development/custom-vectors/seed.rst +++ b/docs/development/custom-vectors/seed.rst @@ -29,4 +29,4 @@ project's Python bindings. Download link: :download:`verify_seed.py </development/custom-vectors/seed/verify_seed.py>` -.. _`Botan`: http://botan.randombit.net +.. _`Botan`: https://botan.randombit.net diff --git a/docs/development/custom-vectors/seed/generate_seed.py b/docs/development/custom-vectors/seed/generate_seed.py index d59597fd..5c62d671 100644 --- a/docs/development/custom-vectors/seed/generate_seed.py +++ b/docs/development/custom-vectors/seed/generate_seed.py @@ -51,6 +51,7 @@ def write_file(data, filename): with open(filename, "w") as f: f.write(data) + OFB_PATH = "vectors/cryptography_vectors/ciphers/AES/OFB/OFBMMT128.rsp" write_file(build_vectors(modes.OFB, OFB_PATH), "seed-ofb.txt") CFB_PATH = "vectors/cryptography_vectors/ciphers/AES/CFB/CFB128MMT128.rsp" diff --git a/docs/development/getting-started.rst b/docs/development/getting-started.rst index b3474002..1d939a9c 100644 --- a/docs/development/getting-started.rst +++ b/docs/development/getting-started.rst @@ -1,25 +1,63 @@ Getting started =============== +Development dependencies +------------------------ Working on ``cryptography`` requires the installation of a small number of development dependencies in addition to the dependencies for :doc:`/installation`. These are listed in ``dev-requirements.txt`` and they can -be installed in a `virtualenv`_ using `pip`_. Once you've installed the -dependencies, install ``cryptography`` in ``editable`` mode. For example: +be installed in a `virtualenv`_ using `pip`_. Before you install them, follow +the **build** instructions in :doc:`/installation` (be sure to stop before +actually installing ``cryptography``). Once you've done that, install the +development dependencies, and then install ``cryptography`` in ``editable`` +mode. For example: .. code-block:: console $ # Create a virtualenv and activate it + $ # Set up your cryptography build environment $ pip install --requirement dev-requirements.txt $ pip install --editable . +Make sure that ``pip install --requirement ...`` has installed the Python +package ``vectors/`` and packages on ``tests/`` . If it didn't, you may +install them manually by using ``pip`` on each directory. + You will also need to install ``enchant`` using your system's package manager to check spelling in the documentation. +.. note:: + There is an upstream bug in ``enchant`` that prevents its installation on + Windows with 64-bit Python. See `this Github issue`_ for more information. + The easiest workaround is to use 32-bit Python for ``cryptography`` + development, even on 64-bit Windows. + You are now ready to run the tests and build the documentation. +OpenSSL on macOS +~~~~~~~~~~~~~~~~ + +You must have installed `OpenSSL`_ via `Homebrew`_ or `MacPorts`_ and must set +``CFLAGS`` and ``LDFLAGS`` environment variables before installing the +``dev-requirements.txt`` otherwise pip will fail with include errors. + +For example, with `Homebrew`_: + +.. code-block:: console + + $ env LDFLAGS="-L$(brew --prefix openssl@1.1)/lib" \ + CFLAGS="-I$(brew --prefix openssl@1.1)/include" \ + pip install --requirement ./dev-requirements.txt + +Alternatively for a static build you can specify +``CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1`` and ensure ``LDFLAGS`` points to the +absolute path for the `OpenSSL`_ libraries before calling pip. + +.. tip:: + You will also need to set these values when `Building documentation`_. + Running tests -~~~~~~~~~~~~~ +------------- ``cryptography`` unit tests are found in the ``tests/`` directory and are designed to be run using `pytest`_. `pytest`_ will discover the tests @@ -27,7 +65,7 @@ automatically, so all you have to do is: .. code-block:: console - $ py.test + $ pytest ... 62746 passed in 220.43 seconds @@ -41,10 +79,9 @@ each supported Python version and run the tests. For example: $ tox ... - ERROR: py26: InterpreterNotFound: python2.6 py27: commands succeeded ERROR: pypy: InterpreterNotFound: pypy - py33: commands succeeded + py38: commands succeeded docs: commands succeeded pep8: commands succeeded @@ -52,22 +89,8 @@ You may not have all the required Python versions installed, in which case you will see one or more ``InterpreterNotFound`` errors. -Explicit backend selection -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -While testing you may want to run tests against a subset of the backends that -cryptography supports. Explicit backend selection can be done via the -``--backend`` flag. This flag should be passed to ``py.test`` with a comma -delimited list of backend names. - - -.. code-block:: console - - $ tox -- --backend=openssl - $ py.test --backend=openssl,commoncrypto - Building documentation -~~~~~~~~~~~~~~~~~~~~~~ +---------------------- ``cryptography`` documentation is stored in the ``docs/`` directory. It is written in `reStructured Text`_ and rendered using `Sphinx`_. @@ -84,9 +107,13 @@ Use `tox`_ to build the documentation. For example: The HTML documentation index can now be found at ``docs/_build/html/index.html``. -.. _`pytest`: https://pypi.python.org/pypi/pytest -.. _`tox`: https://pypi.python.org/pypi/tox -.. _`virtualenv`: https://pypi.python.org/pypi/virtualenv -.. _`pip`: https://pypi.python.org/pypi/pip -.. _`sphinx`: https://pypi.python.org/pypi/Sphinx -.. _`reStructured Text`: http://sphinx-doc.org/rest.html +.. _`Homebrew`: https://brew.sh +.. _`MacPorts`: https://www.macports.org +.. _`OpenSSL`: https://www.openssl.org +.. _`pytest`: https://pypi.org/project/pytest/ +.. _`tox`: https://pypi.org/project/tox/ +.. _`virtualenv`: https://pypi.org/project/virtualenv/ +.. _`pip`: https://pypi.org/project/pip/ +.. _`sphinx`: https://pypi.org/project/Sphinx/ +.. _`reStructured Text`: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html +.. _`this Github issue`: https://github.com/rfk/pyenchant/issues/42 diff --git a/docs/development/index.rst b/docs/development/index.rst index f9bc9eea..81befe43 100644 --- a/docs/development/index.rst +++ b/docs/development/index.rst @@ -17,4 +17,4 @@ bug check out `what to put in your bug report`_. c-bindings .. _`GitHub`: https://github.com/pyca/cryptography -.. _`what to put in your bug report`: http://www.contribution-guide.org/#what-to-put-in-your-bug-report +.. _`what to put in your bug report`: https://www.contribution-guide.org/#what-to-put-in-your-bug-report diff --git a/docs/development/reviewing-patches.rst b/docs/development/reviewing-patches.rst index bd3ee96a..08446183 100644 --- a/docs/development/reviewing-patches.rst +++ b/docs/development/reviewing-patches.rst @@ -7,18 +7,18 @@ review is our opportunity to share knowledge, design ideas and make friends. When reviewing a patch try to keep each of these concepts in mind: -Architecture ------------- - -* Is the proposed change being made in the correct place? Is it a fix in a - backend when it should be in the primitives? - Intent ------ * What is the change being proposed? * Do we want this feature or is the bug they're fixing really a bug? +Architecture +------------ + +* Is the proposed change being made in the correct place? Is it a fix in a + backend when it should be in the primitives? + Implementation -------------- diff --git a/docs/development/submitting-patches.rst b/docs/development/submitting-patches.rst index 66105843..ac1986a3 100644 --- a/docs/development/submitting-patches.rst +++ b/docs/development/submitting-patches.rst @@ -48,7 +48,7 @@ API considerations Most projects' APIs are designed with a philosophy of "make easy things easy, and make hard things possible". One of the perils of writing cryptographic code is that secure code looks just like insecure code, and its results are almost -always indistinguishable. As a result ``cryptography`` has, as a design +always indistinguishable. As a result, ``cryptography`` has, as a design philosophy: "make it hard to do insecure things". Here are a few strategies for API design that should be both followed, and should inspire other API choices: @@ -126,6 +126,11 @@ should begin with the "Hazardous Materials" warning: .. hazmat:: +Always prefer terminology that is most broadly accepted. For example: + +* When referring to class instances use "an instance of ``Foo``" + instead of "a ``Foo`` provider". + When referring to a hypothetical individual (such as "a person receiving an encrypted message") use gender neutral pronouns (they/them/their). @@ -149,8 +154,8 @@ So, specifically: * Use Sphinx parameter/attribute documentation `syntax`_. -.. _`Write comments as complete sentences.`: http://nedbatchelder.com/blog/201401/comments_should_be_sentences.html -.. _`syntax`: http://sphinx-doc.org/domains.html#info-field-lists -.. _`Studies have shown`: https://smartbear.com/smartbear/media/pdfs/wp-cc-11-best-practices-of-peer-code-review.pdf +.. _`Write comments as complete sentences.`: https://nedbatchelder.com/blog/201401/comments_should_be_sentences.html +.. _`syntax`: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#info-field-lists +.. _`Studies have shown`: https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/ .. _`our mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev -.. _`doc8`: https://github.com/stackforge/doc8 +.. _`doc8`: https://github.com/openstack/doc8 diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index bc171b21..182df188 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -8,12 +8,23 @@ not possible ``cryptography`` has chosen to create a set of custom vectors using an official vector file as input to verify consistency between implemented backends. -Vectors are kept in the `cryptography_vectors` package rather than within our +Vectors are kept in the ``cryptography_vectors`` package rather than within our main test suite. Sources ------- +Project Wycheproof +~~~~~~~~~~~~~~~~~~ + +We run vectors from `Project Wycheproof`_ -- a collection of known edge-cases +for various cryptographic algorithms. These are not included in the repository +(or ``cryptography_vectors`` package), but rather cloned from Git in our +continuous integration environments. + +We have ensured all test vectors are used as of commit +``2196000605e45d91097147c9c71f26b72af58003``. + Asymmetric ciphers ~~~~~~~~~~~~~~~~~~ @@ -30,11 +41,16 @@ Asymmetric ciphers * PKCS #8 PEM serialization vectors from * GnuTLS: `enc-rsa-pkcs8.pem`_, `enc2-rsa-pkcs8.pem`_, - `unenc-rsa-pkcs8.pem`_, `pkcs12_s2k_pem.c`_. The contents of - `enc2-rsa-pkcs8.pem`_ was re-encrypted using a stronger PKCS#8 cipher. + `unenc-rsa-pkcs8.pem`_, `pkcs12_s2k_pem.c`_. The encoding error in + `unenc-rsa-pkcs8.pem`_ was fixed, and the contents of `enc-rsa-pkcs8.pem`_ + was re-encrypted to include it. The contents of `enc2-rsa-pkcs8.pem`_ + was re-encrypted using a stronger PKCS#8 cipher. * `Botan's ECC private keys`_. * `asymmetric/public/PKCS1/dsa.pub.pem`_ is a PKCS1 DSA public key from the Ruby test suite. +* X25519 and X448 test vectors from :rfc:`7748`. +* RSA OAEP with custom label from the `BoringSSL evp tests`_. +* Ed448 test vectors from :rfc:`8032`. Custom asymmetric vectors @@ -44,6 +60,7 @@ Custom asymmetric vectors :maxdepth: 1 custom-vectors/secp256k1 + custom-vectors/rsa-oaep-sha2 * ``asymmetric/PEM_Serialization/ec_private_key.pem`` and ``asymmetric/DER_Serialization/ec_private_key.der`` - Contains an Elliptic @@ -69,6 +86,11 @@ Custom asymmetric vectors * ``asymmetric/PEM_Serialization/dsa_public_key.pem`` and ``asymmetric/DER_Serialization/dsa_public_key.der`` - Contains a DSA 2048 bit key generated using OpenSSL from ``dsa_private_key.pem``. +* ``asymmetric/DER_Serialization/dsa_public_key_no_params.der`` - Contains a + DSA public key with the optional parameters removed. +* ``asymmetric/DER_Serialization/dsa_public_key_invalid_bit_string.der`` - + Contains a DSA public key with the bit string padding value set to 2 rather + than the required 0. * ``asymmetric/PKCS8/unenc-dsa-pkcs8.pem`` and ``asymmetric/DER_Serialization/unenc-dsa-pkcs8.der`` - Contains a DSA 1024 bit key generated using OpenSSL. @@ -82,14 +104,86 @@ Custom asymmetric vectors * ``asymmetric/public/PKCS1/rsa.pub.pem`` and ``asymmetric/public/PKCS1/rsa.pub.der`` are PKCS1 conversions of the public key from ``asymmetric/PKCS8/unenc-rsa-pkcs8.pem`` using PEM and DER encoding. +* ``x509/custom/ca/ca_key.pem`` - An unencrypted PCKS8 ``secp256r1`` key. It is + the private key for the certificate ``x509/custom/ca/ca.pem``. This key is + encoded in several of the PKCS12 custom vectors. +* ``asymmetric/EC/compressed_points.txt`` - Contains compressed public points + generated using OpenSSL. +* ``asymmetric/X448/x448-pkcs8-enc.pem`` and + ``asymmetric/X448/x448-pkcs8-enc.der`` contain an X448 key encrypted with + AES 256 CBC with the password ``password``. +* ``asymmetric/X448/x448-pkcs8.pem`` and ``asymmetric/X448/x448-pkcs8.der`` + contain an unencrypted X448 key. +* ``asymmetric/X448/x448-pub.pem`` and ``asymmetric/X448/x448-pub.der`` contain + an X448 public key. +* ``asymmetric/Ed25519/ed25519-pkcs8-enc.pem`` and + ``asymmetric/Ed25519/ed25519-pkcs8-enc.der`` contain an Ed25519 key encrypted + with AES 256 CBC with the password ``password``. +* ``asymmetric/Ed25519/ed25519-pkcs8.pem`` and + ``asymmetric/Ed25519/ed25519-pkcs8.der`` contain an unencrypted Ed25519 key. +* ``asymmetric/Ed25519/ed25519-pub.pem`` and + ``asymmetric/Ed25519/ed25519-pub.der`` contain an Ed25519 public key. +* ``asymmetric/X25519/x25519-pkcs8-enc.pem`` and + ``asymmetric/X25519/x25519-pkcs8-enc.der`` contain an X25519 key encrypted + with AES 256 CBC with the password ``password``. +* ``asymmetric/X25519/x25519-pkcs8.pem`` and + ``asymmetric/X25519/x25519-pkcs8.der`` contain an unencrypted X25519 key. +* ``asymmetric/X25519/x25519-pub.pem`` and ``asymmetric/X25519/x25519-pub.der`` + contain an X25519 public key. +* ``asymmetric/Ed448/ed448-pkcs8-enc.pem`` and + ``asymmetric/Ed448/ed448-pkcs8-enc.der`` contain an Ed448 key encrypted + with AES 256 CBC with the password ``password``. +* ``asymmetric/Ed448/ed448-pkcs8.pem`` and + ``asymmetric/Ed448/ed448-pkcs8.der`` contain an unencrypted Ed448 key. +* ``asymmetric/Ed448/ed448-pub.pem`` and ``asymmetric/Ed448/ed448-pub.der`` + contain an Ed448 public key. Key exchange ~~~~~~~~~~~~ +* ``vectors/cryptography_vectors/asymmetric/DH/rfc3526.txt`` contains + several standardized Diffie-Hellman groups from :rfc:`3526`. + * ``vectors/cryptography_vectors/asymmetric/DH/RFC5114.txt`` contains Diffie-Hellman examples from appendix A.1, A.2 and A.3 of :rfc:`5114`. +* ``vectors/cryptography_vectors/asymmetric/DH/vec.txt`` contains + Diffie-Hellman examples from `botan`_. + +* ``vectors/cryptography_vectors/asymmetric/DH/bad_exchange.txt`` contains + Diffie-Hellman vector pairs that were generated using OpenSSL + ``DH_generate_parameters_ex`` and ``DH_generate_key``. + +* ``vectors/cryptography_vectors/asymmetric/DH/dhp.pem``, + ``vectors/cryptography_vectors/asymmetric/DH/dhkey.pem`` and + ``vectors/cryptography_vectors/asymmetric/DH/dhpub.pem`` contains + Diffie-Hellman parameters and key respectively. The keys were + generated using OpenSSL following `DHKE`_ guide. + ``vectors/cryptography_vectors/asymmetric/DH/dhkey.txt`` contains + all parameter in text. + ``vectors/cryptography_vectors/asymmetric/DH/dhp.der``, + ``vectors/cryptography_vectors/asymmetric/DH/dhkey.der`` and + ``vectors/cryptography_vectors/asymmetric/DH/dhpub.der`` contains + are the above parameters and keys in DER format. + +* ``vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.pem``, + ``vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.pem`` and + ``vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.pem`` contains + Diffie-Hellman parameters and key respectively. The keys were + generated using OpenSSL following `DHKE`_ guide. When creating the + parameters we added the `-pkeyopt dh_rfc5114:2` option to use + :rfc:`5114` 2048 bit DH parameters with 224 bit subgroup. + ``vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.txt`` contains + all parameter in text. + ``vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.der``, + ``vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.der`` and + ``vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.der`` contains + are the above parameters and keys in DER format. + +* ``vectors/cryptoraphy_vectors/asymmetric/ECDH/brainpool.txt`` contains + Brainpool vectors from :rfc:`7027`. + X.509 ~~~~~ @@ -102,8 +196,57 @@ X.509 tree. * ``cryptography.io.pem`` - A leaf certificate issued by RapidSSL for the cryptography website. +* ``rapidssl_sha256_ca_g3.pem`` - The intermediate CA that issued the + ``cryptography.io.pem`` certificate. +* ``cryptography.io.precert.pem`` - A pre-certificate with the CT poison + extension for the cryptography website. +* ``cryptography-scts.io.pem`` - A leaf certificate issued by Let's Encrypt for + the cryptography website which contains signed certificate timestamps. * ``wildcard_san.pem`` - A leaf certificate issued by a public CA for ``langui.sh`` that contains wildcard entries in the SAN extension. +* ``san_edipartyname.der`` - A DSA certificate from a `Mozilla bug`_ + containing a SAN extension with an ``ediPartyName`` general name. +* ``san_x400address.der`` - A DSA certificate from a `Mozilla bug`_ containing + a SAN extension with an ``x400Address`` general name. +* ``department-of-state-root.pem`` - The intermediary CA for the Department of + State, issued by the United States Federal Government's Common Policy CA. + Notably has a ``critical`` policy constraints extensions. +* ``e-trust.ru.der`` - A certificate from a `Russian CA`_ signed using the GOST + cipher and containing numerous unusual encodings such as NUMERICSTRING in + the subject DN. +* ``alternate-rsa-sha1-oid.pem`` - A certificate from an + `unknown signature OID`_ Mozilla bug that uses an alternate signature OID for + RSA with SHA1. +* ``badssl-sct.pem`` - A certificate with the certificate transparency signed + certificate timestamp extension. +* ``bigoid.pem`` - A certificate with a rather long OID in the + Certificate Policies extension. We need to make sure we can parse + long OIDs. +* ``wosign-bc-invalid.pem`` - A certificate issued by WoSign that contains + a basic constraints extension with CA set to false and a path length of zero + in violation of :rfc:`5280`. +* ``tls-feature-ocsp-staple.pem`` - A certificate issued by Let's Encrypt that + contains a TLS Feature extension with the ``status_request`` feature + (commonly known as OCSP Must-Staple). +* ``unique-identifier.pem`` - A certificate containing + a distinguished name with an ``x500UniqueIdentifier``. +* ``utf8-dnsname.pem`` - A certificate containing non-ASCII characters in the + DNS name entries of the SAN extension. +* ``badasn1time.pem`` - A certificate containing an incorrectly specified + UTCTime in its validity->not_after. +* ``letsencryptx3.pem`` - A subordinate certificate used by Let's Encrypt to + issue end entity certificates. +* ``ed25519-rfc8410.pem`` - A certificate containing an X25519 public key with + an ``ed25519`` signature taken from :rfc:`8410`. +* ``root-ed25519.pem`` - An ``ed25519`` root certificate (``ed25519`` signature + with ``ed25519`` public key) from the OpenSSL test suite. + (`root-ed25519.pem`_) +* ``server-ed25519-cert.pem`` - An ``ed25519`` server certificate (RSA + signature with ``ed25519`` public key) from the OpenSSL test suite. + (`server-ed25519-cert.pem`_) +* ``server-ed448-cert.pem`` - An ``ed448`` server certificate (RSA + signature with ``ed448`` public key) from the OpenSSL test suite. + (`server-ed448-cert.pem`_) Custom X.509 Vectors ~~~~~~~~~~~~~~~~~~~~ @@ -135,6 +278,10 @@ Custom X.509 Vectors * ``unsupported_extension.pem`` - An RSA 2048 bit self-signed certificate containing an unsupported extension type. The OID was encoded as "1.2.3.4" with an ``extnValue`` of "value". +* ``unsupported_extension_2.pem`` - A ``secp256r1`` certificate + containing two unsupported extensions. The OIDs are ``1.3.6.1.4.1.41482.2`` + with an ``extnValue`` of ``1.3.6.1.4.1.41482.1.2`` and + ``1.3.6.1.4.1.45724.2.1.1`` with an ``extnValue`` of ``\x03\x02\x040`` * ``unsupported_extension_critical.pem`` - An RSA 2048 bit self-signed certificate containing an unsupported extension type marked critical. The OID was encoded as "1.2.3.4" with an ``extnValue`` of "value". @@ -142,6 +289,9 @@ Custom X.509 Vectors certificate containing a subject alternative name extension with the following general names: ``rfc822Name``, ``dNSName``, ``iPAddress``, ``directoryName``, and ``uniformResourceIdentifier``. +* ``san_empty_hostname.pem`` - An RSA 2048 bit self-signed certificate + containing a subject alternative extension with an empty ``dNSName`` + general name. * ``san_other_name.pem`` - An RSA 2048 bit self-signed certificate containing a subject alternative name extension with the ``otherName`` general name. * ``san_registered_id.pem`` - An RSA 1024 bit certificate containing a @@ -187,6 +337,9 @@ Custom X.509 Vectors containing an authority information access extension with an OCSP entry. * ``aia_ca_issuers.pem`` - An RSA 2048 bit self-signed certificate containing an authority information access extension with a CA issuers entry. +* ``cdp_empty_hostname.pem`` - An RSA 2048 bit self-signed certificate + containing a CRL distribution point extension with ``fullName`` URI without + a hostname. * ``cdp_fullname_reasons_crl_issuer.pem`` - An RSA 1024 bit certificate containing a CRL distribution points extension with ``fullName``, ``cRLIssuer``, and ``reasons`` data. @@ -199,11 +352,25 @@ Custom X.509 Vectors set. * ``nc_permitted_excluded.pem`` - An RSA 2048 bit self-signed certificate containing a name constraints extension with both permitted and excluded - elements. + elements. Contains ``IPv4`` and ``IPv6`` addresses with network mask as well + as ``dNSName`` with a leading period. +* ``nc_permitted_excluded_2.pem`` - An RSA 2048 bit self-signed certificate + containing a name constraints extension with both permitted and excluded + elements. Unlike ``nc_permitted_excluded.pem``, the general names do not + contain any name constraints specific values. * ``nc_permitted.pem`` - An RSA 2048 bit self-signed certificate containing a name constraints extension with permitted elements. +* ``nc_permitted_2.pem`` - An RSA 2048 bit self-signed certificate containing a + name constraints extension with permitted elements that do not contain any + name constraints specific values. * ``nc_excluded.pem`` - An RSA 2048 bit self-signed certificate containing a name constraints extension with excluded elements. +* ``nc_invalid_ip_netmask.pem`` - An RSA 2048 bit self-signed certificate + containing a name constraints extension with a permitted element that has an + ``IPv6`` IP and an invalid network mask. +* ``nc_single_ip_netmask.pem`` - An RSA 2048 bit self-signed certificate + containing a name constraints extension with a permitted element that has two + IPs with ``/32`` and ``/128`` network masks. * ``cp_user_notice_with_notice_reference.pem`` - An RSA 2048 bit self-signed certificate containing a certificate policies extension with a notice reference in the user notice. @@ -215,6 +382,8 @@ Custom X.509 Vectors * ``cp_user_notice_no_explicit_text.pem`` - An RSA 2048 bit self-signed certificate containing a certificate policies extension with a user notice with no explicit text. +* ``cp_invalid.pem`` - An RSA 2048 bit self-signed certificate containing a + certificate policies extension with invalid data. * ``ian_uri.pem`` - An RSA 2048 bit certificate containing an issuer alternative name extension with a ``URI`` general name. * ``ocsp_nocheck.pem`` - An RSA 2048 bit self-signed certificate containing @@ -226,6 +395,20 @@ Custom X.509 Vectors policy constraints extension with an inhibit policy mapping element. * ``pc_require.pem`` - An RSA 2048 bit self-signed certificate containing a policy constraints extension with a require explicit policy element. +* ``unsupported_subject_public_key_info.pem`` - A certificate whose public key + is an unknown OID (``1.3.6.1.4.1.8432.1.1.2``). +* ``policy_constraints_explicit.pem`` - A self-signed certificate containing + a ``policyConstraints`` extension with a ``requireExplicitPolicy`` value. +* ``freshestcrl.pem`` - A self-signed certificate containing a ``freshestCRL`` + extension. +* ``ca/ca.pem`` - A self-signed certificate with ``basicConstraints`` set to + true. Its private key is ``ca/ca_key.pem``. This certificate is encoded in + several of the PKCS12 custom vectors. +* ``negative_serial.pem`` - A certificate with a serial number that is a + negative number. +* ``rsa_pss.pem`` - A certificate with an RSA PSS signature. +* ``root-ed448.pem`` - An ``ed448`` self-signed CA certificate + using ``ed448-pkcs8.pem`` as key. Custom X.509 Request Vectors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -244,7 +427,7 @@ Custom X.509 Request Vectors request using RSA and SHA1 with a subject alternative name extension generated using OpenSSL. * ``two_basic_constraints.pem`` - A certificate signing request - for a RSA 2048 bit key containing two basic constraints extensions. + for an RSA 2048 bit key containing two basic constraints extensions. * ``unsupported_extension.pem`` - A certificate signing request for an RSA 2048 bit key containing containing an unsupported extension type. The OID was encoded as "1.2.3.4" with an @@ -253,9 +436,13 @@ Custom X.509 Request Vectors request for an RSA 2048 bit key containing containing an unsupported extension type marked critical. The OID was encoded as "1.2.3.4" with an ``extnValue`` of "value". -* ``basic_constraints.pem`` - A certificate signing request for a RSA +* ``basic_constraints.pem`` - A certificate signing request for an RSA 2048 bit key containing a basic constraints extension marked as critical. +* ``invalid_signature.pem`` - A certificate signing request for an RSA + 1024 bit key containing an invalid signature with correct padding. +* ``challenge.pem`` - A certificate signing request for an RSA 2048 bit key + containing a challenge password. Custom X.509 Certificate Revocation List Vectors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -272,6 +459,137 @@ Custom X.509 Certificate Revocation List Vectors to "1.2.3.4". The CRL uses an unsupported MD2 signature algorithm. * ``crl_unsupported_reason.pem`` - Contains a CRL with one revocation which has an unsupported reason code. +* ``crl_inval_cert_issuer_entry_ext.pem`` - Contains a CRL with one revocation + which has one entry extension for certificate issuer with an empty value. +* ``crl_empty.pem`` - Contains a CRL with no revoked certificates. +* ``crl_ian_aia_aki.pem`` - Contains a CRL with ``IssuerAlternativeName``, + ``AuthorityInformationAccess``, ``AuthorityKeyIdentifier`` and ``CRLNumber`` + extensions. +* ``valid_signature.pem`` - Contains a CRL with the public key which was used + to generate it. +* ``invalid_signature.pem`` - Contains a CRL with the last signature byte + incremented by 1 to produce an invalid signature, and the public key which + was used to generate it. +* ``crl_delta_crl_indicator.pem`` - Contains a CRL with the + ``DeltaCRLIndicator`` extension. +* ``crl_idp_fullname_only.pem`` - Contains a CRL with an + ``IssuingDistributionPoints`` extension with only a ``fullname`` for the + distribution point. +* ``crl_idp_only_ca.pem`` - Contains a CRL with an + ``IssuingDistributionPoints`` extension that is only valid for CA certificate + revocation. +* ``crl_idp_fullname_only_aa.pem`` - Contains a CRL with an + ``IssuingDistributionPoints`` extension that sets a ``fullname`` and is only + valid for attribute certificate revocation. +* ``crl_idp_fullname_only_user.pem`` - Contains a CRL with an + ``IssuingDistributionPoints`` extension that sets a ``fullname`` and is only + valid for user certificate revocation. +* ``crl_idp_fullname_indirect_crl.pem`` - Contains a CRL with an + ``IssuingDistributionPoints`` extension that sets a ``fullname`` and the + indirect CRL flag. +* ``crl_idp_reasons_only.pem`` - Contains a CRL with an + ``IssuingDistributionPoints`` extension that is only valid for revocations + with the ``keyCompromise`` reason. +* ``crl_idp_relative_user_all_reasons.pem`` - Contains a CRL with an + ``IssuingDistributionPoints`` extension that sets all revocation reasons as + allowed. +* ``crl_idp_relativename_only.pem`` - Contains a CRL with an + ``IssuingDistributionPoints`` extension with only a ``relativename`` for + the distribution point. + +X.509 OCSP Test Vectors +~~~~~~~~~~~~~~~~~~~~~~~ +* ``x509/ocsp/resp-sha256.der`` - An OCSP response for ``cryptography.io`` with + a SHA256 signature. +* ``x509/ocsp/resp-unauthorized.der`` - An OCSP response with an unauthorized + status. +* ``x509/ocsp/resp-revoked.der`` - An OCSP response for ``revoked.badssl.com`` + with a revoked status. +* ``x509/ocsp/resp-delegate-unknown-cert.der`` - An OCSP response for an + unknown cert from ``AC Camerafirma``. This response also contains a delegate + certificate. +* ``x509/ocsp/resp-responder-key-hash.der`` - An OCSP response from the + ``DigiCert`` OCSP responder that uses a key hash for the responder ID. +* ``x509/ocsp/resp-revoked-reason.der`` - An OCSP response from the + ``QuoVadis`` OCSP responder that contains a revoked certificate with a + revocation reason. +* ``x509/ocsp/resp-revoked-no-next-update.der`` - An OCSP response that + contains a revoked certificate and no ``nextUpdate`` value. +* ``x509/ocsp/resp-invalid-signature-oid.der`` - An OCSP response that was + modified to contain an MD2 signature algorithm object identifier. +* ``x509/ocsp/resp-single-extension-reason.der`` - An OCSP response that + contains a ``CRLReason`` single extension. +* ``x509/ocsp/resp-sct-extension.der`` - An OCSP response containing a + ``CT Certificate SCTs`` single extension, from the SwissSign OCSP responder. + +Custom X.509 OCSP Test Vectors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* ``x509/ocsp/req-sha1.der`` - An OCSP request containing a single request and + using SHA1 as the hash algorithm. +* ``x509/ocsp/req-multi-sha1.der`` - An OCSP request containing multiple + requests. +* ``x509/ocsp/req-invalid-hash-alg.der`` - An OCSP request containing an + invalid hash algorithm OID. +* ``x509/ocsp/req-ext-nonce.der`` - An OCSP request containing a nonce + extension. + +Custom PKCS12 Test Vectors +~~~~~~~~~~~~~~~~~~~~~~~~~~ +* ``pkcs12/cert-key-aes256cbc.p12`` - A PKCS12 file containing a cert + (``x509/custom/ca/ca.pem``) and key (``x509/custom/ca/ca_key.pem``) + both encrypted with AES 256 CBC with the password ``cryptography``. +* ``pkcs12/cert-none-key-none.p12`` - A PKCS12 file containing a cert + (``x509/custom/ca/ca.pem``) and key (``x509/custom/ca/ca_key.pem``) + with no encryption. The password (used for integrity checking only) is + ``cryptography``. +* ``pkcs12/cert-rc2-key-3des.p12`` - A PKCS12 file containing a cert + (``x509/custom/ca/ca.pem``) encrypted with RC2 and key + (``x509/custom/ca/ca_key.pem``) encrypted via 3DES with the password + ``cryptography``. +* ``pkcs12/no-password.p12`` - A PKCS12 file containing a cert + (``x509/custom/ca/ca.pem``) and key (``x509/custom/ca/ca_key.pem``) with no + encryption and no password. +* ``pkcs12/no-cert-key-aes256cbc.p12`` - A PKCS12 file containing a key + (``x509/custom/ca/ca_key.pem``) encrypted via AES 256 CBC with the + password ``cryptography`` and no certificate. +* ``pkcs12/cert-aes256cbc-no-key.p12`` - A PKCS12 file containing a cert + (``x509/custom/ca/ca.pem``) encrypted via AES 256 CBC with the + password ``cryptography`` and no private key. + +Custom OpenSSH Test Vectors +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Generated by +``asymmetric/OpenSSH/gen.sh`` +using command-line tools from OpenSSH_7.6p1 package. + +* ``dsa-nopsw.key``, ``dsa-nopsw.key.pub``, ``dsa-nopsw.key-cert.pub`` - + DSA-1024 private key; and corresponding public key in plain format + and with self-signed certificate. +* ``dsa-psw.key``, ``dsa-psw.key.pub`` - + Password-protected DSA-1024 private key and corresponding public key. + Password is "password". +* ``ecdsa-nopsw.key``, ``ecdsa-nopsw.key.pub``, + ``ecdsa-nopsw.key-cert.pub`` - + SECP256R1 private key; and corresponding public key in plain format + and with self-signed certificate. +* ``ecdsa-psw.key``, ``ecdsa-psw.key.pub`` - + Password-protected SECP384R1 private key and corresponding public key. + Password is "password". +* ``ed25519-nopsw.key``, ``ed25519-nopsw.key.pub``, + ``ed25519-nopsw.key-cert.pub`` - + Ed25519 private key; and corresponding public key in plain format + and with self-signed certificate. +* ``ed25519-psw.key``, ``ed25519-psw.key.pub`` - + Password-protected Ed25519 private key and corresponding public key. + Password is "password". +* ``rsa-nopsw.key``, ``rsa-nopsw.key.pub``, + ``rsa-nopsw.key-cert.pub`` - + RSA-2048 private key; and corresponding public key in plain format + and with self-signed certificate. +* ``rsa-psw.key``, ``rsa-psw.key.pub`` - + Password-protected RSA-2048 private key and corresponding public key. + Password is "password". Hashes ~~~~~~ @@ -279,8 +597,10 @@ Hashes * MD5 from :rfc:`1321`. * RIPEMD160 from the `RIPEMD website`_. * SHA1 from `NIST CAVP`_. -* SHA2 (224, 256, 384, 512) from `NIST CAVP`_. -* Whirlpool from the `Whirlpool website`_. +* SHA2 (224, 256, 384, 512, 512/224, 512/256) from `NIST CAVP`_. +* SHA3 (224, 256, 384, 512) from `NIST CAVP`_. +* SHAKE (128, 256) from `NIST CAVP`_. +* Blake2s and Blake2b from OpenSSL `test/evptests.txt`_. HMAC ~~~~ @@ -296,11 +616,15 @@ Key derivation functions * HKDF (SHA1, SHA256) from :rfc:`5869`. * PBKDF2 (HMAC-SHA1) from :rfc:`6070`. * scrypt from the `draft RFC`_. +* X9.63 KDF from `NIST CAVP`_. +* SP 800-108 Counter Mode KDF (HMAC-SHA1, HMAC-SHA224, HMAC-SHA256, + HMAC-SHA384, HMAC-SHA512) from `NIST CAVP`_. Key wrapping ~~~~~~~~~~~~ * AES key wrap (AESKW) and 3DES key wrap test vectors from `NIST CAVP`_. +* AES key wrap with padding vectors from `Botan's key wrap vectors`_. Recipes ~~~~~~~ @@ -310,16 +634,21 @@ Recipes Symmetric ciphers ~~~~~~~~~~~~~~~~~ -* AES (CBC, CFB, ECB, GCM, OFB) from `NIST CAVP`_. +* AES (CBC, CFB, ECB, GCM, OFB, CCM) 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`_. * CAST5 (ECB) from :rfc:`2144`. * CAST5 (CBC, CFB, OFB) generated by this project. See: :doc:`/development/custom-vectors/cast5` +* ChaCha20 from :rfc:`7539`. +* ChaCha20Poly1305 from :rfc:`7539`, `OpenSSL's evpciph.txt`_, and the + `BoringSSL ChaCha20Poly1305 tests`_. * IDEA (ECB) from the `NESSIE IDEA vectors`_ created by `NESSIE`_. * IDEA (CBC, CFB, OFB) generated by this project. See: :doc:`/development/custom-vectors/idea` @@ -340,21 +669,29 @@ CMAC * AES-128, AES-192, AES-256, 3DES from `NIST SP-800-38B`_ +Poly1305 +~~~~~~~~ + +* Test vectors from :rfc:`7539`. + Creating test vectors --------------------- When official vectors are unavailable ``cryptography`` may choose to build its own using existing vectors as source material. -Custom Symmetric Vectors -~~~~~~~~~~~~~~~~~~~~~~~~ +Created Vectors +~~~~~~~~~~~~~~~ .. toctree:: :maxdepth: 1 + custom-vectors/arc4 custom-vectors/cast5 custom-vectors/idea custom-vectors/seed + custom-vectors/hkdf + If official test vectors appear in the future the custom generated vectors should be discarded. @@ -369,18 +706,21 @@ header format (substituting the correct information): # Verified against the CommonCrypto and Go crypto packages # Key Length : 128 -.. _`NIST`: http://www.nist.gov/ +.. _`NIST`: https://www.nist.gov/ .. _`IETF`: https://www.ietf.org/ -.. _`NIST CAVP`: http://csrc.nist.gov/groups/STM/cavp/ +.. _`Project Wycheproof`: https://github.com/google/wycheproof +.. _`NIST CAVP`: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program .. _`Bruce Schneier's vectors`: https://www.schneier.com/code/vectors.txt -.. _`Camellia page`: http://info.isl.ntt.co.jp/crypt/eng/camellia/ -.. _`CRYPTREC`: http://www.cryptrec.go.jp +.. _`Camellia page`: https://info.isl.ntt.co.jp/crypt/eng/camellia/ +.. _`CRYPTREC`: https://www.cryptrec.go.jp .. _`OpenSSL's test vectors`: https://github.com/openssl/openssl/blob/97cf1f6c2854a3a955fd7dd3a1f113deba00c9ef/crypto/evp/evptests.txt#L232 -.. _`RIPEMD website`: http://homes.esat.kuleuven.be/~bosselae/ripemd160.html -.. _`Whirlpool website`: http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html +.. _`OpenSSL's evpciph.txt`: https://github.com/openssl/openssl/blob/5a7bc0be97dee9ac715897fe8180a08e211bc6ea/test/evpciph.txt#L2362 +.. _`BoringSSL ChaCha20Poly1305 tests`: https://boringssl.googlesource.com/boringssl/+/2e2a226ac9201ac411a84b5e79ac3a7333d8e1c9/crypto/cipher_extra/test/chacha20_poly1305_tests.txt +.. _`BoringSSL evp tests`: https://boringssl.googlesource.com/boringssl/+/ce3773f9fe25c3b54390bc51d72572f251c7d7e6/crypto/evp/evp_tests.txt +.. _`RIPEMD website`: https://homes.esat.kuleuven.be/~bosselae/ripemd160.html .. _`draft RFC`: https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01 .. _`Specification repository`: https://github.com/fernet/spec -.. _`errata`: http://www.rfc-editor.org/errata_search.php?rfc=6238 +.. _`errata`: https://www.rfc-editor.org/errata_search.php?rfc=6238 .. _`OpenSSL example key`: https://github.com/openssl/openssl/blob/d02b48c63a58ea4367a0e905979f140b7d090f86/test/testrsa.pem .. _`GnuTLS key parsing tests`: https://gitlab.com/gnutls/gnutls/commit/f16ef39ef0303b02d7fa590a37820440c466ce8d .. _`enc-rsa-pkcs8.pem`: https://gitlab.com/gnutls/gnutls/blob/f8d943b38bf74eaaa11d396112daf43cb8aa82ae/tests/pkcs8-decode/encpkcs8.pem @@ -391,10 +731,20 @@ header format (substituting the correct information): .. _`GnuTLS example keys`: https://gitlab.com/gnutls/gnutls/commit/ad2061deafdd7db78fd405f9d143b0a7c579da7b .. _`NESSIE IDEA vectors`: https://www.cosic.esat.kuleuven.be/nessie/testvectors/bc/idea/Idea-128-64.verified.test-vectors .. _`NESSIE`: https://en.wikipedia.org/wiki/NESSIE -.. _`Ed25519 website`: http://ed25519.cr.yp.to/software.html -.. _`NIST SP-800-38B`: http://csrc.nist.gov/publications/nistpubs/800-38B/Updated_CMAC_Examples.pdf -.. _`NIST PKI Testing`: http://csrc.nist.gov/groups/ST/crypto_apps_infra/pki/pkitesting.html +.. _`Ed25519 website`: https://ed25519.cr.yp.to/software.html +.. _`NIST SP-800-38B`: https://csrc.nist.gov/publications/detail/sp/800-38b/archive/2005-05-01 +.. _`NIST PKI Testing`: https://csrc.nist.gov/Projects/PKI-Testing .. _`testx509.pem`: https://github.com/openssl/openssl/blob/master/test/testx509.pem .. _`DigiCert Global Root G3`: http://cacerts.digicert.com/DigiCertGlobalRootG3.crt .. _`root data`: https://hg.mozilla.org/projects/nss/file/25b2922cc564/security/nss/lib/ckfw/builtins/certdata.txt#l2053 .. _`asymmetric/public/PKCS1/dsa.pub.pem`: https://github.com/ruby/ruby/blob/4ccb387f3bc436a08fc6d72c4931994f5de95110/test/openssl/test_pkey_dsa.rb#L53 +.. _`Mozilla bug`: https://bugzilla.mozilla.org/show_bug.cgi?id=233586 +.. _`Russian CA`: https://e-trust.gosuslugi.ru/MainCA +.. _`test/evptests.txt`: https://github.com/openssl/openssl/blob/2d0b44126763f989a4cbffbffe9d0c7518158bb7/test/evptests.txt +.. _`unknown signature OID`: https://bugzilla.mozilla.org/show_bug.cgi?id=405966 +.. _`botan`: https://github.com/randombit/botan/blob/57789bdfc55061002b2727d0b32587612829a37c/src/tests/data/pubkey/dh.vec +.. _`DHKE`: https://sandilands.info/sgordon/diffie-hellman-secret-key-exchange-with-openssl +.. _`Botan's key wrap vectors`: https://github.com/randombit/botan/blob/737f33c09a18500e044dca3e2ae13bd2c08bafdd/src/tests/data/keywrap/nist_key_wrap.vec +.. _`root-ed25519.pem`: https://github.com/openssl/openssl/blob/2a1e2fe145c6eb8e75aa2e1b3a8c3a49384b2852/test/certs/root-ed25519.pem +.. _`server-ed25519-cert.pem`: https://github.com/openssl/openssl/blob/2a1e2fe145c6eb8e75aa2e1b3a8c3a49384b2852/test/certs/server-ed25519-cert.pem +.. _`server-ed448-cert.pem`: https://github.com/openssl/openssl/blob/2a1e2fe145c6eb8e75aa2e1b3a8c3a49384b2852/test/certs/server-ed448-cert.pem |
