Skip to content

Shares converted to mnemonic form intermittently fail to recombine #27

@dcow

Description

@dcow

Roughly half the time shares are generated and converted to their mnemonic representation, they recombine yielding an incorrect result. Here's some sample code to demonstrate the issue:

use bitcoin_wallet::{
    account::Seed,
    error::Error,
    sss::{ShamirSecretSharing, Share},
};

pub fn split(data: &[u8; 32], scheme: &[(u8,u8)]) -> Result<Vec<Share>, Error>
{
    let seed = Seed(data.to_vec());
    ShamirSecretSharing::generate(1, scheme, &seed, None, 1)
}

pub fn combine(shares: &[Share]) -> Result<Vec<u8>, Error>
{
    let seed = ShamirSecretSharing::combine(shares, None)?;
    Ok(seed.0)
}

#[cfg(test)]
mod unit
{
    use super::*;
    use rand::RngCore;

    #[test]
    pub fn sss_roundtrip_internal()
    {
        let mut data = [0u8;32];
        rand::thread_rng().fill_bytes(&mut data);

        let shares = split(&data, &[(2,3)])
            .unwrap();

        let r1 = combine(&shares[..2])
            .unwrap();
        let r2 = combine(&shares[1..3])
            .unwrap();

        assert_eq!(data, &r1[..]);
        assert_eq!(data, &r2[..]);
    }

    #[test]
    pub fn sss_roundtrip_mnemonic()
    {
        let mut data = [0u8;32];
        rand::thread_rng().fill_bytes(&mut data);

        let shares = split(&data, &[(2,3)])
            .unwrap();

        let mnemonics = shares.iter()
            .map(|s| s.to_mnemonic())
            .collect::<Vec<_>>();

        let internals = mnemonics.iter()
            .map(|i| Share::from_mnemonic(&i).unwrap())
            .collect::<Vec<_>>();

        let r1 = combine(&internals[..2])
            .unwrap();
        let r2 = combine(&internals[1..3])
            .unwrap();

        assert_eq!(data, &r1[..]);
        assert_eq!(data, &r2[..]);
    }
}

The sss_roundtrip_internal test never fails, however, the sss_roundtrip_mnemonic test fails intermittently.
Here is some example output (6 success, 3 failure):

% cargo test --package adi
     Running target/debug/deps/adi-d0276c4a348266c4

running 2 tests
test unit::sss_roundtrip_internal ... ok
test unit::sss_roundtrip_mnemonic ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

% cargo test --project adi
     Running target/debug/deps/adi-d0276c4a348266c4

running 2 tests
test unit::sss_roundtrip_internal ... ok
test unit::sss_roundtrip_mnemonic ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

% cargo test --package adi
     Running target/debug/deps/adi-d0276c4a348266c4

running 2 tests
test unit::sss_roundtrip_internal ... ok
test unit::sss_roundtrip_mnemonic ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

% cargo test --package adi
    Finished test [unoptimized + debuginfo] target(s) in 0.02s
     Running target/debug/deps/adi-d0276c4a348266c4

running 2 tests
test unit::sss_roundtrip_internal ... ok
test unit::sss_roundtrip_mnemonic ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

% cargo test --package adi
     Running target/debug/deps/adi-d0276c4a348266c4

running 2 tests
test unit::sss_roundtrip_internal ... ok
test unit::sss_roundtrip_mnemonic ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

% cargo test --package adi
     Running target/debug/deps/adi-d0276c4a348266c4

running 2 tests
test unit::sss_roundtrip_internal ... ok
test unit::sss_roundtrip_mnemonic ... FAILED

failures:

---- unit::sss_roundtrip_mnemonic stdout ----
thread 'unit::sss_roundtrip_mnemonic' panicked at 'assertion failed: `(left == right)`
  left: `[124, 73, 15, 220, 128, 78, 234, 135, 187, 43, 221, 55, 182, 237, 162, 30, 228, 106, 239, 61, 254, 176, 109, 129, 196, 216, 125, 68, 29, 164, 26, 12]`,
 right: `[14, 153, 8, 23, 69, 184, 201, 116, 216, 126, 136, 164, 120, 231, 52, 206, 176, 198, 221, 190, 86, 44, 196, 53, 6, 169, 139, 77, 47, 5, 189, 198]`', adi/src/lib.rs:99:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    unit::sss_roundtrip_mnemonic

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '-p adi --lib'

% cargo test --package adi
     Running target/debug/deps/adi-d0276c4a348266c4

running 2 tests
test unit::sss_roundtrip_internal ... ok
test unit::sss_roundtrip_mnemonic ... FAILED

failures:

---- unit::sss_roundtrip_mnemonic stdout ----
thread 'unit::sss_roundtrip_mnemonic' panicked at 'assertion failed: `(left == right)`
  left: `[190, 184, 191, 130, 17, 80, 154, 245, 204, 38, 145, 178, 106, 235, 87, 21, 79, 57, 239, 33, 35, 99, 180, 148, 143, 242, 250, 8, 226, 203, 232, 151]`,
 right: `[126, 235, 37, 157, 73, 41, 228, 71, 35, 159, 119, 113, 31, 108, 153, 163, 222, 33, 161, 108, 235, 50, 177, 174, 110, 163, 75, 247, 206, 13, 175, 47]`', adi/src/lib.rs:99:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    unit::sss_roundtrip_mnemonic

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '-p adi --lib'

% cargo test --package adi
     Running target/debug/deps/adi-d0276c4a348266c4

running 2 tests
test unit::sss_roundtrip_internal ... ok
test unit::sss_roundtrip_mnemonic ... FAILED

failures:

---- unit::sss_roundtrip_mnemonic stdout ----
thread 'unit::sss_roundtrip_mnemonic' panicked at 'assertion failed: `(left == right)`
  left: `[58, 163, 196, 91, 115, 103, 154, 106, 255, 85, 132, 34, 13, 81, 78, 242, 205, 110, 161, 167, 125, 160, 250, 101, 8, 138, 14, 84, 28, 209, 89, 139]`,
 right: `[53, 177, 175, 17, 73, 175, 133, 126, 176, 228, 193, 159, 230, 250, 18, 149, 167, 153, 147, 83, 235, 134, 41, 235, 50, 15, 44, 50, 87, 226, 247, 207]`', adi/src/lib.rs:99:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    unit::sss_roundtrip_mnemonic

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '-p adi --lib'

% cargo test --package adi
     Running target/debug/deps/adi-d0276c4a348266c4

running 2 tests
test unit::sss_roundtrip_internal ... ok
test unit::sss_roundtrip_mnemonic ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

I've been poking around a little. I notice the to_mnemonic function is not actually tested in sss.rs. The from_mnemonic function is tested to correctly parse the TREZOR test vectors, and a roundtrip is tested using the internal Share representation, however there does not appear to be any code that tests converting shares to their mnemonic form. This leads me to suspect an issue with the encoding process. I started combing the code to see if anything jumps out, but would love some help (:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions