aboutsummaryrefslogtreecommitdiffstats
path: root/3rdparty/imgui/examples/imgui_impl_opengl3.cpp
blob: 11109817a0d54022e9c35995a5e8dd53c6795f4f (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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..)

// Implemented features:
//  [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.

// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui

// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
//  2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
//  2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
//  2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
//  2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
//  2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
//  2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
//  2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
//  2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
//  2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
//  2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
//  2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
//  2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
//  2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
//  2017-05-01: OpenGL: Fixed save and restore of current blend func state.
//  2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
//  2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
//  2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)

//----------------------------------------
// OpenGL    GLSL      GLSL
// version   version   string
//----------------------------------------
//  2.0       110       "#version 110"
//  2.1       120
//  3.0       130
//  3.1       140
//  3.2       150       "#version 150"
//  3.3       330
//  4.0       400
//  4.1       410       "#version 410 core"
//  4.2       420
//  4.3       430
//  ES 2.0    100       "#version 100"
//  ES 3.0    300       "#version 300 es"
//----------------------------------------

#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h>     // intptr_t
#else
#include <stdint.h>     // intptr_t
#endif
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif

// iOS, Android and Emscripten can use GL ES 3
// Call ImGui_ImplOpenGL3_Init() with "#version 300 es"
#if (defined(__APPLE__) && TARGET_OS_IOS) || (defined(__ANDROID__)) || (defined(__EMSCRIPTEN__))
#define USE_GL_ES3
#endif

#ifdef USE_GL_ES3
// OpenGL ES 3
#include <GLES3/gl3.h>  // Use GL ES 3
#else
// Regular OpenGL
// About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. 
// Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad.
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
#include <GL/gl3w.h>
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
#include <GL/glew.h>
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h>
#else
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif
#endif

// OpenGL Data
static char         g_GlslVersionString[32] = "";
static GLuint       g_FontTexture = 0;
static GLuint       g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static int          g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0;
static int          g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0;
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;

// Functions
bool    ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
    // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
#ifdef USE_GL_ES3
    if (glsl_version == NULL)
        glsl_version = "#version 300 es";
#else
    if (glsl_version == NULL)
        glsl_version = "#version 130";
#endif
    IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
    strcpy(g_GlslVersionString, glsl_version);
    strcat(g_GlslVersionString, "\n");
    return true;
}

void    ImGui_ImplOpenGL3_Shutdown()
{
    ImGui_ImplOpenGL3_DestroyDeviceObjects();
}

void    ImGui_ImplOpenGL3_NewFrame()
{
    if (!g_FontTexture)
        ImGui_ImplOpenGL3_CreateDeviceObjects();
}

// OpenGL3 Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
void    ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
    // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
    ImGuiIO& io = ImGui::GetIO();
    int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x);
    int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y);
    if (fb_width <= 0 || fb_height <= 0)
        return;
    draw_data->ScaleClipRects(io.DisplayFramebufferScale);

    // Backup GL state
    GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
    glActiveTexture(GL_TEXTURE0);
    GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
    GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
#ifdef GL_SAMPLER_BINDING
    GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
#endif
    GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
    GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#ifdef GL_POLYGON_MODE
    GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
    GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
    GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
    GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
    GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
    GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
    GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
    GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
    GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
    GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
    GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
    GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
    GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);

    // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
    glEnable(GL_BLEND);
    glBlendEquation(GL_FUNC_ADD);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDisable(GL_CULL_FACE);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif

    // Setup viewport, orthographic projection matrix
    // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
    glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
    float L = draw_data->DisplayPos.x;
    float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
    float T = draw_data->DisplayPos.y;
    float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
    const float ortho_projection[4][4] =
    {
        { 2.0f/(R-L),   0.0f,         0.0f,   0.0f },
        { 0.0f,         2.0f/(T-B),   0.0f,   0.0f },
        { 0.0f,         0.0f,        -1.0f,   0.0f },
        { (R+L)/(L-R),  (T+B)/(B-T),  0.0f,   1.0f },
    };
    glUseProgram(g_ShaderHandle);
    glUniform1i(g_AttribLocationTex, 0);
    glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef GL_SAMPLER_BINDING
    glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
    // Recreate the VAO every time
    // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.)
    GLuint vao_handle = 0;
    glGenVertexArrays(1, &vao_handle);
    glBindVertexArray(vao_handle);
    glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
    glEnableVertexAttribArray(g_AttribLocationPosition);
    glEnableVertexAttribArray(g_AttribLocationUV);
    glEnableVertexAttribArray(g_AttribLocationColor);
    glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
    glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
    glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));

    // Draw
    ImVec2 pos = draw_data->DisplayPos;
    for (int n = 0; n < draw_data->CmdListsCount; n++)
    {
        const ImDrawList* cmd_list = draw_data->CmdLists[n];
        const ImDrawIdx* idx_buffer_offset = 0;

        glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
        glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);

        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
        {
            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
            if (pcmd->UserCallback)
            {
                // User callback (registered via ImDrawList::AddCallback)
                pcmd->UserCallback(cmd_list, pcmd);
            }
            else
            {
                ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y);
                if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
                {
                    // Apply scissor/clipping rectangle
                    glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));

                    // Bind texture, Draw
                    glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
                    glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
                }
            }
            idx_buffer_offset += pcmd->ElemCount;
        }
    }
    glDeleteVertexArrays(1, &vao_handle);

    // Restore modified GL state
    glUseProgram(last_program);
    glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef GL_SAMPLER_BINDING
    glBindSampler(0, last_sampler);
#endif
    glActiveTexture(last_active_texture);
    glBindVertexArray(last_vertex_array);
    glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
    glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
    glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
    if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
    if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
    if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
    if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
    glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
    glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
    glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
}

bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
    // Build texture atlas
    ImGuiIO& io = ImGui::GetIO();
    unsigned char* pixels;
    int width, height;
    io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);   // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.

    // Upload texture to graphics system
    GLint last_texture;
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
    glGenTextures(1, &g_FontTexture);
    glBindTexture(GL_TEXTURE_2D, g_FontTexture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

    // Store our identifier
    io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;

    // Restore state
    glBindTexture(GL_TEXTURE_2D, last_texture);

    return true;
}

void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
    if (g_FontTexture)
    {
        ImGuiIO& io = ImGui::GetIO();
        glDeleteTextures(1, &g_FontTexture);
        io.Fonts->TexID = 0;
        g_FontTexture = 0;
    }
}

// If you get an error please report on github. You may try different GL context version or GLSL version.
static bool CheckShader(GLuint handle, const char* desc)
{
    GLint status = 0, log_length = 0;
    glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
    glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
    if ((GLboolean)status == GL_FALSE)
        fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
    if (log_length > 0)
    {
        ImVector<char> buf;
        buf.resize((int)(log_length + 1));
        glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
        fprintf(stderr, "%s\n", buf.begin());
    }
    return (GLboolean)status == GL_TRUE;
}

// If you get an error please report on github. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
    GLint status = 0, log_length = 0;
    glGetProgramiv(handle, GL_LINK_STATUS, &status);
    glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
    if ((GLboolean)status == GL_FALSE)
        fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s!\n", desc);
    if (log_length > 0)
    {
        ImVector<char> buf;
        buf.resize((int)(log_length + 1));
        glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
        fprintf(stderr, "%s\n", buf.begin());
    }
    return (GLboolean)status == GL_TRUE;
}

bool    ImGui_ImplOpenGL3_CreateDeviceObjects()
{
    // Backup GL state
    GLint last_texture, last_array_buffer, last_vertex_array;
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
    glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
    glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);

    // Parse GLSL version string
    int glsl_version = 130;
    sscanf(g_GlslVersionString, "#version %d", &glsl_version);

    const GLchar* vertex_shader_glsl_120 =
        "uniform mat4 ProjMtx;\n"
        "attribute vec2 Position;\n"
        "attribute vec2 UV;\n"
        "attribute vec4 Color;\n"
        "varying vec2 Frag_UV;\n"
        "varying vec4 Frag_Color;\n"
        "void main()\n"
        "{\n"
        "    Frag_UV = UV;\n"
        "    Frag_Color = Color;\n"
        "    gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
        "}\n";

    const GLchar* vertex_shader_glsl_130 =
        "uniform mat4 ProjMtx;\n"
        "in vec2 Position;\n"
        "in vec2 UV;\n"
        "in vec4 Color;\n"
        "out vec2 Frag_UV;\n"
        "out vec4 Frag_Color;\n"
        "void main()\n"
        "{\n"
        "    Frag_UV = UV;\n"
        "    Frag_Color = Color;\n"
        "    gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
        "}\n";

    const GLchar* vertex_shader_glsl_300_es =
        "precision mediump float;\n"
        "layout (location = 0) in vec2 Position;\n"
        "layout (location = 1) in vec2 UV;\n"
        "layout (location = 2) in vec4 Color;\n"
        "uniform mat4 ProjMtx;\n"
        "out vec2 Frag_UV;\n"
        "out vec4 Frag_Color;\n"
        "void main()\n"
        "{\n"
        "    Frag_UV = UV;\n"
        "    Frag_Color = Color;\n"
        "    gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
        "}\n";

    const GLchar* vertex_shader_glsl_410_core =
        "layout (location = 0) in vec2 Position;\n"
        "layout (location = 1) in vec2 UV;\n"
        "layout (location = 2) in vec4 Color;\n"
        "uniform mat4 ProjMtx;\n"
        "out vec2 Frag_UV;\n"
        "out vec4 Frag_Color;\n"
        "void main()\n"
        "{\n"
        "    Frag_UV = UV;\n"
        "    Frag_Color = Color;\n"
        "    gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
        "}\n";

    const GLchar* fragment_shader_glsl_120 =
        "#ifdef GL_ES\n"
        "    precision mediump float;\n"
        "#endif\n"
        "uniform sampler2D Texture;\n"
        "varying vec2 Frag_UV;\n"
        "varying vec4 Frag_Color;\n"
        "void main()\n"
        "{\n"
        "    gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
        "}\n";

    const GLchar* fragment_shader_glsl_130 =
        "uniform sampler2D Texture;\n"
        "in vec2 Frag_UV;\n"
        "in vec4 Frag_Color;\n"
        "out vec4 Out_Color;\n"
        "void main()\n"
        "{\n"
        "    Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
        "}\n";

    const GLchar* fragment_shader_glsl_300_es =
        "precision mediump float;\n"
        "uniform sampler2D Texture;\n"
        "in vec2 Frag_UV;\n"
        "in vec4 Frag_Color;\n"
        "layout (location = 0) out vec4 Out_Color;\n"
        "void main()\n"
        "{\n"
        "    Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
        "}\n";

    const GLchar* fragment_shader_glsl_410_core =
        "in vec2 Frag_UV;\n"
        "in vec4 Frag_Color;\n"
        "uniform sampler2D Texture;\n"
        "layout (location = 0) out vec4 Out_Color;\n"
        "void main()\n"
        "{\n"
        "    Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
        "}\n";

    // Select shaders matching our GLSL versions
    const GLchar* vertex_shader = NULL;
    const GLchar* fragment_shader = NULL;
    if (glsl_version < 130)
    {
        vertex_shader = vertex_shader_glsl_120;
        fragment_shader = fragment_shader_glsl_120;
    }
    else if (glsl_version == 410)
    {
        vertex_shader = vertex_shader_glsl_410_core;
        fragment_shader = fragment_shader_glsl_410_core;
    }
    else if (glsl_version == 300)
    {
        vertex_shader = vertex_shader_glsl_300_es;
        fragment_shader = fragment_shader_glsl_300_es;
    }
    else
    {
        vertex_shader = vertex_shader_glsl_130;
        fragment_shader = fragment_shader_glsl_130;
    }

    // Create shaders
    const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
    g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
    glCompileShader(g_VertHandle);
    CheckShader(g_VertHandle, "vertex shader");

    const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
    g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
    glCompileShader(g_FragHandle);
    CheckShader(g_FragHandle, "fragment shader");

    g_ShaderHandle = glCreateProgram();
    glAttachShader(g_ShaderHandle, g_VertHandle);
    glAttachShader(g_ShaderHandle, g_FragHandle);
    glLinkProgram(g_ShaderHandle);
    CheckProgram(g_ShaderHandle, "shader program");

    g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
    g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
    g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position");
    g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV");
    g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color");

    // Create buffers
    glGenBuffers(1, &g_VboHandle);
    glGenBuffers(1, &g_ElementsHandle);

    ImGui_ImplOpenGL3_CreateFontsTexture();

    // Restore modified GL state
    glBindTexture(GL_TEXTURE_2D, last_texture);
    glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
    glBindVertexArray(last_vertex_array);

    return true;
}

void    ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
    if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle);
    if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle);
    g_VboHandle = g_ElementsHandle = 0;

    if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle);
    if (g_VertHandle) glDeleteShader(g_VertHandle);
    g_VertHandle = 0;

    if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle);
    if (g_FragHandle) glDeleteShader(g_FragHandle);
    g_FragHandle = 0;

    if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle);
    g_ShaderHandle = 0;

    ImGui_ImplOpenGL3_DestroyFontsTexture();
}
rmat( cipher_cls, mode_cls) ) self._cipher_registry[cipher_cls, mode_cls] = adapter def _register_default_ciphers(self): for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8, GCM]: self.register_cipher_adapter( AES, mode_cls, GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") ) for mode_cls in [CBC, CTR, ECB, OFB, CFB]: self.register_cipher_adapter( Camellia, mode_cls, GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") ) for mode_cls in [CBC, CFB, CFB8, OFB]: self.register_cipher_adapter( TripleDES, mode_cls, GetCipherByName("des-ede3-{mode.name}") ) self.register_cipher_adapter( TripleDES, ECB, GetCipherByName("des-ede3") ) for mode_cls in [CBC, CFB, OFB, ECB]: self.register_cipher_adapter( Blowfish, mode_cls, GetCipherByName("bf-{mode.name}") ) for mode_cls in [CBC, CFB, OFB, ECB]: self.register_cipher_adapter( SEED, mode_cls, GetCipherByName("seed-{mode.name}") ) for cipher_cls, mode_cls in itertools.product( [CAST5, IDEA], [CBC, OFB, CFB, ECB], ): self.register_cipher_adapter( cipher_cls, mode_cls, GetCipherByName("{cipher.name}-{mode.name}") ) self.register_cipher_adapter( ARC4, type(None), GetCipherByName("rc4") ) def create_symmetric_encryption_ctx(self, cipher, mode): return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) def create_symmetric_decryption_ctx(self, cipher, mode): return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) def pbkdf2_hmac_supported(self, algorithm): return self.hmac_supported(algorithm) def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations, key_material): buf = self._ffi.new("unsigned char[]", length) evp_md = self._lib.EVP_get_digestbyname( algorithm.name.encode("ascii")) self.openssl_assert(evp_md != self._ffi.NULL) res = self._lib.PKCS5_PBKDF2_HMAC( key_material, len(key_material), salt, len(salt), iterations, evp_md, length, buf ) self.openssl_assert(res == 1) return self._ffi.buffer(buf)[:] def _consume_errors(self): return binding._consume_errors(self._lib) def _bn_to_int(self, bn): assert bn != self._ffi.NULL if six.PY3: # Python 3 has constant time from_bytes, so use that. bn_num_bytes = self._lib.BN_num_bytes(bn) bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes) bin_len = self._lib.BN_bn2bin(bn, bin_ptr) # A zero length means the BN has value 0 self.openssl_assert(bin_len >= 0) return int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big") else: # Under Python 2 the best we can do is hex() hex_cdata = self._lib.BN_bn2hex(bn) self.openssl_assert(hex_cdata != self._ffi.NULL) hex_str = self._ffi.string(hex_cdata) self._lib.OPENSSL_free(hex_cdata) return int(hex_str, 16) def _int_to_bn(self, num, bn=None): """ Converts a python integer to a BIGNUM. The returned BIGNUM will not be garbage collected (to support adding them to structs that take ownership of the object). Be sure to register it for GC if it will be discarded after use. """ assert bn is None or bn != self._ffi.NULL if bn is None: bn = self._ffi.NULL if six.PY3: # Python 3 has constant time to_bytes, so use that. binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big") bn_ptr = self._lib.BN_bin2bn(binary, len(binary), bn) self.openssl_assert(bn_ptr != self._ffi.NULL) return bn_ptr else: # Under Python 2 the best we can do is hex(), [2:] removes the 0x # prefix. hex_num = hex(num).rstrip("L")[2:].encode("ascii") bn_ptr = self._ffi.new("BIGNUM **") bn_ptr[0] = bn res = self._lib.BN_hex2bn(bn_ptr, hex_num) self.openssl_assert(res != 0) self.openssl_assert(bn_ptr[0] != self._ffi.NULL) return bn_ptr[0] def generate_rsa_private_key(self, public_exponent, key_size): rsa._verify_rsa_parameters(public_exponent, key_size) rsa_cdata = self._lib.RSA_new() self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) bn = self._int_to_bn(public_exponent) bn = self._ffi.gc(bn, self._lib.BN_free) res = self._lib.RSA_generate_key_ex( rsa_cdata, key_size, bn, self._ffi.NULL ) self.openssl_assert(res == 1) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPrivateKey(self, rsa_cdata, evp_pkey) def generate_rsa_parameters_supported(self, public_exponent, key_size): return (public_exponent >= 3 and public_exponent & 1 != 0 and key_size >= 512) def load_rsa_private_numbers(self, numbers): rsa._check_private_key_components( numbers.p, numbers.q, numbers.d, numbers.dmp1, numbers.dmq1, numbers.iqmp, numbers.public_numbers.e, numbers.public_numbers.n ) rsa_cdata = self._lib.RSA_new() self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) p = self._int_to_bn(numbers.p) q = self._int_to_bn(numbers.q) d = self._int_to_bn(numbers.d) dmp1 = self._int_to_bn(numbers.dmp1) dmq1 = self._int_to_bn(numbers.dmq1) iqmp = self._int_to_bn(numbers.iqmp) e = self._int_to_bn(numbers.public_numbers.e) n = self._int_to_bn(numbers.public_numbers.n) res = self._lib.RSA_set0_factors(rsa_cdata, p, q) self.openssl_assert(res == 1) res = self._lib.RSA_set0_key(rsa_cdata, n, e, d) self.openssl_assert(res == 1) res = self._lib.RSA_set0_crt_params(rsa_cdata, dmp1, dmq1, iqmp) self.openssl_assert(res == 1) res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL) self.openssl_assert(res == 1) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPrivateKey(self, rsa_cdata, evp_pkey) def load_rsa_public_numbers(self, numbers): rsa._check_public_key_components(numbers.e, numbers.n) rsa_cdata = self._lib.RSA_new() self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) e = self._int_to_bn(numbers.e) n = self._int_to_bn(numbers.n) res = self._lib.RSA_set0_key(rsa_cdata, n, e, self._ffi.NULL) self.openssl_assert(res == 1) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPublicKey(self, rsa_cdata, evp_pkey) def _create_evp_pkey_gc(self): evp_pkey = self._lib.EVP_PKEY_new() self.openssl_assert(evp_pkey != self._ffi.NULL) evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) return evp_pkey def _rsa_cdata_to_evp_pkey(self, rsa_cdata): evp_pkey = self._create_evp_pkey_gc() res = self._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata) self.openssl_assert(res == 1) return evp_pkey def _bytes_to_bio(self, data): """ Return a _MemoryBIO namedtuple of (BIO, char*). The char* is the storage for the BIO and it must stay alive until the BIO is finished with. """ data_char_p = self._ffi.new("char[]", data) bio = self._lib.BIO_new_mem_buf( data_char_p, len(data) ) self.openssl_assert(bio != self._ffi.NULL) return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_char_p) def _create_mem_bio_gc(self): """ Creates an empty memory BIO. """ bio_method = self._lib.BIO_s_mem() self.openssl_assert(bio_method != self._ffi.NULL) bio = self._lib.BIO_new(bio_method) self.openssl_assert(bio != self._ffi.NULL) bio = self._ffi.gc(bio, self._lib.BIO_free) return bio def _read_mem_bio(self, bio): """ Reads a memory BIO. This only works on memory BIOs. """ buf = self._ffi.new("char **") buf_len = self._lib.BIO_get_mem_data(bio, buf) self.openssl_assert(buf_len > 0) self.openssl_assert(buf[0] != self._ffi.NULL) bio_data = self._ffi.buffer(buf[0], buf_len)[:] return bio_data def _evp_pkey_to_private_key(self, evp_pkey): """ Return the appropriate type of PrivateKey given an evp_pkey cdata pointer. """ key_type = self._lib.EVP_PKEY_id(evp_pkey) if key_type == self._lib.EVP_PKEY_RSA: rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) return _RSAPrivateKey(self, rsa_cdata, evp_pkey) elif key_type == self._lib.EVP_PKEY_DSA: dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) return _DSAPrivateKey(self, dsa_cdata, evp_pkey) elif key_type == self._lib.EVP_PKEY_EC: ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) elif key_type in self._dh_types: dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) self.openssl_assert(dh_cdata != self._ffi.NULL) dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) return _DHPrivateKey(self, dh_cdata, evp_pkey) else: raise UnsupportedAlgorithm("Unsupported key type.") def _evp_pkey_to_public_key(self, evp_pkey): """ Return the appropriate type of PublicKey given an evp_pkey cdata pointer. """ key_type = self._lib.EVP_PKEY_id(evp_pkey) if key_type == self._lib.EVP_PKEY_RSA: rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) return _RSAPublicKey(self, rsa_cdata, evp_pkey) elif key_type == self._lib.EVP_PKEY_DSA: dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) return _DSAPublicKey(self, dsa_cdata, evp_pkey) elif key_type == self._lib.EVP_PKEY_EC: ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) elif key_type in self._dh_types: dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) self.openssl_assert(dh_cdata != self._ffi.NULL) dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) return _DHPublicKey(self, dh_cdata, evp_pkey) else: raise UnsupportedAlgorithm("Unsupported key type.") def _oaep_hash_supported(self, algorithm): if self._lib.Cryptography_HAS_RSA_OAEP_MD: return isinstance( algorithm, ( hashes.SHA1, hashes.SHA224, hashes.SHA256, hashes.SHA384, hashes.SHA512, ) ) else: return isinstance(algorithm, hashes.SHA1) def rsa_padding_supported(self, padding): if isinstance(padding, PKCS1v15): return True elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): return self.hash_supported(padding._mgf._algorithm) elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): return ( self._oaep_hash_supported(padding._mgf._algorithm) and self._oaep_hash_supported(padding._algorithm) ) else: return False def generate_dsa_parameters(self, key_size): if key_size not in (1024, 2048, 3072): raise ValueError("Key size must be 1024 or 2048 or 3072 bits.") ctx = self._lib.DSA_new() self.openssl_assert(ctx != self._ffi.NULL) ctx = self._ffi.gc(ctx, self._lib.DSA_free) res = self._lib.DSA_generate_parameters_ex( ctx, key_size, self._ffi.NULL, 0, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) self.openssl_assert(res == 1) return _DSAParameters(self, ctx) def generate_dsa_private_key(self, parameters): ctx = self._lib.DSAparams_dup(parameters._dsa_cdata) self.openssl_assert(ctx != self._ffi.NULL) ctx = self._ffi.gc(ctx, self._lib.DSA_free) self._lib.DSA_generate_key(ctx) evp_pkey = self._dsa_cdata_to_evp_pkey(ctx) return _DSAPrivateKey(self, ctx, evp_pkey) def generate_dsa_private_key_and_parameters(self, key_size): parameters = self.generate_dsa_parameters(key_size) return self.generate_dsa_private_key(parameters) def _dsa_cdata_set_values(self, dsa_cdata, p, q, g, pub_key, priv_key): res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) self.openssl_assert(res == 1) res = self._lib.DSA_set0_key(dsa_cdata, pub_key, priv_key) self.openssl_assert(res == 1) def load_dsa_private_numbers(self, numbers): dsa._check_dsa_private_numbers(numbers) parameter_numbers = numbers.public_numbers.parameter_numbers dsa_cdata = self._lib.DSA_new() self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) p = self._int_to_bn(parameter_numbers.p) q = self._int_to_bn(parameter_numbers.q) g = self._int_to_bn(parameter_numbers.g) pub_key = self._int_to_bn(numbers.public_numbers.y) priv_key = self._int_to_bn(numbers.x) self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) return _DSAPrivateKey(self, dsa_cdata, evp_pkey) def load_dsa_public_numbers(self, numbers): dsa._check_dsa_parameters(numbers.parameter_numbers) dsa_cdata = self._lib.DSA_new() self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) p = self._int_to_bn(numbers.parameter_numbers.p) q = self._int_to_bn(numbers.parameter_numbers.q) g = self._int_to_bn(numbers.parameter_numbers.g) pub_key = self._int_to_bn(numbers.y) priv_key = self._ffi.NULL self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) return _DSAPublicKey(self, dsa_cdata, evp_pkey) def load_dsa_parameter_numbers(self, numbers): dsa._check_dsa_parameters(numbers) dsa_cdata = self._lib.DSA_new() self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) p = self._int_to_bn(numbers.p) q = self._int_to_bn(numbers.q) g = self._int_to_bn(numbers.g) res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) self.openssl_assert(res == 1) return _DSAParameters(self, dsa_cdata) def _dsa_cdata_to_evp_pkey(self, dsa_cdata): evp_pkey = self._create_evp_pkey_gc() res = self._lib.EVP_PKEY_set1_DSA(evp_pkey, dsa_cdata) self.openssl_assert(res == 1) return evp_pkey def dsa_hash_supported(self, algorithm): return self.hash_supported(algorithm) def dsa_parameters_supported(self, p, q, g): return True def cmac_algorithm_supported(self, algorithm): return self.cipher_supported( algorithm, CBC(b"\x00" * algorithm.block_size) ) def create_cmac_ctx(self, algorithm): return _CMACContext(self, algorithm) def create_x509_csr(self, builder, private_key, algorithm): if not isinstance(algorithm, hashes.HashAlgorithm): raise TypeError('Algorithm must be a registered hash algorithm.') if ( isinstance(algorithm, hashes.MD5) and not isinstance(private_key, rsa.RSAPrivateKey) ): raise ValueError( "MD5 is not a supported hash algorithm for EC/DSA CSRs" ) # Resolve the signature algorithm. evp_md = self._lib.EVP_get_digestbyname( algorithm.name.encode('ascii') ) self.openssl_assert(evp_md != self._ffi.NULL) # Create an empty request. x509_req = self._lib.X509_REQ_new() self.openssl_assert(x509_req != self._ffi.NULL) x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) # Set x509 version. res = self._lib.X509_REQ_set_version(x509_req, x509.Version.v1.value) self.openssl_assert(res == 1) # Set subject name. res = self._lib.X509_REQ_set_subject_name( x509_req, _encode_name_gc(self, builder._subject_name) ) self.openssl_assert(res == 1) # Set subject public key. public_key = private_key.public_key() res = self._lib.X509_REQ_set_pubkey( x509_req, public_key._evp_pkey ) self.openssl_assert(res == 1) # Add extensions. sk_extension = self._lib.sk_X509_EXTENSION_new_null() self.openssl_assert(sk_extension != self._ffi.NULL) sk_extension = self._ffi.gc( sk_extension, self._lib.sk_X509_EXTENSION_free ) # gc is not necessary for CSRs, as sk_X509_EXTENSION_free # will release all the X509_EXTENSIONs. self._create_x509_extensions( extensions=builder._extensions, handlers=_EXTENSION_ENCODE_HANDLERS, x509_obj=sk_extension, add_func=self._lib.sk_X509_EXTENSION_insert, gc=False ) res = self._lib.X509_REQ_add_extensions(x509_req, sk_extension) self.openssl_assert(res == 1) # Sign the request using the requester's private key. res = self._lib.X509_REQ_sign( x509_req, private_key._evp_pkey, evp_md ) if res == 0: errors = self._consume_errors() self.openssl_assert( errors[0]._lib_reason_match( self._lib.ERR_LIB_RSA, self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY ) ) raise ValueError("Digest too big for RSA key") return _CertificateSigningRequest(self, x509_req) def create_x509_certificate(self, builder, private_key, algorithm): if not isinstance(builder, x509.CertificateBuilder): raise TypeError('Builder type mismatch.') if not isinstance(algorithm, hashes.HashAlgorithm): raise TypeError('Algorithm must be a registered hash algorithm.') if ( isinstance(algorithm, hashes.MD5) and not isinstance(private_key, rsa.RSAPrivateKey) ): raise ValueError( "MD5 is not a supported hash algorithm for EC/DSA certificates" ) # Resolve the signature algorithm. evp_md = self._lib.EVP_get_digestbyname( algorithm.name.encode('ascii') ) self.openssl_assert(evp_md != self._ffi.NULL) # Create an empty certificate. x509_cert = self._lib.X509_new() x509_cert = self._ffi.gc(x509_cert, backend._lib.X509_free) # Set the x509 version. res = self._lib.X509_set_version(x509_cert, builder._version.value) self.openssl_assert(res == 1) # Set the subject's name. res = self._lib.X509_set_subject_name( x509_cert, _encode_name_gc(self, builder._subject_name) ) self.openssl_assert(res == 1) # Set the subject's public key. res = self._lib.X509_set_pubkey( x509_cert, builder._public_key._evp_pkey ) self.openssl_assert(res == 1) # Set the certificate serial number. serial_number = _encode_asn1_int_gc(self, builder._serial_number) res = self._lib.X509_set_serialNumber(x509_cert, serial_number) self.openssl_assert(res == 1) # Set the "not before" time. res = self._lib.ASN1_TIME_set( self._lib.X509_get_notBefore(x509_cert), calendar.timegm(builder._not_valid_before.timetuple()) ) if res == self._ffi.NULL: self._raise_time_set_error() # Set the "not after" time. res = self._lib.ASN1_TIME_set( self._lib.X509_get_notAfter(x509_cert), calendar.timegm(builder._not_valid_after.timetuple()) ) if res == self._ffi.NULL: self._raise_time_set_error() # Add extensions. self._create_x509_extensions( extensions=builder._extensions, handlers=_EXTENSION_ENCODE_HANDLERS, x509_obj=x509_cert, add_func=self._lib.X509_add_ext, gc=True ) # Set the issuer name. res = self._lib.X509_set_issuer_name( x509_cert, _encode_name_gc(self, builder._issuer_name) ) self.openssl_assert(res == 1) # Sign the certificate with the issuer's private key. res = self._lib.X509_sign( x509_cert, private_key._evp_pkey, evp_md ) if res == 0: errors = self._consume_errors() self.openssl_assert( errors[0]._lib_reason_match( self._lib.ERR_LIB_RSA, self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY ) ) raise ValueError("Digest too big for RSA key") return _Certificate(self, x509_cert) def _raise_time_set_error(self): errors = self._consume_errors() self.openssl_assert( errors[0]._lib_reason_match( self._lib.ERR_LIB_ASN1, self._lib.ASN1_R_ERROR_GETTING_TIME ) ) raise ValueError( "Invalid time. This error can occur if you set a time too far in " "the future on Windows." ) def create_x509_crl(self, builder, private_key, algorithm): if not isinstance(builder, x509.CertificateRevocationListBuilder): raise TypeError('Builder type mismatch.') if not isinstance(algorithm, hashes.HashAlgorithm): raise TypeError('Algorithm must be a registered hash algorithm.') if ( isinstance(algorithm, hashes.MD5) and not isinstance(private_key, rsa.RSAPrivateKey) ): raise ValueError( "MD5 is not a supported hash algorithm for EC/DSA CRLs" ) evp_md = self._lib.EVP_get_digestbyname( algorithm.name.encode('ascii') ) self.openssl_assert(evp_md != self._ffi.NULL) # Create an empty CRL. x509_crl = self._lib.X509_CRL_new() x509_crl = self._ffi.gc(x509_crl, backend._lib.X509_CRL_free) # Set the x509 CRL version. We only support v2 (integer value 1). res = self._lib.X509_CRL_set_version(x509_crl, 1) self.openssl_assert(res == 1) # Set the issuer name. res = self._lib.X509_CRL_set_issuer_name( x509_crl, _encode_name_gc(self, builder._issuer_name) ) self.openssl_assert(res == 1) # Set the last update time. last_update = self._lib.ASN1_TIME_set( self._ffi.NULL, calendar.timegm(builder._last_update.timetuple()) ) self.openssl_assert(last_update != self._ffi.NULL) last_update = self._ffi.gc(last_update, self._lib.ASN1_TIME_free) res = self._lib.X509_CRL_set_lastUpdate(x509_crl, last_update) self.openssl_assert(res == 1) # Set the next update time. next_update = self._lib.ASN1_TIME_set( self._ffi.NULL, calendar.timegm(builder._next_update.timetuple()) ) self.openssl_assert(next_update != self._ffi.NULL) next_update = self._ffi.gc(next_update, self._lib.ASN1_TIME_free) res = self._lib.X509_CRL_set_nextUpdate(x509_crl, next_update) self.openssl_assert(res == 1) # Add extensions. self._create_x509_extensions( extensions=builder._extensions, handlers=_CRL_EXTENSION_ENCODE_HANDLERS, x509_obj=x509_crl, add_func=self._lib.X509_CRL_add_ext, gc=True ) # add revoked certificates for revoked_cert in builder._revoked_certificates: # Duplicating because the X509_CRL takes ownership and will free # this memory when X509_CRL_free is called. revoked = self._lib.Cryptography_X509_REVOKED_dup( revoked_cert._x509_revoked ) self.openssl_assert(revoked != self._ffi.NULL) res = self._lib.X509_CRL_add0_revoked(x509_crl, revoked) self.openssl_assert(res == 1) res = self._lib.X509_CRL_sign( x509_crl, private_key._evp_pkey, evp_md ) if res == 0: errors = self._consume_errors() self.openssl_assert( errors[0]._lib_reason_match( self._lib.ERR_LIB_RSA, self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY ) ) raise ValueError("Digest too big for RSA key") return _CertificateRevocationList(self, x509_crl) def _create_x509_extensions(self, extensions, handlers, x509_obj, add_func, gc): for i, extension in enumerate(extensions): x509_extension = self._create_x509_extension( handlers, extension ) self.openssl_assert(x509_extension != self._ffi.NULL) if gc: x509_extension = self._ffi.gc( x509_extension, self._lib.X509_EXTENSION_free ) res = add_func(x509_obj, x509_extension, i) self.openssl_assert(res >= 1) def _create_x509_extension(self, handlers, extension): if isinstance(extension.value, x509.UnrecognizedExtension): obj = _txt2obj_gc(self, extension.oid.dotted_string) value = _encode_asn1_str_gc( self, extension.value.value, len(extension.value.value) ) return self._lib.X509_EXTENSION_create_by_OBJ( self._ffi.NULL, obj, 1 if extension.critical else 0, value ) else: try: encode = handlers[extension.oid] except KeyError: raise NotImplementedError( 'Extension not supported: {0}'.format(extension.oid) ) ext_struct = encode(self, extension.value) nid = self._lib.OBJ_txt2nid( extension.oid.dotted_string.encode("ascii") ) backend.openssl_assert(nid != self._lib.NID_undef) return self._lib.X509V3_EXT_i2d( nid, 1 if extension.critical else 0, ext_struct ) def create_x509_revoked_certificate(self, builder): if not isinstance(builder, x509.RevokedCertificateBuilder): raise TypeError('Builder type mismatch.') x509_revoked = self._lib.X509_REVOKED_new() self.openssl_assert(x509_revoked != self._ffi.NULL) x509_revoked = self._ffi.gc(x509_revoked, self._lib.X509_REVOKED_free) serial_number = _encode_asn1_int_gc(self, builder._serial_number) res = self._lib.X509_REVOKED_set_serialNumber( x509_revoked, serial_number ) self.openssl_assert(res == 1) rev_date = self._lib.ASN1_TIME_set( self._ffi.NULL, calendar.timegm(builder._revocation_date.timetuple()) ) self.openssl_assert(rev_date != self._ffi.NULL) rev_date = self._ffi.gc(rev_date, self._lib.ASN1_TIME_free) res = self._lib.X509_REVOKED_set_revocationDate(x509_revoked, rev_date) self.openssl_assert(res == 1) # add CRL entry extensions self._create_x509_extensions( extensions=builder._extensions, handlers=_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS, x509_obj=x509_revoked, add_func=self._lib.X509_REVOKED_add_ext, gc=True ) return _RevokedCertificate(self, None, x509_revoked) def load_pem_private_key(self, data, password): return self._load_key( self._lib.PEM_read_bio_PrivateKey, self._evp_pkey_to_private_key, data, password, ) def load_pem_public_key(self, data): mem_bio = self._bytes_to_bio(data) evp_pkey = self._lib.PEM_read_bio_PUBKEY( mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) if evp_pkey != self._ffi.NULL: evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) return self._evp_pkey_to_public_key(evp_pkey) else: # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still # need to check to see if it is a pure PKCS1 RSA public key (not # embedded in a subjectPublicKeyInfo) self._consume_errors() res = self._lib.BIO_reset(mem_bio.bio) self.openssl_assert(res == 1) rsa_cdata = self._lib.PEM_read_bio_RSAPublicKey( mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) if rsa_cdata != self._ffi.NULL: rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPublicKey(self, rsa_cdata, evp_pkey) else: self._handle_key_loading_error() def load_pem_parameters(self, data): mem_bio = self._bytes_to_bio(data) # only DH is supported currently dh_cdata = self._lib.PEM_read_bio_DHparams( mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL) if dh_cdata != self._ffi.NULL: dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) return _DHParameters(self, dh_cdata) else: self._handle_key_loading_error() def load_der_private_key(self, data, password): # OpenSSL has a function called d2i_AutoPrivateKey that in theory # handles this automatically, however it doesn't handle encrypted # private keys. Instead we try to load the key two different ways. # First we'll try to load it as a traditional key. bio_data = self._bytes_to_bio(data) key = self._evp_pkey_from_der_traditional_key(bio_data, password) if key: return self._evp_pkey_to_private_key(key) else: # Finally we try to load it with the method that handles encrypted # PKCS8 properly. return self._load_key( self._lib.d2i_PKCS8PrivateKey_bio, self._evp_pkey_to_private_key, data, password, ) def _evp_pkey_from_der_traditional_key(self, bio_data, password): key = self._lib.d2i_PrivateKey_bio(bio_data.bio, self._ffi.NULL) if key != self._ffi.NULL: key = self._ffi.gc(key, self._lib.EVP_PKEY_free) if password is not None: raise TypeError( "Password was given but private key is not encrypted." ) return key else: self._consume_errors() return None def load_der_public_key(self, data): mem_bio = self._bytes_to_bio(data) evp_pkey = self._lib.d2i_PUBKEY_bio(mem_bio.bio, self._ffi.NULL) if evp_pkey != self._ffi.NULL: evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) return self._evp_pkey_to_public_key(evp_pkey) else: # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still # need to check to see if it is a pure PKCS1 RSA public key (not # embedded in a subjectPublicKeyInfo) self._consume_errors() res = self._lib.BIO_reset(mem_bio.bio) self.openssl_assert(res == 1) rsa_cdata = self._lib.d2i_RSAPublicKey_bio( mem_bio.bio, self._ffi.NULL ) if rsa_cdata != self._ffi.NULL: rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPublicKey(self, rsa_cdata, evp_pkey) else: self._handle_key_loading_error() def load_der_parameters(self, data): mem_bio = self._bytes_to_bio(data) dh_cdata = self._lib.d2i_DHparams_bio( mem_bio.bio, self._ffi.NULL ) if dh_cdata != self._ffi.NULL: dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) return _DHParameters(self, dh_cdata) elif self._lib.Cryptography_HAS_EVP_PKEY_DHX: # We check to see if the is dhx. self._consume_errors() res = self._lib.BIO_reset(mem_bio.bio) self.openssl_assert(res == 1) dh_cdata = self._lib.Cryptography_d2i_DHxparams_bio( mem_bio.bio, self._ffi.NULL ) if dh_cdata != self._ffi.NULL: dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) return _DHParameters(self, dh_cdata) self._handle_key_loading_error() def load_pem_x509_certificate(self, data): mem_bio = self._bytes_to_bio(data) x509 = self._lib.PEM_read_bio_X509( mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) if x509 == self._ffi.NULL: self._consume_errors() raise ValueError("Unable to load certificate") x509 = self._ffi.gc(x509, self._lib.X509_free) return _Certificate(self, x509) def load_der_x509_certificate(self, data): mem_bio = self._bytes_to_bio(data) x509 = self._lib.d2i_X509_bio(mem_bio.bio, self._ffi.NULL) if x509 == self._ffi.NULL: self._consume_errors() raise ValueError("Unable to load certificate") x509 = self._ffi.gc(x509, self._lib.X509_free) return _Certificate(self, x509) def load_pem_x509_crl(self, data): mem_bio = self._bytes_to_bio(data) x509_crl = self._lib.PEM_read_bio_X509_CRL( mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) if x509_crl == self._ffi.NULL: self._consume_errors() raise ValueError("Unable to load CRL") x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) return _CertificateRevocationList(self, x509_crl) def load_der_x509_crl(self, data): mem_bio = self._bytes_to_bio(data) x509_crl = self._lib.d2i_X509_CRL_bio(mem_bio.bio, self._ffi.NULL) if x509_crl == self._ffi.NULL: self._consume_errors() raise ValueError("Unable to load CRL") x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) return _CertificateRevocationList(self, x509_crl) def load_pem_x509_csr(self, data): mem_bio = self._bytes_to_bio(data) x509_req = self._lib.PEM_read_bio_X509_REQ( mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) if x509_req == self._ffi.NULL: self._consume_errors() raise ValueError("Unable to load request") x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) return _CertificateSigningRequest(self, x509_req) def load_der_x509_csr(self, data): mem_bio = self._bytes_to_bio(data) x509_req = self._lib.d2i_X509_REQ_bio(mem_bio.bio, self._ffi.NULL) if x509_req == self._ffi.NULL: self._consume_errors() raise ValueError("Unable to load request") x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) return _CertificateSigningRequest(self, x509_req) def _load_key(self, openssl_read_func, convert_func, data, password): mem_bio = self._bytes_to_bio(data) if password is not None and not isinstance(password, bytes): raise TypeError("Password must be bytes") userdata = self._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *") if password is not None: password_buf = self._ffi.new("char []", password) userdata.password = password_buf userdata.length = len(password) evp_pkey = openssl_read_func( mem_bio.bio, self._ffi.NULL, self._ffi.addressof( self._lib._original_lib, "Cryptography_pem_password_cb" ), userdata, ) if evp_pkey == self._ffi.NULL: if userdata.error != 0: errors = self._consume_errors() self.openssl_assert(errors) if userdata.error == -1: raise TypeError( "Password was not given but private key is encrypted" ) else: assert userdata.error == -2 raise ValueError( "Passwords longer than {0} bytes are not supported " "by this backend.".format(userdata.maxsize - 1) ) else: self._handle_key_loading_error() evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) if password is not None and userdata.called == 0: raise TypeError( "Password was given but private key is not encrypted.") assert ( (password is not None and userdata.called == 1) or password is None ) return convert_func(evp_pkey) def _handle_key_loading_error(self): errors = self._consume_errors() if not errors: raise ValueError("Could not deserialize key data.") elif ( errors[0]._lib_reason_match( self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT ) or errors[0]._lib_reason_match( self._lib.ERR_LIB_PKCS12, self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR ) ): raise ValueError("Bad decrypt. Incorrect password?") elif ( errors[0]._lib_reason_match( self._lib.ERR_LIB_EVP, self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM ) or errors[0]._lib_reason_match( self._lib.ERR_LIB_PEM, self._lib.PEM_R_UNSUPPORTED_ENCRYPTION ) ): raise UnsupportedAlgorithm( "PEM data is encrypted with an unsupported cipher", _Reasons.UNSUPPORTED_CIPHER ) elif any( error._lib_reason_match( self._lib.ERR_LIB_EVP, self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM ) for error in errors ): raise ValueError("Unsupported public key algorithm.") else: assert errors[0].lib in ( self._lib.ERR_LIB_EVP, self._lib.ERR_LIB_PEM, self._lib.ERR_LIB_ASN1, ) raise ValueError("Could not deserialize key data.") def elliptic_curve_supported(self, curve): try: curve_nid = self._elliptic_curve_to_nid(curve) except UnsupportedAlgorithm: curve_nid = self._lib.NID_undef ctx = self._lib.EC_GROUP_new_by_curve_name(curve_nid) if ctx == self._ffi.NULL: errors = self._consume_errors() self.openssl_assert( curve_nid == self._lib.NID_undef or errors[0]._lib_reason_match( self._lib.ERR_LIB_EC, self._lib.EC_R_UNKNOWN_GROUP ) ) return False else: self.openssl_assert(curve_nid != self._lib.NID_undef) self._lib.EC_GROUP_free(ctx) return True def elliptic_curve_signature_algorithm_supported( self, signature_algorithm, curve ): # We only support ECDSA right now. if not isinstance(signature_algorithm, ec.ECDSA): return False return self.elliptic_curve_supported(curve) def generate_elliptic_curve_private_key(self, curve): """ Generate a new private key on the named curve. """ if self.elliptic_curve_supported(curve): curve_nid = self._elliptic_curve_to_nid(curve) ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) res = self._lib.EC_KEY_generate_key(ec_cdata) self.openssl_assert(res == 1) evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) else: raise UnsupportedAlgorithm( "Backend object does not support {0}.".format(curve.name), _Reasons.UNSUPPORTED_ELLIPTIC_CURVE ) def load_elliptic_curve_private_numbers(self, numbers): public = numbers.public_numbers curve_nid = self._elliptic_curve_to_nid(public.curve) ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) ec_cdata = self._ec_key_set_public_key_affine_coordinates( ec_cdata, public.x, public.y) private_value = self._ffi.gc( self._int_to_bn(numbers.private_value), self._lib.BN_free ) res = self._lib.EC_KEY_set_private_key(ec_cdata, private_value) self.openssl_assert(res == 1) evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) def load_elliptic_curve_public_numbers(self, numbers): curve_nid = self._elliptic_curve_to_nid(numbers.curve) ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) ec_cdata = self._ec_key_set_public_key_affine_coordinates( ec_cdata, numbers.x, numbers.y) evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) def derive_elliptic_curve_private_key(self, private_value, curve): curve_nid = self._elliptic_curve_to_nid(curve) ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) get_func, group = self._ec_key_determine_group_get_func(ec_cdata) point = self._lib.EC_POINT_new(group) self.openssl_assert(point != self._ffi.NULL) point = self._ffi.gc(point, self._lib.EC_POINT_free) value = self._int_to_bn(private_value) value = self._ffi.gc(value, self._lib.BN_free) with self._tmp_bn_ctx() as bn_ctx: res = self._lib.EC_POINT_mul(group, point, value, self._ffi.NULL, self._ffi.NULL, bn_ctx) self.openssl_assert(res == 1) bn_x = self._lib.BN_CTX_get(bn_ctx) bn_y = self._lib.BN_CTX_get(bn_ctx) res = get_func(group, point, bn_x, bn_y, bn_ctx) self.openssl_assert(res == 1) res = self._lib.EC_KEY_set_public_key(ec_cdata, point) self.openssl_assert(res == 1) res = self._lib.EC_KEY_set_private_key( ec_cdata, self._int_to_bn(private_value)) self.openssl_assert(res == 1) evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): return ( self.elliptic_curve_supported(curve) and isinstance(algorithm, ec.ECDH) ) def _ec_cdata_to_evp_pkey(self, ec_cdata): evp_pkey = self._create_evp_pkey_gc() res = self._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, ec_cdata) self.openssl_assert(res == 1) return evp_pkey def _elliptic_curve_to_nid(self, curve): """ Get the NID for a curve name. """ curve_aliases = { "secp192r1": "prime192v1", "secp256r1": "prime256v1" } curve_name = curve_aliases.get(curve.name, curve.name) curve_nid = self._lib.OBJ_sn2nid(curve_name.encode()) if curve_nid == self._lib.NID_undef: raise UnsupportedAlgorithm( "{0} is not a supported elliptic curve".format(curve.name), _Reasons.UNSUPPORTED_ELLIPTIC_CURVE ) return curve_nid @contextmanager def _tmp_bn_ctx(self): bn_ctx = self._lib.BN_CTX_new() self.openssl_assert(bn_ctx != self._ffi.NULL) bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free) self._lib.BN_CTX_start(bn_ctx) try: yield bn_ctx finally: self._lib.BN_CTX_end(bn_ctx) def _ec_key_determine_group_get_func(self, ctx): """ Given an EC_KEY determine the group and what function is required to get point coordinates. """ self.openssl_assert(ctx != self._ffi.NULL) nid_two_field = self._lib.OBJ_sn2nid(b"characteristic-two-field") self.openssl_assert(nid_two_field != self._lib.NID_undef) group = self._lib.EC_KEY_get0_group(ctx) self.openssl_assert(group != self._ffi.NULL) method = self._lib.EC_GROUP_method_of(group) self.openssl_assert(method != self._ffi.NULL) nid = self._lib.EC_METHOD_get_field_type(method) self.openssl_assert(nid != self._lib.NID_undef) if nid == nid_two_field and self._lib.Cryptography_HAS_EC2M: get_func = self._lib.EC_POINT_get_affine_coordinates_GF2m else: get_func = self._lib.EC_POINT_get_affine_coordinates_GFp assert get_func return get_func, group def _ec_key_set_public_key_affine_coordinates(self, ctx, x, y): """ Sets the public key point in the EC_KEY context to the affine x and y values. """ if x < 0 or y < 0: raise ValueError( "Invalid EC key. Both x and y must be non-negative." ) x = self._ffi.gc(self._int_to_bn(x), self._lib.BN_free) y = self._ffi.gc(self._int_to_bn(y), self._lib.BN_free) res = self._lib.EC_KEY_set_public_key_affine_coordinates(ctx, x, y) if res != 1: self._consume_errors() raise ValueError("Invalid EC key.") return ctx def _private_key_bytes(self, encoding, format, encryption_algorithm, evp_pkey, cdata): if not isinstance(format, serialization.PrivateFormat): raise TypeError( "format must be an item from the PrivateFormat enum" ) if not isinstance(encryption_algorithm, serialization.KeySerializationEncryption): raise TypeError( "Encryption algorithm must be a KeySerializationEncryption " "instance" ) if isinstance(encryption_algorithm, serialization.NoEncryption): password = b"" passlen = 0 evp_cipher = self._ffi.NULL elif isinstance(encryption_algorithm, serialization.BestAvailableEncryption): # This is a curated value that we will update over time. evp_cipher = self._lib.EVP_get_cipherbyname( b"aes-256-cbc" ) password = encryption_algorithm.password passlen = len(password) if passlen > 1023: raise ValueError( "Passwords longer than 1023 bytes are not supported by " "this backend" ) else: raise ValueError("Unsupported encryption type") key_type = self._lib.EVP_PKEY_id(evp_pkey) if encoding is serialization.Encoding.PEM: if format is serialization.PrivateFormat.PKCS8: write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey key = evp_pkey else: assert format is serialization.PrivateFormat.TraditionalOpenSSL if key_type == self._lib.EVP_PKEY_RSA: write_bio = self._lib.PEM_write_bio_RSAPrivateKey elif key_type == self._lib.EVP_PKEY_DSA: write_bio = self._lib.PEM_write_bio_DSAPrivateKey else: assert key_type == self._lib.EVP_PKEY_EC write_bio = self._lib.PEM_write_bio_ECPrivateKey key = cdata elif encoding is serialization.Encoding.DER: if format is serialization.PrivateFormat.TraditionalOpenSSL: if not isinstance( encryption_algorithm, serialization.NoEncryption ): raise ValueError( "Encryption is not supported for DER encoded " "traditional OpenSSL keys" ) return self._private_key_bytes_traditional_der(key_type, cdata) else: assert format is serialization.PrivateFormat.PKCS8 write_bio = self._lib.i2d_PKCS8PrivateKey_bio key = evp_pkey else: raise TypeError("encoding must be an item from the Encoding enum") bio = self._create_mem_bio_gc() res = write_bio( bio, key, evp_cipher, password, passlen, self._ffi.NULL, self._ffi.NULL ) self.openssl_assert(res == 1) return self._read_mem_bio(bio) def _private_key_bytes_traditional_der(self, key_type, cdata): if key_type == self._lib.EVP_PKEY_RSA: write_bio = self._lib.i2d_RSAPrivateKey_bio elif key_type == self._lib.EVP_PKEY_EC: write_bio = self._lib.i2d_ECPrivateKey_bio else: self.openssl_assert(key_type == self._lib.EVP_PKEY_DSA) write_bio = self._lib.i2d_DSAPrivateKey_bio bio = self._create_mem_bio_gc() res = write_bio(bio, cdata) self.openssl_assert(res == 1) return self._read_mem_bio(bio) def _public_key_bytes(self, encoding, format, key, evp_pkey, cdata): if not isinstance(encoding, serialization.Encoding): raise TypeError("encoding must be an item from the Encoding enum") if ( format is serialization.PublicFormat.OpenSSH or encoding is serialization.Encoding.OpenSSH ): if ( format is not serialization.PublicFormat.OpenSSH or encoding is not serialization.Encoding.OpenSSH ): raise ValueError( "OpenSSH format must be used with OpenSSH encoding" ) return self._openssh_public_key_bytes(key) elif format is serialization.PublicFormat.SubjectPublicKeyInfo: if encoding is serialization.Encoding.PEM: write_bio = self._lib.PEM_write_bio_PUBKEY else: assert encoding is serialization.Encoding.DER write_bio = self._lib.i2d_PUBKEY_bio key = evp_pkey elif format is serialization.PublicFormat.PKCS1: # Only RSA is supported here. assert self._lib.EVP_PKEY_id(evp_pkey) == self._lib.EVP_PKEY_RSA if encoding is serialization.Encoding.PEM: write_bio = self._lib.PEM_write_bio_RSAPublicKey else: assert encoding is serialization.Encoding.DER write_bio = self._lib.i2d_RSAPublicKey_bio key = cdata else: raise TypeError( "format must be an item from the PublicFormat enum" ) bio = self._create_mem_bio_gc() res = write_bio(bio, key) self.openssl_assert(res == 1) return self._read_mem_bio(bio) def _openssh_public_key_bytes(self, key): if isinstance(key, rsa.RSAPublicKey): public_numbers = key.public_numbers() return b"ssh-rsa " + base64.b64encode( serialization._ssh_write_string(b"ssh-rsa") + serialization._ssh_write_mpint(public_numbers.e) + serialization._ssh_write_mpint(public_numbers.n) ) elif isinstance(key, dsa.DSAPublicKey): public_numbers = key.public_numbers() parameter_numbers = public_numbers.parameter_numbers return b"ssh-dss " + base64.b64encode( serialization._ssh_write_string(b"ssh-dss") + serialization._ssh_write_mpint(parameter_numbers.p) + serialization._ssh_write_mpint(parameter_numbers.q) + serialization._ssh_write_mpint(parameter_numbers.g) + serialization._ssh_write_mpint(public_numbers.y) ) else: assert isinstance(key, ec.EllipticCurvePublicKey) public_numbers = key.public_numbers() try: curve_name = { ec.SECP256R1: b"nistp256", ec.SECP384R1: b"nistp384", ec.SECP521R1: b"nistp521", }[type(public_numbers.curve)] except KeyError: raise ValueError( "Only SECP256R1, SECP384R1, and SECP521R1 curves are " "supported by the SSH public key format" ) return b"ecdsa-sha2-" + curve_name + b" " + base64.b64encode( serialization._ssh_write_string(b"ecdsa-sha2-" + curve_name) + serialization._ssh_write_string(curve_name) + serialization._ssh_write_string(public_numbers.encode_point()) ) def _parameter_bytes(self, encoding, format, cdata): if encoding is serialization.Encoding.OpenSSH: raise TypeError( "OpenSSH encoding is not supported" ) # Only DH is supported here currently. q = self._ffi.new("BIGNUM **") self._lib.DH_get0_pqg(cdata, self._ffi.NULL, q, self._ffi.NULL) if encoding is serialization.Encoding.PEM: if q[0] != self._ffi.NULL: write_bio = self._lib.PEM_write_bio_DHxparams else: write_bio = self._lib.PEM_write_bio_DHparams elif encoding is serialization.Encoding.DER: if q[0] != self._ffi.NULL: write_bio = self._lib.Cryptography_i2d_DHxparams_bio else: write_bio = self._lib.i2d_DHparams_bio else: raise TypeError("encoding must be an item from the Encoding enum") bio = self._create_mem_bio_gc() res = write_bio(bio, cdata) self.openssl_assert(res == 1) return self._read_mem_bio(bio) def generate_dh_parameters(self, generator, key_size): if key_size < 512: raise ValueError("DH key_size must be at least 512 bits") if generator not in (2, 5): raise ValueError("DH generator must be 2 or 5") dh_param_cdata = self._lib.DH_new() self.openssl_assert(dh_param_cdata != self._ffi.NULL) dh_param_cdata = self._ffi.gc(dh_param_cdata, self._lib.DH_free) res = self._lib.DH_generate_parameters_ex( dh_param_cdata, key_size, generator, self._ffi.NULL ) self.openssl_assert(res == 1) return _DHParameters(self, dh_param_cdata) def _dh_cdata_to_evp_pkey(self, dh_cdata): evp_pkey = self._create_evp_pkey_gc() res = self._lib.EVP_PKEY_set1_DH(evp_pkey, dh_cdata) self.openssl_assert(res == 1) return evp_pkey def generate_dh_private_key(self, parameters): dh_key_cdata = _dh_params_dup(parameters._dh_cdata, self) res = self._lib.DH_generate_key(dh_key_cdata) self.openssl_assert(res == 1) evp_pkey = self._dh_cdata_to_evp_pkey(dh_key_cdata) return _DHPrivateKey(self, dh_key_cdata, evp_pkey) def generate_dh_private_key_and_parameters(self, generator, key_size): return self.generate_dh_private_key( self.generate_dh_parameters(generator, key_size)) def load_dh_private_numbers(self, numbers): parameter_numbers = numbers.public_numbers.parameter_numbers dh_cdata = self._lib.DH_new() self.openssl_assert(dh_cdata != self._ffi.NULL) dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) p = self._int_to_bn(parameter_numbers.p) g = self._int_to_bn(parameter_numbers.g) if parameter_numbers.q is not None: q = self._int_to_bn(parameter_numbers.q) else: q = self._ffi.NULL pub_key = self._int_to_bn(numbers.public_numbers.y) priv_key = self._int_to_bn(numbers.x) res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) self.openssl_assert(res == 1) res = self._lib.DH_set0_key(dh_cdata, pub_key, priv_key) self.openssl_assert(res == 1) codes = self._ffi.new("int[]", 1) res = self._lib.Cryptography_DH_check(dh_cdata, codes) self.openssl_assert(res == 1) if codes[0] != 0: raise ValueError("DH private numbers did not pass safety checks.") evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata) return _DHPrivateKey(self, dh_cdata, evp_pkey) def load_dh_public_numbers(self, numbers): dh_cdata = self._lib.DH_new() self.openssl_assert(dh_cdata != self._ffi.NULL) dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) parameter_numbers = numbers.parameter_numbers p = self._int_to_bn(parameter_numbers.p) g = self._int_to_bn(parameter_numbers.g) if parameter_numbers.q is not None: q = self._int_to_bn(parameter_numbers.q) else: q = self._ffi.NULL pub_key = self._int_to_bn(numbers.y) res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) self.openssl_assert(res == 1) res = self._lib.DH_set0_key(dh_cdata, pub_key, self._ffi.NULL) self.openssl_assert(res == 1) evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata) return _DHPublicKey(self, dh_cdata, evp_pkey) def load_dh_parameter_numbers(self, numbers): dh_cdata = self._lib.DH_new() self.openssl_assert(dh_cdata != self._ffi.NULL) dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) p = self._int_to_bn(numbers.p) g = self._int_to_bn(numbers.g) if numbers.q is not None: q = self._int_to_bn(numbers.q) else: q = self._ffi.NULL res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) self.openssl_assert(res == 1) return _DHParameters(self, dh_cdata) def dh_parameters_supported(self, p, g, q=None): dh_cdata = self._lib.DH_new() self.openssl_assert(dh_cdata != self._ffi.NULL) dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) p = self._int_to_bn(p) g = self._int_to_bn(g) if q is not None: q = self._int_to_bn(q) else: q = self._ffi.NULL res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) self.openssl_assert(res == 1) codes = self._ffi.new("int[]", 1) res = self._lib.Cryptography_DH_check(dh_cdata, codes) self.openssl_assert(res == 1) return codes[0] == 0 def dh_x942_serialization_supported(self): return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 def x509_name_bytes(self, name): x509_name = _encode_name_gc(self, name) pp = self._ffi.new("unsigned char **") res = self._lib.i2d_X509_NAME(x509_name, pp) self.openssl_assert(pp[0] != self._ffi.NULL) pp = self._ffi.gc( pp, lambda pointer: self._lib.OPENSSL_free(pointer[0]) ) self.openssl_assert(res > 0) return self._ffi.buffer(pp[0], res)[:] def x25519_load_public_bytes(self, data): evp_pkey = self._create_evp_pkey_gc() res = self._lib.EVP_PKEY_set_type(evp_pkey, self._lib.NID_X25519) backend.openssl_assert(res == 1) res = self._lib.EVP_PKEY_set1_tls_encodedpoint( evp_pkey, data, len(data) ) backend.openssl_assert(res == 1) return _X25519PublicKey(self, evp_pkey) def x25519_load_private_bytes(self, data): # OpenSSL only has facilities for loading PKCS8 formatted private # keys using the algorithm identifiers specified in # https://tools.ietf.org/html/draft-ietf-curdle-pkix-03. # This is the standard PKCS8 prefix for a 32 byte X25519 key. # The form is: # 0:d=0 hl=2 l= 46 cons: SEQUENCE # 2:d=1 hl=2 l= 1 prim: INTEGER :00 # 5:d=1 hl=2 l= 5 cons: SEQUENCE # 7:d=2 hl=2 l= 3 prim: OBJECT :1.3.101.110 # 12:d=1 hl=2 l= 34 prim: OCTET STRING (the key) # Of course there's a bit more complexity. In reality OCTET STRING # contains an OCTET STRING of length 32! So the last two bytes here # are \x04\x20, which is an OCTET STRING of length 32. pkcs8_prefix = b'0.\x02\x01\x000\x05\x06\x03+en\x04"\x04 ' bio = self._bytes_to_bio(pkcs8_prefix + data) evp_pkey = backend._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL) self.openssl_assert(evp_pkey != self._ffi.NULL) evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) return _X25519PrivateKey(self, evp_pkey) def x25519_generate_key(self): evp_pkey_ctx = self._lib.EVP_PKEY_CTX_new_id( self._lib.NID_X25519, self._ffi.NULL ) self.openssl_assert(evp_pkey_ctx != self._ffi.NULL) evp_pkey_ctx = self._ffi.gc( evp_pkey_ctx, self._lib.EVP_PKEY_CTX_free ) res = self._lib.EVP_PKEY_keygen_init(evp_pkey_ctx) self.openssl_assert(res == 1) evp_ppkey = self._ffi.new("EVP_PKEY **") res = self._lib.EVP_PKEY_keygen(evp_pkey_ctx, evp_ppkey) self.openssl_assert(res == 1) self.openssl_assert(evp_ppkey[0] != self._ffi.NULL) evp_pkey = self._ffi.gc(evp_ppkey[0], self._lib.EVP_PKEY_free) return _X25519PrivateKey(self, evp_pkey) def x25519_supported(self): return self._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER def derive_scrypt(self, key_material, salt, length, n, r, p): buf = self._ffi.new("unsigned char[]", length) res = self._lib.EVP_PBE_scrypt( key_material, len(key_material), salt, len(salt), n, r, p, scrypt._MEM_LIMIT, buf, length ) self.openssl_assert(res == 1) return self._ffi.buffer(buf)[:] def aead_cipher_supported(self, cls): cipher_name = aead._aead_cipher_name(cls, None) return ( self._lib.EVP_get_cipherbyname(cipher_name) != self._ffi.NULL ) class GetCipherByName(object): def __init__(self, fmt): self._fmt = fmt def __call__(self, backend, cipher, mode): cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower() return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii")) backend = Backend()