Skip to content
This repository was archived by the owner on Oct 28, 2024. It is now read-only.

Commit 10682fc

Browse files
passkey calculation as a C extension
given the string overhead involved in the calculation.
1 parent bed5e38 commit 10682fc

9 files changed

Lines changed: 149 additions & 11 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/Gemfile.lock
99
/vendor
1010
.env
11+
/ext/Makefile
1112

1213
*.DS_Store
1314
*.sw[po]

Gemfile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ruby RUBY_VERSION
55

66
gemspec
77

8-
gem "rake", "~> 12.3"
8+
gem "rake", "~> 13"
99
gem "rspec", "~> 3.5"
1010

1111
gem "pry"
@@ -36,3 +36,8 @@ else
3636
end
3737

3838
end
39+
40+
gem "jruby-openssl", "~> 0.14", platform: :jruby
41+
gem "ruby-prof"
42+
gem "rake-compiler"
43+
gem "benchmark-ips"

Rakefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# frozen_string_literal: true
22

33
require "bundler/gem_tasks"
4+
require 'rake/extensiontask'
5+
6+
Rake::ExtensionTask.new('netsnmp')
47

58
begin
69
require "rspec/core/rake_task"

ext/netsnmp/extconf.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
require 'mkmf'
2+
create_makefile 'netsnmp_ext'

ext/netsnmp/netsnmp_ext.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#include <ruby.h>
2+
// #include <openssl/ossl.h>
3+
// #include <openssl/evp.h>
4+
5+
#define USM_LENGTH_EXPANDED_PASSPHRASE (1024 * 1024) /* 1Meg. */
6+
#define USM_LENGTH_KU_HASHBLOCK 64
7+
8+
// // from https://github.com/ruby/openssl/blob/master/ext/openssl/ossl_digest.c
9+
// static void
10+
// ossl_digest_free(void *ctx)
11+
// {
12+
// EVP_MD_CTX_destroy(ctx);
13+
// }
14+
15+
// static const rb_data_type_t ossl_digest_type = {
16+
// "OpenSSL/Digest",
17+
// {
18+
// 0, ossl_digest_free,
19+
// },
20+
// 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
21+
// };
22+
23+
// #define GetDigest(obj, ctx) do { \
24+
// TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_digest_type, (ctx)); \
25+
// if (!(ctx)) { \
26+
// ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \
27+
// } \
28+
// } while (0)
29+
30+
static VALUE mNETSNMP;
31+
static VALUE cNETSNMP_Sec_Params;
32+
33+
// static VALUE NETSNMP_passkey(VALUE self, VALUE password)
34+
// {
35+
// char *P;
36+
// size_t P_len;
37+
// int nbytes = USM_LENGTH_EXPANDED_PASSPHRASE,
38+
// auth_type;
39+
// EVP_MD_CTX *ctx = NULL;
40+
// u_int i, pindex = 0;
41+
// u_char buf[USM_LENGTH_KU_HASHBLOCK], *bufp;
42+
// unsigned char *Ku;
43+
// size_t kulen;
44+
// VALUE digest;
45+
46+
// StringValue(password);
47+
// P = RSTRING_PTR(password);
48+
// P_len = RSTRING_LEN(password);
49+
50+
// digest = rb_funcall(self, rb_intern("digest"), 0);
51+
// if (NIL_P(digest)) {
52+
// // raise exception
53+
// }
54+
// rb_funcall(digest, rb_intern("reset"), 0);
55+
56+
// GetDigest(digest, ctx);
57+
58+
// while (nbytes > 0) {
59+
// bufp = buf;
60+
// for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) {
61+
// *bufp++ = P[pindex++ % P_len];
62+
// }
63+
// if (!EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK))
64+
// ossl_raise(eDigestError, "EVP_DigestUpdate");
65+
66+
// nbytes -= USM_LENGTH_KU_HASHBLOCK;
67+
// }
68+
69+
// if (!EVP_DigestFinal_ex(ctx, &Ku, &kulen))
70+
// ossl_raise(eDigestError, "EVP_DigestFinal_ex");
71+
72+
// memset(buf, 0, sizeof(buf));
73+
74+
// // TODO: trim to 16 bytes if auth protocol is md5
75+
76+
// return rb_usascii_str_new(Ku, kulen);
77+
// }
78+
79+
80+
static VALUE NETSNMP_expand_passphrase(VALUE self, VALUE password)
81+
{
82+
char *P;
83+
size_t P_len;
84+
int nbytes = USM_LENGTH_EXPANDED_PASSPHRASE;
85+
u_int pindex = 0;
86+
u_char buf[USM_LENGTH_EXPANDED_PASSPHRASE], *bufp;
87+
88+
StringValue(password);
89+
P = RSTRING_PTR(password);
90+
P_len = RSTRING_LEN(password);
91+
92+
bufp = buf;
93+
while (nbytes > 0) {
94+
*bufp++ = P[pindex++ % P_len];
95+
// if (!EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK))
96+
// ossl_raise(eDigestError, "EVP_DigestUpdate");
97+
98+
nbytes--;
99+
}
100+
101+
// if (!EVP_DigestFinal_ex(ctx, &Ku, &kulen))
102+
// ossl_raise(eDigestError, "EVP_DigestFinal_ex");
103+
104+
// memset(buf, 0, sizeof(buf));
105+
106+
// TODO: trim to 16 bytes if auth protocol is md5
107+
108+
return rb_usascii_str_new((unsigned char *) buf, USM_LENGTH_EXPANDED_PASSPHRASE);
109+
}
110+
111+
void Init_netsnmp_ext()
112+
{
113+
mNETSNMP = rb_define_module("NETSNMP");
114+
cNETSNMP_Sec_Params = rb_define_class_under(mNETSNMP, "SecurityParameters", rb_cObject);
115+
// rb_define_method(cNETSNMP_Sec_Params, "passkey", NETSNMP_passkey, 1);
116+
rb_define_method(cNETSNMP_Sec_Params, "expand_passphrase", NETSNMP_expand_passphrase, 1);
117+
}

lib/netsnmp.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def xor(other)
5151
require "netsnmp/message"
5252
require "netsnmp/encryption/des"
5353
require "netsnmp/encryption/aes"
54+
require "netsnmp_ext" if RUBY_ENGINE == "ruby"
5455

5556
require "netsnmp/client"
5657

lib/netsnmp/security_parameters.rb

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,22 +191,23 @@ def localize_key(key)
191191

192192
def passkey(password)
193193
digest.reset
194-
password_index = 0
194+
digest << expand_passphrase(password)
195195

196-
# buffer = +""
196+
dig = digest.digest
197+
dig = dig[0, 16] if @auth_protocol == :md5
198+
dig || ""
199+
end
200+
201+
def expand_passphrase(password)
202+
buffer = "".b
197203
password_length = password.length
198204
while password_index < 1048576
199205
initial = password_index % password_length
200-
rotated = String(password[initial..-1]) + String(password[0, initial])
201-
buffer = (rotated * (64 / rotated.length)) + String(rotated[0, 64 % rotated.length])
206+
rotated = String(password.byteslice(initial..-1)) + String(password.byteslice(0, initial))
207+
buffer << (rotated * (64 / rotated.length)) + String(rotated.byteslice(0, 64 % rotated.length))
202208
password_index += 64
203-
digest << buffer
204-
buffer.clear
205209
end
206-
207-
dig = digest.digest
208-
dig = dig[0, 16] if @auth_protocol == :md5
209-
dig || ""
210+
buffer
210211
end
211212

212213
def digest

netsnmp.gemspec

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Gem::Specification.new do |gem|
2121
# Manifest
2222
gem.files = Dir["LICENSE.txt", "README.md", "AUTHORS", "lib/**/*.rb", "sig/**/*.rbs"]
2323
gem.require_paths = ["lib"]
24+
if RUBY_ENGINE == "ruby"
25+
gem.extensions = ["ext/extconf.rb"]
26+
end
2427

2528
gem.add_runtime_dependency "parslet"
2629
gem.metadata["rubygems_mfa_required"] = "true"

spec/support/specs.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ cd /home
2323
bundle -v
2424
bundle install
2525

26+
27+
if [[ "$RUBY_ENGINE" = "ruby" ]]; then
28+
bundle exec rake compile
29+
fi
30+
2631
if [[ ${RUBY_VERSION:0:1} = "3" ]]; then
2732
export RUBYOPT='-rbundler/setup -rrbs/test/setup'
2833
export RBS_TEST_RAISE=true

0 commit comments

Comments
 (0)