Some confusion with how `num_bigint` generates bigint(s)

I'm trying to get a bigint from a slice of bytes. My slice contains 192 bytes. So, I'm expecting a bigint with 192 * 8 = 1536 bits

The python version of the code generates the right bigint. However num_bigint generates something totally different.

Here's the rust code:

#![allow(unused)]
#![feature(min_const_generics)]

use core::convert::TryInto;
use num_bigint::{BigUint};

pub const DH_GROUP_5_PRIME: &str = "
	FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
	29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
	EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
	E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
	EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
	C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
	83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
	670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF
	";

pub fn unhexlify_to_bytearray<const N: usize>(prime: &str) -> [u8; N] {
    let mut bytearray = [0; N];
    let hex_string = prime;
    for i in (0..hex_string.len()).step_by(2) {
        if i > (2*N - 2) {
            break;
        }
        let substring = &hex_string[i..i + 2];
        let z = (u8::from_str_radix(substring, 16)).unwrap();
        bytearray[(i - (i / 2))] = z;
    }
    return bytearray;
}

pub fn bigint_to_bytes<const N: usize>(bigint: &BigUint) -> [u8; N] {
     let x: [u8; N] = BigUint::to_bytes_le(&bigint).try_into().unwrap();
     x

}

fn main() {
    let primestring = DH_GROUP_5_PRIME.replace(" ", "")
        .replace("\n\t", "");
    let primebytes = unhexlify_to_bytearray::<192>(&primestring);
    let bin = BigUint::from_bytes_le(&primebytes);
    println!("{:#?}", bin);
    //println!("{:?}", bigint_to_bytes::<192>(&bin))
}
" Rust Output ===============
BigUint {
    data: [
        18446744073709551615,
        3801715529129529289,
        15068160897927268036,
        8416215646191092265,
        2493607965601172226,
        15925911968809503313,
        1964478517611173359,
        3968902666436356912,
        5026669664524427599,
        14302973243566687716,
        7776933321556249844,
        17129167355828305675,
        11934408561413273838,
        16582055349237096366,
        4421379162823338057,
        414159236977393858,
        11156354281585040024,
        6903777425650292329,
        10857514442541589891,
        13497997407127036444,
        7896664404541429150,
        331221599937432679,
        2842655140432606449,
        18446744073709551615,
    ],
}

The result from my rust code isn't correct as it gives me a slightly bigger int than 1536 bits. Here's the python version


from binascii import unhexlify

DH_GROUP_5_PRIME = """
	FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
	29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
	EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
	E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
	EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
	C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
	83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
	670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF
	"""

def bytes_to_int(bytes):
    result = 0;
    for i in range(len(bytes) - 1, -1, -1):
        result += bytes[(len(bytes) - 1) - i] << (8*i);
    return result;

DH_GROUP_5_BINARY_PRIME = bytes_to_int(bytearray(unhexlify(DH_GROUP_5_PRIME.replace(" ", "").replace("\n", "").replace("\t", ""))));

print(DH_GROUP_5_BINARY_PRIME)

"Python output =====================
24103124269210325885520
76022197566074856950548
50245994265411694195810
88316826122288900938582
61341614673227141477904
01219650364895705058263
19427307068050092230627
34745341073406696246014
58936165977404102716924
94532003787294341703258
43778659198143763193776
85986952408894019557734
61198435453015470437472
07749969763750084308926
33929555996888245787241
29938101291302945929999
47926365264059284647209
73038494721168143446471
44384885209401274598442
88859336526896320919633
919"

I pretty sure I'm missing something here but cant seem to figure it out. Also, another point - my rust code is able to retrieve the original bytes from the bigint it generated (which is again a bit puzzling to me).

Any suggestions would be great!

Thanks to @KMK - figured out this one. We use Display to print num_bigint's output (and not debug)

Its suppose to be

println!("{}", bin);

and not

println!("{:#?}", bin);

Well, I only wished I asked for help 1 hour earlier :sweat_smile:

I've been thinking we might change Debug for BigUint to just print the number like Display. The raw data array is interesting if you're hacking on num-bigint itself, but that's pretty obnoxious to see when you have a BigUint in another type's Debug output.

2 Likes

You might consider (ab) using the alternate format flag to have both. "Thin" debug format would be BigInt(actual_number), "Tall" could give both the raw data array and the decimal value.

Well I would like dbg! to look nice, which uses the alt flag, but it seems weird if the non-alt format would be more raw/verbose. Maybe the raw version could come from some inherent method returning an impl Debug wrapper.

1 Like

Could even just be as_digits(&self) -> &[BigDigit], perhaps.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.