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
uses: actions/checkout@v2
- name: Extract branch name
run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV
- name: Get branch name
run: |
BRANCH=${GITHUB_REF#refs/*/}
SANITIZED=$(echo "$BRANCH" | sed -e 's/[^a-zA-Z0-9-]/-/g')
echo "BRANCH_NAME=$SANITIZED" >> $GITHUB_ENV
### Set version
- name: Set version
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",
"loglevel": "1.7.0",
"multiaddr": "10.0.0",
"noble-ed25519": "^1.2.5",
"peer-id": "0.15.3",
"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 PeerId from 'peer-id';
import { KeyPair } from '../../internal/KeyPair';
describe('KeyPair tests', () => {
@ -10,23 +9,48 @@ describe('KeyPair tests', () => {
// act
const keyPair = await KeyPair.fromEd25519SK(sk);
const sk2 = peerIdToEd25519SK(keyPair.Libp2pPeerId);
const privateKey = keyPair.toEd25519PrivateKey();
const sk2 = base64.fromByteArray(privateKey)
// assert
expect(sk2).toBe(sk);
});
});
/**
* Converts peer id into base64 string contatining the 32 byte Ed25519S secret key
* @returns - base64 of Ed25519S secret key
*/
export const peerIdToEd25519SK = (peerId: PeerId): string => {
// export as [...private, ...public] array
const privateAndPublicKeysArray = peerId.privKey.marshal();
// extract the private key
const pk = privateAndPublicKeysArray.slice(0, 32);
// serialize private key as base64
const b64 = base64.fromByteArray(pk);
return b64;
};
it('generate keypair from seed', async function () {
// arrange
const random = await KeyPair.randomEd25519();
const privateKey = random.toEd25519PrivateKey();
// act
const keyPair = await KeyPair.fromBytes(privateKey);
const privateKey2 = keyPair.toEd25519PrivateKey();
// assert
expect(privateKey).toStrictEqual(privateKey2);
});
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 base64 from 'base64-js';
import * as ed from 'noble-ed25519';
import { keys } from 'libp2p-crypto';
export class KeyPair {
/**
* @deprecated
* Key pair in libp2p format. Used for backward compatibility with the current FluencePeer implementation
*/
public Libp2pPeerId: PeerId;
/**
* Generates a new KeyPair from base64 string contatining the 32 byte Ed25519 secret key
* @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;
constructor(libp2pPeerId: PeerId) {
this.Libp2pPeerId = libp2pPeerId
}
/**
* 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
*/
static async randomEd25519(): Promise<KeyPair> {
const res = new KeyPair();
res.Libp2pPeerId = await PeerId.create({ keyType: 'Ed25519' });
return res;
const lib2p2Pid = await PeerId.create({ keyType: 'Ed25519' });
return new KeyPair(lib2p2Pid);
}
/**
* @returns 32 byte private key
*/
toEd25519PrivateKey(): Uint8Array {
return this.Libp2pPeerId.privKey.marshal().subarray(0, 32);
}
}