Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / openssl / digest.rb.ffi
blobf390b5a29fdf2ca3fe25b499df9a63a9e62e7ddc
1 module OpenSSL
2   module Digest
3     # Native OpenSSL functions and structs
4     module Foreign
5       set_ffi_lib "libssl"
7       # OpenSSL structure representing a message digest algorithm
8       class EVP_MD < FFI::Struct
9         @@@
10         struct do |s|
11           s.include "openssl/ossl_typ.h"
12           s.include "openssl/evp.h"
14           s.name 'struct env_md_st'
15           s.field  :type, :int
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
29         end
30         @@@
31       end
33       # OpenSSL structure representing a message digest context
34       class EVP_MD_CTX < FFI::Struct
35         @@@
36         struct do |s|
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
45         end
46         @@@
48         # Returns the size of the digest associated with this context
49         def digest_size
50           evp_md = EVP_MD.new self[:digest]
51           evp_md[:md_size]
52         end
53       end
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
79     end # Foreign
81     class Digest
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)
91         if digest_type then
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})."
95         else
96           raise ArgumentError, "wrong number of arguments (0 for 1)"
97         end
98       end
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
113         update(data) if data
114       end
116       # Update the digest with new data
117       def update(data)
118         data = StringValue(data)
119         err = Foreign.ossl_digest_update @context, data, data.size
120         Errno.handle if err.zero?
121         return self
122       end
124       # Returns the digest in binary form
125       def digest
126         buffer = finalized_context
127         buffer.read_string(buffer.total)
128       end
130       # Returns the digest as a lowercase hex string
131       def hexdigest
132         buffer = finalized_context
133         OpenSSL.digest_to_hex digest
134       end
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?
159         buffer
160       end
161       private :finalized_context
163       # Returns the underlying EVP_MD structure. Used by OpenSSL::HMAC
164       def message_digest_backend
165         @digest
166       end
167       private :message_digest_backend
168     end # Digest
170     class SHA1   < Digest; DigestName = "SHA1";  end
171     class MD5    < Digest; DigestName = "MD5" ;  end
172   end # OpenSSL::Digest
173 end # OpenSSL