aboutsummaryrefslogtreecommitdiffstats
path: root/3rdparty/pybind11/tests/test_factory_constructors.cpp
blob: f42d1f29b93580063271a6ce674cd49b77e9a43e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/*
    tests/test_factory_constructors.cpp -- tests construction from a factory function
                                           via py::init_factory()

    Copyright (c) 2017 Jason Rhinelander <jason@imaginary.ca>

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in the LICENSE file.
*/

#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <cmath>
#include <new>

// Classes for testing python construction via C++ factory function:
// Not publicly constructible, copyable, or movable:
class TestFactory1 {
    friend class TestFactoryHelper;
    TestFactory1() : value("(empty)") { print_default_created(this); }
    TestFactory1(int v) : value(std::to_string(v)) { print_created(this, value); }
    TestFactory1(std::string v) : value(std::move(v)) { print_created(this, value); }
    TestFactory1(TestFactory1 &&) = delete;
    TestFactory1(const TestFactory1 &) = delete;
    TestFactory1 &operator=(TestFactory1 &&) = delete;
    TestFactory1 &operator=(const TestFactory1 &) = delete;
public:
    std::string value;
    ~TestFactory1() { print_destroyed(this); }
};
// Non-public construction, but moveable:
class TestFactory2 {
    friend class TestFactoryHelper;
    TestFactory2() : value("(empty2)") { print_default_created(this); }
    TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); }
    TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); }
public:
    TestFactory2(TestFactory2 &&m) { value = std::move(m.value); print_move_created(this); }
    TestFactory2 &operator=(TestFactory2 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; }
    std::string value;
    ~TestFactory2() { print_destroyed(this); }
};
// Mixed direct/factory construction:
class TestFactory3 {
protected:
    friend class TestFactoryHelper;
    TestFactory3() : value("(empty3)") { print_default_created(this); }
    TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); }
public:
    TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); }
    TestFactory3(TestFactory3 &&m) { value = std::move(m.value); print_move_created(this); }
    TestFactory3 &operator=(TestFactory3 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; }
    std::string value;
    virtual ~TestFactory3() { print_destroyed(this); }
};
// Inheritance test
class TestFactory4 : public TestFactory3 {
public:
    TestFactory4() : TestFactory3() { print_default_created(this); }
    TestFactory4(int v) : TestFactory3(v) { print_created(this, v); }
    ~TestFactory4() override { print_destroyed(this); }
};
// Another class for an invalid downcast test
class TestFactory5 : public TestFactory3 {
public:
    TestFactory5(int i) : TestFactory3(i) { print_created(this, i); }
    ~TestFactory5() override { print_destroyed(this); }
};

class TestFactory6 {
protected:
    int value;
    bool alias = false;
public:
    TestFactory6(int i) : value{i} { print_created(this, i); }
    TestFactory6(TestFactory6 &&f) { print_move_created(this); value = f.value; alias = f.alias; }
    TestFactory6(const TestFactory6 &f) { print_copy_created(this); value = f.value; alias = f.alias; }
    virtual ~TestFactory6() { print_destroyed(this); }
    virtual int get() { return value; }
    bool has_alias() { return alias; }
};
class PyTF6 : public TestFactory6 {
public:
    // Special constructor that allows the factory to construct a PyTF6 from a TestFactory6 only
    // when an alias is needed:
    PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) { alias = true; print_created(this, "move", value); }
    PyTF6(int i) : TestFactory6(i) { alias = true; print_created(this, i); }
    PyTF6(PyTF6 &&f) : TestFactory6(std::move(f)) { print_move_created(this); }
    PyTF6(const PyTF6 &f) : TestFactory6(f) { print_copy_created(this); }
    PyTF6(std::string s) : TestFactory6((int) s.size()) { alias = true; print_created(this, s); }
    ~PyTF6() override { print_destroyed(this); }
    int get() override { PYBIND11_OVERRIDE(int, TestFactory6, get, /*no args*/); }
};

class TestFactory7 {
protected:
    int value;
    bool alias = false;
public:
    TestFactory7(int i) : value{i} { print_created(this, i); }
    TestFactory7(TestFactory7 &&f) { print_move_created(this); value = f.value; alias = f.alias; }
    TestFactory7(const TestFactory7 &f) { print_copy_created(this); value = f.value; alias = f.alias; }
    virtual ~TestFactory7() { print_destroyed(this); }
    virtual int get() { return value; }
    bool has_alias() { return alias; }
};
class PyTF7 : public TestFactory7 {
public:
    PyTF7(int i) : TestFactory7(i) { alias = true; print_created(this, i); }
    PyTF7(PyTF7 &&f) : TestFactory7(std::move(f)) { print_move_created(this); }
    PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); }
    ~PyTF7() override { print_destroyed(this); }
    int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); }
};


class TestFactoryHelper {
public:
    // Non-movable, non-copyable type:
    // Return via pointer:
    static TestFactory1 *construct1() { return new TestFactory1(); }
    // Holder:
    static std::unique_ptr<TestFactory1> construct1(int a) { return std::unique_ptr<TestFactory1>(new TestFactory1(a)); }
    // pointer again
    static TestFactory1 *construct1_string(std::string a) { return new TestFactory1(a); }

    // Moveable type:
    // pointer:
    static TestFactory2 *construct2() { return new TestFactory2(); }
    // holder:
    static std::unique_ptr<TestFactory2> construct2(int a) { return std::unique_ptr<TestFactory2>(new TestFactory2(a)); }
    // by value moving:
    static TestFactory2 construct2(std::string a) { return TestFactory2(a); }

    // shared_ptr holder type:
    // pointer:
    static TestFactory3 *construct3() { return new TestFactory3(); }
    // holder:
    static std::shared_ptr<TestFactory3> construct3(int a) { return std::shared_ptr<TestFactory3>(new TestFactory3(a)); }
};

TEST_SUBMODULE(factory_constructors, m) {

    // Define various trivial types to allow simpler overload resolution:
    py::module_ m_tag = m.def_submodule("tag");
#define MAKE_TAG_TYPE(Name) \
    struct Name##_tag {}; \
    py::class_<Name##_tag>(m_tag, #Name "_tag").def(py::init<>()); \
    m_tag.attr(#Name) = py::cast(Name##_tag{})
    MAKE_TAG_TYPE(pointer);
    MAKE_TAG_TYPE(unique_ptr);
    MAKE_TAG_TYPE(move);
    MAKE_TAG_TYPE(shared_ptr);
    MAKE_TAG_TYPE(derived);
    MAKE_TAG_TYPE(TF4);
    MAKE_TAG_TYPE(TF5);
    MAKE_TAG_TYPE(null_ptr);
    MAKE_TAG_TYPE(null_unique_ptr);
    MAKE_TAG_TYPE(null_shared_ptr);
    MAKE_TAG_TYPE(base);
    MAKE_TAG_TYPE(invalid_base);
    MAKE_TAG_TYPE(alias);
    MAKE_TAG_TYPE(unaliasable);
    MAKE_TAG_TYPE(mixed);

    // test_init_factory_basic, test_bad_type
    py::class_<TestFactory1>(m, "TestFactory1")
        .def(py::init([](unique_ptr_tag, int v) { return TestFactoryHelper::construct1(v); }))
        .def(py::init(&TestFactoryHelper::construct1_string)) // raw function pointer
        .def(py::init([](pointer_tag) { return TestFactoryHelper::construct1(); }))
        .def(py::init([](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); }))
        .def_readwrite("value", &TestFactory1::value)
        ;
    py::class_<TestFactory2>(m, "TestFactory2")
        .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); }))
        .def(py::init([](unique_ptr_tag, std::string v) { return TestFactoryHelper::construct2(v); }))
        .def(py::init([](move_tag) { return TestFactoryHelper::construct2(); }))
        .def_readwrite("value", &TestFactory2::value)
        ;

    // Stateful & reused:
    int c = 1;
    auto c4a = [c](pointer_tag, TF4_tag, int a) { (void) c; return new TestFactory4(a);};

    // test_init_factory_basic, test_init_factory_casting
    py::class_<TestFactory3, std::shared_ptr<TestFactory3>>(m, "TestFactory3")
        .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); }))
        .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); }))
        .def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }) // placement-new ctor

        // factories returning a derived type:
        .def(py::init(c4a)) // derived ptr
        .def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); }))
        // derived shared ptr:
        .def(py::init([](shared_ptr_tag, TF4_tag, int a) { return std::make_shared<TestFactory4>(a); }))
        .def(py::init([](shared_ptr_tag, TF5_tag, int a) { return std::make_shared<TestFactory5>(a); }))

        // Returns nullptr:
        .def(py::init([](null_ptr_tag) { return (TestFactory3 *) nullptr; }))
        .def(py::init([](null_unique_ptr_tag) { return std::unique_ptr<TestFactory3>(); }))
        .def(py::init([](null_shared_ptr_tag) { return std::shared_ptr<TestFactory3>(); }))

        .def_readwrite("value", &TestFactory3::value)
        ;

    // test_init_factory_casting
    py::class_<TestFactory4, TestFactory3, std::shared_ptr<TestFactory4>>(m, "TestFactory4")
        .def(py::init(c4a)) // pointer
        ;

    // Doesn't need to be registered, but registering makes getting ConstructorStats easier:
    py::class_<TestFactory5, TestFactory3, std::shared_ptr<TestFactory5>>(m, "TestFactory5");

    // test_init_factory_alias
    // Alias testing
    py::class_<TestFactory6, PyTF6>(m, "TestFactory6")
        .def(py::init([](base_tag, int i) { return TestFactory6(i); }))
        .def(py::init([](alias_tag, int i) { return PyTF6(i); }))
        .def(py::init([](alias_tag, std::string s) { return PyTF6(s); }))
        .def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF6(i); }))
        .def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory6(i); }))
        .def(py::init([](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); }))

        .def("get", &TestFactory6::get)
        .def("has_alias", &TestFactory6::has_alias)

        .def_static("get_cstats", &ConstructorStats::get<TestFactory6>, py::return_value_policy::reference)
        .def_static("get_alias_cstats", &ConstructorStats::get<PyTF6>, py::return_value_policy::reference)
        ;

    // test_init_factory_dual
    // Separate alias constructor testing
    py::class_<TestFactory7, PyTF7, std::shared_ptr<TestFactory7>>(m, "TestFactory7")
        .def(py::init(
            [](int i) { return TestFactory7(i); },
            [](int i) { return PyTF7(i); }))
        .def(py::init(
            [](pointer_tag, int i) { return new TestFactory7(i); },
            [](pointer_tag, int i) { return new PyTF7(i); }))
        .def(py::init(
            [](mixed_tag, int i) { return new TestFactory7(i); },
            [](mixed_tag, int i) { return PyTF7(i); }))
        .def(py::init(
            [](mixed_tag, std::string s) { return TestFactory7((int) s.size()); },
            [](mixed_tag, std::string s) { return new PyTF7((int) s.size()); }))
        .def(py::init(
            [](base_tag, pointer_tag, int i) { return new TestFactory7(i); },
            [](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); }))
        .def(py::init(
            [](alias_tag, pointer_tag, int i) { return new PyTF7(i); },
            [](alias_tag, pointer_tag, int i) { return new PyTF7(10*i); }))
        .def(py::init(
            [](shared_ptr_tag, base_tag, int i) { return std::make_shared<TestFactory7>(i); },
            [](shared_ptr_tag, base_tag, int i) { auto *p = new PyTF7(i); return std::shared_ptr<TestFactory7>(p); }))
        .def(py::init(
            [](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared<TestFactory7>(i); },
            [](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared<TestFactory7>(i); })) // <-- invalid alias factory

        .def("get", &TestFactory7::get)
        .def("has_alias", &TestFactory7::has_alias)

        .def_static("get_cstats", &ConstructorStats::get<TestFactory7>, py::return_value_policy::reference)
        .def_static("get_alias_cstats", &ConstructorStats::get<PyTF7>, py::return_value_policy::reference)
        ;

    // test_placement_new_alternative
    // Class with a custom new operator but *without* a placement new operator (issue #948)
    class NoPlacementNew {
    public:
        NoPlacementNew(int i) : i(i) { }
        static void *operator new(std::size_t s) {
            auto *p = ::operator new(s);
            py::print("operator new called, returning", reinterpret_cast<uintptr_t>(p));
            return p;
        }
        static void operator delete(void *p) {
            py::print("operator delete called on", reinterpret_cast<uintptr_t>(p));
            ::operator delete(p);
        }
        int i;
    };
    // As of 2.2, `py::init<args>` no longer requires placement new
    py::class_<NoPlacementNew>(m, "NoPlacementNew")
        .def(py::init<int>())
        .def(py::init([]() { return new NoPlacementNew(100); }))
        .def_readwrite("i", &NoPlacementNew::i)
        ;


    // test_reallocations
    // Class that has verbose operator_new/operator_delete calls
    struct NoisyAlloc {
        NoisyAlloc(const NoisyAlloc &) = default;
        NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); }
        NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); }
        ~NoisyAlloc() { py::print("~NoisyAlloc()"); }

        static void *operator new(size_t s) { py::print("noisy new"); return ::operator new(s); }
        static void *operator new(size_t, void *p) { py::print("noisy placement new"); return p; }
        static void operator delete(void *p, size_t) { py::print("noisy delete"); ::operator delete(p); }
        static void operator delete(void *, void *) { py::print("noisy placement delete"); }
#if defined(_MSC_VER) && _MSC_VER < 1910
        // MSVC 2015 bug: the above "noisy delete" isn't invoked (fixed in MSVC 2017)
        static void operator delete(void *p) { py::print("noisy delete"); ::operator delete(p); }
#endif
    };
    py::class_<NoisyAlloc>(m, "NoisyAlloc")
        // Since these overloads have the same number of arguments, the dispatcher will try each of
        // them until the arguments convert.  Thus we can get a pre-allocation here when passing a
        // single non-integer:
        .def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }) // Regular constructor, runs first, requires preallocation
        .def(py::init([](double d) { return new NoisyAlloc(d); }))

        // The two-argument version: first the factory pointer overload.
        .def(py::init([](int i, int) { return new NoisyAlloc(i); }))
        // Return-by-value:
        .def(py::init([](double d, int) { return NoisyAlloc(d); }))
        // Old-style placement new init; requires preallocation
        .def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); })
        // Requires deallocation of previous overload preallocated value:
        .def(py::init([](int i, double) { return new NoisyAlloc(i); }))
        // Regular again: requires yet another preallocation
        .def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); })
        ;




    // static_assert testing (the following def's should all fail with appropriate compilation errors):
#if 0
    struct BadF1Base {};
    struct BadF1 : BadF1Base {};
    struct PyBadF1 : BadF1 {};
    py::class_<BadF1, PyBadF1, std::shared_ptr<BadF1>> bf1(m, "BadF1");
    // wrapped factory function must return a compatible pointer, holder, or value
    bf1.def(py::init([]() { return 3; }));
    // incompatible factory function pointer return type
    bf1.def(py::init([]() { static int three = 3; return &three; }));
    // incompatible factory function std::shared_ptr<T> return type: cannot convert shared_ptr<T> to holder
    // (non-polymorphic base)
    bf1.def(py::init([]() { return std::shared_ptr<BadF1Base>(new BadF1()); }));
#endif
}
2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473
# 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 datetime
import ipaddress
import os

from pyasn1.codec.der import decoder

from pyasn1_modules import rfc2459

import pytest

import six

from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.backends.interfaces import (
    DSABackend, EllipticCurveBackend, RSABackend, X509Backend
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding, rsa
from cryptography.hazmat.primitives.asymmetric.utils import (
    decode_dss_signature
)
from cryptography.x509.oid import (
    AuthorityInformationAccessOID, ExtendedKeyUsageOID, ExtensionOID, NameOID
)

from .hazmat.primitives.fixtures_dsa import DSA_KEY_2048
from .hazmat.primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512
from .hazmat.primitives.test_ec import _skip_curve_unsupported
from .utils import load_vectors_from_file


@utils.register_interface(x509.ExtensionType)
class DummyExtension(object):
    oid = x509.ObjectIdentifier("1.2.3.4")


@utils.register_interface(x509.GeneralName)
class FakeGeneralName(object):
    def __init__(self, value):
        self._value = value

    value = utils.read_only_property("_value")


def _load_cert(filename, loader, backend):
    cert = load_vectors_from_file(
        filename=filename,
        loader=lambda pemfile: loader(pemfile.read(), backend),
        mode="rb"
    )
    return cert


@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestCertificateRevocationList(object):
    def test_load_pem_crl(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        assert isinstance(crl, x509.CertificateRevocationList)
        fingerprint = binascii.hexlify(crl.fingerprint(hashes.SHA1()))
        assert fingerprint == b"3234b0cb4c0cedf6423724b736729dcfc9e441ef"
        assert isinstance(crl.signature_hash_algorithm, hashes.SHA256)

    def test_load_der_crl(self, backend):
        crl = _load_cert(
            os.path.join("x509", "PKITS_data", "crls", "GoodCACRL.crl"),
            x509.load_der_x509_crl,
            backend
        )

        assert isinstance(crl, x509.CertificateRevocationList)
        fingerprint = binascii.hexlify(crl.fingerprint(hashes.SHA1()))
        assert fingerprint == b"dd3db63c50f4c4a13e090f14053227cb1011a5ad"
        assert isinstance(crl.signature_hash_algorithm, hashes.SHA256)

    def test_invalid_pem(self, backend):
        with pytest.raises(ValueError):
            x509.load_pem_x509_crl(b"notacrl", backend)

    def test_invalid_der(self, backend):
        with pytest.raises(ValueError):
            x509.load_der_x509_crl(b"notacrl", backend)

    def test_unknown_signature_algorithm(self, backend):
        crl = _load_cert(
            os.path.join(
                "x509", "custom", "crl_md2_unknown_crit_entry_ext.pem"
            ),
            x509.load_pem_x509_crl,
            backend
        )

        with pytest.raises(UnsupportedAlgorithm):
                crl.signature_hash_algorithm()

    def test_issuer(self, backend):
        crl = _load_cert(
            os.path.join("x509", "PKITS_data", "crls", "GoodCACRL.crl"),
            x509.load_der_x509_crl,
            backend
        )

        assert isinstance(crl.issuer, x509.Name)
        assert list(crl.issuer) == [
            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
            x509.NameAttribute(
                x509.OID_ORGANIZATION_NAME, u'Test Certificates 2011'
            ),
            x509.NameAttribute(x509.OID_COMMON_NAME, u'Good CA')
        ]
        assert crl.issuer.get_attributes_for_oid(x509.OID_COMMON_NAME) == [
            x509.NameAttribute(x509.OID_COMMON_NAME, u'Good CA')
        ]

    def test_equality(self, backend):
        crl1 = _load_cert(
            os.path.join("x509", "PKITS_data", "crls", "GoodCACRL.crl"),
            x509.load_der_x509_crl,
            backend
        )

        crl2 = _load_cert(
            os.path.join("x509", "PKITS_data", "crls", "GoodCACRL.crl"),
            x509.load_der_x509_crl,
            backend
        )

        crl3 = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        assert crl1 == crl2
        assert crl1 != crl3
        assert crl1 != object()

    def test_update_dates(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        assert isinstance(crl.next_update, datetime.datetime)
        assert isinstance(crl.last_update, datetime.datetime)

        assert crl.next_update.isoformat() == "2016-01-01T00:00:00"
        assert crl.last_update.isoformat() == "2015-01-01T00:00:00"

    def test_revoked_cert_retrieval(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        for r in crl:
            assert isinstance(r, x509.RevokedCertificate)

        # Check that len() works for CRLs.
        assert len(crl) == 12

    def test_revoked_cert_retrieval_retain_only_revoked(self, backend):
        """
        This test attempts to trigger the crash condition described in
        https://github.com/pyca/cryptography/issues/2557
        PyPy does gc at its own pace, so it will only be reliable on CPython.
        """
        revoked = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )[11]
        assert revoked.revocation_date == datetime.datetime(2015, 1, 1, 0, 0)
        assert revoked.serial_number == 11

    def test_extensions(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_ian_aia_aki.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        crl_number = crl.extensions.get_extension_for_oid(
            ExtensionOID.CRL_NUMBER
        )
        aki = crl.extensions.get_extension_for_class(
            x509.AuthorityKeyIdentifier
        )
        aia = crl.extensions.get_extension_for_class(
            x509.AuthorityInformationAccess
        )
        ian = crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName
        )
        assert crl_number.value == x509.CRLNumber(1)
        assert crl_number.critical is False
        assert aki.value == x509.AuthorityKeyIdentifier(
            key_identifier=(
                b'yu\xbb\x84:\xcb,\xdez\t\xbe1\x1bC\xbc\x1c*MSX'
            ),
            authority_cert_issuer=None,
            authority_cert_serial_number=None
        )
        assert aia.value == x509.AuthorityInformationAccess([
            x509.AccessDescription(
                AuthorityInformationAccessOID.CA_ISSUERS,
                x509.DNSName(u"cryptography.io")
            )
        ])
        assert ian.value == x509.IssuerAlternativeName([
            x509.UniformResourceIdentifier(u"https://cryptography.io"),
        ])

    def test_signature(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        assert crl.signature == binascii.unhexlify(
            b"536a5a0794f68267361e7bc2f19167a3e667a2ab141535616855d8deb2ba1af"
            b"9fd4546b1fe76b454eb436af7b28229fedff4634dfc9dd92254266219ae0ea8"
            b"75d9ff972e9a2da23d5945f073da18c50a4265bfed9ca16586347800ef49dd1"
            b"6856d7265f4f3c498a57f04dc04404e2bd2e2ada1f5697057aacef779a18371"
            b"c621edc9a5c2b8ec1716e8fa22feeb7fcec0ce9156c8d344aa6ae8d1a5d99d0"
            b"9386df36307df3b63c83908f4a61a0ff604c1e292ad63b349d1082ddd7ae1b7"
            b"c178bba995523ec6999310c54da5706549797bfb1230f5593ba7b4353dade4f"
            b"d2be13a57580a6eb20b5c4083f000abac3bf32cd8b75f23e4c8f4b3a79e1e2d"
            b"58a472b0"
        )

    def test_tbs_certlist_bytes(self, backend):
        crl = _load_cert(
            os.path.join("x509", "PKITS_data", "crls", "GoodCACRL.crl"),
            x509.load_der_x509_crl,
            backend
        )

        ca_cert = _load_cert(
            os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
            x509.load_der_x509_certificate,
            backend
        )

        verifier = ca_cert.public_key().verifier(
            crl.signature, padding.PKCS1v15(), crl.signature_hash_algorithm
        )
        verifier.update(crl.tbs_certlist_bytes)
        verifier.verify()

    def test_public_bytes_pem(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_empty.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        # Encode it to PEM and load it back.
        crl = x509.load_pem_x509_crl(crl.public_bytes(
            encoding=serialization.Encoding.PEM,
        ), backend)

        assert len(crl) == 0
        assert crl.last_update == datetime.datetime(2015, 12, 20, 23, 44, 47)
        assert crl.next_update == datetime.datetime(2015, 12, 28, 0, 44, 47)

    def test_public_bytes_der(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        # Encode it to DER and load it back.
        crl = x509.load_der_x509_crl(crl.public_bytes(
            encoding=serialization.Encoding.DER,
        ), backend)

        assert len(crl) == 12
        assert crl.last_update == datetime.datetime(2015, 1, 1, 0, 0, 0)
        assert crl.next_update == datetime.datetime(2016, 1, 1, 0, 0, 0)

    @pytest.mark.parametrize(
        ("cert_path", "loader_func", "encoding"),
        [
            (
                os.path.join("x509", "custom", "crl_all_reasons.pem"),
                x509.load_pem_x509_crl,
                serialization.Encoding.PEM,
            ),
            (
                os.path.join("x509", "PKITS_data", "crls", "GoodCACRL.crl"),
                x509.load_der_x509_crl,
                serialization.Encoding.DER,
            ),
        ]
    )
    def test_public_bytes_match(self, cert_path, loader_func, encoding,
                                backend):
        crl_bytes = load_vectors_from_file(
            cert_path, lambda pemfile: pemfile.read(), mode="rb"
        )
        crl = loader_func(crl_bytes, backend)
        serialized = crl.public_bytes(encoding)
        assert serialized == crl_bytes

    def test_public_bytes_invalid_encoding(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_empty.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        with pytest.raises(TypeError):
            crl.public_bytes('NotAnEncoding')


@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestRevokedCertificate(object):
    def test_revoked_basics(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        for i, rev in enumerate(crl):
            assert isinstance(rev, x509.RevokedCertificate)
            assert isinstance(rev.serial_number, int)
            assert isinstance(rev.revocation_date, datetime.datetime)
            assert isinstance(rev.extensions, x509.Extensions)

            assert rev.serial_number == i
            assert rev.revocation_date.isoformat() == "2015-01-01T00:00:00"

    def test_revoked_extensions(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        exp_issuer = [
            x509.DirectoryName(x509.Name([
                x509.NameAttribute(x509.OID_COUNTRY_NAME, u"US"),
                x509.NameAttribute(x509.OID_COMMON_NAME, u"cryptography.io"),
            ]))
        ]

        # First revoked cert doesn't have extensions, test if it is handled
        # correctly.
        rev0 = crl[0]
        # It should return an empty Extensions object.
        assert isinstance(rev0.extensions, x509.Extensions)
        assert len(rev0.extensions) == 0
        with pytest.raises(x509.ExtensionNotFound):
            rev0.extensions.get_extension_for_oid(x509.OID_CRL_REASON)
        with pytest.raises(x509.ExtensionNotFound):
            rev0.extensions.get_extension_for_oid(x509.OID_CERTIFICATE_ISSUER)
        with pytest.raises(x509.ExtensionNotFound):
            rev0.extensions.get_extension_for_oid(x509.OID_INVALIDITY_DATE)

        # Test manual retrieval of extension values.
        rev1 = crl[1]
        assert isinstance(rev1.extensions, x509.Extensions)

        reason = rev1.extensions.get_extension_for_class(
            x509.CRLReason).value
        assert reason == x509.CRLReason(x509.ReasonFlags.unspecified)

        issuer = rev1.extensions.get_extension_for_class(
            x509.CertificateIssuer).value
        assert issuer == x509.CertificateIssuer(exp_issuer)

        date = rev1.extensions.get_extension_for_class(
            x509.InvalidityDate).value
        assert date == x509.InvalidityDate(datetime.datetime(2015, 1, 1, 0, 0))

        # Check if all reason flags can be found in the CRL.
        flags = set(x509.ReasonFlags)
        for rev in crl:
            try:
                r = rev.extensions.get_extension_for_class(x509.CRLReason)
            except x509.ExtensionNotFound:
                # Not all revoked certs have a reason extension.
                pass
            else:
                flags.discard(r.value.reason)

        assert len(flags) == 0

    def test_no_revoked_certs(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_empty.pem"),
            x509.load_pem_x509_crl,
            backend
        )
        assert len(crl) == 0

    def test_duplicate_entry_ext(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_dup_entry_ext.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        with pytest.raises(x509.DuplicateExtension):
            crl[0].extensions

    def test_unsupported_crit_entry_ext(self, backend):
        crl = _load_cert(
            os.path.join(
                "x509", "custom", "crl_md2_unknown_crit_entry_ext.pem"
            ),
            x509.load_pem_x509_crl,
            backend
        )

        with pytest.raises(x509.UnsupportedExtension):
            crl[0].extensions

    def test_unsupported_reason(self, backend):
        crl = _load_cert(
            os.path.join(
                "x509", "custom", "crl_unsupported_reason.pem"
            ),
            x509.load_pem_x509_crl,
            backend
        )

        with pytest.raises(ValueError):
            crl[0].extensions

    def test_invalid_cert_issuer_ext(self, backend):
        crl = _load_cert(
            os.path.join(
                "x509", "custom", "crl_inval_cert_issuer_entry_ext.pem"
            ),
            x509.load_pem_x509_crl,
            backend
        )

        with pytest.raises(ValueError):
            crl[0].extensions

    def test_indexing(self, backend):
        crl = _load_cert(
            os.path.join("x509", "custom", "crl_all_reasons.pem"),
            x509.load_pem_x509_crl,
            backend
        )

        with pytest.raises(IndexError):
            crl[-13]
        with pytest.raises(IndexError):
            crl[12]

        assert crl[-1].serial_number == crl[11].serial_number
        assert len(crl[2:4]) == 2
        assert crl[2:4][0].serial_number == crl[2].serial_number
        assert crl[2:4][1].serial_number == crl[3].serial_number


@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestRSACertificate(object):
    def test_load_pem_cert(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "post2000utctime.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        assert isinstance(cert, x509.Certificate)
        assert cert.serial == 11559813051657483483
        fingerprint = binascii.hexlify(cert.fingerprint(hashes.SHA1()))
        assert fingerprint == b"2b619ed04bfc9c3b08eb677d272192286a0947a8"
        assert isinstance(cert.signature_hash_algorithm, hashes.SHA1)

    def test_load_der_cert(self, backend):
        cert = _load_cert(
            os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
            x509.load_der_x509_certificate,
            backend
        )
        assert isinstance(cert, x509.Certificate)
        assert cert.serial == 2
        fingerprint = binascii.hexlify(cert.fingerprint(hashes.SHA1()))
        assert fingerprint == b"6f49779533d565e8b7c1062503eab41492c38e4d"
        assert isinstance(cert.signature_hash_algorithm, hashes.SHA256)

    def test_signature(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "post2000utctime.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        assert cert.signature == binascii.unhexlify(
            b"8e0f72fcbebe4755abcaf76c8ce0bae17cde4db16291638e1b1ce04a93cdb4c"
            b"44a3486070986c5a880c14fdf8497e7d289b2630ccb21d24a3d1aa1b2d87482"
            b"07f3a1e16ccdf8daa8a7ea1a33d49774f513edf09270bd8e665b6300a10f003"
            b"66a59076905eb63cf10a81a0ca78a6ef3127f6cb2f6fb7f947fce22a30d8004"
            b"8c243ba2c1a54c425fe12310e8a737638f4920354d4cce25cbd9dea25e6a2fe"
            b"0d8579a5c8d929b9275be221975479f3f75075bcacf09526523b5fd67f7683f"
            b"3cda420fabb1e9e6fc26bc0649cf61bb051d6932fac37066bb16f55903dfe78"
            b"53dc5e505e2a10fbba4f9e93a0d3b53b7fa34b05d7ba6eef869bfc34b8e514f"
            b"d5419f75"
        )
        assert len(cert.signature) == cert.public_key().key_size // 8

    def test_tbs_certificate_bytes(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "post2000utctime.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        assert cert.tbs_certificate_bytes == binascii.unhexlify(
            b"308202d8a003020102020900a06cb4b955f7f4db300d06092a864886f70d010"
            b"10505003058310b3009060355040613024155311330110603550408130a536f"
            b"6d652d53746174653121301f060355040a1318496e7465726e6574205769646"
            b"769747320507479204c74643111300f0603550403130848656c6c6f20434130"
            b"1e170d3134313132363231343132305a170d3134313232363231343132305a3"
            b"058310b3009060355040613024155311330110603550408130a536f6d652d53"
            b"746174653121301f060355040a1318496e7465726e657420576964676974732"
            b"0507479204c74643111300f0603550403130848656c6c6f2043413082012230"
            b"0d06092a864886f70d01010105000382010f003082010a0282010100b03af70"
            b"2059e27f1e2284b56bbb26c039153bf81f295b73a49132990645ede4d2da0a9"
            b"13c42e7d38d3589a00d3940d194f6e6d877c2ef812da22a275e83d8be786467"
            b"48b4e7f23d10e873fd72f57a13dec732fc56ab138b1bb308399bb412cd73921"
            b"4ef714e1976e09603405e2556299a05522510ac4574db5e9cb2cf5f99e8f48c"
            b"1696ab3ea2d6d2ddab7d4e1b317188b76a572977f6ece0a4ad396f0150e7d8b"
            b"1a9986c0cb90527ec26ca56e2914c270d2a198b632fa8a2fda55079d3d39864"
            b"b6fb96ddbe331cacb3cb8783a8494ccccd886a3525078847ca01ca5f803e892"
            b"14403e8a4b5499539c0b86f7a0daa45b204a8e079d8a5b03db7ba1ba3d7011a"
            b"70203010001a381bc3081b9301d0603551d0e04160414d8e89dc777e4472656"
            b"f1864695a9f66b7b0400ae3081890603551d23048181307f8014d8e89dc777e"
            b"4472656f1864695a9f66b7b0400aea15ca45a3058310b300906035504061302"
            b"4155311330110603550408130a536f6d652d53746174653121301f060355040"
            b"a1318496e7465726e6574205769646769747320507479204c74643111300f06"
            b"03550403130848656c6c6f204341820900a06cb4b955f7f4db300c0603551d1"
            b"3040530030101ff"
        )
        verifier = cert.public_key().verifier(
            cert.signature, padding.PKCS1v15(), cert.signature_hash_algorithm
        )
        verifier.update(cert.tbs_certificate_bytes)
        verifier.verify()

    def test_issuer(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "PKITS_data", "certs",
                "Validpre2000UTCnotBeforeDateTest3EE.crt"
            ),
            x509.load_der_x509_certificate,
            backend
        )
        issuer = cert.issuer
        assert isinstance(issuer, x509.Name)
        assert list(issuer) == [
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(
                NameOID.ORGANIZATION_NAME, u'Test Certificates 2011'
            ),
            x509.NameAttribute(NameOID.COMMON_NAME, u'Good CA')
        ]
        assert issuer.get_attributes_for_oid(NameOID.COMMON_NAME) == [
            x509.NameAttribute(NameOID.COMMON_NAME, u'Good CA')
        ]

    def test_all_issuer_name_types(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "custom",
                "all_supported_names.pem"
            ),
            x509.load_pem_x509_certificate,
            backend
        )
        issuer = cert.issuer

        assert isinstance(issuer, x509.Name)
        assert list(issuer) == [
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'CA'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Illinois'),
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Chicago'),
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Zero, LLC'),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'One, LLC'),
            x509.NameAttribute(NameOID.COMMON_NAME, u'common name 0'),
            x509.NameAttribute(NameOID.COMMON_NAME, u'common name 1'),
            x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'OU 0'),
            x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'OU 1'),
            x509.NameAttribute(NameOID.DN_QUALIFIER, u'dnQualifier0'),
            x509.NameAttribute(NameOID.DN_QUALIFIER, u'dnQualifier1'),
            x509.NameAttribute(NameOID.SERIAL_NUMBER, u'123'),
            x509.NameAttribute(NameOID.SERIAL_NUMBER, u'456'),
            x509.NameAttribute(NameOID.TITLE, u'Title 0'),
            x509.NameAttribute(NameOID.TITLE, u'Title 1'),
            x509.NameAttribute(NameOID.SURNAME, u'Surname 0'),
            x509.NameAttribute(NameOID.SURNAME, u'Surname 1'),
            x509.NameAttribute(NameOID.GIVEN_NAME, u'Given Name 0'),
            x509.NameAttribute(NameOID.GIVEN_NAME, u'Given Name 1'),
            x509.NameAttribute(NameOID.PSEUDONYM, u'Incognito 0'),
            x509.NameAttribute(NameOID.PSEUDONYM, u'Incognito 1'),
            x509.NameAttribute(NameOID.GENERATION_QUALIFIER, u'Last Gen'),
            x509.NameAttribute(NameOID.GENERATION_QUALIFIER, u'Next Gen'),
            x509.NameAttribute(NameOID.DOMAIN_COMPONENT, u'dc0'),
            x509.NameAttribute(NameOID.DOMAIN_COMPONENT, u'dc1'),
            x509.NameAttribute(NameOID.EMAIL_ADDRESS, u'test0@test.local'),
            x509.NameAttribute(NameOID.EMAIL_ADDRESS, u'test1@test.local'),
        ]

    def test_subject(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "PKITS_data", "certs",
                "Validpre2000UTCnotBeforeDateTest3EE.crt"
            ),
            x509.load_der_x509_certificate,
            backend
        )
        subject = cert.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(
                NameOID.ORGANIZATION_NAME, u'Test Certificates 2011'
            ),
            x509.NameAttribute(
                NameOID.COMMON_NAME,
                u'Valid pre2000 UTC notBefore Date EE Certificate Test3'
            )
        ]
        assert subject.get_attributes_for_oid(NameOID.COMMON_NAME) == [
            x509.NameAttribute(
                NameOID.COMMON_NAME,
                u'Valid pre2000 UTC notBefore Date EE Certificate Test3'
            )
        ]

    def test_unicode_name(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "custom",
                "utf8_common_name.pem"
            ),
            x509.load_pem_x509_certificate,
            backend
        )
        assert cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME) == [
            x509.NameAttribute(
                NameOID.COMMON_NAME,
                u'We heart UTF8!\u2122'
            )
        ]
        assert cert.issuer.get_attributes_for_oid(NameOID.COMMON_NAME) == [
            x509.NameAttribute(
                NameOID.COMMON_NAME,
                u'We heart UTF8!\u2122'
            )
        ]

    def test_all_subject_name_types(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "custom",
                "all_supported_names.pem"
            ),
            x509.load_pem_x509_certificate,
            backend
        )
        subject = cert.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'AU'),
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'DE'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'California'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'New York'),
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'San Francisco'),
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Ithaca'),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Org Zero, LLC'),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'Org One, LLC'),
            x509.NameAttribute(NameOID.COMMON_NAME, u'CN 0'),
            x509.NameAttribute(NameOID.COMMON_NAME, u'CN 1'),
            x509.NameAttribute(
                NameOID.ORGANIZATIONAL_UNIT_NAME, u'Engineering 0'
            ),
            x509.NameAttribute(
                NameOID.ORGANIZATIONAL_UNIT_NAME, u'Engineering 1'
            ),
            x509.NameAttribute(NameOID.DN_QUALIFIER, u'qualified0'),
            x509.NameAttribute(NameOID.DN_QUALIFIER, u'qualified1'),
            x509.NameAttribute(NameOID.SERIAL_NUMBER, u'789'),
            x509.NameAttribute(NameOID.SERIAL_NUMBER, u'012'),
            x509.NameAttribute(NameOID.TITLE, u'Title IX'),
            x509.NameAttribute(NameOID.TITLE, u'Title X'),
            x509.NameAttribute(NameOID.SURNAME, u'Last 0'),
            x509.NameAttribute(NameOID.SURNAME, u'Last 1'),
            x509.NameAttribute(NameOID.GIVEN_NAME, u'First 0'),
            x509.NameAttribute(NameOID.GIVEN_NAME, u'First 1'),
            x509.NameAttribute(NameOID.PSEUDONYM, u'Guy Incognito 0'),
            x509.NameAttribute(NameOID.PSEUDONYM, u'Guy Incognito 1'),
            x509.NameAttribute(NameOID.GENERATION_QUALIFIER, u'32X'),
            x509.NameAttribute(NameOID.GENERATION_QUALIFIER, u'Dreamcast'),
            x509.NameAttribute(NameOID.DOMAIN_COMPONENT, u'dc2'),
            x509.NameAttribute(NameOID.DOMAIN_COMPONENT, u'dc3'),
            x509.NameAttribute(NameOID.EMAIL_ADDRESS, u'test2@test.local'),
            x509.NameAttribute(NameOID.EMAIL_ADDRESS, u'test3@test.local'),
        ]

    def test_load_good_ca_cert(self, backend):
        cert = _load_cert(
            os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
            x509.load_der_x509_certificate,
            backend
        )

        assert cert.not_valid_before == datetime.datetime(2010, 1, 1, 8, 30)
        assert cert.not_valid_after == datetime.datetime(2030, 12, 31, 8, 30)
        assert cert.serial == 2
        public_key = cert.public_key()
        assert isinstance(public_key, rsa.RSAPublicKey)
        assert cert.version is x509.Version.v3
        fingerprint = binascii.hexlify(cert.fingerprint(hashes.SHA1()))
        assert fingerprint == b"6f49779533d565e8b7c1062503eab41492c38e4d"

    def test_utc_pre_2000_not_before_cert(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "PKITS_data", "certs",
                "Validpre2000UTCnotBeforeDateTest3EE.crt"
            ),
            x509.load_der_x509_certificate,
            backend
        )

        assert cert.not_valid_before == datetime.datetime(1950, 1, 1, 12, 1)

    def test_pre_2000_utc_not_after_cert(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "PKITS_data", "certs",
                "Invalidpre2000UTCEEnotAfterDateTest7EE.crt"
            ),
            x509.load_der_x509_certificate,
            backend
        )

        assert cert.not_valid_after == datetime.datetime(1999, 1, 1, 12, 1)

    def test_post_2000_utc_cert(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "post2000utctime.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        assert cert.not_valid_before == datetime.datetime(
            2014, 11, 26, 21, 41, 20
        )
        assert cert.not_valid_after == datetime.datetime(
            2014, 12, 26, 21, 41, 20
        )

    def test_generalized_time_not_before_cert(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "PKITS_data", "certs",
                "ValidGeneralizedTimenotBeforeDateTest4EE.crt"
            ),
            x509.load_der_x509_certificate,
            backend
        )
        assert cert.not_valid_before == datetime.datetime(2002, 1, 1, 12, 1)
        assert cert.not_valid_after == datetime.datetime(2030, 12, 31, 8, 30)
        assert cert.version is x509.Version.v3

    def test_generalized_time_not_after_cert(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "PKITS_data", "certs",
                "ValidGeneralizedTimenotAfterDateTest8EE.crt"
            ),
            x509.load_der_x509_certificate,
            backend
        )
        assert cert.not_valid_before == datetime.datetime(2010, 1, 1, 8, 30)
        assert cert.not_valid_after == datetime.datetime(2050, 1, 1, 12, 1)
        assert cert.version is x509.Version.v3

    def test_invalid_version_cert(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "invalid_version.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        with pytest.raises(x509.InvalidVersion) as exc:
            cert.version

        assert exc.value.parsed_version == 7

    def test_eq(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "post2000utctime.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        cert2 = _load_cert(
            os.path.join("x509", "custom", "post2000utctime.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        assert cert == cert2

    def test_ne(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "post2000utctime.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        cert2 = _load_cert(
            os.path.join(
                "x509", "PKITS_data", "certs",
                "ValidGeneralizedTimenotAfterDateTest8EE.crt"
            ),
            x509.load_der_x509_certificate,
            backend
        )
        assert cert != cert2
        assert cert != object()

    def test_hash(self, backend):
        cert1 = _load_cert(
            os.path.join("x509", "custom", "post2000utctime.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        cert2 = _load_cert(
            os.path.join("x509", "custom", "post2000utctime.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        cert3 = _load_cert(
            os.path.join(
                "x509", "PKITS_data", "certs",
                "ValidGeneralizedTimenotAfterDateTest8EE.crt"
            ),
            x509.load_der_x509_certificate,
            backend
        )

        assert hash(cert1) == hash(cert2)
        assert hash(cert1) != hash(cert3)

    def test_version_1_cert(self, backend):
        cert = _load_cert(
            os.path.join("x509", "v1_cert.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        assert cert.version is x509.Version.v1

    def test_invalid_pem(self, backend):
        with pytest.raises(ValueError):
            x509.load_pem_x509_certificate(b"notacert", backend)

    def test_invalid_der(self, backend):
        with pytest.raises(ValueError):
            x509.load_der_x509_certificate(b"notacert", backend)

    def test_unsupported_signature_hash_algorithm_cert(self, backend):
        cert = _load_cert(
            os.path.join("x509", "verisign_md2_root.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        with pytest.raises(UnsupportedAlgorithm):
            cert.signature_hash_algorithm

    def test_public_bytes_pem(self, backend):
        # Load an existing certificate.
        cert = _load_cert(
            os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
            x509.load_der_x509_certificate,
            backend
        )

        # Encode it to PEM and load it back.
        cert = x509.load_pem_x509_certificate(cert.public_bytes(
            encoding=serialization.Encoding.PEM,
        ), backend)

        # We should recover what we had to start with.
        assert cert.not_valid_before == datetime.datetime(2010, 1, 1, 8, 30)
        assert cert.not_valid_after == datetime.datetime(2030, 12, 31, 8, 30)
        assert cert.serial == 2
        public_key = cert.public_key()
        assert isinstance(public_key, rsa.RSAPublicKey)
        assert cert.version is x509.Version.v3
        fingerprint = binascii.hexlify(cert.fingerprint(hashes.SHA1()))
        assert fingerprint == b"6f49779533d565e8b7c1062503eab41492c38e4d"

    def test_public_bytes_der(self, backend):
        # Load an existing certificate.
        cert = _load_cert(
            os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
            x509.load_der_x509_certificate,
            backend
        )

        # Encode it to DER and load it back.
        cert = x509.load_der_x509_certificate(cert.public_bytes(
            encoding=serialization.Encoding.DER,
        ), backend)

        # We should recover what we had to start with.
        assert cert.not_valid_before == datetime.datetime(2010, 1, 1, 8, 30)
        assert cert.not_valid_after == datetime.datetime(2030, 12, 31, 8, 30)
        assert cert.serial == 2
        public_key = cert.public_key()
        assert isinstance(public_key, rsa.RSAPublicKey)
        assert cert.version is x509.Version.v3
        fingerprint = binascii.hexlify(cert.fingerprint(hashes.SHA1()))
        assert fingerprint == b"6f49779533d565e8b7c1062503eab41492c38e4d"

    def test_public_bytes_invalid_encoding(self, backend):
        cert = _load_cert(
            os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
            x509.load_der_x509_certificate,
            backend
        )

        with pytest.raises(TypeError):
            cert.public_bytes('NotAnEncoding')

    @pytest.mark.parametrize(
        ("cert_path", "loader_func", "encoding"),
        [
            (
                os.path.join("x509", "v1_cert.pem"),
                x509.load_pem_x509_certificate,
                serialization.Encoding.PEM,
            ),
            (
                os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
                x509.load_der_x509_certificate,
                serialization.Encoding.DER,
            ),
        ]
    )
    def test_public_bytes_match(self, cert_path, loader_func, encoding,
                                backend):
        cert_bytes = load_vectors_from_file(
            cert_path, lambda pemfile: pemfile.read(), mode="rb"
        )
        cert = loader_func(cert_bytes, backend)
        serialized = cert.public_bytes(encoding)
        assert serialized == cert_bytes

    def test_certificate_repr(self, backend):
        cert = _load_cert(
            os.path.join(
                "x509", "cryptography.io.pem"
            ),
            x509.load_pem_x509_certificate,
            backend
        )
        if six.PY3:
            assert repr(cert) == (
                "<Certificate(subject=<Name([<NameAttribute(oid=<ObjectIdentif"
                "ier(oid=2.5.4.11, name=organizationalUnitName)>, value='GT487"
                "42965')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, "
                "name=organizationalUnitName)>, value='See www.rapidssl.com/re"
                "sources/cps (c)14')>, <NameAttribute(oid=<ObjectIdentifier(oi"
                "d=2.5.4.11, name=organizationalUnitName)>, value='Domain Cont"
                "rol Validated - RapidSSL(R)')>, <NameAttribute(oid=<ObjectIde"
                "ntifier(oid=2.5.4.3, name=commonName)>, value='www.cryptograp"
                "hy.io')>])>, ...)>"
            )
        else:
            assert repr(cert) == (
                "<Certificate(subject=<Name([<NameAttribute(oid=<ObjectIdentif"
                "ier(oid=2.5.4.11, name=organizationalUnitName)>, value=u'GT48"
                "742965')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11,"
                " name=organizationalUnitName)>, value=u'See www.rapidssl.com/"
                "resources/cps (c)14')>, <NameAttribute(oid=<ObjectIdentifier("
                "oid=2.5.4.11, name=organizationalUnitName)>, value=u'Domain C"
                "ontrol Validated - RapidSSL(R)')>, <NameAttribute(oid=<Object"
                "Identifier(oid=2.5.4.3, name=commonName)>, value=u'www.crypto"
                "graphy.io')>])>, ...)>"
            )


@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestRSACertificateRequest(object):
    @pytest.mark.parametrize(
        ("path", "loader_func"),
        [
            [
                os.path.join("x509", "requests", "rsa_sha1.pem"),
                x509.load_pem_x509_csr
            ],
            [
                os.path.join("x509", "requests", "rsa_sha1.der"),
                x509.load_der_x509_csr
            ],
        ]
    )
    def test_load_rsa_certificate_request(self, path, loader_func, backend):
        request = _load_cert(path, loader_func, backend)
        assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
        public_key = request.public_key()
        assert isinstance(public_key, rsa.RSAPublicKey)
        subject = request.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'PyCA'),
            x509.NameAttribute(NameOID.COMMON_NAME, u'cryptography.io'),
        ]
        extensions = request.extensions
        assert isinstance(extensions, x509.Extensions)
        assert list(extensions) == []

    @pytest.mark.parametrize(
        "loader_func",
        [x509.load_pem_x509_csr, x509.load_der_x509_csr]
    )
    def test_invalid_certificate_request(self, loader_func, backend):
        with pytest.raises(ValueError):
            loader_func(b"notacsr", backend)

    def test_unsupported_signature_hash_algorithm_request(self, backend):
        request = _load_cert(
            os.path.join("x509", "requests", "rsa_md4.pem"),
            x509.load_pem_x509_csr,
            backend
        )
        with pytest.raises(UnsupportedAlgorithm):
            request.signature_hash_algorithm

    def test_duplicate_extension(self, backend):
        request = _load_cert(
            os.path.join(
                "x509", "requests", "two_basic_constraints.pem"
            ),
            x509.load_pem_x509_csr,
            backend
        )
        with pytest.raises(x509.DuplicateExtension) as exc:
            request.extensions

        assert exc.value.oid == ExtensionOID.BASIC_CONSTRAINTS

    def test_unsupported_critical_extension(self, backend):
        request = _load_cert(
            os.path.join(
                "x509", "requests", "unsupported_extension_critical.pem"
            ),
            x509.load_pem_x509_csr,
            backend
        )
        with pytest.raises(x509.UnsupportedExtension) as exc:
            request.extensions

        assert exc.value.oid == x509.ObjectIdentifier('1.2.3.4')

    def test_unsupported_extension(self, backend):
        request = _load_cert(
            os.path.join(
                "x509", "requests", "unsupported_extension.pem"
            ),
            x509.load_pem_x509_csr,
            backend
        )
        extensions = request.extensions
        assert len(extensions) == 1
        assert extensions[0].oid == x509.ObjectIdentifier("1.2.3.4")
        assert extensions[0].value == x509.UnrecognizedExtension(
            x509.ObjectIdentifier("1.2.3.4"), b"value"
        )

    def test_request_basic_constraints(self, backend):
        request = _load_cert(
            os.path.join(
                "x509", "requests", "basic_constraints.pem"
            ),
            x509.load_pem_x509_csr,
            backend
        )
        extensions = request.extensions
        assert isinstance(extensions, x509.Extensions)
        assert list(extensions) == [
            x509.Extension(
                ExtensionOID.BASIC_CONSTRAINTS,
                True,
                x509.BasicConstraints(ca=True, path_length=1),
            ),
        ]

    def test_subject_alt_name(self, backend):
        request = _load_cert(
            os.path.join("x509", "requests", "san_rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend,
        )
        ext = request.extensions.get_extension_for_oid(
            ExtensionOID.SUBJECT_ALTERNATIVE_NAME
        )
        assert list(ext.value) == [
            x509.DNSName(u"cryptography.io"),
            x509.DNSName(u"sub.cryptography.io"),
        ]

    def test_public_bytes_pem(self, backend):
        # Load an existing CSR.
        request = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )

        # Encode it to PEM and load it back.
        request = x509.load_pem_x509_csr(request.public_bytes(
            encoding=serialization.Encoding.PEM,
        ), backend)

        # We should recover what we had to start with.
        assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
        public_key = request.public_key()
        assert isinstance(public_key, rsa.RSAPublicKey)
        subject = request.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'PyCA'),
            x509.NameAttribute(NameOID.COMMON_NAME, u'cryptography.io'),
        ]

    def test_public_bytes_der(self, backend):
        # Load an existing CSR.
        request = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )

        # Encode it to DER and load it back.
        request = x509.load_der_x509_csr(request.public_bytes(
            encoding=serialization.Encoding.DER,
        ), backend)

        # We should recover what we had to start with.
        assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
        public_key = request.public_key()
        assert isinstance(public_key, rsa.RSAPublicKey)
        subject = request.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'PyCA'),
            x509.NameAttribute(NameOID.COMMON_NAME, u'cryptography.io'),
        ]

    def test_signature(self, backend):
        request = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )
        assert request.signature == binascii.unhexlify(
            b"8364c86ffbbfe0bfc9a21f831256658ca8989741b80576d36f08a934603a43b1"
            b"837246d00167a518abb1de7b51a1e5b7ebea14944800818b1a923c804f120a0d"
            b"624f6310ef79e8612755c2b01dcc7f59dfdbce0db3f2630f185f504b8c17af80"
            b"cbd364fa5fda68337153930948226cd4638287a0aed6524d3006885c19028a1e"
            b"e2f5a91d6e77dbaa0b49996ee0a0c60b55b61bd080a08bb34aa7f3e07e91f37f"
            b"6a11645be2d8654c1570dcda145ed7cc92017f7d53225d7f283f3459ec5bda41"
            b"cf6dd75d43676c543483385226b7e4fa29c8739f1b0eaf199613593991979862"
            b"e36181e8c4c270c354b7f52c128db1b70639823324c7ea24791b7bc3d7005f3b"
        )

    def test_tbs_certrequest_bytes(self, backend):
        request = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )
        assert request.tbs_certrequest_bytes == binascii.unhexlify(
            b"308201840201003057310b3009060355040613025553310e300c060355040813"
            b"055465786173310f300d0603550407130641757374696e310d300b060355040a"
            b"130450794341311830160603550403130f63727970746f6772617068792e696f"
            b"30820122300d06092a864886f70d01010105000382010f003082010a02820101"
            b"00a840a78460cb861066dfa3045a94ba6cf1b7ab9d24c761cffddcc2cb5e3f1d"
            b"c3e4be253e7039ef14fe9d6d2304f50d9f2e1584c51530ab75086f357138bff7"
            b"b854d067d1d5f384f1f2f2c39cc3b15415e2638554ef8402648ae3ef08336f22"
            b"b7ecc6d4331c2b21c3091a7f7a9518180754a646640b60419e4cc6f5c798110a"
            b"7f030a639fe87e33b4776dfcd993940ec776ab57a181ad8598857976dc303f9a"
            b"573ca619ab3fe596328e92806b828683edc17cc256b41948a2bfa8d047d2158d"
            b"3d8e069aa05fa85b3272abb1c4b4422b6366f3b70e642377b145cd6259e5d3e7"
            b"db048d51921e50766a37b1b130ee6b11f507d20a834001e8de16a92c14f2e964"
            b"a30203010001a000"
        )
        verifier = request.public_key().verifier(
            request.signature,
            padding.PKCS1v15(),
            request.signature_hash_algorithm
        )
        verifier.update(request.tbs_certrequest_bytes)
        verifier.verify()

    def test_public_bytes_invalid_encoding(self, backend):
        request = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )

        with pytest.raises(TypeError):
            request.public_bytes('NotAnEncoding')

    @pytest.mark.parametrize(
        ("request_path", "loader_func", "encoding"),
        [
            (
                os.path.join("x509", "requests", "rsa_sha1.pem"),
                x509.load_pem_x509_csr,
                serialization.Encoding.PEM,
            ),
            (
                os.path.join("x509", "requests", "rsa_sha1.der"),
                x509.load_der_x509_csr,
                serialization.Encoding.DER,
            ),
        ]
    )
    def test_public_bytes_match(self, request_path, loader_func, encoding,
                                backend):
        request_bytes = load_vectors_from_file(
            request_path, lambda pemfile: pemfile.read(), mode="rb"
        )
        request = loader_func(request_bytes, backend)
        serialized = request.public_bytes(encoding)
        assert serialized == request_bytes

    def test_eq(self, backend):
        request1 = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )
        request2 = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )

        assert request1 == request2

    def test_ne(self, backend):
        request1 = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )
        request2 = _load_cert(
            os.path.join("x509", "requests", "san_rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )

        assert request1 != request2
        assert request1 != object()

    def test_hash(self, backend):
        request1 = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )
        request2 = _load_cert(
            os.path.join("x509", "requests", "rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )
        request3 = _load_cert(
            os.path.join("x509", "requests", "san_rsa_sha1.pem"),
            x509.load_pem_x509_csr,
            backend
        )

        assert hash(request1) == hash(request2)
        assert hash(request1) != hash(request3)

    def test_build_cert(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'PyCA'),
            x509.NameAttribute(NameOID.COMMON_NAME, u'cryptography.io'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'PyCA'),
            x509.NameAttribute(NameOID.COMMON_NAME, u'cryptography.io'),
        ])).public_key(
            subject_private_key.public_key()
        ).add_extension(
            x509.BasicConstraints(ca=False, path_length=None), True,
        ).add_extension(
            x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]),
            critical=False,
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        )

        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)

        assert cert.version is x509.Version.v3
        assert cert.not_valid_before == not_valid_before
        assert cert.not_valid_after == not_valid_after
        basic_constraints = cert.extensions.get_extension_for_oid(
            ExtensionOID.BASIC_CONSTRAINTS
        )
        assert basic_constraints.value.ca is False
        assert basic_constraints.value.path_length is None
        subject_alternative_name = cert.extensions.get_extension_for_oid(
            ExtensionOID.SUBJECT_ALTERNATIVE_NAME
        )
        assert list(subject_alternative_name.value) == [
            x509.DNSName(u"cryptography.io"),
        ]

    def test_build_cert_printable_string_country_name(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
        ])).public_key(
            subject_private_key.public_key()
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        )

        cert = builder.sign(issuer_private_key, hashes.SHA256(), backend)

        parsed, _ = decoder.decode(
            cert.public_bytes(serialization.Encoding.DER),
            asn1Spec=rfc2459.Certificate()
        )
        tbs_cert = parsed.getComponentByName('tbsCertificate')
        subject = tbs_cert.getComponentByName('subject')
        issuer = tbs_cert.getComponentByName('issuer')
        # \x13 is printable string. The first byte of the value of the
        # node corresponds to the ASN.1 string type.
        assert subject[0][0][0][1][0] == b"\x13"[0]
        assert issuer[0][0][0][1][0] == b"\x13"[0]


class TestCertificateBuilder(object):
    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_checks_for_unsupported_extensions(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateBuilder().subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            private_key.public_key()
        ).serial_number(
            777
        ).not_valid_before(
            datetime.datetime(1999, 1, 1)
        ).not_valid_after(
            datetime.datetime(2020, 1, 1)
        ).add_extension(
            DummyExtension(), False
        )

        with pytest.raises(NotImplementedError):
            builder.sign(private_key, hashes.SHA1(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_encode_nonstandard_aia(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        aia = x509.AuthorityInformationAccess([
            x509.AccessDescription(
                x509.ObjectIdentifier("2.999.7"),
                x509.UniformResourceIdentifier(u"http://example.com")
            ),
        ])

        builder = x509.CertificateBuilder().subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            private_key.public_key()
        ).serial_number(
            777
        ).not_valid_before(
            datetime.datetime(1999, 1, 1)
        ).not_valid_after(
            datetime.datetime(2020, 1, 1)
        ).add_extension(
            aia, False
        )

        builder.sign(private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_no_subject_name(self, backend):
        subject_private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).not_valid_before(
            datetime.datetime(2002, 1, 1, 12, 1)
        ).not_valid_after(
            datetime.datetime(2030, 12, 31, 8, 30)
        )
        with pytest.raises(ValueError):
            builder.sign(subject_private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_no_issuer_name(self, backend):
        subject_private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateBuilder().serial_number(
            777
        ).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).not_valid_before(
            datetime.datetime(2002, 1, 1, 12, 1)
        ).not_valid_after(
            datetime.datetime(2030, 12, 31, 8, 30)
        )
        with pytest.raises(ValueError):
            builder.sign(subject_private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_no_public_key(self, backend):
        subject_private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).not_valid_before(
            datetime.datetime(2002, 1, 1, 12, 1)
        ).not_valid_after(
            datetime.datetime(2030, 12, 31, 8, 30)
        )
        with pytest.raises(ValueError):
            builder.sign(subject_private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_no_not_valid_before(self, backend):
        subject_private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).not_valid_after(
            datetime.datetime(2030, 12, 31, 8, 30)
        )
        with pytest.raises(ValueError):
            builder.sign(subject_private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_no_not_valid_after(self, backend):
        subject_private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).not_valid_before(
            datetime.datetime(2002, 1, 1, 12, 1)
        )
        with pytest.raises(ValueError):
            builder.sign(subject_private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_no_serial_number(self, backend):
        subject_private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateBuilder().issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).not_valid_before(
            datetime.datetime(2002, 1, 1, 12, 1)
        ).not_valid_after(
            datetime.datetime(2030, 12, 31, 8, 30)
        )
        with pytest.raises(ValueError):
            builder.sign(subject_private_key, hashes.SHA256(), backend)

    def test_issuer_name_must_be_a_name_type(self):
        builder = x509.CertificateBuilder()

        with pytest.raises(TypeError):
            builder.issuer_name("subject")

        with pytest.raises(TypeError):
            builder.issuer_name(object)

    def test_issuer_name_may_only_be_set_once(self):
        name = x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])
        builder = x509.CertificateBuilder().issuer_name(name)

        with pytest.raises(ValueError):
            builder.issuer_name(name)

    def test_subject_name_must_be_a_name_type(self):
        builder = x509.CertificateBuilder()

        with pytest.raises(TypeError):
            builder.subject_name("subject")

        with pytest.raises(TypeError):
            builder.subject_name(object)

    def test_subject_name_may_only_be_set_once(self):
        name = x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])
        builder = x509.CertificateBuilder().subject_name(name)

        with pytest.raises(ValueError):
            builder.subject_name(name)

    def test_not_valid_before_after_not_valid_after(self):
        builder = x509.CertificateBuilder()

        builder = builder.not_valid_after(
            datetime.datetime(2002, 1, 1, 12, 1)
        )
        with pytest.raises(ValueError):
            builder.not_valid_before(
                datetime.datetime(2003, 1, 1, 12, 1)
            )

    def test_not_valid_after_before_not_valid_before(self):
        builder = x509.CertificateBuilder()

        builder = builder.not_valid_before(
            datetime.datetime(2002, 1, 1, 12, 1)
        )
        with pytest.raises(ValueError):
            builder.not_valid_after(
                datetime.datetime(2001, 1, 1, 12, 1)
            )

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_public_key_must_be_public_key(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateBuilder()

        with pytest.raises(TypeError):
            builder.public_key(private_key)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_public_key_may_only_be_set_once(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        public_key = private_key.public_key()
        builder = x509.CertificateBuilder().public_key(public_key)

        with pytest.raises(ValueError):
            builder.public_key(public_key)

    def test_serial_number_must_be_an_integer_type(self):
        with pytest.raises(TypeError):
            x509.CertificateBuilder().serial_number(10.0)

    def test_serial_number_must_be_non_negative(self):
        with pytest.raises(ValueError):
            x509.CertificateBuilder().serial_number(-10)

    def test_serial_number_must_be_less_than_160_bits_long(self):
        with pytest.raises(ValueError):
            # 2 raised to the 160th power is actually 161 bits
            x509.CertificateBuilder().serial_number(2 ** 160)

    def test_serial_number_may_only_be_set_once(self):
        builder = x509.CertificateBuilder().serial_number(10)

        with pytest.raises(ValueError):
            builder.serial_number(20)

    def test_invalid_not_valid_after(self):
        with pytest.raises(TypeError):
            x509.CertificateBuilder().not_valid_after(104204304504)

        with pytest.raises(TypeError):
            x509.CertificateBuilder().not_valid_after(datetime.time())

        with pytest.raises(ValueError):
            x509.CertificateBuilder().not_valid_after(
                datetime.datetime(1960, 8, 10)
            )

    def test_not_valid_after_may_only_be_set_once(self):
        builder = x509.CertificateBuilder().not_valid_after(
            datetime.datetime.now()
        )

        with pytest.raises(ValueError):
            builder.not_valid_after(
                datetime.datetime.now()
            )

    def test_invalid_not_valid_before(self):
        with pytest.raises(TypeError):
            x509.CertificateBuilder().not_valid_before(104204304504)

        with pytest.raises(TypeError):
            x509.CertificateBuilder().not_valid_before(datetime.time())

        with pytest.raises(ValueError):
            x509.CertificateBuilder().not_valid_before(
                datetime.datetime(1960, 8, 10)
            )

    def test_not_valid_before_may_only_be_set_once(self):
        builder = x509.CertificateBuilder().not_valid_before(
            datetime.datetime.now()
        )

        with pytest.raises(ValueError):
            builder.not_valid_before(
                datetime.datetime.now()
            )

    def test_add_extension_checks_for_duplicates(self):
        builder = x509.CertificateBuilder().add_extension(
            x509.BasicConstraints(ca=False, path_length=None), True,
        )

        with pytest.raises(ValueError):
            builder.add_extension(
                x509.BasicConstraints(ca=False, path_length=None), True,
            )

    def test_add_invalid_extension_type(self):
        builder = x509.CertificateBuilder()

        with pytest.raises(TypeError):
            builder.add_extension(object(), False)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_sign_with_unsupported_hash(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateBuilder()
        builder = builder.subject_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).serial_number(
            1
        ).public_key(
            private_key.public_key()
        ).not_valid_before(
            datetime.datetime(2002, 1, 1, 12, 1)
        ).not_valid_after(
            datetime.datetime(2032, 1, 1, 12, 1)
        )

        with pytest.raises(TypeError):
            builder.sign(private_key, object(), backend)

    @pytest.mark.parametrize(
        "cdp",
        [
            x509.CRLDistributionPoints([
                x509.DistributionPoint(
                    full_name=None,
                    relative_name=x509.Name([
                        x509.NameAttribute(
                            NameOID.COMMON_NAME,
                            u"indirect CRL for indirectCRL CA3"
                        ),
                    ]),
                    reasons=None,
                    crl_issuer=[x509.DirectoryName(
                        x509.Name([
                            x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
                            x509.NameAttribute(
                                NameOID.ORGANIZATION_NAME,
                                u"Test Certificates 2011"
                            ),
                            x509.NameAttribute(
                                NameOID.ORGANIZATIONAL_UNIT_NAME,
                                u"indirectCRL CA3 cRLIssuer"
                            ),
                        ])
                    )],
                )
            ]),
            x509.CRLDistributionPoints([
                x509.DistributionPoint(
                    full_name=[x509.DirectoryName(
                        x509.Name([
                            x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
                        ])
                    )],
                    relative_name=None,
                    reasons=None,
                    crl_issuer=[x509.DirectoryName(
                        x509.Name([
                            x509.NameAttribute(
                                NameOID.ORGANIZATION_NAME,
                                u"cryptography Testing"
                            ),
                        ])
                    )],
                )
            ]),
            x509.CRLDistributionPoints([
                x509.DistributionPoint(
                    full_name=[
                        x509.UniformResourceIdentifier(
                            u"http://myhost.com/myca.crl"
                        ),
                        x509.UniformResourceIdentifier(
                            u"http://backup.myhost.com/myca.crl"
                        )
                    ],
                    relative_name=None,
                    reasons=frozenset([
                        x509.ReasonFlags.key_compromise,
                        x509.ReasonFlags.ca_compromise
                    ]),
                    crl_issuer=[x509.DirectoryName(
                        x509.Name([
                            x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
                            x509.NameAttribute(
                                NameOID.COMMON_NAME, u"cryptography CA"
                            ),
                        ])
                    )],
                )
            ]),
            x509.CRLDistributionPoints([
                x509.DistributionPoint(
                    full_name=[x509.UniformResourceIdentifier(
                        u"http://domain.com/some.crl"
                    )],
                    relative_name=None,
                    reasons=frozenset([
                        x509.ReasonFlags.key_compromise,
                        x509.ReasonFlags.ca_compromise,
                        x509.ReasonFlags.affiliation_changed,
                        x509.ReasonFlags.superseded,
                        x509.ReasonFlags.privilege_withdrawn,
                        x509.ReasonFlags.cessation_of_operation,
                        x509.ReasonFlags.aa_compromise,
                        x509.ReasonFlags.certificate_hold,
                    ]),
                    crl_issuer=None
                )
            ]),
            x509.CRLDistributionPoints([
                x509.DistributionPoint(
                    full_name=None,
                    relative_name=None,
                    reasons=None,
                    crl_issuer=[x509.DirectoryName(
                        x509.Name([
                            x509.NameAttribute(
                                NameOID.COMMON_NAME, u"cryptography CA"
                            ),
                        ])
                    )],
                )
            ]),
            x509.CRLDistributionPoints([
                x509.DistributionPoint(
                    full_name=[x509.UniformResourceIdentifier(
                        u"http://domain.com/some.crl"
                    )],
                    relative_name=None,
                    reasons=frozenset([x509.ReasonFlags.aa_compromise]),
                    crl_issuer=None
                )
            ])
        ]
    )
    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_crl_distribution_points(self, backend, cdp):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        builder = x509.CertificateBuilder().serial_number(
            4444444
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
        ])).public_key(
            subject_private_key.public_key()
        ).add_extension(
            cdp,
            critical=False,
        ).not_valid_before(
            datetime.datetime(2002, 1, 1, 12, 1)
        ).not_valid_after(
            datetime.datetime(2030, 12, 31, 8, 30)
        )

        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)

        ext = cert.extensions.get_extension_for_oid(
            ExtensionOID.CRL_DISTRIBUTION_POINTS
        )
        assert ext.critical is False
        assert ext.value == cdp

    @pytest.mark.requires_backend_interface(interface=DSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_build_cert_with_dsa_private_key(self, backend):
        if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000:
            pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")

        issuer_private_key = DSA_KEY_2048.private_key(backend)
        subject_private_key = DSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).add_extension(
            x509.BasicConstraints(ca=False, path_length=None), True,
        ).add_extension(
            x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]),
            critical=False,
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        )

        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)

        assert cert.version is x509.Version.v3
        assert cert.not_valid_before == not_valid_before
        assert cert.not_valid_after == not_valid_after
        basic_constraints = cert.extensions.get_extension_for_oid(
            ExtensionOID.BASIC_CONSTRAINTS
        )
        assert basic_constraints.value.ca is False
        assert basic_constraints.value.path_length is None
        subject_alternative_name = cert.extensions.get_extension_for_oid(
            ExtensionOID.SUBJECT_ALTERNATIVE_NAME
        )
        assert list(subject_alternative_name.value) == [
            x509.DNSName(u"cryptography.io"),
        ]

    @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_build_cert_with_ec_private_key(self, backend):
        if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000:
            pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")

        _skip_curve_unsupported(backend, ec.SECP256R1())
        issuer_private_key = ec.generate_private_key(ec.SECP256R1(), backend)
        subject_private_key = ec.generate_private_key(ec.SECP256R1(), backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).add_extension(
            x509.BasicConstraints(ca=False, path_length=None), True,
        ).add_extension(
            x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]),
            critical=False,
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        )

        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)

        assert cert.version is x509.Version.v3
        assert cert.not_valid_before == not_valid_before
        assert cert.not_valid_after == not_valid_after
        basic_constraints = cert.extensions.get_extension_for_oid(
            ExtensionOID.BASIC_CONSTRAINTS
        )
        assert basic_constraints.value.ca is False
        assert basic_constraints.value.path_length is None
        subject_alternative_name = cert.extensions.get_extension_for_oid(
            ExtensionOID.SUBJECT_ALTERNATIVE_NAME
        )
        assert list(subject_alternative_name.value) == [
            x509.DNSName(u"cryptography.io"),
        ]

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_build_cert_with_rsa_key_too_small(self, backend):
        issuer_private_key = RSA_KEY_512.private_key(backend)
        subject_private_key = RSA_KEY_512.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        )

        with pytest.raises(ValueError):
            builder.sign(issuer_private_key, hashes.SHA512(), backend)

    @pytest.mark.parametrize(
        "cp",
        [
            x509.CertificatePolicies([
                x509.PolicyInformation(
                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
                    [u"http://other.com/cps"]
                )
            ]),
            x509.CertificatePolicies([
                x509.PolicyInformation(
                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
                    None
                )
            ]),
            x509.CertificatePolicies([
                x509.PolicyInformation(
                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
                    [
                        u"http://example.com/cps",
                        u"http://other.com/cps",
                        x509.UserNotice(
                            x509.NoticeReference(u"my org", [1, 2, 3, 4]),
                            u"thing"
                        )
                    ]
                )
            ]),
            x509.CertificatePolicies([
                x509.PolicyInformation(
                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
                    [
                        u"http://example.com/cps",
                        x509.UserNotice(
                            x509.NoticeReference(u"UTF8\u2122'", [1, 2, 3, 4]),
                            u"We heart UTF8!\u2122"
                        )
                    ]
                )
            ]),
            x509.CertificatePolicies([
                x509.PolicyInformation(
                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
                    [x509.UserNotice(None, u"thing")]
                )
            ]),
            x509.CertificatePolicies([
                x509.PolicyInformation(
                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
                    [
                        x509.UserNotice(
                            x509.NoticeReference(u"my org", [1, 2, 3, 4]),
                            None
                        )
                    ]
                )
            ])
        ]
    )
    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_certificate_policies(self, cp, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        cert = x509.CertificateBuilder().subject_name(
            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
        ).issuer_name(
            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        ).public_key(
            subject_private_key.public_key()
        ).serial_number(
            123
        ).add_extension(
            cp, critical=False
        ).sign(issuer_private_key, hashes.SHA256(), backend)

        ext = cert.extensions.get_extension_for_oid(
            x509.OID_CERTIFICATE_POLICIES
        )
        assert ext.value == cp

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_issuer_alt_name(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        cert = x509.CertificateBuilder().subject_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        ).public_key(
            subject_private_key.public_key()
        ).serial_number(
            123
        ).add_extension(
            x509.IssuerAlternativeName([
                x509.DNSName(u"myissuer"),
                x509.RFC822Name(u"email@domain.com"),
            ]), critical=False
        ).sign(issuer_private_key, hashes.SHA256(), backend)

        ext = cert.extensions.get_extension_for_oid(
            ExtensionOID.ISSUER_ALTERNATIVE_NAME
        )
        assert ext.critical is False
        assert ext.value == x509.IssuerAlternativeName([
            x509.DNSName(u"myissuer"),
            x509.RFC822Name(u"email@domain.com"),
        ])

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_extended_key_usage(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        cert = x509.CertificateBuilder().subject_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        ).public_key(
            subject_private_key.public_key()
        ).serial_number(
            123
        ).add_extension(
            x509.ExtendedKeyUsage([
                ExtendedKeyUsageOID.CLIENT_AUTH,
                ExtendedKeyUsageOID.SERVER_AUTH,
                ExtendedKeyUsageOID.CODE_SIGNING,
            ]), critical=False
        ).sign(issuer_private_key, hashes.SHA256(), backend)

        eku = cert.extensions.get_extension_for_oid(
            ExtensionOID.EXTENDED_KEY_USAGE
        )
        assert eku.critical is False
        assert eku.value == x509.ExtendedKeyUsage([
            ExtendedKeyUsageOID.CLIENT_AUTH,
            ExtendedKeyUsageOID.SERVER_AUTH,
            ExtendedKeyUsageOID.CODE_SIGNING,
        ])

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_inhibit_any_policy(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        cert = x509.CertificateBuilder().subject_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        ).public_key(
            subject_private_key.public_key()
        ).serial_number(
            123
        ).add_extension(
            x509.InhibitAnyPolicy(3), critical=False
        ).sign(issuer_private_key, hashes.SHA256(), backend)

        ext = cert.extensions.get_extension_for_oid(
            ExtensionOID.INHIBIT_ANY_POLICY
        )
        assert ext.value == x509.InhibitAnyPolicy(3)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_name_constraints(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        excluded = [x509.DNSName(u"name.local")]
        nc = x509.NameConstraints(
            permitted_subtrees=None, excluded_subtrees=excluded
        )

        cert = x509.CertificateBuilder().subject_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        ).public_key(
            subject_private_key.public_key()
        ).serial_number(
            123
        ).add_extension(
            nc, critical=False
        ).sign(issuer_private_key, hashes.SHA256(), backend)

        ext = cert.extensions.get_extension_for_oid(
            ExtensionOID.NAME_CONSTRAINTS
        )
        assert ext.value == nc

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_key_usage(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        cert = x509.CertificateBuilder().subject_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        ).public_key(
            subject_private_key.public_key()
        ).serial_number(
            123
        ).add_extension(
            x509.KeyUsage(
                digital_signature=True,
                content_commitment=True,
                key_encipherment=False,
                data_encipherment=False,
                key_agreement=False,
                key_cert_sign=True,
                crl_sign=False,
                encipher_only=False,
                decipher_only=False
            ),
            critical=False
        ).sign(issuer_private_key, hashes.SHA256(), backend)

        ext = cert.extensions.get_extension_for_oid(ExtensionOID.KEY_USAGE)
        assert ext.critical is False
        assert ext.value == x509.KeyUsage(
            digital_signature=True,
            content_commitment=True,
            key_encipherment=False,
            data_encipherment=False,
            key_agreement=False,
            key_cert_sign=True,
            crl_sign=False,
            encipher_only=False,
            decipher_only=False
        )

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_build_ca_request_with_path_length_none(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        request = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.ORGANIZATION_NAME,
                                   u'PyCA'),
            ])
        ).add_extension(
            x509.BasicConstraints(ca=True, path_length=None), critical=True
        ).sign(private_key, hashes.SHA1(), backend)

        loaded_request = x509.load_pem_x509_csr(
            request.public_bytes(encoding=serialization.Encoding.PEM), backend
        )
        subject = loaded_request.subject
        assert isinstance(subject, x509.Name)
        basic_constraints = request.extensions.get_extension_for_oid(
            ExtensionOID.BASIC_CONSTRAINTS
        )
        assert basic_constraints.value.path_length is None


@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestCertificateSigningRequestBuilder(object):
    @pytest.mark.requires_backend_interface(interface=RSABackend)
    def test_sign_invalid_hash_algorithm(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        builder = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([])
        )
        with pytest.raises(TypeError):
            builder.sign(private_key, 'NotAHash', backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    def test_no_subject_name(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        builder = x509.CertificateSigningRequestBuilder()
        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    def test_build_ca_request_with_rsa(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        request = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'PyCA'),
            ])
        ).add_extension(
            x509.BasicConstraints(ca=True, path_length=2), critical=True
        ).sign(private_key, hashes.SHA1(), backend)

        assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
        public_key = request.public_key()
        assert isinstance(public_key, rsa.RSAPublicKey)
        subject = request.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'PyCA'),
        ]
        basic_constraints = request.extensions.get_extension_for_oid(
            ExtensionOID.BASIC_CONSTRAINTS
        )
        assert basic_constraints.value.ca is True
        assert basic_constraints.value.path_length == 2

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    def test_build_ca_request_with_unicode(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        request = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.ORGANIZATION_NAME,
                                   u'PyCA\U0001f37a'),
            ])
        ).add_extension(
            x509.BasicConstraints(ca=True, path_length=2), critical=True
        ).sign(private_key, hashes.SHA1(), backend)

        loaded_request = x509.load_pem_x509_csr(
            request.public_bytes(encoding=serialization.Encoding.PEM), backend
        )
        subject = loaded_request.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'PyCA\U0001f37a'),
        ]

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    def test_build_nonca_request_with_rsa(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        request = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            ])
        ).add_extension(
            x509.BasicConstraints(ca=False, path_length=None), critical=True,
        ).sign(private_key, hashes.SHA1(), backend)

        assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
        public_key = request.public_key()
        assert isinstance(public_key, rsa.RSAPublicKey)
        subject = request.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ]
        basic_constraints = request.extensions.get_extension_for_oid(
            ExtensionOID.BASIC_CONSTRAINTS
        )
        assert basic_constraints.value.ca is False
        assert basic_constraints.value.path_length is None

    @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
    def test_build_ca_request_with_ec(self, backend):
        if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000:
            pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")

        _skip_curve_unsupported(backend, ec.SECP256R1())
        private_key = ec.generate_private_key(ec.SECP256R1(), backend)

        request = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
            ])
        ).add_extension(
            x509.BasicConstraints(ca=True, path_length=2), critical=True
        ).sign(private_key, hashes.SHA1(), backend)

        assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
        public_key = request.public_key()
        assert isinstance(public_key, ec.EllipticCurvePublicKey)
        subject = request.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'),
        ]
        basic_constraints = request.extensions.get_extension_for_oid(
            ExtensionOID.BASIC_CONSTRAINTS
        )
        assert basic_constraints.value.ca is True
        assert basic_constraints.value.path_length == 2

    @pytest.mark.requires_backend_interface(interface=DSABackend)
    def test_build_ca_request_with_dsa(self, backend):
        if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000:
            pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")

        private_key = DSA_KEY_2048.private_key(backend)

        request = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            ])
        ).add_extension(
            x509.BasicConstraints(ca=True, path_length=2), critical=True
        ).sign(private_key, hashes.SHA1(), backend)

        assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
        public_key = request.public_key()
        assert isinstance(public_key, dsa.DSAPublicKey)
        subject = request.subject
        assert isinstance(subject, x509.Name)
        assert list(subject) == [
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ]
        basic_constraints = request.extensions.get_extension_for_oid(
            ExtensionOID.BASIC_CONSTRAINTS
        )
        assert basic_constraints.value.ca is True
        assert basic_constraints.value.path_length == 2

    def test_add_duplicate_extension(self):
        builder = x509.CertificateSigningRequestBuilder().add_extension(
            x509.BasicConstraints(True, 2), critical=True,
        )
        with pytest.raises(ValueError):
            builder.add_extension(
                x509.BasicConstraints(True, 2), critical=True,
            )

    def test_set_invalid_subject(self):
        builder = x509.CertificateSigningRequestBuilder()
        with pytest.raises(TypeError):
            builder.subject_name('NotAName')

    def test_add_invalid_extension_type(self):
        builder = x509.CertificateSigningRequestBuilder()

        with pytest.raises(TypeError):
            builder.add_extension(object(), False)

    def test_add_unsupported_extension(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateSigningRequestBuilder()
        builder = builder.subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            ])
        ).add_extension(
            x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]),
            critical=False,
        ).add_extension(
            DummyExtension(), False
        )
        with pytest.raises(NotImplementedError):
            builder.sign(private_key, hashes.SHA256(), backend)

    def test_key_usage(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateSigningRequestBuilder()
        request = builder.subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            ])
        ).add_extension(
            x509.KeyUsage(
                digital_signature=True,
                content_commitment=True,
                key_encipherment=False,
                data_encipherment=False,
                key_agreement=False,
                key_cert_sign=True,
                crl_sign=False,
                encipher_only=False,
                decipher_only=False
            ),
            critical=False
        ).sign(private_key, hashes.SHA256(), backend)
        assert len(request.extensions) == 1
        ext = request.extensions.get_extension_for_oid(ExtensionOID.KEY_USAGE)
        assert ext.critical is False
        assert ext.value == x509.KeyUsage(
            digital_signature=True,
            content_commitment=True,
            key_encipherment=False,
            data_encipherment=False,
            key_agreement=False,
            key_cert_sign=True,
            crl_sign=False,
            encipher_only=False,
            decipher_only=False
        )

    def test_key_usage_key_agreement_bit(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateSigningRequestBuilder()
        request = builder.subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            ])
        ).add_extension(
            x509.KeyUsage(
                digital_signature=False,
                content_commitment=False,
                key_encipherment=False,
                data_encipherment=False,
                key_agreement=True,
                key_cert_sign=True,
                crl_sign=False,
                encipher_only=False,
                decipher_only=True
            ),
            critical=False
        ).sign(private_key, hashes.SHA256(), backend)
        assert len(request.extensions) == 1
        ext = request.extensions.get_extension_for_oid(ExtensionOID.KEY_USAGE)
        assert ext.critical is False
        assert ext.value == x509.KeyUsage(
            digital_signature=False,
            content_commitment=False,
            key_encipherment=False,
            data_encipherment=False,
            key_agreement=True,
            key_cert_sign=True,
            crl_sign=False,
            encipher_only=False,
            decipher_only=True
        )

    def test_add_two_extensions(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateSigningRequestBuilder()
        request = builder.subject_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).add_extension(
            x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]),
            critical=False,
        ).add_extension(
            x509.BasicConstraints(ca=True, path_length=2), critical=True
        ).sign(private_key, hashes.SHA1(), backend)

        assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
        public_key = request.public_key()
        assert isinstance(public_key, rsa.RSAPublicKey)
        basic_constraints = request.extensions.get_extension_for_oid(
            ExtensionOID.BASIC_CONSTRAINTS
        )
        assert basic_constraints.value.ca is True
        assert basic_constraints.value.path_length == 2
        ext = request.extensions.get_extension_for_oid(
            ExtensionOID.SUBJECT_ALTERNATIVE_NAME
        )
        assert list(ext.value) == [x509.DNSName(u"cryptography.io")]

    def test_set_subject_twice(self):
        builder = x509.CertificateSigningRequestBuilder()
        builder = builder.subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
            ])
        )
        with pytest.raises(ValueError):
            builder.subject_name(
                x509.Name([
                    x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
                ])
            )

    def test_subject_alt_names(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        csr = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"SAN"),
            ])
        ).add_extension(
            x509.SubjectAlternativeName([
                x509.DNSName(u"example.com"),
                x509.DNSName(u"*.example.com"),
                x509.RegisteredID(x509.ObjectIdentifier("1.2.3.4.5.6.7")),
                x509.DirectoryName(x509.Name([
                    x509.NameAttribute(NameOID.COMMON_NAME, u'PyCA'),
                    x509.NameAttribute(
                        NameOID.ORGANIZATION_NAME, u'We heart UTF8!\u2122'
                    )
                ])),
                x509.IPAddress(ipaddress.ip_address(u"127.0.0.1")),
                x509.IPAddress(ipaddress.ip_address(u"ff::")),
                x509.OtherName(
                    type_id=x509.ObjectIdentifier("1.2.3.3.3.3"),
                    value=b"0\x03\x02\x01\x05"
                ),
                x509.RFC822Name(u"test@example.com"),
                x509.RFC822Name(u"email"),
                x509.RFC822Name(u"email@em\xe5\xefl.com"),
                x509.UniformResourceIdentifier(
                    u"https://\u043f\u044b\u043a\u0430.cryptography"
                ),
                x509.UniformResourceIdentifier(
                    u"gopher://cryptography:70/some/path"
                ),
            ]),
            critical=False,
        ).sign(private_key, hashes.SHA256(), backend)

        assert len(csr.extensions) == 1
        ext = csr.extensions.get_extension_for_oid(
            ExtensionOID.SUBJECT_ALTERNATIVE_NAME
        )
        assert not ext.critical
        assert ext.oid == ExtensionOID.SUBJECT_ALTERNATIVE_NAME
        assert list(ext.value) == [
            x509.DNSName(u"example.com"),
            x509.DNSName(u"*.example.com"),
            x509.RegisteredID(x509.ObjectIdentifier("1.2.3.4.5.6.7")),
            x509.DirectoryName(x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u'PyCA'),
                x509.NameAttribute(
                    NameOID.ORGANIZATION_NAME, u'We heart UTF8!\u2122'
                ),
            ])),
            x509.IPAddress(ipaddress.ip_address(u"127.0.0.1")),
            x509.IPAddress(ipaddress.ip_address(u"ff::")),
            x509.OtherName(
                type_id=x509.ObjectIdentifier("1.2.3.3.3.3"),
                value=b"0\x03\x02\x01\x05"
            ),
            x509.RFC822Name(u"test@example.com"),
            x509.RFC822Name(u"email"),
            x509.RFC822Name(u"email@em\xe5\xefl.com"),
            x509.UniformResourceIdentifier(
                u"https://\u043f\u044b\u043a\u0430.cryptography"
            ),
            x509.UniformResourceIdentifier(
                u"gopher://cryptography:70/some/path"
            ),
        ]

    def test_invalid_asn1_othername(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        builder = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"SAN"),
            ])
        ).add_extension(
            x509.SubjectAlternativeName([
                x509.OtherName(
                    type_id=x509.ObjectIdentifier("1.2.3.3.3.3"),
                    value=b"\x01\x02\x01\x05"
                ),
            ]),
            critical=False,
        )
        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    def test_subject_alt_name_unsupported_general_name(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        builder = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"SAN"),
            ])
        ).add_extension(
            x509.SubjectAlternativeName([FakeGeneralName("")]),
            critical=False,
        )

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    def test_extended_key_usage(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateSigningRequestBuilder()
        request = builder.subject_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).add_extension(
            x509.ExtendedKeyUsage([
                ExtendedKeyUsageOID.CLIENT_AUTH,
                ExtendedKeyUsageOID.SERVER_AUTH,
                ExtendedKeyUsageOID.CODE_SIGNING,
            ]), critical=False
        ).sign(private_key, hashes.SHA256(), backend)

        eku = request.extensions.get_extension_for_oid(
            ExtensionOID.EXTENDED_KEY_USAGE
        )
        assert eku.critical is False
        assert eku.value == x509.ExtendedKeyUsage([
            ExtendedKeyUsageOID.CLIENT_AUTH,
            ExtendedKeyUsageOID.SERVER_AUTH,
            ExtendedKeyUsageOID.CODE_SIGNING,
        ])

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    def test_rsa_key_too_small(self, backend):
        private_key = rsa.generate_private_key(65537, 512, backend)
        builder = x509.CertificateSigningRequestBuilder()
        builder = builder.subject_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        )

        with pytest.raises(ValueError) as exc:
            builder.sign(private_key, hashes.SHA512(), backend)

        assert str(exc.value) == "Digest too big for RSA key"

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_build_cert_with_aia(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        aia = x509.AuthorityInformationAccess([
            x509.AccessDescription(
                AuthorityInformationAccessOID.OCSP,
                x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
            ),
            x509.AccessDescription(
                AuthorityInformationAccessOID.CA_ISSUERS,
                x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
            )
        ])

        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).add_extension(
            aia, critical=False
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        )

        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)

        ext = cert.extensions.get_extension_for_oid(
            ExtensionOID.AUTHORITY_INFORMATION_ACCESS
        )
        assert ext.value == aia

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_build_cert_with_ski(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        ski = x509.SubjectKeyIdentifier.from_public_key(
            subject_private_key.public_key()
        )

        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).add_extension(
            ski, critical=False
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        )

        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)

        ext = cert.extensions.get_extension_for_oid(
            ExtensionOID.SUBJECT_KEY_IDENTIFIER
        )
        assert ext.value == ski

    @pytest.mark.parametrize(
        "aki",
        [
            x509.AuthorityKeyIdentifier(
                b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08"
                b"\xcbY",
                None,
                None
            ),
            x509.AuthorityKeyIdentifier(
                b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08"
                b"\xcbY",
                [
                    x509.DirectoryName(
                        x509.Name([
                            x509.NameAttribute(
                                NameOID.ORGANIZATION_NAME, u"PyCA"
                            ),
                            x509.NameAttribute(
                                NameOID.COMMON_NAME, u"cryptography CA"
                            )
                        ])
                    )
                ],
                333
            ),
            x509.AuthorityKeyIdentifier(
                None,
                [
                    x509.DirectoryName(
                        x509.Name([
                            x509.NameAttribute(
                                NameOID.ORGANIZATION_NAME, u"PyCA"
                            ),
                            x509.NameAttribute(
                                NameOID.COMMON_NAME, u"cryptography CA"
                            )
                        ])
                    )
                ],
                333
            ),
        ]
    )
    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_build_cert_with_aki(self, aki, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).add_extension(
            aki, critical=False
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        )

        cert = builder.sign(issuer_private_key, hashes.SHA256(), backend)

        ext = cert.extensions.get_extension_for_oid(
            ExtensionOID.AUTHORITY_KEY_IDENTIFIER
        )
        assert ext.value == aki

    def test_ocsp_nocheck(self, backend):
        issuer_private_key = RSA_KEY_2048.private_key(backend)
        subject_private_key = RSA_KEY_2048.private_key(backend)

        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)

        builder = x509.CertificateBuilder().serial_number(
            777
        ).issuer_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
        ])).public_key(
            subject_private_key.public_key()
        ).add_extension(
            x509.OCSPNoCheck(), critical=False
        ).not_valid_before(
            not_valid_before
        ).not_valid_after(
            not_valid_after
        )

        cert = builder.sign(issuer_private_key, hashes.SHA256(), backend)

        ext = cert.extensions.get_extension_for_oid(
            ExtensionOID.OCSP_NO_CHECK
        )
        assert isinstance(ext.value, x509.OCSPNoCheck)


@pytest.mark.requires_backend_interface(interface=DSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestDSACertificate(object):
    def test_load_dsa_cert(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "dsa_selfsigned_ca.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        assert isinstance(cert.signature_hash_algorithm, hashes.SHA1)
        public_key = cert.public_key()
        assert isinstance(public_key, dsa.DSAPublicKey)
        num = public_key.public_numbers()
        assert num.y == int(
            "4c08bfe5f2d76649c80acf7d431f6ae2124b217abc8c9f6aca776ddfa94"
            "53b6656f13e543684cd5f6431a314377d2abfa068b7080cb8ddc065afc2"
            "dea559f0b584c97a2b235b9b69b46bc6de1aed422a6f341832618bcaae2"
            "198aba388099dafb05ff0b5efecb3b0ae169a62e1c72022af50ae68af3b"
            "033c18e6eec1f7df4692c456ccafb79cc7e08da0a5786e9816ceda651d6"
            "1b4bb7b81c2783da97cea62df67af5e85991fdc13aff10fc60e06586386"
            "b96bb78d65750f542f86951e05a6d81baadbcd35a2e5cad4119923ae6a2"
            "002091a3d17017f93c52970113cdc119970b9074ca506eac91c3dd37632"
            "5df4af6b3911ef267d26623a5a1c5df4a6d13f1c", 16
        )
        assert num.parameter_numbers.g == int(
            "4b7ced71dc353965ecc10d441a9a06fc24943a32d66429dd5ef44d43e67"
            "d789d99770aec32c0415dc92970880872da45fef8dd1e115a3e4801387b"
            "a6d755861f062fd3b6e9ea8e2641152339b828315b1528ee6c7b79458d2"
            "1f3db973f6fc303f9397174c2799dd2351282aa2d8842c357a73495bbaa"
            "c4932786414c55e60d73169f5761036fba29e9eebfb049f8a3b1b7cee6f"
            "3fbfa136205f130bee2cf5b9c38dc1095d4006f2e73335c07352c64130a"
            "1ab2b89f13b48f628d3cc3868beece9bb7beade9f830eacc6fa241425c0"
            "b3fcc0df416a0c89f7bf35668d765ec95cdcfbe9caff49cfc156c668c76"
            "fa6247676a6d3ac945844a083509c6a1b436baca", 16
        )
        assert num.parameter_numbers.p == int(
            "bfade6048e373cd4e48b677e878c8e5b08c02102ae04eb2cb5c46a523a3"
            "af1c73d16b24f34a4964781ae7e50500e21777754a670bd19a7420d6330"
            "84e5556e33ca2c0e7d547ea5f46a07a01bf8669ae3bdec042d9b2ae5e6e"
            "cf49f00ba9dac99ab6eff140d2cedf722ee62c2f9736857971444c25d0a"
            "33d2017dc36d682a1054fe2a9428dda355a851ce6e6d61e03e419fd4ca4"
            "e703313743d86caa885930f62ed5bf342d8165627681e9cc3244ba72aa2"
            "2148400a6bbe80154e855d042c9dc2a3405f1e517be9dea50562f56da93"
            "f6085f844a7e705c1f043e65751c583b80d29103e590ccb26efdaa0893d"
            "833e36468f3907cfca788a3cb790f0341c8a31bf", 16
        )
        assert num.parameter_numbers.q == int(
            "822ff5d234e073b901cf5941f58e1f538e71d40d", 16
        )

    def test_signature(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "dsa_selfsigned_ca.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        assert cert.signature == binascii.unhexlify(
            b"302c021425c4a84a936ab311ee017d3cbd9a3c650bb3ae4a02145d30c64b4326"
            b"86bdf925716b4ed059184396bcce"
        )
        r, s = decode_dss_signature(cert.signature)
        assert r == 215618264820276283222494627481362273536404860490
        assert s == 532023851299196869156027211159466197586787351758

    def test_tbs_certificate_bytes(self, backend):
        cert = _load_cert(
            os.path.join("x509", "custom", "dsa_selfsigned_ca.pem"),
            x509.load_pem_x509_certificate,
            backend
        )
        assert cert.tbs_certificate_bytes == binascii.unhexlify(
            b"3082051aa003020102020900a37352e0b2142f86300906072a8648ce3804033"
            b"067310b3009060355040613025553310e300c06035504081305546578617331"
            b"0f300d0603550407130641757374696e3121301f060355040a1318496e74657"
            b"26e6574205769646769747320507479204c7464311430120603550403130b50"
            b"79434120445341204341301e170d3134313132373035313431375a170d31343"
            b"13232373035313431375a3067310b3009060355040613025553310e300c0603"
            b"55040813055465786173310f300d0603550407130641757374696e3121301f0"
            b"60355040a1318496e7465726e6574205769646769747320507479204c746431"
            b"1430120603550403130b50794341204453412043413082033a3082022d06072"
            b"a8648ce380401308202200282010100bfade6048e373cd4e48b677e878c8e5b"
            b"08c02102ae04eb2cb5c46a523a3af1c73d16b24f34a4964781ae7e50500e217"
            b"77754a670bd19a7420d633084e5556e33ca2c0e7d547ea5f46a07a01bf8669a"
            b"e3bdec042d9b2ae5e6ecf49f00ba9dac99ab6eff140d2cedf722ee62c2f9736"
            b"857971444c25d0a33d2017dc36d682a1054fe2a9428dda355a851ce6e6d61e0"
            b"3e419fd4ca4e703313743d86caa885930f62ed5bf342d8165627681e9cc3244"
            b"ba72aa22148400a6bbe80154e855d042c9dc2a3405f1e517be9dea50562f56d"
            b"a93f6085f844a7e705c1f043e65751c583b80d29103e590ccb26efdaa0893d8"
            b"33e36468f3907cfca788a3cb790f0341c8a31bf021500822ff5d234e073b901"
            b"cf5941f58e1f538e71d40d028201004b7ced71dc353965ecc10d441a9a06fc2"
            b"4943a32d66429dd5ef44d43e67d789d99770aec32c0415dc92970880872da45"
            b"fef8dd1e115a3e4801387ba6d755861f062fd3b6e9ea8e2641152339b828315"
            b"b1528ee6c7b79458d21f3db973f6fc303f9397174c2799dd2351282aa2d8842"
            b"c357a73495bbaac4932786414c55e60d73169f5761036fba29e9eebfb049f8a"
            b"3b1b7cee6f3fbfa136205f130bee2cf5b9c38dc1095d4006f2e73335c07352c"
            b"64130a1ab2b89f13b48f628d3cc3868beece9bb7beade9f830eacc6fa241425"
            b"c0b3fcc0df416a0c89f7bf35668d765ec95cdcfbe9caff49cfc156c668c76fa"
            b"6247676a6d3ac945844a083509c6a1b436baca0382010500028201004c08bfe"
            b"5f2d76649c80acf7d431f6ae2124b217abc8c9f6aca776ddfa9453b6656f13e"
            b"543684cd5f6431a314377d2abfa068b7080cb8ddc065afc2dea559f0b584c97"
            b"a2b235b9b69b46bc6de1aed422a6f341832618bcaae2198aba388099dafb05f"