Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
fix: land program addresses off-curve (#11355)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmay authored Aug 6, 2020
1 parent 9abb7db commit 5c4b815
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 7 deletions.
5 changes: 5 additions & 0 deletions web3.js/module.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import * as BufferLayout from 'buffer-layout';

declare module '@solana/web3.js' {
// === src/publickey.js ===
declare export type PublicKeyNonce = [PublicKey, number];
declare export class PublicKey {
constructor(
value: number | string | Buffer | Uint8Array | Array<number>,
Expand All @@ -28,6 +29,10 @@ declare module '@solana/web3.js' {
seeds: Array<Buffer | Uint8Array>,
programId: PublicKey,
): Promise<PublicKey>;
static findProgramAddress(
seeds: Array<Buffer | Uint8Array>,
programId: PublicKey,
): Promise<PublicKeyNonce>;
equals(publickey: PublicKey): boolean;
toBase58(): string;
toBuffer(): Buffer;
Expand Down
113 changes: 111 additions & 2 deletions web3.js/src/publickey.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

import BN from 'bn.js';
import bs58 from 'bs58';
import nacl from 'tweetnacl';
import {sha256} from 'crypto-hash';

//$FlowFixMe
let naclLowLevel = nacl.lowlevel;

type PublicKeyNonce = [PublicKey, number]; // This type exists to workaround an esdoc parse error

/**
* A public key
*/
Expand Down Expand Up @@ -104,7 +110,110 @@ export class PublicKey {
Buffer.from('ProgramDerivedAddress'),
]);
let hash = await sha256(new Uint8Array(buffer));
hash = await sha256(new Uint8Array(new BN(hash, 16).toBuffer()));
return new PublicKey('0x' + hash);
let publicKeyBytes = new BN(hash, 16).toBuffer();
if (is_on_curve(publicKeyBytes)) {
throw new Error(`Invalid seeds, address must fall off the curve`);
}
return new PublicKey(publicKeyBytes);
}

/**
* Find a valid program address
*
* Valid program addresses must fall off the ed25519 curve. This function
* iterates a nonce until it finds one that when combined with the seeds
* results in a valid program address.
*/
static async findProgramAddress(
seeds: Array<Buffer | Uint8Array>,
programId: PublicKey,
): Promise<PublicKeyNonce> {
let nonce = 255;
let address;
while (nonce != 0) {
try {
const seedsWithNonce = seeds.concat(Buffer.from([nonce]));
address = await this.createProgramAddress(seedsWithNonce, programId);
} catch (err) {
nonce--;
continue;
}
return [address, nonce];
}
throw new Error(`Unable to find a viable program address nonce`);
}
}

// Check that a pubkey is on the curve.
// This function and its dependents were sourced from:
// https://github.com/dchest/tweetnacl-js/blob/f1ec050ceae0861f34280e62498b1d3ed9c350c6/nacl.js#L792
function is_on_curve(p) {
var r = [
naclLowLevel.gf(),
naclLowLevel.gf(),
naclLowLevel.gf(),
naclLowLevel.gf(),
];

var t = naclLowLevel.gf(),
chk = naclLowLevel.gf(),
num = naclLowLevel.gf(),
den = naclLowLevel.gf(),
den2 = naclLowLevel.gf(),
den4 = naclLowLevel.gf(),
den6 = naclLowLevel.gf();

naclLowLevel.set25519(r[2], gf1);
naclLowLevel.unpack25519(r[1], p);
naclLowLevel.S(num, r[1]);
naclLowLevel.M(den, num, naclLowLevel.D);
naclLowLevel.Z(num, num, r[2]);
naclLowLevel.A(den, r[2], den);

naclLowLevel.S(den2, den);
naclLowLevel.S(den4, den2);
naclLowLevel.M(den6, den4, den2);
naclLowLevel.M(t, den6, num);
naclLowLevel.M(t, t, den);

naclLowLevel.pow2523(t, t);
naclLowLevel.M(t, t, num);
naclLowLevel.M(t, t, den);
naclLowLevel.M(t, t, den);
naclLowLevel.M(r[0], t, den);

naclLowLevel.S(chk, r[0]);
naclLowLevel.M(chk, chk, den);
if (neq25519(chk, num)) naclLowLevel.M(r[0], r[0], I);

naclLowLevel.S(chk, r[0]);
naclLowLevel.M(chk, chk, den);
if (neq25519(chk, num)) return 0;
return 1;
}
let gf1 = naclLowLevel.gf([1]);
let I = naclLowLevel.gf([
0xa0b0,
0x4a0e,
0x1b27,
0xc4ee,
0xe478,
0xad2f,
0x1806,
0x2f43,
0xd7a7,
0x3dfb,
0x0099,
0x2b4d,
0xdf0b,
0x4fc1,
0x2480,
0x2b83,
]);
function neq25519(a, b) {
var c = new Uint8Array(32),
d = new Uint8Array(32);
naclLowLevel.pack25519(c, a);
naclLowLevel.pack25519(d, b);
return naclLowLevel.crypto_verify_32(c, 0, d, 0);
}
28 changes: 23 additions & 5 deletions web3.js/test/publickey.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,12 @@ test('createProgramAddress', async () => {
);

let programAddress = await PublicKey.createProgramAddress(
[Buffer.from('', 'utf8')],
[Buffer.from('', 'utf8'), Buffer.from([1])],
programId,
);
expect(
programAddress.equals(
new PublicKey('CsdSsqp6Upkh2qajhZMBM8xT4GAyDNSmcV37g4pN8rsc'),
new PublicKey('3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT'),
),
).toBe(true);

Expand All @@ -255,7 +255,7 @@ test('createProgramAddress', async () => {
);
expect(
programAddress.equals(
new PublicKey('A8mYnN8Pfx7Nn6f8RoQgsPNtAGAWmmKSBCDfyDvE6sXF'),
new PublicKey('7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7'),
),
).toBe(true);

Expand All @@ -265,7 +265,7 @@ test('createProgramAddress', async () => {
);
expect(
programAddress.equals(
new PublicKey('CawYq8Rmj4JRR992wVnGEFUjMEkmtmcFgEL4iS1qPczu'),
new PublicKey('HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds'),
),
).toBe(true);

Expand All @@ -275,7 +275,7 @@ test('createProgramAddress', async () => {
);
expect(
programAddress.equals(
new PublicKey('4ak7qJacCKMAGP8xJtDkg2VYZh5QKExa71ijMDjZGQyb'),
new PublicKey('GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K'),
),
).toBe(true);

Expand All @@ -285,3 +285,21 @@ test('createProgramAddress', async () => {
);
expect(programAddress.equals(programAddress2)).toBe(false);
});

test('findProgramAddress', async () => {
const programId = new PublicKey(
'BPFLoader1111111111111111111111111111111111',
);
let [programAddress, nonce] = await PublicKey.findProgramAddress(
[Buffer.from('', 'utf8')],
programId,
);
expect(
programAddress.equals(
await PublicKey.createProgramAddress(
[Buffer.from('', 'utf8'), Buffer.from([nonce])],
programId,
),
),
).toBe(true);
});

0 comments on commit 5c4b815

Please sign in to comment.