Skip to content

Commit 6e213fd

Browse files
committed
get BN.new("...", 0) working as OpenSSL does - using MPI format
1 parent 75242d4 commit 6e213fd

File tree

2 files changed

+73
-16
lines changed

2 files changed

+73
-16
lines changed

src/main/java/org/jruby/ext/openssl/BN.java

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,7 @@ public final BigInteger getValue() {
115115
}
116116

117117
@JRubyMethod(name="initialize", required=1, optional=1, visibility = Visibility.PRIVATE)
118-
public IRubyObject initialize(final ThreadContext context,
119-
final IRubyObject[] args) {
118+
public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) {
120119
final Ruby runtime = context.runtime;
121120
if (this.value != BigInteger.ZERO) { // already initialized
122121
throw newBNError(runtime, "illegal initialization");
@@ -126,14 +125,7 @@ public IRubyObject initialize(final ThreadContext context,
126125
final RubyString str = args[0].asString();
127126
switch (base) {
128127
case 0:
129-
final byte[] bytes = str.getBytes(); final int signum;
130-
if ( ( bytes[0] & 0x80 ) != 0 ) {
131-
bytes[0] &= 0x7f; signum = -1;
132-
}
133-
else {
134-
signum = 1;
135-
}
136-
this.value = new BigInteger(signum, bytes);
128+
this.value = initBigIntegerMPI(runtime, str);
137129
break;
138130
case 2:
139131
// this seems wrong to me, but is the behavior of the
@@ -147,18 +139,44 @@ public IRubyObject initialize(final ThreadContext context,
147139
case 10:
148140
case 16:
149141
// here, the ASCII-encoded decimal or hex string is used
150-
try {
151-
this.value = new BigInteger(str.toString(), base);
152-
break;
153-
} catch (NumberFormatException e) {
154-
throw runtime.newArgumentError("value " + str + " is not legal for radix " + base);
155-
}
142+
this.value = initBigIntegerBase(runtime, str, base);
143+
break;
156144
default:
157145
throw runtime.newArgumentError("illegal radix: " + base);
158146
}
159147
return this;
160148
}
161149

150+
private static BigInteger initBigIntegerMPI(final Ruby runtime, RubyString str) {
151+
final ByteList byteList = str.getByteList();
152+
final int off = byteList.getBegin();
153+
final byte[] b = byteList.getUnsafeBytes();
154+
155+
long len = ((b[off] & 0xFFl) << 24) | ((b[off+1] & 0xFF) << 16) | ((b[off+2] & 0xFF) << 8) | (b[off+3] & 0xFF);
156+
final byte[] bytes = new byte[(int) len];
157+
System.arraycopy(b, off + 4, bytes, 0, bytes.length);
158+
159+
final int signum;
160+
if ( (bytes[0] & 0x80) == 0x80 ) {
161+
signum = -1; bytes[0] &= 0x7f;
162+
}
163+
else {
164+
signum = +1;
165+
}
166+
return new BigInteger(signum, bytes);
167+
168+
}
169+
170+
private static BigInteger initBigIntegerBase(final Ruby runtime, RubyString str, final int base) {
171+
// here, the ASCII-encoded decimal or hex string is used
172+
try {
173+
return new BigInteger(str.toString(), base);
174+
}
175+
catch (NumberFormatException e) {
176+
throw runtime.newArgumentError("value " + str + " is not legal for radix " + base);
177+
}
178+
}
179+
162180
@Override
163181
public IRubyObject initialize_copy(final IRubyObject that) {
164182
super.initialize_copy(that);

src/test/ruby/test_bn.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,43 @@ def test_to_java
7676
assert_equal java.math.BigInteger.valueOf(24), OpenSSL::BN.new('24').to_java
7777
end if defined? JRUBY_VERSION
7878

79+
def test_new_str
80+
e1 = OpenSSL::BN.new(999.to_s(16), 16) # OpenSSL::BN.new(str, 16) must be most stable
81+
e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
82+
assert_equal(e1, OpenSSL::BN.new("999"))
83+
assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s))
84+
assert_equal(e1, OpenSSL::BN.new("999", 10))
85+
assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s, 10))
86+
assert_equal(e1, OpenSSL::BN.new("\x03\xE7", 2))
87+
assert_equal(e2, OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2))
88+
assert_equal(e1, OpenSSL::BN.new("\x00\x00\x00\x02\x03\xE7", 0))
89+
assert_equal(e2, OpenSSL::BN.new("\x00\x00\x00\x0E\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0))
90+
end
91+
92+
def test_new_bn
93+
e1 = OpenSSL::BN.new(999.to_s(16), 16)
94+
e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
95+
assert_equal(e1, OpenSSL::BN.new(e1))
96+
assert_equal(e2, OpenSSL::BN.new(e2))
97+
end
98+
99+
def test_new_integer
100+
assert_equal(999.to_bn, OpenSSL::BN.new(999))
101+
assert_equal((2 ** 107 - 1).to_bn, OpenSSL::BN.new(2 ** 107 - 1))
102+
assert_equal(-999.to_bn, OpenSSL::BN.new(-999))
103+
assert_equal((-(2 ** 107 - 1)).to_bn, OpenSSL::BN.new(-(2 ** 107 - 1)))
104+
end
105+
106+
def test_to_bn
107+
e1 = OpenSSL::BN.new(999.to_s(16), 16)
108+
e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
109+
assert_equal(e1, 999.to_bn)
110+
assert_equal(e2, (2**107-1).to_bn)
111+
end
112+
113+
def test_prime_p
114+
assert_equal(true, OpenSSL::BN.new((2 ** 107 - 1).to_s(16), 16).prime?)
115+
assert_equal(true, OpenSSL::BN.new((2 ** 127 - 1).to_s(16), 16).prime?(1))
116+
end
117+
79118
end

0 commit comments

Comments
 (0)