OpenSSL BIO源码简析

news2024/12/23 6:22:05

文章目录

  • 1. BIO简介
    • BIO chain
    • BIO数据结构
    • BIO_METHOD数据结构
  • 2. Base64示例分析
    • 初始化
    • 构造BIO链
    • 写数据
    • free

1. BIO简介

相关文档

/html/man7/bio.html
/html/man3/BIO_*.html

bio - Basic I/O abstraction,即IO抽象层。

BIO有两种:

  • source/sink BIO,即数据源,如socket BIO、file BIO,初始化接口以BIO_s_开头;
  • filter BIO,过滤器,用来接收和传递数据,初始化接口以BIO_f_开头;

BIO chain

BIO可以组成一条链,即使是单个BIO,实质也是有一个节点的链。

一个链通常由1个source/sink BIO、1个或多个filter BIO组成。

数据从第一个BIO写入或读出,并传递到最后一个节点(通常是source/sink BIO)。

相关api:

  • BIO_push
  • BIO_free_all 释放整个链

BIO数据结构

源码位置:\crypto\bio\bio_local.h

struct bio_st {
    // BIO_new初始化需要提供libctx和method参数
    // BIO *BIO_new(const BIO_METHOD *method)
    // {
    //     return BIO_new_ex(NULL, method);
    // }
    OSSL_LIB_CTX *libctx;	// NULL: default contextz
    const BIO_METHOD *method;
    
    
    /* bio, mode, argp, argi, argl, ret */
#ifndef OPENSSL_NO_DEPRECATED_3_0
    BIO_callback_fn callback;
#endif
    BIO_callback_fn_ex callback_ex;
    char *cb_arg;               /* first argument for the callback */
    
    // 这里用int来作标志应该有点浪费
    // 用bool或bit更好些
    int init;		// 初始化标志
    int shutdown;
    int flags;                  /* extra storage */
    int retry_reason;
    int num;
    void *ptr;		// BIO_set_data() 
    
    // bio链本质是双向链表
    struct bio_st *next_bio;    /* used by filter BIOs */
    struct bio_st *prev_bio;    /* used by filter BIOs */
    
    CRYPTO_REF_COUNT references;
    uint64_t num_read;
    uint64_t num_write;
    CRYPTO_EX_DATA ex_data;
    CRYPTO_RWLOCK *lock;	// 线程读写锁
};

BIO_METHOD数据结构

源码路径:\include\internal\bio.h

struct bio_method_st {
    int type;
    char *name;
    int (*bwrite) (BIO *, const char *, size_t, size_t *);
    int (*bwrite_old) (BIO *, const char *, int);
    int (*bread) (BIO *, char *, size_t, size_t *);
    int (*bread_old) (BIO *, char *, int);
    int (*bputs) (BIO *, const char *);
    int (*bgets) (BIO *, char *, int);
    long (*ctrl) (BIO *, int, long, void *);
    int (*create) (BIO *);
    int (*destroy) (BIO *);
    long (*callback_ctrl) (BIO *, int, BIO_info_cb *);
};

该结构除了类型和名称,其余均是函数指针。

2. Base64示例分析

借用官网Base64示例:https://www.openssl.org/docs/man3.0/man3/BIO_f_base64.html,它将"hello world \n"的base64输出到stdout:

#include <iostream>
#include <openssl/bio.h>
#include <openssl/evp.h>
int main()
{
	BIO* bio, * b64;
	char message[] = "Hello World \n";

	b64 = BIO_new(BIO_f_base64());
	bio = BIO_new_fp(stdout, BIO_NOCLOSE);
	BIO_push(b64, bio);
	BIO_write(b64, message, strlen(message));
	BIO_flush(b64);
    // SGVsbG8gV29ybGQgCg==
    
	BIO_free_all(b64);
    
	getchar();
	return 0;
}

用vscode在源码目录搜索“base64”,并没有找到实现源码,于是全局搜索编码表,定位到源码路径:

// \crypto\evp\encode.c
static const unsigned char data_bin2ascii[65] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

把encode.c拖到vs中,在引用编码表的地方下断点,调试示例程序,成功断下:

请添加图片描述

BIO_flush调用栈如下:

libcrypto-3.dll!evp_encodeblock_int(evp_Encode_Ctx_st * ctx, unsigned char * t, const unsigned char * f, int dlen) Line 238	C
libcrypto-3.dll!EVP_EncodeFinal(evp_Encode_Ctx_st * ctx, unsigned char * out, int * outl) Line 222	C
libcrypto-3.dll!b64_ctrl(bio_st * b, int cmd, long num, void * ptr) Line 506	C
libcrypto-3.dll!BIO_ctrl(bio_st * b, int cmd, long larg, void * parg) Line 579	C
TestOpenSSL.exe!main() Line 48	C++

EVP是OpenSSL的算法实现接口,结合evp文档(/html/man7/evp.html),这里正是base64编解码逻辑:

The EVP_EncodeXXX and EVP_DecodeXXX functions implement base 64 encoding and decoding.

初始化

b64 = BIO_new(BIO_f_base64());
bio = BIO_new_fp(stdout, BIO_NOCLOSE);

BIO_new_fp(),内部其实是调用了BIO_new(BIO_s_file())初始化一个source/sink BIO。所以只分析第一个base64 filter BIO。

BIO_f_base64()返回一个base64的静态BIO_METHOD

// \crypto\evp\bio_b64.c
static const BIO_METHOD methods_b64 = {
    BIO_TYPE_BASE64,
    "base64 encoding",
    bwrite_conv,
    b64_write,
    bread_conv,
    b64_read,
    b64_puts,
    NULL,                       /* b64_gets, */
    b64_ctrl,
    b64_new,
    b64_free,
    b64_callback_ctrl,
};


const BIO_METHOD *BIO_f_base64(void)
{
    return &methods_b64;
}

通过BIO_TYPE_BASE64这个宏type,可以在\include\openssl\bio.h定位到其它method type:

/*
	在源码层面,BIO其实是有3种。
*/
/* There are the classes of BIOs */
# define BIO_TYPE_DESCRIPTOR     0x0100 /* socket, fd, connect or accept */
# define BIO_TYPE_FILTER         0x0200
# define BIO_TYPE_SOURCE_SINK    0x0400

/* These are the 'types' of BIOs */
# define BIO_TYPE_NONE             0
# define BIO_TYPE_MEM            ( 1|BIO_TYPE_SOURCE_SINK)
# define BIO_TYPE_FILE           ( 2|BIO_TYPE_SOURCE_SINK)

# define BIO_TYPE_FD             ( 4|BIO_TYPE_SOURCE_SINK|BIO_TYPE_DESCRIPTOR)
// ...
# define BIO_TYPE_BASE64         (11|BIO_TYPE_FILTER)

BIO_new则根据base 64 method初始化filter BIO:

请添加图片描述

method->createb64_new(),用于初始化bio->ptr==b64_ctx

// \crypto\evp\bio_b64.c
typedef struct b64_struct {
    /*
     * BIO *bio; moved to the BIO structure
     */
    int buf_len;
    int buf_off;
    int tmp_len;                /* used to find the start when decoding */
    int tmp_nl;                 /* If true, scan until '\n' */
    int encode;
    int start;                  /* have we started decoding yet? */
    int cont;                   /* <= 0 when finished */
    EVP_ENCODE_CTX *base64;		
    char buf[EVP_ENCODE_LENGTH(B64_BLOCK_SIZE) + 10];
    char tmp[B64_BLOCK_SIZE];
} BIO_B64_CTX;

函数栈如下:

>	libcrypto-3.dll!b64_new(bio_st * bi) Line 71	C
    	    BIO_B64_CTX *ctx;

            //...

            BIO_set_data(bi, ctx); // set bi->ptr
            BIO_set_init(bi, 1);	// initialized 
 	libcrypto-3.dll!BIO_new_ex(ossl_lib_ctx_st * libctx, const bio_method_st * method) Line 104	C
        	if (method->create != NULL && !method->create(bio)) {
                // ...
                goto err;
            }
            if (method->create == NULL)
                bio->init = 1;	// 若没有单独的create初始化函数,则直接设置init标志
 	libcrypto-3.dll!BIO_new(const bio_method_st * method) Line 122	C
        	return BIO_new_ex(NULL, method);
 	TestOpenSSL.exe!main() Line 44	C++

构造BIO链

源码位置:\crypto\bio\bio_lib.c

// BIO_push(b64, bio);
BIO *BIO_push(BIO *b, BIO *bio)
{
    BIO *lb;

    if (b == NULL)
        return bio;
    lb = b;
    while (lb->next_bio != NULL)
        lb = lb->next_bio;
    lb->next_bio = bio;
    if (bio != NULL)
        bio->prev_bio = lb;
    /* called to do internal processing */
    BIO_ctrl(b, BIO_CTRL_PUSH, 0, lb);
    return b;
}

在BIO链表中,stdout source/sink BIO是在 base64 filter BIO之后的。

写数据

BIO_write(b64, message, strlen(message));
BIO_flush(b64);

再看一下methods_b64:

static const BIO_METHOD methods_b64 = {
    BIO_TYPE_BASE64,
    "base64 encoding",
    bwrite_conv,	// int (*bwrite) (BIO *, const char *, size_t, size_t *);
    b64_write,		// int (*bwrite_old) (BIO *, const char *, int);
    //...
}

调用BIO_write,其实是调用base64 method的bwritebwrite_old函数,形成如下函数栈:

libcrypto-3.dll!EVP_EncodeInit(evp_Encode_Ctx_st * ctx) Line 156	C
libcrypto-3.dll!b64_write(bio_st * b, const char * in, int inl) Line 346	C
libcrypto-3.dll!bwrite_conv(bio_st * bio, const char * data, unsigned int datal, unsigned int * written) Line 77	C
        ret = bio->method->bwrite_old(bio, data, (int)datal);
libcrypto-3.dll!bio_write_intern(bio_st * b, const void * data, unsigned int dlen, unsigned int * written) Line 362	C
        ret = b->method->bwrite(b, data, dlen, &local_written);
libcrypto-3.dll!BIO_write(bio_st * b, const void * data, int dlen) Line 384	C
TestOpenSSL.exe!main() Line 47	C++

但经过调试,执行BIO_write后标准输出(就是屏幕)并没有回显,而最开始贴出调用栈的BIO_flush刷新缓冲区后才有回显,底层是调用BIO_ctrl()实现的,文档如下:

https://www.openssl.org/docs/man3.0/man3/BIO_flush.html
BIO_flush() normally writes out any internally buffered data, in some cases it is used to signal EOF and that no more data will be written.

函数栈如下:

libcrypto-3.dll!b64_write(bio_st * b, const char * in, int inl) Line 366	C
libcrypto-3.dll!b64_ctrl(bio_st * b, int cmd, long num, void * ptr) Line 490	C
libcrypto-3.dll!BIO_ctrl(bio_st * b, int cmd, long larg, void * parg) Line 579	C
    ret = b->method->ctrl(b, cmd, larg, parg);	// b64_ctrl()
TestOpenSSL.exe!main() Line 48	C++

free

BIO_free_all(b64);

这个函数的作用,猜也能猜的到,遍历链表逐个释放空间。

// \crypto\bio\bio_lib.c
void BIO_free_all(BIO *bio)
{
    BIO *b;
    int ref;

    while (bio != NULL) {
        b = bio;	// 从第一个节点开始释放
        ref = b->references;
        bio = bio->next_bio;
        BIO_free(b);
        /* 
        	Since ref count > 1, don't free anyone else.
        	意思是别人还在用 别删
        */
        if (ref > 1)
            break;
    }
}

// 需要释放的东西还是很多的
int BIO_free(BIO *a)
{
    int ret;

    if (a == NULL)
        return 0;
	
    // 引用数减1
    if (CRYPTO_DOWN_REF(&a->references, &ret, a->lock) <= 0)
        return 0;

    REF_PRINT_COUNT("BIO", a);
    if (ret > 0)
        return 1;
    REF_ASSERT_ISNT(ret < 0);

    if (HAS_CALLBACK(a)) {
        ret = (int)bio_call_callback(a, BIO_CB_FREE, NULL, 0, 0, 0L, 1L, NULL);
        if (ret <= 0)
            return 0;
    }

    if ((a->method != NULL) && (a->method->destroy != NULL))
        a->method->destroy(a);	// b64_free()

    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, a, &a->ex_data);

    CRYPTO_THREAD_lock_free(a->lock);

    OPENSSL_free(a);

    return 1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/100711.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

win7系统升级IE11,打补丁KB2729094失败解决办法

因银行这边很多都需要IE11版本&#xff0c;但win7系统大部分需要打一些补丁才能安装。其他补丁都打上了&#xff0c;唯独这个KB2729094一直失败&#xff0c;搞得很无语。还好找到可以直接用命令安装。就不需要打这个补丁了&#xff0c;直接安装使用即可。 1、下载IE11离线安装…

DBCO-PEG-Dopamine,二苯并环辛炔-聚乙二醇-多巴胺,DBCO聚乙二醇衍生物

●中文名&#xff1a;二苯并环辛炔-聚乙二醇-多巴胺&#xff0c;多巴胺聚乙二醇环辛炔 ●英文名&#xff1a;DBCO-PEG-Dopamine&#xff0c;Dopamine-PEG-DBCO ●外观以及性质&#xff1a; DBCO-PEG-Dopamine产物呈固体或粘性液体&#xff0c;取决于PEG分子量&#xff0c;DBCO…

“R语言+遥感”的水环境综合评价方法

目标&#xff1a; 1、掌握R语言基础应用及水环境数据分析方法 2、掌握水环境遥感数据预处理方法 3、掌握水线提取——水体指数与阈值混合法&#xff08;遥感&#xff09; 4、掌握水深提取——多元回归分析方法&#xff08;R语言遥感&#xff09; 5、掌握水温提取——支持向…

(附源码)springboot学生社团信息管理 毕业设计 011238

目 录 摘要 1 1 绪论 1 1.1 研究背景 1 1.2 研究意义 1 1.3论文结构与章节安排 1 2 学生社团信息管理系统系统分析 3 2.1 可行性分析 3 2.2 系统流程分析 3 2.2.1 数据增加流程 4 2.2.2 数据修改流程 4 2.2.3 数据删除流程 5 2.3 系统功能分析 5 2.3.1 功能性分析 5 2.3.2 非功…

D. Secret Santa(构造)

Problem - 1530D - Codeforces 每年12月&#xff0c;VK都会为其员工举办名为 "秘密圣诞老人 "的传统活动。它是这样发生的。 从1到n的n名员工参加了这个活动。每个员工i被分配到一个不同的员工bi&#xff0c;员工i必须给这个员工做一份新年礼物。每个员工正好被分配…

如何使用Chrome浏览器模拟弱网情况

点击谷歌浏览器图标 打开浏览器后&#xff0c;按下F12键 弹出开发者工具窗口 刷新网页&#xff0c;页面的加载速度为597ms 在开发者工具中&#xff0c;点击Online&#xff0c;在弹出的菜单中点击Slow 3G&#xff08;慢速3G网络&#xff09; 重新加载网站&#xff0c;…

PyTorch logit函数

1.PyTorch vs TensorFlow tensorflow是静态图&#xff0c;需要你把啥都准备好&#xff0c;然后它像个傻子一样执行&#xff0c;tensorflow&#xff0c;目前业界更适合部署&#xff0c;毕竟是静态图&#xff0c;infer的时候速度快。 pytorch&#xff0c;它会在执行的时候&…

Android -- 每日一问:如何检测内存泄露,如何进行内存优化?

经典回答 Android 系统为每一个应用程序都设置了一个硬性的 Dalvik Heap Size 最大限制阈值&#xff0c;这个阈值在不同的设备上会因为 RAM 大小不同而各有差异。如果你的应用占用内存空间已经接近这个阈值&#xff0c;此时再尝试分配内存的话&#xff0c;很容易引起 OOM 。 …

效果分析的关键是指标能算出来……

看到题目会不会有一些奇怪&#xff1f; 这算什么关键…… 经历过才知道&#xff0c;这是一个不起眼但却极为重要的部分&#xff0c;企业在数据驱动发展进程中必然会遇到指标算不出来的情况&#xff0c;而且随着企业规模的不断扩大&#xff0c;这一问题会持续伴随。“指标能算…

[附源码]Python计算机毕业设计Django学习互助辅助系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

外汇天眼:美国12月Markit制造业PMI初值不及预期 市场担忧经济下滑

美国12月企业活动进一步萎缩&#xff0c;因新订单降至两年半以来最低水平&#xff0c;但需求疲软帮助大幅冷却通胀。标普全球(NYSE:SPGI)周五表示&#xff0c;追踪制造业和服务业的美国综合PMI产出指数预览值本月从11月的46.4降至44.6。这是该指数连续第六个月低于私营部门萎缩…

Python学习-9.1 程序界面-main函数的作用

main函数的两种使用功能 情况1&#xff1a;直接执行本py代码文件时&#xff0c;把包含的代码块视为脚本代码顺序执行&#xff1b; 情况2&#xff1a;当本py代码文件作为其他代码import对象时&#xff0c;不执行如下被包含的代码 下面可以通过程序来更好地了解main函数的作用&a…

java-函数式编程浅谈

了解函数式编程的实际应用场景以及优点。 文章目录什么是函数式编程函数式编程的使用原理解析什么是函数式编程 以数学中的函数作为切入点&#xff0c;只关注参数之间的运算满足某种规则&#xff0c;例如zxy。 那么如何体现在编程中呢&#xff0c;熟知的function定义可以作为…

使用dompdf/dompdf实现生成pdf文件

一&#xff1a;dompdf/dompdf地址 github&#xff1a;https://github.com/dompdf/dompdf packagist&#xff1a;http://packagist.p2hp.com/packages/dompdf/dompdf 二&#xff1a;dompdf/dompdf安装 composer require dompdf/dompdf 三&#xff1a;dompdf/dompdf简单实例…

20221218-19英语学习

今日新词&#xff1a; wit n.智力; 头脑; 理解力; 风趣; 风趣的话语; 智者; 才子 alcohol n.酒精, 乙醇, 含酒精饮料 pub n.酒吧, 酒馆 instructor n.教员&#xff1b;教师 granted adv.&#xff08;表示肯定属实&#xff0c;然后再作另一番表述&#xff09;不错&#xff…

左神:高级进阶班4

1.让N个人过河所需最少船​编辑 2.最长回文子序列 3.最少添加字符让字符串变回文串​编辑 4.回文子串的最少切割次数 5.移除字符使字符串变回文串的方案数​编辑 1.让N个人过河所需最少船 思路&#xff1a;1.排序数组&#xff0c;用基数排序&#xff08;元素&#xff08;体…

计算机毕设Python+Vue学生信息管理系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

推特群推营销解读

推特群推王发现&#xff0c;许多品牌使用社交媒体作为提供更好客户支持的一种渠道。使用社交媒体可以实时响应客户的需求。通过电子邮件获取品牌有时可能需要长达48小时。但是&#xff0c;通常在社交媒体上回应的时间要快得多。客户将通过你的品牌发推文&#xff0c;你将在手机…

被动与主动信息收集 | 系统性学习 | 无知的我费曼笔记

文章目录信息收集-被动信息收集介绍收集手段收集内容信息用途信息收集-域名解析过程以手段域名解析过程信息收集 DNS**DNS 信息收集-NSLOOKUP****DNS 信息收集-DIG****查询网站的域名注册信息和备案信息**信息收集-被动信息收集手段大全**使用 Maltego 收集子域名信息**子域名介…

【论文简述】PatchmatchNet: Learned Multi-View Patchmatch Stereo(CVPR 2021)

一、论文简述 1. 第一作者&#xff1a;Fangjinhua Wang 2. 发表年份&#xff1a;2021 3. 发表期刊&#xff1a;CVPR 4. 关键词&#xff1a;MVS、深度学习、Patchmatch、自适应、迭代优化 5. 探索动机&#xff1a;可扩展性、时间、内存占用等效率问题依然没有解决。 While…