KeyPair: add fromBytes, toEd25519PrivateKey (#78)

- fromBytes allows to pass any seed you want
- constructor takes PeerId (previously was empty and maybe private)
- toEd25519PrivateKey to complete the lifecycle.
This commit is contained in:
folex 2021-09-14 12:16:17 +03:00 committed by GitHub
parent 608506db9f
commit 763e641fa2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 93 additions and 8382 deletions

View File

@ -17,9 +17,11 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Extract branch name - name: Get branch name
run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV run: |
BRANCH=${GITHUB_REF#refs/*/}
SANITIZED=$(echo "$BRANCH" | sed -e 's/[^a-zA-Z0-9-]/-/g')
echo "BRANCH_NAME=$SANITIZED" >> $GITHUB_ENV
### Set version ### Set version
- name: Set version - name: Set version
run: npm version prerelease --no-git-tag-version --preid ${{ env.BRANCH_NAME }}-${{ github.run_number }} run: npm version prerelease --no-git-tag-version --preid ${{ env.BRANCH_NAME }}-${{ github.run_number }}

8348
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,6 @@
"libp2p-websockets": "0.16.1", "libp2p-websockets": "0.16.1",
"loglevel": "1.7.0", "loglevel": "1.7.0",
"multiaddr": "10.0.0", "multiaddr": "10.0.0",
"noble-ed25519": "^1.2.5",
"peer-id": "0.15.3", "peer-id": "0.15.3",
"uuid": "8.3.0" "uuid": "8.3.0"
}, },

View File

@ -1,6 +1,5 @@
import { encode } from 'bs58'; import * as bs58 from 'bs58';
import * as base64 from 'base64-js'; import * as base64 from 'base64-js';
import PeerId from 'peer-id';
import { KeyPair } from '../../internal/KeyPair'; import { KeyPair } from '../../internal/KeyPair';
describe('KeyPair tests', () => { describe('KeyPair tests', () => {
@ -10,23 +9,48 @@ describe('KeyPair tests', () => {
// act // act
const keyPair = await KeyPair.fromEd25519SK(sk); const keyPair = await KeyPair.fromEd25519SK(sk);
const sk2 = peerIdToEd25519SK(keyPair.Libp2pPeerId); const privateKey = keyPair.toEd25519PrivateKey();
const sk2 = base64.fromByteArray(privateKey)
// assert // assert
expect(sk2).toBe(sk); expect(sk2).toBe(sk);
}); });
});
/** it('generate keypair from seed', async function () {
* Converts peer id into base64 string contatining the 32 byte Ed25519S secret key // arrange
* @returns - base64 of Ed25519S secret key const random = await KeyPair.randomEd25519();
*/ const privateKey = random.toEd25519PrivateKey();
export const peerIdToEd25519SK = (peerId: PeerId): string => {
// export as [...private, ...public] array // act
const privateAndPublicKeysArray = peerId.privKey.marshal(); const keyPair = await KeyPair.fromBytes(privateKey);
// extract the private key const privateKey2 = keyPair.toEd25519PrivateKey();
const pk = privateAndPublicKeysArray.slice(0, 32);
// serialize private key as base64 // assert
const b64 = base64.fromByteArray(pk); expect(privateKey).toStrictEqual(privateKey2);
return b64; });
};
it('create keypair from ed25519 private key', async function() {
// arrange
const rustSK = "jDaxLJzYtzgwTMrELJCAqavtmx85ktQNfB2rLcK7MhH";
const sk = bs58.decode(rustSK);
// act
const keyPair = await KeyPair.fromBytes(sk);
// assert
const expectedPeerId = "12D3KooWH1W3VznVZ87JH4FwABK4mkntcspTVWJDta6c2xg9Pzbp";
expect(keyPair.Libp2pPeerId.toB58String()).toStrictEqual(expectedPeerId);
});
it('create keypair from a seed phrase', async function() {
// arrange
const seedArray = new Uint8Array(32).fill(1);
// act
const keyPair = await KeyPair.fromBytes(seedArray);
// assert
const expectedPeerId = "12D3KooWK99VoVxNE7XzyBwXEzW7xhK7Gpv85r9F3V3fyKSUKPH5";
expect(keyPair.Libp2pPeerId.toB58String()).toStrictEqual(expectedPeerId);
});
});

View File

@ -16,45 +16,53 @@
import * as PeerId from 'peer-id'; import * as PeerId from 'peer-id';
import * as base64 from 'base64-js'; import * as base64 from 'base64-js';
import * as ed from 'noble-ed25519';
import { keys } from 'libp2p-crypto'; import { keys } from 'libp2p-crypto';
export class KeyPair { export class KeyPair {
/** /**
* @deprecated
* Key pair in libp2p format. Used for backward compatibility with the current FluencePeer implementation * Key pair in libp2p format. Used for backward compatibility with the current FluencePeer implementation
*/ */
public Libp2pPeerId: PeerId; public Libp2pPeerId: PeerId;
/** constructor(libp2pPeerId: PeerId) {
* Generates a new KeyPair from base64 string contatining the 32 byte Ed25519 secret key this.Libp2pPeerId = libp2pPeerId
* @returns - Promise with the created KeyPair
*/
static async fromEd25519SK(sk: string): Promise<KeyPair> {
// deserialize secret key from base64
const bytes = base64.toByteArray(sk);
// calculate ed25519 public key
const publicKey = await ed.getPublicKey(bytes);
// concatenate secret + public because that's what libp2p-crypto expects
const privateAndPublicKeysArray = new Uint8Array([...bytes, ...publicKey]);
// deserialize keys.supportedKeys.Ed25519PrivateKey
const privateKey = await keys.supportedKeys.ed25519.unmarshalEd25519PrivateKey(privateAndPublicKeysArray);
// serialize it to protobuf encoding because that's what PeerId expects
const protobuf = keys.marshalPrivateKey(privateKey);
// deserialize PeerId from protobuf encoding
const lib2p2Pid = await PeerId.createFromPrivKey(protobuf);
const res = new KeyPair();
res.Libp2pPeerId = lib2p2Pid;
return res;
} }
/** /**
* Generates a new KeyPair with random secret key * Generates new KeyPair from base64 string containing the 32 byte Ed25519 private key
* @returns - Promise with the created KeyPair
*/
static async fromEd25519SK(base64Key: string): Promise<KeyPair> {
// deserialize private key from base64
const key = base64.toByteArray(base64Key);
return await KeyPair.fromBytes(key);
}
/**
* Generates new KeyPair from a 32 byte array
* @param key - Any sequence of 32 bytes
* @returns - Promise with the created KeyPair
*/
static async fromBytes(arr: Uint8Array): Promise<KeyPair> {
// generateKeyPairFromSeed takes seed and copies it to private key as is
const privateKey = await keys.generateKeyPairFromSeed('Ed25519', arr, 256);
const lib2p2Pid = await PeerId.createFromPrivKey(privateKey.bytes);
return new KeyPair(lib2p2Pid);
}
/**
* Generates new KeyPair with a random secret key
* @returns - Promise with the created KeyPair * @returns - Promise with the created KeyPair
*/ */
static async randomEd25519(): Promise<KeyPair> { static async randomEd25519(): Promise<KeyPair> {
const res = new KeyPair(); const lib2p2Pid = await PeerId.create({ keyType: 'Ed25519' });
res.Libp2pPeerId = await PeerId.create({ keyType: 'Ed25519' }); return new KeyPair(lib2p2Pid);
return res; }
/**
* @returns 32 byte private key
*/
toEd25519PrivateKey(): Uint8Array {
return this.Libp2pPeerId.privKey.marshal().subarray(0, 32);
} }
} }