Remove calling sc_reduce32 when deriving a keypair from a mnemonic seed.

This is the reason that seeds across desktop and mobile devices would never restore to the same keypair. This function was there from the initial messenger days and with further discussion with Maxim, we deemed calling this function an unnecessary step for generating a keypair. There are no security implications for this change as we generate keypairs the exact same way as Signal does.

The biggest downside to this change however is that if you try to restore a desktop device with a seed from before this change, that seed will generate a completely different key pair and thus a different session ID.
This will only affect users who plan to restore any device from the seed of a desktop device before this change.
pull/955/head
Mikunj 4 years ago
parent 62abcfcdc6
commit 012575be35

@ -31,4 +31,3 @@ ts/**/*.js
# Libloki specific files
libloki/test/components.js
libloki/modules/mnemonic.js
libloki/modules/sc_reduce32.js

@ -1,2 +0,0 @@
use emscripten to convert c to javascript:
`emcc sc_reduce32.c -o sc_reduce32.js -s EXPORTED_FUNCTIONS="['_sc_reduce32']" -s WASM=0 -s ENVIRONMENT=node -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall"]'`

@ -1,134 +0,0 @@
#include <stdint.h>
uint64_t load_3(const unsigned char *in) {
uint64_t result;
result = (uint64_t) in[0];
result |= ((uint64_t) in[1]) << 8;
result |= ((uint64_t) in[2]) << 16;
return result;
}
uint64_t load_4(const unsigned char *in)
{
uint64_t result;
result = (uint64_t) in[0];
result |= ((uint64_t) in[1]) << 8;
result |= ((uint64_t) in[2]) << 16;
result |= ((uint64_t) in[3]) << 24;
return result;
}
void sc_reduce32(unsigned char *s) {
int64_t s0 = 2097151 & load_3(s);
int64_t s1 = 2097151 & (load_4(s + 2) >> 5);
int64_t s2 = 2097151 & (load_3(s + 5) >> 2);
int64_t s3 = 2097151 & (load_4(s + 7) >> 7);
int64_t s4 = 2097151 & (load_4(s + 10) >> 4);
int64_t s5 = 2097151 & (load_3(s + 13) >> 1);
int64_t s6 = 2097151 & (load_4(s + 15) >> 6);
int64_t s7 = 2097151 & (load_3(s + 18) >> 3);
int64_t s8 = 2097151 & load_3(s + 21);
int64_t s9 = 2097151 & (load_4(s + 23) >> 5);
int64_t s10 = 2097151 & (load_3(s + 26) >> 2);
int64_t s11 = (load_4(s + 28) >> 7);
int64_t s12 = 0;
int64_t carry0;
int64_t carry1;
int64_t carry2;
int64_t carry3;
int64_t carry4;
int64_t carry5;
int64_t carry6;
int64_t carry7;
int64_t carry8;
int64_t carry9;
int64_t carry10;
int64_t carry11;
carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
s[0] = s0 >> 0;
s[1] = s0 >> 8;
s[2] = (s0 >> 16) | (s1 << 5);
s[3] = s1 >> 3;
s[4] = s1 >> 11;
s[5] = (s1 >> 19) | (s2 << 2);
s[6] = s2 >> 6;
s[7] = (s2 >> 14) | (s3 << 7);
s[8] = s3 >> 1;
s[9] = s3 >> 9;
s[10] = (s3 >> 17) | (s4 << 4);
s[11] = s4 >> 4;
s[12] = s4 >> 12;
s[13] = (s4 >> 20) | (s5 << 1);
s[14] = s5 >> 7;
s[15] = (s5 >> 15) | (s6 << 6);
s[16] = s6 >> 2;
s[17] = s6 >> 10;
s[18] = (s6 >> 18) | (s7 << 3);
s[19] = s7 >> 5;
s[20] = s7 >> 13;
s[21] = s8 >> 0;
s[22] = s8 >> 8;
s[23] = (s8 >> 16) | (s9 << 5);
s[24] = s9 >> 3;
s[25] = s9 >> 11;
s[26] = (s9 >> 19) | (s10 << 2);
s[27] = s10 >> 6;
s[28] = (s10 >> 14) | (s11 << 7);
s[29] = s11 >> 1;
s[30] = s11 >> 9;
s[31] = s11 >> 17;
}

@ -1,37 +1,13 @@
const crc32 = require('buffer-crc32');
const sc_reduce32_module = require('./sc_reduce32');
module.exports = {
mn_encode,
mn_decode,
sc_reduce32,
get_languages,
pubkey_to_secret_words,
};
class MnemonicError extends Error {}
function hexToUint8Array(e) {
if (e.length % 2 != 0) throw 'Hex string has invalid length!';
for (var t = new Uint8Array(e.length / 2), r = 0; r < e.length / 2; ++r)
t[r] = parseInt(e.slice(2 * r, 2 * r + 2), 16);
return t;
}
function Uint8ArrayToHex(e) {
for (var t = [], r = 0; r < e.length; ++r)
t.push(('0' + e[r].toString(16)).slice(-2));
return t.join('');
}
function sc_reduce32(e) {
var t = hexToUint8Array(e);
if (32 !== t.length) throw 'Invalid input length';
var r = sc_reduce32_module._malloc(32);
sc_reduce32_module.HEAPU8.set(t, r),
sc_reduce32_module.ccall('sc_reduce32', 'void', ['number'], [r]);
var o = sc_reduce32_module.HEAPU8.subarray(r, r + 32);
return sc_reduce32_module._free(r), Uint8ArrayToHex(o);
}
/*
mnemonic.js : Converts between 4-byte aligned strings and a human-readable
sequence of words. Uses 1626 common words taken from wikipedia article:

File diff suppressed because it is too large Load Diff

@ -132,12 +132,8 @@
seedHex = seedHex.concat(seedHex);
seedHex = seedHex.substring(0, privKeyHexLength);
}
const privKeyHex = window.mnemonic.sc_reduce32(seedHex);
const privKey = dcodeIO.ByteBuffer.wrap(
privKeyHex,
'hex'
).toArrayBuffer();
return libsignal.Curve.async.createKeyPair(privKey);
const seed = dcodeIO.ByteBuffer.wrap(seedHex, 'hex').toArrayBuffer();
return libsignal.Curve.async.createKeyPair(seed);
};
} else {
generateKeypair = libsignal.KeyHelper.generateIdentityKeyPair;

@ -143,12 +143,11 @@ export class RegistrationTabs extends React.Component<{}, State> {
seedHex = seedHex.concat(seedHex);
seedHex = seedHex.substring(0, privKeyHexLength);
}
const privKeyHex = window.mnemonic.sc_reduce32(seedHex);
const privKey = window.dcodeIO.ByteBuffer.wrap(
privKeyHex,
const seed = window.dcodeIO.ByteBuffer.wrap(
seedHex,
'hex'
).toArrayBuffer();
const keyPair = await window.libsignal.Curve.async.createKeyPair(privKey);
const keyPair = await window.libsignal.Curve.async.createKeyPair(seed);
const hexGeneratedPubKey = Buffer.from(keyPair.pubKey).toString('hex');
this.setState({

Loading…
Cancel
Save