Skip to content
/ slip39-js Public

The javascript implementation of the SLIP39 for Shamir's Secret-Sharing for Mnemonic Codes.

License

Notifications You must be signed in to change notification settings

ilap/slip39-js

Repository files navigation

SLIP39

npm

The javascript implementation of the SLIP39 for Shamir's Secret-Sharing for Mnemonic Codes.

The code based on my Dart implementation of SLIP-0039.

DISCLAIMER

This project is still in early development phase. Use it at your own risk.

Description

This SLIP39 implementation uses a 3 level height (l=3) of a 16 degree (d=16) tree (T), which is represented as an array of the level two nodes (groups, G).

The degree (d) and the level (l) of the tree are 16 and 3 respectively, which means that max d^(l-1), i.e. 16^2, leaf nodes (M) can be in a complete tree (or forest).

The first level (l=1) node of the tree is the root (R), the level 2 ones are the SSS groups (Gs or group nodes) e.g. [G0, ..., Gd].

The last, the third, level nodes are the only leaves (M, group members) which contain the generated mnemonics.

Every node has two values:

  • the N and
  • M i.e. n(N,M).

Which means, that N (threshold) number of M children are required to reconstruct the node's secret.

Format

The tree's human friendly array representation only uses the group (l=2) nodes as arrays. For example: [[1,1], [1,1], [3,5], [2,6]] The group's first parameter is the N (group threshold) while the second is the M, the number of members in the group. See, and example in Using.

Installing

npm install slip39

Using

See example/main.js

const slip39 = require("../src/slip39.js");
const assert = require("assert");
// threshold (N) number of group shares required to reconstruct the master secret.
const threshold = 2;
const masterSecret = "ABCDEFGHIJKLMNOP".slip39EncodeHex();
const passphrase = "TREZOR";

/**
 * 4 groups shares.
 * = two for Alice
 * = one for friends and
 * = one for family members
 * Two of these group shares are required to reconstruct the master secret.
 */
const groups = [
  // Alice group shares. 1 is enough to reconstruct a group share,
  // therefore she needs at least two group shares to be reconstructed,
  [1, 1],
  [1, 1],
  // 3 of 5 Friends' shares are required to reconstruct this group share
  [3, 5],
  // 2 of 6 Family's shares are required to reconstruct this group share
  [2, 6],
];

const slip = slip39.fromArray(masterSecret, {
  passphrase: passphrase,
  threshold: threshold,
  groups: groups,
});

// One of Alice's share
const aliceShare = slip.fromPath("r/0").mnemonics;

// and any two of family's shares.
const familyShares = slip
  .fromPath("r/3/1")
  .mnemonics.concat(slip.fromPath("r/3/3").mnemonics);

const allShares = aliceShare.concat(familyShares);

console.log("Shares used for restoring the master secret:");
allShares.forEach((s) => console.log(s));

const recoveredSecret = slip39.recoverSecret(allShares, passphrase);
console.log("Master secret: " + masterSecret.slip39DecodeHex());
console.log("Recovered one: " + recoveredSecret.slip39DecodeHex());
assert(masterSecret.slip39DecodeHex() === recoveredSecret.slip39DecodeHex());

Testing

 $ npm install
 $ npm test

  Basic Tests
    Test threshold 1 with 5 of 7 shares of a group combinations
      ✓ Test combination 0 1 2 3 4.
      ✓ Test combination 0 1 2 3 5.
      ✓ Test combination 0 1 2 3 6.
      ✓ Test combination 0 1 2 4 5.
      ✓ Test combination 0 1 2 4 6.
      ✓ Test combination 0 1 2 5 6.
      ✓ Test combination 0 1 3 4 5.
      ✓ Test combination 0 1 3 4 6.
      ✓ Test combination 0 1 3 5 6.
      ✓ Test combination 0 1 4 5 6.
      ✓ Test combination 0 2 3 4 5.
      ✓ Test combination 0 2 3 4 6.
      ✓ Test combination 0 2 3 5 6.
      ✓ Test combination 0 2 4 5 6.
      ✓ Test combination 0 3 4 5 6.
      ✓ Test combination 1 2 3 4 5.
      ✓ Test combination 1 2 3 4 6.
      ✓ Test combination 1 2 3 5 6.
      ✓ Test combination 1 2 4 5 6.
      ✓ Test combination 1 3 4 5 6.
      ✓ Test combination 2 3 4 5 6.
    Test passhrase
      ✓ should return valid mastersecret when user submits valid passphrse
      ✓ should NOT return valid mastersecret when user submits invalid passphrse
      ✓ should return valid mastersecret when user does not submit passphrse
    Test iteration exponent
      ✓ should return valid mastersecret when user apply valid iteration exponent (44ms)
      ✓ should throw an Error when user submits invalid iteration exponent

  Group Shares Tests
    Test all valid combinations of mnemonics
      ✓ should return the valid mastersecret when valid mnemonics used for recovery
    Original test vectors Tests
      ✓ 1. Valid mnemonic without sharing (128 bits)
      ✓ 2. Mnemonic with invalid checksum (128 bits)
      ✓ 3. Mnemonic with invalid padding (128 bits)
      ✓ 4. Basic sharing 2-of-3 (128 bits)
      ✓ 5. Basic sharing 2-of-3 (128 bits)
      ✓ 6. Mnemonics with different identifiers (128 bits)
      ✓ 7. Mnemonics with different iteration exponents (128 bits)
      ✓ 8. Mnemonics with mismatching group thresholds (128 bits)
      ✓ 9. Mnemonics with mismatching group counts (128 bits)
      ✓ 10. Mnemonics with greater group threshold than group counts (128 bits)
      ✓ 11. Mnemonics with duplicate member indices (128 bits)
      ✓ 12. Mnemonics with mismatching member thresholds (128 bits)
      ✓ 13. Mnemonics giving an invalid digest (128 bits)
      ✓ 14. Insufficient number of groups (128 bits, case 1)
      ✓ 15. Insufficient number of groups (128 bits, case 2)
      ✓ 16. Threshold number of groups, but insufficient number of members in one group (128 bits)
      ✓ 17. Threshold number of groups and members in each group (128 bits, case 1)
      ✓ 18. Threshold number of groups and members in each group (128 bits, case 2)
      ✓ 19. Threshold number of groups and members in each group (128 bits, case 3)
      ✓ 20. Valid mnemonic without sharing (256 bits)
      ✓ 21. Mnemonic with invalid checksum (256 bits)
      ✓ 22. Mnemonic with invalid padding (256 bits)
      ✓ 23. Basic sharing 2-of-3 (256 bits)
      ✓ 24. Basic sharing 2-of-3 (256 bits)
      ✓ 25. Mnemonics with different identifiers (256 bits)
      ✓ 26. Mnemonics with different iteration exponents (256 bits)
      ✓ 27. Mnemonics with mismatching group thresholds (256 bits)
      ✓ 28. Mnemonics with mismatching group counts (256 bits)
      ✓ 29. Mnemonics with greater group threshold than group counts (256 bits)
      ✓ 30. Mnemonics with duplicate member indices (256 bits)
      ✓ 31. Mnemonics with mismatching member thresholds (256 bits)
      ✓ 32. Mnemonics giving an invalid digest (256 bits)
      ✓ 33. Insufficient number of groups (256 bits, case 1)
      ✓ 34. Insufficient number of groups (256 bits, case 2)
      ✓ 35. Threshold number of groups, but insufficient number of members in one group (256 bits)
      ✓ 36. Threshold number of groups and members in each group (256 bits, case 1)
      ✓ 37. Threshold number of groups and members in each group (256 bits, case 2)
      ✓ 38. Threshold number of groups and members in each group (256 bits, case 3)
      ✓ 39. Mnemonic with insufficient length
      ✓ 40. Mnemonic with invalid master secret length
    Invalid Shares
      ✓ Short master secret
      ✓ Odd length master secret
      ✓ Group threshold exceeds number of groups
      ✓ Invalid group threshold.
      ✓ Member threshold exceeds number of members
      ✓ Invalid member threshold
      ✓ Group with multiple members and threshold 1


  74 passing (477ms)

TODOS

  • Add unit tests.
  • Test with the reference code's test vectors.
  • Refactor the helpers to different helper classes e.g. CryptoHelper(), ShamirHelper() etc.
  • Add JSON representation, see JSON representation below.
  • Refactor to much simpler code.

JSON Representation

{
  "name": "Slip39",
  "threshold": 2,
  "shares": [
    {
      "name": "My Primary",
      "threshold": 1,
      "shares": ["Primary"]
    },
    {
      "name": "My Secondary",
      "threshold": 1,
      "shares": ["Secondary"]
    },
    {
      "name": "Friends",
      "threshold": 3,
      "shares": ["Alice", "Bob", "Charlie", "David", "Erin"]
    },
    {
      "name": "Family",
      "threshold": 2,
      "shares": ["Adam", "Brenda", "Carol", "Dan", "Edward", "Frank"]
    }
  ]
}

LICENSE

CopyRight (c) 2019 Pal Dorogi "iLap" pal.dorogi@gmail.com

MIT License

About

The javascript implementation of the SLIP39 for Shamir's Secret-Sharing for Mnemonic Codes.

Resources

License

Stars

Watchers

Forks

Packages