3 # Native OpenSSL functions and structs
7 # OpenSSL structure representing a message digest algorithm
8 class EVP_MD < FFI::Struct
11 s.include "openssl/ossl_typ.h"
12 s.include "openssl/evp.h"
14 s.name 'struct env_md_st'
16 s.field :pkey_type, :int
17 s.field :md_size, :int
18 s.field :flags, :ulong
19 s.field :init, :pointer
20 s.field :update, :pointer
21 s.field :final, :pointer
22 s.field :copy, :pointer
23 s.field :cleanup, :pointer
24 s.field :sign, :pointer
25 s.field :verify, :pointer
26 # int required_pkey[5] goes here but we don't care
27 s.field :block_size, :int
28 s.field :ctx_size, :int
33 # OpenSSL structure representing a message digest context
34 class EVP_MD_CTX < FFI::Struct
37 s.include "openssl/ossl_typ.h"
38 s.include "openssl/evp.h"
40 s.name 'struct env_md_ctx_st'
41 s.field :digest, :pointer
42 s.field :engine, :pointer
43 s.field :flags, :ulong
44 s.field :md_data, :pointer
48 # Returns the size of the digest associated with this context
50 evp_md = EVP_MD.new self[:digest]
55 # const EVP_MD *EVP_get_digestbyname(const char *name);
56 attach_function "EVP_get_digestbyname", "ossl_digest_getbyname", [:string], :pointer
58 #void OpenSSL_add_all_digests(void);
59 attach_function "OpenSSL_add_all_digests", "ossl_add_digests", [], :void
61 # int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl);
62 attach_function "EVP_DigestInit_ex", "ossl_digest_init_ex", [:pointer, :pointer, :pointer], :int
64 #int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt);
65 attach_function "EVP_DigestUpdate", "ossl_digest_update", [:pointer, :string, :size_t], :int
67 #EVP_MD_CTX *EVP_MD_CTX_create(void);
68 attach_function "EVP_MD_CTX_create", "ossl_digest_ctx_create", [], :pointer
70 #int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out,const EVP_MD_CTX *in);
71 attach_function "EVP_MD_CTX_copy_ex", "ossl_digest_ctx_copy", [:pointer, :pointer], :int
73 #int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx);
74 attach_function "EVP_MD_CTX_cleanup", "ossl_digest_ctx_cleanup", [:pointer], :int
76 #int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s);
77 attach_function "EVP_DigestFinal_ex", "ossl_digest_final_ex", [:pointer, :pointer, :pointer], :int
82 # Populate OpenSSL's digest type table
83 Foreign.ossl_add_digests
85 # The API for OpenSSL::Digest is insane, and the args to "new"
86 # differ in subclasses. This madness here is designed to handle that.
87 def self.new(digest_type = nil)
88 # We are in a subclass, so no further work is needed
89 return super(digest_type) if const_defined?(:DigestName)
92 subclass = OpenSSL::Digest::const_get(digest_type.to_s) rescue nil
93 return subclass.new if subclass
94 raise RuntimeError, "Unsupported digest algorithm (#{digest_type})."
96 raise ArgumentError, "wrong number of arguments (0 for 1)"
100 # Initialize the OpenSSL structures. This code is only executed in
101 # Digest subclasses, never in Digest itself.
102 def initialize(data = nil)
103 @context, @digest = nil, nil
104 # Fetch the digest algorithm that matches the name of this class
105 @digest = Foreign.ossl_digest_getbyname self.class.const_get(:DigestName)
106 Errno.handle if @digest.nil?
107 # Create a new digest context (EVP_MD_CTX)
108 @context = Foreign.ossl_digest_ctx_create
109 Errno.handle if @context.nil?
110 # Initialize the digest context
111 Foreign.ossl_digest_init_ex(@context, @digest, nil)
112 # Subclasses of Digest allow the initial data to be passed as an arg
116 # Update the digest with new data
118 data = StringValue(data)
119 err = Foreign.ossl_digest_update @context, data, data.size
120 Errno.handle if err.zero?
124 # Returns the digest in binary form
126 buffer = finalized_context
127 buffer.read_string(buffer.total)
130 # Returns the digest as a lowercase hex string
132 buffer = finalized_context
133 OpenSSL.digest_to_hex digest
135 alias_method :to_s, :hexdigest
136 alias_method :inspect, :hexdigest
138 # Copy the current digest context and then finalize it
139 # to prevent further updates
140 def finalized_context
141 # Create a blank context and then duplicate the current one
142 final = Foreign.ossl_digest_ctx_create
143 err = Foreign.ossl_digest_ctx_copy(final, @context)
144 Errno.handle if err.zero?
146 # Wrap the returned pointer in a context struct so that we
147 # can access its fields.
148 final_ctx = Foreign::EVP_MD_CTX.new(final)
150 # Create a buffer to hold the finalized output
151 buffer = MemoryPointer.new(final_ctx.digest_size)
152 buffer_size = MemoryPointer.new(:uint)
153 buffer_size.write_int(buffer.total)
155 # Finalize and write-protect the duplicated context
156 err = Foreign.ossl_digest_final_ex(final, buffer, buffer_size)
157 Foreign.ossl_digest_ctx_cleanup(final)
158 Errno.handle if err.zero?
161 private :finalized_context
163 # Returns the underlying EVP_MD structure. Used by OpenSSL::HMAC
164 def message_digest_backend
167 private :message_digest_backend
170 class SHA1 < Digest; DigestName = "SHA1"; end
171 class MD5 < Digest; DigestName = "MD5" ; end
172 end # OpenSSL::Digest