Skip to content

Commit

Permalink
Added key management system.
Browse files Browse the repository at this point in the history
  • Loading branch information
5eeman committed Jul 29, 2024
1 parent dfefbbc commit 2ebae42
Show file tree
Hide file tree
Showing 29 changed files with 697 additions and 47 deletions.
4 changes: 4 additions & 0 deletions lib/common/kms/index.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export 'kms.dart';
export 'key-providers/index.dart';
export 'store/index.dart';
export 'provider-helpers.dart';
104 changes: 104 additions & 0 deletions lib/common/kms/key-providers/bjj-provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import "dart:typed_data";

import "package:encrypt/encrypt.dart";
import "package:polygonid_flutter_sdk/common/kms/index.dart";
import "package:polygonid_flutter_sdk/common/utils/big_int_extension.dart";
import "package:polygonid_flutter_sdk/common/utils/uint8_list_utils.dart";
import "package:web3dart/crypto.dart";

import "../../../identity/libs/bjj/eddsa_babyjub.dart";

/// Provider for Baby Jub Jub keys
/// @public
/// @class BjjProvider
/// @implements implements IKeyProvider interface
class BjjProvider implements IKeyProvider {
/// key type that is handled by BJJ Provider
/// @type {KmsKeyType}
KmsKeyType get keyType => KmsKeyType.BabyJubJub;

AbstractPrivateKeyStore keyStore;

/// Creates an instance of BjjProvider.
/// @param {KmsKeyType} keyType - kms key type
/// @param {AbstractPrivateKeyStore} keyStore - key store for kms
BjjProvider(KmsKeyType keyType, this.keyStore) {
if (keyType != KmsKeyType.BabyJubJub) {
throw Exception('Key type must be BabyJubJub');
}
}

/// generates a baby jub jub key from a seed phrase
/// @param {Uint8List} seed - byte array seed
/// @returns kms key identifier
@override
Future<KmsKeyId> newPrivateKeyFromSeed(Uint8List seed) async {
final privateKey = PrivateKey(seed);

final publicKey = privateKey.public();

final kmsId = KmsKeyId(
type: keyType,
id: keyPath(keyType, publicKey.hex()),
);
await keyStore.importKey(alias: kmsId.id, key: privateKey.hex());

return kmsId;
}

/// Gets public key by kmsKeyId
///
/// @param {KmsKeyId} keyId - key identifier
@override
Future<String> publicKey(KmsKeyId keyId) async {
final privateKey = await _privateKey(keyId);
return privateKey.public().hex();
}

/// signs prepared payload of size,
/// with a key id
///
/// @param {KmsKeyId} keyId - key identifier
/// @param {Uint8List} data - data to sign (32 bytes)
/// @returns Uint8List signature
@override
Future<Uint8List> sign(
KmsKeyId keyId,
Uint8List data, [
Map<String, dynamic>? opts,
]) async {
if (data.length != 32) {
throw Exception('data to sign is too large');
}

final i = bytesToUnsignedInt(data);
if (!i.checkBigIntInField()) {
throw Exception('data to sign is too large');
}
final privateKey = await _privateKey(keyId);

final signature = privateKey.sign(i);

return Uint8ArrayUtils.uint8ListfromString(signature);
}

@override
Future<bool> verify(
Uint8List message, String signatureHex, KmsKeyId keyId) async {
final publicKey = await this.publicKey(keyId);

final bytes = hexToBytes(publicKey);
final pbkey = PublicKey.newFromCompressed(bytesToUnsignedInt(bytes));

return pbkey.verify(
Uint8ArrayUtils.uint8ListToString(message),
Signature.newFromCompressed(hexToBytes(signatureHex)),
);
}

Future<PrivateKey> _privateKey(KmsKeyId keyId) async {
final privateKeyHex = await keyStore.get(alias: keyId.id);

return PrivateKey(decodeHexString(privateKeyHex));
}
}
97 changes: 97 additions & 0 deletions lib/common/kms/key-providers/ed25519-provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import 'dart:typed_data';

import 'package:polygonid_flutter_sdk/common/kms/kms.dart';
import 'package:polygonid_flutter_sdk/common/kms/provider-helpers.dart';
import 'package:polygonid_flutter_sdk/common/kms/store/abstract-key-store.dart';
import 'package:polygonid_flutter_sdk/common/kms/store/types.dart';
import 'package:polygonid_flutter_sdk/common/utils/uint8_list_utils.dart';
import 'package:web3dart/crypto.dart';
import 'package:ed25519_edwards/ed25519_edwards.dart' as ed25519;

/// Provider for Ed25519 keys
/// @public
/// @class Ed25519Provider
/// @implements IKeyProvider interface
class Ed25519Provider implements IKeyProvider {
final KmsKeyType keyType;
final AbstractPrivateKeyStore _keyStore;

/// Creates an instance of Ed25519Provider.
/// @param {KmsKeyType} keyType - kms key type
/// @param {AbstractPrivateKeyStore} keyStore - key store for kms
Ed25519Provider(this.keyType, this._keyStore);

/// generates a ed25519 key from a seed phrase
/// @param {Uint8List} seed - byte array seed
/// @returns {Future<KmsKeyId>} kms key identifier
@override
Future<KmsKeyId> newPrivateKeyFromSeed(Uint8List seed) async {
if (seed.length != 32) {
throw Exception('Seed should be 32 bytes');
}
final publicKey = ed25519.PublicKey(seed);
final kmsId = KmsKeyId(
type: keyType,
id: keyPath(keyType, bytesToHex(publicKey.bytes)),
);

await _keyStore.importKey(
alias: kmsId.id,
key: bytesToHex(seed),
);

return kmsId;
}

/// Gets public key by kmsKeyId
/// @param {KmsKeyId} keyId - key identifier
/// @returns {Future<String>} Public key as a hex String
@override
Future<String> publicKey(KmsKeyId keyId) async {
final privateKeyHex = await _privateKey(keyId);
final privateKey = ed25519.newKeyFromSeed(hexToBytes(privateKeyHex));
final publicKey = ed25519.public(privateKey);
return bytesToHex(publicKey.bytes);
}

/// signs prepared payload of size,
/// with a key id
/// @param {KmsKeyId} keyId - key identifier
/// @param {Uint8List} digest - data to sign (32 bytes)
/// @returns {Future<Uint8List>} signature
@override
Future<Uint8List> sign(
KmsKeyId keyId,
Uint8List digest, [
Map<String, dynamic>? opts,
]) async {
final privateKeyHex = await _privateKey(keyId);
final privateKey = ed25519.newKeyFromSeed(hexToBytes(privateKeyHex));

return ed25519.sign(privateKey, digest);
}

/// Verifies a signature for the given message and key identifier.
/// @param digest - The message to verify the signature against.
/// @param signatureHex - The signature to verify, as a hexadecimal String.
/// @param keyId - The key identifier to use for verification.
/// @returns A Future that resolves to a boolean indicating whether the signature is valid.
Future<bool> verify(
Uint8List digest, String signatureHex, KmsKeyId keyId) async {
final publicKeyHex = await this.publicKey(keyId);
final publicKey = ed25519.PublicKey(hexToBytes(publicKeyHex));

return ed25519.verify(
publicKey,
digest,
hexToBytes(signatureHex),
);
}

/// Retrieves the private key for a given keyId from the key store.
/// @param {KmsKeyId} keyId - The identifier of the key to retrieve.
/// @returns {Future<String>} The private key associated with the keyId.
Future<String> _privateKey(KmsKeyId keyId) async {
return _keyStore.get(alias: keyId.id);
}
}
3 changes: 3 additions & 0 deletions lib/common/kms/key-providers/index.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'bjj-provider.dart';
export 'ed25519-provider.dart';
export 'secp256k1-provider.dart';
126 changes: 126 additions & 0 deletions lib/common/kms/key-providers/secp256k1-provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import 'dart:typed_data';

import 'package:crypto/crypto.dart';
import 'package:polygonid_flutter_sdk/common/kms/kms.dart';
import 'package:polygonid_flutter_sdk/common/kms/provider-helpers.dart';
import 'package:polygonid_flutter_sdk/common/kms/store/abstract-key-store.dart';
import 'package:polygonid_flutter_sdk/common/kms/store/types.dart';
import 'package:web3dart/crypto.dart';
import 'package:secp256k1/secp256k1.dart' as secp256k1;

/// Provider for Secp256k1
/// @public
/// @class Secp256k1Provider
/// @implements implements IKeyProvider interface
class Sec256k1Provider implements IKeyProvider {
/// key type that is handled by BJJ Provider
/// @type {KmsKeyType}
final KmsKeyType keyType;
final AbstractPrivateKeyStore _keyStore;

/// Creates an instance of Sec256k1Provider.
/// @param {KmsKeyType} keyType - kms key type
/// @param {AbstractPrivateKeyStore} keyStore - key store for kms
Sec256k1Provider(this.keyType, this._keyStore) {
if (keyType != KmsKeyType.Secp256k1) {
throw Exception('Key type must be Secp256k1');
}
}

/// generates a baby jub jub key from a seed phrase
/// @param {Uint8List} seed - byte array seed
/// @returns kms key identifier
@override
Future<KmsKeyId> newPrivateKeyFromSeed(Uint8List seed) async {
if (seed.length != 32) {
throw Exception('Seed should be 32 bytes');
}
final privateKey = secp256k1.PrivateKey.fromHex(bytesToHex(seed));
final publicKey = privateKey.publicKey;

final kmsId = KmsKeyId(
type: keyType,
id: keyPath(keyType, publicKey.toHex()),
);

await _keyStore.importKey(
alias: kmsId.id,
key: bytesToHex(seed).padLeft(64, '0'),
);

return kmsId;
}

/// Gets public key by kmsKeyId
///
/// @param {KmsKeyId} keyId - key identifier
@override
Future<String> publicKey(KmsKeyId keyId) async {
final privateKeyHex = await _privateKey(keyId);
final privateKey = secp256k1.PrivateKey.fromHex(privateKeyHex);
final publicKey = privateKey.publicKey;
return publicKey.toHex(); // 04 + x + y (uncompressed key)
}

/// Signs the given data using the private key associated with the specified key identifier.
/// @param keyId - The key identifier to use for signing.
/// @param data - The data to sign.
/// @param opts - Signing options, such as the algorithm to use.
/// @returns A Future that resolves to the signature as a Uint8List.
@override
Future<Uint8List> sign(
KmsKeyId keyId,
Uint8List data, [
Map<String, dynamic>? opts,
]) async {
final privateKeyHex = await _privateKey(keyId);
final privateKey = secp256k1.PrivateKey.fromHex(privateKeyHex);

final signature = privateKey.signature(bytesToHex(data));

final signatureBytes = Uint8List(64);
final rBytes = hexToBytes(signature.R.toRadixString(16).padLeft(32, '0'));
signatureBytes.setRange(0, 32, rBytes);
final sBytes = hexToBytes(signature.S.toRadixString(16).padLeft(32, '0'));
signatureBytes.setRange(32, 64, sBytes);

// final signatureBase64 = await ES256KSigner(
// hexToBytes(privateKeyHex), opts?["alg"] == 'ES256K-R')(data);
//
// if (signatureBase64.runtimeType != String) {
// throw Exception('signatureBase64 must be a String');
// }

return signatureBytes;
}

/// Verifies a signature for the given message and key identifier.
/// @param message - The message to verify the signature against.
/// @param signatureHex - The signature to verify, as a hexadecimal String.
/// @param keyId - The key identifier to use for verification.
/// @returns A Future that resolves to a boolean indicating whether the signature is valid.
@override
Future<bool> verify(
Uint8List message,
String signatureHex,
KmsKeyId keyId,
) async {
final publicKeyHex = await this.publicKey(keyId);

final publicKey = secp256k1.PublicKey.fromHex(publicKeyHex);

final signature = secp256k1.Signature.fromHexes(
signatureHex.substring(0, signatureHex.length ~/ 2),
signatureHex.substring(signatureHex.length ~/ 2, signatureHex.length),
);

return signature.verify(
publicKey,
bytesToHex(message),
);
}

Future<String> _privateKey(KmsKeyId keyId) async {
return _keyStore.get(alias: keyId.id);
}
}
Loading

0 comments on commit 2ebae42

Please sign in to comment.