Skip to content

jruby extension#30

Open
kares wants to merge 5 commits into
net-ssh:mainfrom
kares:jruby-ext
Open

jruby extension#30
kares wants to merge 5 commits into
net-ssh:mainfrom
kares:jruby-ext

Conversation

@kares

@kares kares commented May 29, 2026

Copy link
Copy Markdown

adds a Java implementation (LLM generated based on current C sources)

the rest is plumbing for the JRuby extension -> -java variant of the gem

generated based on C sources
@kares kares marked this pull request as ready for review May 29, 2026 07:52
@kares kares marked this pull request as draft May 29, 2026 10:55
@kares kares marked this pull request as ready for review May 29, 2026 11:14

@headius headius left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me overall. Will be worth running some benchmarks to clean up excess allocation. A few minor review comments should be addressed.

import java.util.Arrays;

public final class BCryptPbkdf {
private static final int BLF_N = 16;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix indentation to match Java standard, otherwise this will be a constant maintenance headache.

engine.getSingletonClass().defineAnnotatedMethods(BCryptPbkdfExt.class);
}

@JRubyMethod(required = 4, rest = true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These don't appear to be rest-arg methods so remove rest = true.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately, failing when rest = true is removed

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10.0.5.0 (fails the same on 9.4 as well):

java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
	at org.jruby.dist/org.jruby.internal.runtime.methods.InvocationMethodFactory.loadArguments(InvocationMethodFactory.java:559)
	at org.jruby.dist/org.jruby.internal.runtime.methods.InvocationMethodFactory.createAnnotatedMethodInvocation(InvocationMethodFactory.java:782)
	at org.jruby.dist/org.jruby.internal.runtime.methods.InvocationMethodFactory.addAnnotatedMethodInvoker(InvocationMethodFactory.java:719)
	at org.jruby.dist/org.jruby.internal.runtime.methods.InvocationMethodFactory.getAnnotatedMethodClass(InvocationMethodFactory.java:279)
	at org.jruby.dist/org.jruby.internal.runtime.methods.InvocationMethodFactory.getAnnotatedMethod(InvocationMethodFactory.java:328)
	at org.jruby.dist/org.jruby.RubyModule.defineAnnotatedMethod(RubyModule.java:1400)
	at org.jruby.dist/org.jruby.RubyModule.defineAnnotatedMethod(RubyModule.java:1374)
	at org.jruby.dist/org.jruby.anno.TypePopulator$ReflectiveTypePopulator.populate(TypePopulator.java:119)
	at org.jruby.dist/org.jruby.RubyModule.defineAnnotatedMethodsIndividually(RubyModule.java:1368)
	at org.jruby.dist/org.jruby.RubyModule.defineAnnotatedMethods(RubyModule.java:1272)
	at org.netssh.bcrypt_pbkdf.BCryptPbkdfExt.load(BCryptPbkdfExt.java:18)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.jruby.dist/org.jruby.ext.jruby.JRubyUtilLibrary.loadExtension(JRubyUtilLibrary.java:258)
	at org.jruby.dist/org.jruby.ext.jruby.JRubyUtilLibrary.load_ext(JRubyUtilLibrary.java:219)

}


@JRubyMethod(required = 2, rest = true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove rest = true and use two explicit arguments.

@mfazekas

mfazekas commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Explored a pure Ruby fallback as an alternative to this Java extension. Benchmarks show it's ~30x slower than the Java extension on JRuby — about 2.4 seconds per key load at rounds=16 (the openssh default). That's too slow to be acceptable for something that blocks every SSH connection open.

So this Java extension is the right path. One thing worth considering: instead of maintaining our own Java port of the Blowfish/bcrypt_pbkdf logic, we could pull in jBCrypt (Kenny Root's fork, org.connectbot:jbcrypt:1.0.2 on Maven Central, ~17KB) as a Maven dependency via jar-dependencies. It already has a pbkdf method and is the source of the test vectors we added in #32. That would reduce the amount of crypto code we own and maintain.

@kares

kares commented Jun 14, 2026

Copy link
Copy Markdown
Author

Explored a pure Ruby fallback as an alternative to this Java extension. Benchmarks show it's ~30x slower than the Java extension on JRuby — about 2.4 seconds per key load at rounds=16 (the openssh default). That's too slow to be acceptable for something that blocks every SSH connection open.

That is surprising, even after some decent warmup?
Anyway I think having a pure Ruby version would be acceptable for JRuby as well, given the use-case AFAIU is deployment and it's really just a transitive dependency at this point...

@kares

kares commented Jun 14, 2026

Copy link
Copy Markdown
Author

we could pull in jBCrypt (Kenny Root's fork, org.connectbot:jbcrypt:1.0.2 on Maven Central, ~17KB) as a Maven dependency via jar-dependencies

the fork itself seem abandoned (just like the parent) for 5 years, not much going on the repo, see: kruton/jbcrypt#1

it's probably easier/safer to maintain parity C-Java sources (esp. with LLM), the jar-dependencies story is not a headache free path and gets a bit invasive since the preferred path is to vendor the .jar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants