Reputation: 189
I am using BIO to compute the digest of some file with OpenSSL. My code is as following:
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
int main(){
BIO* bio_infile;
bio_infile = BIO_new_file("test.txt", "rb");
BIO* bio_md = BIO_new(BIO_f_md());
BIO_set_md(bio_md, EVP_sha1());
BIO* bio_outfile;
bio_outfile = BIO_new_file("dgst.txt","w");
BIO_push(bio_md, bio_outfile);
BIO_push(bio_infile, bio_md);
BIO_flush(bio_infile);
BIO_flush(bio_md);
BIO_free(bio_infile);
BIO_free(bio_md);
BIO_free(bio_outfile);
return 0;
}
However, When my program run out, I find nothings in file dgst.txt
.
Why?
Upvotes: 2
Views: 609
Reputation: 4620
The BIO
chaining mechanism is meant for prepending filter BIO
s to a source/sink BIO
. It is not designed for connecting a source BIO
with a sink BIO
.
So what you can do:
Create a BIO
chain of filters with a final BIO
sink. When writing data to the head of the chain, the input will traverse each filter until it reaches the sink.
Create a BIO
chain of filters with a final BIO
source. When reading data from the head of the chain, the read request will be passed along the chain up to the source. The data read from the source will then traverse the chain in the reverse direction through each filter.
But what you can not do:
BIO
chain that starts with a source and ends with a sink with filters in between. There is no mechanism for "pumping" data from the source to the sink automatically.One additional note: The BIO_f_md
is a filter BIO
that calculates the digest of the data passing through it, but it does not modify the data. So the output of the BIO
is the original data, not the digest of the data. The digest can be retrieved after all data has been processed with BIO_get_md
(reference). So in your example, even if there was support for chaining a source to a sink, the final dgst.txt
would only contain the original data from test.txt
.
To fix your example, there are two approaches:
Create a chain with the digest BIO
followed by the source BIO
for the input file. We can then read from the digest BIO
, which internally pulls the data from the source BIO
. We read the data and immediately discard it until the complete file has been processed. We can then retrieve the digest from the digest BIO
.
// Create chain: Digest -> Source File
BIO* bio_infile;
bio_infile = BIO_new_file("test.txt", "rb");
BIO* bio_md = BIO_new(BIO_f_md());
BIO_set_md(bio_md, EVP_sha1());
BIO* head = BIO_push(bio_md, bio_infile);
// Read from head of chain
std::array<char,1024> buf{};
int read;
while ((read = BIO_read(head, buf.data(), buf.size())) > 0) {
// Ignore buffer, we only want the data to pass throug the digest BIO
}
// Read result from digest BIO
EVP_MD *md;
BIO_get_md(bio_md, &md);
std::array<char, EVP_MAX_MD_SIZE> md_buf{};
int mdlen = BIO_gets(bio_md, md_buf.data(), md_buf.size());
// Print digest to stdout
for (int i = 0; i < mdlen; i++) {
printf("%02x", static_cast<uint8_t>(md_buf[i]));
}
printf("\n");
// Cleanup
BIO_free_all(head);
Create a chain with the digest BIO
followed by a special sink BIO
for discarding data (BIO_s_null
). We then read the data from source BIO
for the input file (without any chaining) and write the data to the digest BIO
. The written data passes through the digest BIO
to the sink BIO
, which will discard the data. When all data has been processed, we can retrieve the digest from the digest BIO
.
// Create chain: Digest -> Null Sink
BIO* bio_infile;
bio_infile = BIO_new_file("test.txt", "rb");
BIO* bio_md = BIO_new(BIO_f_md());
BIO_set_md(bio_md, EVP_sha1());
BIO* bio_null = BIO_new(BIO_s_null());
BIO* head = BIO_push(bio_md, bio_null);
// Read from file and write to chain
std::array<char,1024> buf{};
int read;
while ((read = BIO_read(bio_infile, buf.data(), buf.size())) > 0) {
BIO_write(head, buf.data(), read);
}
// Read result from digest BIO
EVP_MD *md;
BIO_get_md(bio_md, &md);
std::array<char, EVP_MAX_MD_SIZE> md_buf{};
int mdlen = BIO_gets(bio_md, md_buf.data(), md_buf.size());
// Print digest to stdout
for (int i = 0; i < mdlen; i++) {
printf("%02x", static_cast<uint8_t>(md_buf[i]));
}
printf("\n");
// Cleanup
BIO_free(bio_infile);
BIO_free_all(head);
Upvotes: 3