Skip to content

Commit

Permalink
Add OpenSSL::DigestIO to wrap an IO while calculating a digest (#4260)
Browse files Browse the repository at this point in the history
* Change DigestIO digest on read or write flags for exclusive mode enum
  • Loading branch information
Santiago Palladino authored and Martin Verzilli committed Jun 2, 2017
1 parent e7caa7d commit 66ac243
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 0 deletions.
82 changes: 82 additions & 0 deletions spec/std/openssl/digest_io_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
require "spec"
require "../src/openssl"

describe OpenSSL::DigestIO do
it "calculates digest from reading" do
base_io = IO::Memory.new("foo")
base_digest = OpenSSL::Digest.new("SHA256")
io = OpenSSL::DigestIO.new(base_io, base_digest)
slice = Bytes.new(256)
io.read(slice).should eq(3)

slice[0, 3].should eq("foo".to_slice)
io.digest.should eq("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae".hexbytes)
end

it "calculates digest from multiple reads" do
base_io = IO::Memory.new("foo")
base_digest = OpenSSL::Digest.new("SHA256")
io = OpenSSL::DigestIO.new(base_io, base_digest)
slice = Bytes.new(2)
io.read(slice).should eq(2)
slice[0, 2].should eq("fo".to_slice)

io.read(slice).should eq(1)
slice[0, 1].should eq("o".to_slice)

io.digest.should eq("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae".hexbytes)
end

it "does not calculate digest on read" do
base_io = IO::Memory.new("foo")
base_digest = OpenSSL::Digest.new("SHA256")
empty_digest = OpenSSL::Digest.new("SHA256").digest
io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Write)
slice = Bytes.new(256)
io.read(slice).should eq(3)
slice[0, 3].should eq("foo".to_slice)
io.digest.should eq(empty_digest)
end

it "calculates digest from writing" do
base_io = IO::Memory.new
base_digest = OpenSSL::Digest.new("SHA256")
io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Write)
io.write("foo".to_slice)

base_io.to_slice[0, 3].should eq("foo".to_slice)
io.digest.should eq("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae".hexbytes)
end

it "calculates digest from writing a string" do
base_io = IO::Memory.new
base_digest = OpenSSL::Digest.new("SHA256")
io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Write)
io.print("foo")

base_io.to_slice[0, 3].should eq("foo".to_slice)
io.digest.should eq("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae".hexbytes)
end

it "calculates digest from multiple writes" do
base_io = IO::Memory.new
base_digest = OpenSSL::Digest.new("SHA256")
io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Write)
io.write("fo".to_slice)
io.write("o".to_slice)
base_io.to_slice[0, 3].should eq("foo".to_slice)

io.digest.should eq("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae".hexbytes)
end

it "does not calculate digest on write" do
base_io = IO::Memory.new
base_digest = OpenSSL::Digest.new("SHA256")
empty_digest = OpenSSL::Digest.new("SHA256").digest
io = OpenSSL::DigestIO.new(base_io, base_digest, OpenSSL::DigestIO::DigestMode::Read)
io.write("foo".to_slice)

base_io.to_slice[0, 3].should eq("foo".to_slice)
io.digest.should eq(empty_digest)
end
end
54 changes: 54 additions & 0 deletions src/openssl/digest/digest_io.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require "./digest_base"

module OpenSSL
# Wraps an IO by calculating a specified digest on read or write operations.
#
# ### Example
#
# ```
# require "openssl"
#
# underlying_io = IO::Memory.new("foo")
# io = OpenSSL::DigestIO.new(underlying_io, "SHA256")
# buffer = Bytes.new(256)
# io.read(buffer)
# io.digest # => 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
# ```
class DigestIO
include IO

getter io : IO
getter digest_algorithm : OpenSSL::Digest
getter mode : DigestMode

delegate close, closed?, flush, peek, tty?, rewind, to: @io
delegate digest, hexdigest, base64digest, to: @digest_algorithm

enum DigestMode
Read
Write
end

def initialize(@io : IO, @digest_algorithm : OpenSSL::Digest, @mode = DigestMode::Read)
end

def initialize(@io : IO, algorithm : String, @mode = DigestMode::Read)
@digest_algorithm = OpenSSL::Digest.new(algorithm)
end

def read(slice : Bytes)
read_bytes = io.read(slice)
if @mode.read?
digest_algorithm.update(slice[0, read_bytes])
end
read_bytes
end

def write(slice : Bytes)
if @mode.write?
digest_algorithm.update(slice)
end
io.write(slice)
end
end
end

0 comments on commit 66ac243

Please sign in to comment.