Skip to content

Commit

Permalink
feat: Add did:key P256 in menu #1980
Browse files Browse the repository at this point in the history
  • Loading branch information
bibash28 committed Oct 9, 2023
1 parent c074390 commit 48084c3
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 7 deletions.
1 change: 1 addition & 0 deletions lib/app/shared/constants/secure_storage_keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class SecureStorageKeys {
static const String ssiKey = 'ssi/key';

static const String p256PrivateKey = 'p256PrivateKey';
static const String p256PrivateKey2 = 'p256PrivateKey2';

static const String cryptoAccount = 'cryptoAccount';
static const String cryptoAccounTrackingIndex = 'cryptoAccounTrackingIndex';
Expand Down
30 changes: 28 additions & 2 deletions lib/app/shared/helper_functions/helper_functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ Future<String> web3RpcMainnetInfuraURL() async {
return '$prefixUrl$infuraApiKey';
}

Future<String> getRandomP256PrivateKey(
Future<String> getEBSIV3P256PrivateKey(
SecureStorageProvider secureStorage,
) async {
final String? p256PrivateKey = await secureStorage.get(
Expand Down Expand Up @@ -273,6 +273,32 @@ Future<String> getRandomP256PrivateKey(
}
}

Future<String> getP256PrivateKey(SecureStorageProvider secureStorage) async {
final String? p256PrivateKey = await secureStorage.get(
SecureStorageKeys.p256PrivateKey2,
);

if (p256PrivateKey == null) {
final jwk = JsonWebKey.generate('ES256');

final json = jwk.toJson();

// Sort the keys in ascending order and remove alg
final sortedJwk = Map.fromEntries(
json.entries.toList()..sort((e1, e2) => e1.key.compareTo(e2.key)),
)..remove('keyOperations');

await secureStorage.set(
SecureStorageKeys.p256PrivateKey2,
jsonEncode(sortedJwk),
);

return jsonEncode(sortedJwk);
} else {
return p256PrivateKey;
}
}

Future<String> fetchPrivateKey({
required OIDC4VC oidc4vc,
required SecureStorageProvider secureStorage,
Expand All @@ -283,7 +309,7 @@ Future<String> fetchPrivateKey({
final index = getIndexValue(isEBSIV3: true);

if (isEBSIV3) {
privateKey = await getRandomP256PrivateKey(getSecureStorage);
privateKey = await getEBSIV3P256PrivateKey(getSecureStorage);
} else {
final OIDC4VC oidc4vc = OIDC4VC();
final mnemonic = await getSecureStorage.get(SecureStorageKeys.ssiMnemonic);
Expand Down
2 changes: 2 additions & 0 deletions lib/dashboard/drawer/ssi/manage_did/manage_did.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export 'view/did_ebsi_v3/did_ebsi_v3_private_key_page.dart';
export 'view/did_ebsi_v3/manage_did_ebsi_v3_page.dart';
export 'view/did_edDSA/did_ed_dsa_private_key_page.dart';
export 'view/did_edDSA/manage_did_ed_dsa_page.dart';
export 'view/did_p256/did_p256_private_key_page.dart';
export 'view/did_p256/manage_did_p256_page.dart';
export 'view/did_polygon_id/manage_did_polygon_id_page.dart';
export 'view/did_secp256k1/did_secp256k1_private_key_page.dart';
export 'view/did_secp256k1/manage_did_secp256k1_page.dart';
Expand Down
6 changes: 6 additions & 0 deletions lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ class DidView extends StatelessWidget {
.push<void>(ManageDidEbsiV3Page.route());
},
),
DrawerItem(
title: l10n.keyDecentralizedIDP256,
onTap: () {
Navigator.of(context).push<void>(ManageDidP256Page.route());
},
),
DrawerItem(
title: l10n.polygonDecentralizedID,
onTap: () async {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import 'package:altme/app/app.dart';
import 'package:altme/l10n/l10n.dart';
import 'package:altme/theme/theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:oidc4vc/oidc4vc.dart';
import 'package:secure_storage/secure_storage.dart';

class DidP256PrivateKeyPage extends StatefulWidget {
const DidP256PrivateKeyPage({super.key});

static Route<dynamic> route() {
return MaterialPageRoute<void>(
builder: (_) => const DidP256PrivateKeyPage(),
settings: const RouteSettings(name: '/DidP256PrivateKeyPage'),
);
}

@override
State<DidP256PrivateKeyPage> createState() => _DidP256PrivateKeyPageState();
}

class _DidP256PrivateKeyPageState extends State<DidP256PrivateKeyPage>
with SingleTickerProviderStateMixin {
late Animation<double> animation;
late AnimationController animationController;

Future<String> getKey() async {
final privateKey = await getP256PrivateKey(getSecureStorage);
return privateKey;
}

@override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 20),
);

final Tween<double> rotationTween = Tween(begin: 20, end: 0);

animation = rotationTween.animate(animationController)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
Navigator.pop(context);
}
});
animationController.forward();
}

@override
void dispose() {
animationController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return BasePage(
scrollView: false,
title: l10n.decentralizedIDKey,
titleAlignment: Alignment.topCenter,
titleLeading: const BackLeadingButton(),
secureScreen: true,
body: BackgroundCard(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
l10n.didPrivateKey,
style: Theme.of(context).textTheme.title,
),
const SizedBox(
height: Sizes.spaceNormal,
),
FutureBuilder<String>(
future: getKey(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
return Column(
children: [
Text(
snapshot.data!,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: Sizes.spaceXLarge),
CopyButton(
onTap: () async {
await Clipboard.setData(
ClipboardData(text: snapshot.data!),
);
AlertMessage.showStateMessage(
context: context,
stateMessage: StateMessage.success(
stringMessage: l10n.copiedToClipboard,
),
);
},
),
],
);
case ConnectionState.waiting:
case ConnectionState.none:
case ConnectionState.active:
return const Text('');
}
},
),
Expanded(
child: Center(
child: AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget? child) {
return Text(
timeFormatter(timeInSecond: animation.value.toInt()),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.displayMedium,
);
},
),
),
),
],
),
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'package:altme/app/app.dart';
import 'package:altme/dashboard/dashboard.dart';
import 'package:altme/l10n/l10n.dart';
import 'package:did_kit/did_kit.dart';
import 'package:flutter/material.dart';
import 'package:oidc4vc/oidc4vc.dart';
import 'package:secure_storage/secure_storage.dart';

class ManageDidP256Page extends StatefulWidget {
const ManageDidP256Page({super.key});

static Route<dynamic> route() {
return MaterialPageRoute<void>(
builder: (_) => const ManageDidP256Page(),
settings: const RouteSettings(name: '/ManageDidP256Page'),
);
}

@override
State<ManageDidP256Page> createState() => _ManageDidP256PageState();
}

class _ManageDidP256PageState extends State<ManageDidP256Page> {
Future<String> getDid() async {
final privateKey = await getP256PrivateKey(getSecureStorage);

final (did, _) = await getDidAndKid(
isEBSIV3: false,
privateKey: privateKey,
didKitProvider: DIDKitProvider(),
);

return did;
}

@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return BasePage(
title: l10n.keyDecentralizedIDP256,
titleAlignment: Alignment.topCenter,
scrollView: false,
titleLeading: const BackLeadingButton(),
body: BackgroundCard(
height: double.infinity,
width: double.infinity,
padding: const EdgeInsets.all(Sizes.spaceSmall),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
FutureBuilder<String>(
future: getDid(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
final did = snapshot.data!;
return Did(did: did);
case ConnectionState.waiting:
case ConnectionState.none:
case ConnectionState.active:
return const SizedBox();
}
},
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: Sizes.spaceNormal),
child: Divider(),
),
DidPrivateKey(route: DidP256PrivateKeyPage.route()),
],
),
),
);
}
}
3 changes: 2 additions & 1 deletion lib/l10n/arb/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,7 @@
"thisCredentialFormatIsNotSupported": "This credential format is not supported",
"moreDetails": "More Details",
"theCredentialOfferIsInvalid": "The credential offer is invalid",
"dateOfRequest": "Date of Request"
"dateOfRequest": "Date of Request",
"keyDecentralizedIDP256": "Key Decentralized ID P-256"
}

12 changes: 8 additions & 4 deletions lib/l10n/untranslated.json
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,8 @@
"thisCredentialFormatIsNotSupported",
"moreDetails",
"theCredentialOfferIsInvalid",
"dateOfRequest"
"dateOfRequest",
"keyDecentralizedIDP256"
],

"es": [
Expand Down Expand Up @@ -1774,7 +1775,8 @@
"thisCredentialFormatIsNotSupported",
"moreDetails",
"theCredentialOfferIsInvalid",
"dateOfRequest"
"dateOfRequest",
"keyDecentralizedIDP256"
],

"fr": [
Expand Down Expand Up @@ -1966,7 +1968,8 @@
"thisCredentialFormatIsNotSupported",
"moreDetails",
"theCredentialOfferIsInvalid",
"dateOfRequest"
"dateOfRequest",
"keyDecentralizedIDP256"
],

"it": [
Expand Down Expand Up @@ -2855,6 +2858,7 @@
"thisCredentialFormatIsNotSupported",
"moreDetails",
"theCredentialOfferIsInvalid",
"dateOfRequest"
"dateOfRequest",
"keyDecentralizedIDP256"
]
}

0 comments on commit 48084c3

Please sign in to comment.