fix: sign and export with each address's own derivation index (closes #510)
Sending, site transactions, site signatures and private key export derived the key at the address's position in the wallet's list. After an import whose scan passed over an unused index, or after removing an address that was not the last, that position named another address's key. Each HD and xprv address record now stores the index it was derived at (at wallet creation, by the import scan, by "+"). getSignerForAddress() derives at that index, refuses a record without one, and refuses any key whose address differs from the record's. The confirmation screen signs as the From address it shows, not the selected one. The export screen reports a refusal instead of calling the password wrong. Model: opus-5-5
This commit is contained in:
+49
-5
@@ -416,7 +416,7 @@ describe("extended key depth", () => {
|
||||
expect(() =>
|
||||
wallet.getSignerForAddress(
|
||||
{ type: "xprv" },
|
||||
0,
|
||||
{ address: VECTOR_ADDRESSES[0], index: 0 },
|
||||
accountXprv(VECTOR_PHRASE),
|
||||
),
|
||||
).toThrow(/master/i);
|
||||
@@ -504,7 +504,7 @@ describe("getSignerForAddress", () => {
|
||||
test.each([0, 1, 2])("hd wallet, address index %i", (index) => {
|
||||
const signer = wallet.getSignerForAddress(
|
||||
{ type: "hd" },
|
||||
index,
|
||||
{ address: VECTOR_ADDRESSES[index], index },
|
||||
VECTOR_PHRASE,
|
||||
);
|
||||
|
||||
@@ -515,7 +515,7 @@ describe("getSignerForAddress", () => {
|
||||
test.each([0, 1, 2])("xprv wallet, address index %i", (index) => {
|
||||
const signer = wallet.getSignerForAddress(
|
||||
{ type: "xprv" },
|
||||
index,
|
||||
{ address: VECTOR_ADDRESSES[index], index },
|
||||
masterXprv(VECTOR_PHRASE),
|
||||
);
|
||||
|
||||
@@ -527,7 +527,7 @@ describe("getSignerForAddress", () => {
|
||||
for (const index of [0, 1, 2]) {
|
||||
const signer = wallet.getSignerForAddress(
|
||||
{ type: "privkey" },
|
||||
index,
|
||||
{ address: VECTOR_ADDRESSES[1], index },
|
||||
VECTOR_PRIVATE_KEYS[1],
|
||||
);
|
||||
|
||||
@@ -535,10 +535,54 @@ describe("getSignerForAddress", () => {
|
||||
}
|
||||
});
|
||||
|
||||
// https://git.eeqj.de/sneak/AutistMask/issues/510: an address whose stored
|
||||
// index does not derive it, or a private key that is not its key.
|
||||
test.each([
|
||||
["hd", { address: VECTOR_ADDRESSES[2], index: 1 }, VECTOR_PHRASE],
|
||||
[
|
||||
"xprv",
|
||||
{ address: VECTOR_ADDRESSES[2], index: 1 },
|
||||
masterXprv(VECTOR_PHRASE),
|
||||
],
|
||||
["privkey", { address: VECTOR_ADDRESSES[2] }, VECTOR_PRIVATE_KEYS[1]],
|
||||
])(
|
||||
"%s wallet refuses a key that is not the address's own",
|
||||
(type, addr, secret) => {
|
||||
expect(() =>
|
||||
wallet.getSignerForAddress({ type }, addr, secret),
|
||||
).toThrow(
|
||||
"The key this wallet holds for " +
|
||||
VECTOR_ADDRESSES[2] +
|
||||
" belongs to a different address, so it was not used.",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// An HD or xprv address record without a usable `index` is refused in a
|
||||
// sentence, not with what ethers says about the value it was handed.
|
||||
test.each([
|
||||
["hd", VECTOR_PHRASE],
|
||||
["xprv", masterXprv(VECTOR_PHRASE)],
|
||||
])("%s wallet refuses an address with no valid index", (type, secret) => {
|
||||
for (const index of [undefined, null, -1, 1.5, "1"]) {
|
||||
expect(() =>
|
||||
wallet.getSignerForAddress(
|
||||
{ type },
|
||||
{ address: VECTOR_ADDRESSES[1], index },
|
||||
secret,
|
||||
),
|
||||
).toThrow(
|
||||
"No derivation index is stored for " +
|
||||
VECTOR_ADDRESSES[1] +
|
||||
", so its key cannot be derived.",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("the returned signer signs recoverably as the expected address", async () => {
|
||||
const signer = wallet.getSignerForAddress(
|
||||
{ type: "hd" },
|
||||
1,
|
||||
{ address: VECTOR_ADDRESSES[1], index: 1 },
|
||||
VECTOR_PHRASE,
|
||||
);
|
||||
const message = "AutistMask derivation test";
|
||||
|
||||
Reference in New Issue
Block a user