diff --git a/lib/models/nip19/models.dart b/lib/models/nip19/models.dart deleted file mode 100644 index dee1543..0000000 --- a/lib/models/nip19/models.dart +++ /dev/null @@ -1,71 +0,0 @@ -class ProfilePointer { - final String pubkey; - final List? relays; - - ProfilePointer({required this.pubkey, this.relays}); -} - -/// EventPointer is a class that represents a pointer to an event in the Nostr protocol. -class EventPointer { - /// The unique identifier of the event. - final String id; - - /// A list of relays to use to reach the event. - final List? relays; - - /// The author of the event. - final String? author; - - /// Constructs an EventPointer object with the given properties. - /// - /// The [id] parameter is required and represents the unique identifier of the event. - /// The [relays] parameter is optional and represents a list of relays to use to reach the event. - /// The [author] parameter is optional and represents the author of the event. - EventPointer({required this.id, this.relays, this.author}); -} - -/// A pointer to a Nostr address. -/// -/// The [AddressPointer] class is used to store information about a Nostr address -/// including an identifier, a public key, a kind, and a list of relays. -class AddressPointer { - /// The identifier for the address. - final String identifier; - - /// The public key associated with the address. - final String pubkey; - - /// The kind of the address. - final int kind; - - /// The list of relays associated with the address. - final List? relays; - - /// Creates a new [AddressPointer] instance. - /// - /// The [identifier], [pubkey], and [kind] parameters are required. - /// The [relays] parameter is optional and can be `null`. - AddressPointer({ - required this.identifier, - required this.pubkey, - required this.kind, - this.relays, - }); -} - -class SignatureVerificationException implements Exception { - final String message; - - SignatureVerificationException(this.message); - - @override - String toString() => 'SignatureVerificationException: $message'; -} - -class ChecksumVerificationException implements Exception { - final String message; - ChecksumVerificationException(this.message); - - @override - String toString() => 'ChecksumVerificationException: $message'; -} diff --git a/lib/models/nip19/nip19.dart b/lib/models/nip19/nip19.dart deleted file mode 100644 index 73624b8..0000000 --- a/lib/models/nip19/nip19.dart +++ /dev/null @@ -1,4 +0,0 @@ -// import '../nip19/models.dart'; -// import '../nip19/nip19_api.dart'; -// import '../nip19/nip19_impl.dart'; - diff --git a/lib/models/nip19/nip19_api.dart b/lib/models/nip19/nip19_api.dart deleted file mode 100644 index 1c9f285..0000000 --- a/lib/models/nip19/nip19_api.dart +++ /dev/null @@ -1,34 +0,0 @@ -/// This library provides the interface for NIP-19 encoding and decoding. -library api.nip19; - -export 'nip19_impl.dart'; - -import 'models.dart'; -import 'nip19_impl.dart'; - -/// The abstract class [Nip19] is the public API for encoding and decoding NIP-19 codes. -abstract class Nip19 { - /// Creates a [Nip19Impl] instance. - factory Nip19() => Nip19Impl(); - - /// Encodes a given hexadecimal string into a NIP-19 'nsec' string. - String nsecEncode(String hex); - - /// Encodes a given hexadecimal string into a NIP-19 'npub' string. - String npubEncode(String hex); - - /// Encodes a given hexadecimal string into a NIP-19 'note' string. - String noteEncode(String hex); - - /// Encodes a given [ProfilePointer] object into a NIP-19 'nprofile' string. - String nprofileEncode(ProfilePointer profile); - - /// Encodes a given [EventPointer] object into a NIP-19 'nevent' string. - String neventEncode(EventPointer event); - - /// Encodes a given [AddressPointer] object into a NIP-19 'naddr' string. - String naddrEncode(AddressPointer addr); - - /// Decodes a given NIP-19 code into a [Map] of type and data. - Map decode(String nip19); -} diff --git a/lib/models/nip19/nip19_impl.dart b/lib/models/nip19/nip19_impl.dart deleted file mode 100644 index a2e01c9..0000000 --- a/lib/models/nip19/nip19_impl.dart +++ /dev/null @@ -1,306 +0,0 @@ -library impl.nip19; - -import 'dart:math'; -import 'package:convert/convert.dart'; -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:bip340/bip340.dart' as bip340; - -import 'package:bech32/bech32.dart'; - -import 'models.dart'; -import 'nip19_api.dart'; - -typedef TLV = Map>; - -class Nip19Impl implements Nip19 { - static const _bech32MaxSize = 5000; - - List _convertBits(List data, int fromBits, int toBits, bool pad) { - int acc = 0; - int bits = 0; - List ret = []; - for (int value in data) { - acc = (acc << fromBits) | value; - bits += fromBits; - while (bits >= toBits) { - bits -= toBits; - ret.add((acc >> bits) & ((1 << toBits) - 1)); - } - } - if (pad) { - if (bits > 0) { - ret.add((acc << (toBits - bits)) & ((1 << toBits) - 1)); - } - } else if (bits >= fromBits || (acc & ((1 << bits) - 1)) != 0) { - throw Exception('[!] Invalid padding'); - } - return ret; - } - - String _encodeBytes(String prefix, String hex) { - final bytes = HexUtil.decode(hex); - List fiveBitWords = _convertBits(bytes, 8, 5, true); - var bech32String = const Bech32Codec().encode(Bech32(prefix, fiveBitWords)); - return bech32String; - } - - TLV _parseTLV(Uint8List data) { - TLV result = {}; - Uint8List rest = data; - while (rest.isNotEmpty) { - int t = rest[0]; - int l = rest[1]; - Uint8List v = rest.sublist(2, 2 + l); - rest = rest.sublist(2 + l); - if (v.length < l) continue; - result[t] = result[t] ?? []; - result[t]?.add(v); - } - - return result; - } - - Uint8List _concatBytes(List bytesList) { - int length = bytesList.fold(0, (sum, bytes) => sum + bytes.length); - Uint8List result = Uint8List(length); - int offset = 0; - for (Uint8List bytes in bytesList) { - result.setRange(offset, offset + bytes.length, bytes); - offset += bytes.length; - } - return result; - } - - Uint8List _encodeTLV(TLV tlv) { - List entries = []; - for (var entry in tlv.entries) { - for (var v in entry.value) { - Uint8List bytes = Uint8List(v.length + 2); - bytes.setRange(0, 1, [entry.key]); - bytes.setRange(1, 2, [v.length]); - bytes.setRange(2, v.length + 2, v); - entries.add(bytes); - } - } - - return _concatBytes(entries); - } - - @override - String nsecEncode(String hex) { - return _encodeBytes('nsec', hex); - } - - @override - String npubEncode(String hex) { - return _encodeBytes('npub', hex); - } - - @override - String noteEncode(String hex) { - return _encodeBytes('note', hex); - } - - @override - String naddrEncode(AddressPointer addr) { - Uint8List kind = Uint8List(4) - ..buffer.asByteData().setUint32(0, addr.kind, Endian.big); - - var identifier = utf8.encode(addr.identifier); - List relays = (addr.relays ?? []) - .map((url) => utf8.encode(url)) - .toList() - .cast(); - Uint8List pubkeyBytes = Uint8List.fromList(HexUtil.decode(addr.pubkey)); - - TLV tlv = { - 0: [identifier].cast(), - 1: relays, - 2: [pubkeyBytes].cast(), - 3: [kind].cast(), - }; - - Uint8List data = _encodeTLV(tlv); - List fiveBitWords = _convertBits(data, 8, 5, true); - var bech32String = const Bech32Codec() - .encode(Bech32('naddr', fiveBitWords), _bech32MaxSize); - return bech32String; - } - - @override - String neventEncode(EventPointer event) { - Uint8List id = Uint8List.fromList(HexUtil.decode(event.id)); - List relayUrls = - (event.relays ?? []).map(utf8.encode).toList().cast(); - List author = event.author != null - ? Uint8List.fromList(HexUtil.decode(event.author!)) - .toList() - .cast() - : []; - - TLV tlv = { - 0: [id], - 1: relayUrls, - 2: author, - }; - - Uint8List data = _encodeTLV(tlv); - - List fiveBitWords = _convertBits(data, 8, 5, true); - - var bech32String = const Bech32Codec() - .encode(Bech32('nevent', fiveBitWords), _bech32MaxSize); - return bech32String; - } - - @override - String nprofileEncode(ProfilePointer profile) { - Uint8List pubkeyBytes = Uint8List.fromList(HexUtil.decode(profile.pubkey)); - - List relayUrls = - (profile.relays ?? []).map(utf8.encode).toList().cast(); - - TLV tlv = { - 0: [pubkeyBytes], - 1: relayUrls, - }; - - Uint8List data = _encodeTLV(tlv); - - List fiveBitWords = _convertBits(data, 8, 5, true); - - var bech32String = const Bech32Codec() - .encode(Bech32('nprofile', fiveBitWords), _bech32MaxSize); - return bech32String; - } - - @override - Map decode(String nip19) { - Bech32 bech32; - try { - bech32 = const Bech32Codec().decode(nip19, _bech32MaxSize); - } catch (e) { - throw ChecksumVerificationException('Checksum verification failed'); - } - - List data = _convertBits(bech32.data, 5, 8, false); - final prefix = bech32.hrp; - - switch (prefix) { - case 'nprofile': - TLV tlv = _parseTLV(Uint8List.fromList(data)); - if (tlv[0]?.isEmpty ?? true) { - throw Exception('missing TLV 0 for nprofile'); - } - if (tlv[0]?.isNotEmpty ?? false) { - if (tlv[0]![0].length != 32) { - throw Exception('TLV 0 should be 32 bytes'); - } - } else { - throw Exception('missing TLV 0 for nprofile'); - } - return { - 'type': 'nprofile', - 'data': { - 'pubkey': HexUtil.encode(tlv[0]![0]), - 'relays': tlv[1]?.map((d) => utf8.decode(d)).toList() ?? [], - }, - }; - - case 'nevent': - TLV tlv = _parseTLV(Uint8List.fromList(data)); - if (tlv[0] == null) { - throw Exception('missing TLV 0 for nevent'); - } - if (tlv[0]![0].length != 32) { - throw Exception('TLV 0 should be 32 bytes'); - } - if (tlv[2] != null && tlv[2]![0].length != 32) { - throw Exception('TLV 2 should be 32 bytes'); - } - - return { - 'type': 'nevent', - 'data': { - 'id': HexUtil.encode(tlv[0]![0]), - 'relays': tlv[1] != null - ? tlv[1]!.map((e) => utf8.decode(e)).toList() - : [], - 'author': tlv[2] != null ? HexUtil.encode(tlv[2]![0]) : null, - }, - }; - - case 'naddr': - TLV tlv = _parseTLV(Uint8List.fromList(data)); - if (tlv[0] == null) { - throw Exception('missing TLV 0 for naddr'); - } - if (tlv[0] == null) { - throw Exception('missing TLV 0 for naddr'); - } - if (tlv[0] == null) { - throw Exception('missing TLV 0 for naddr'); - } - if (tlv[3] == null) { - throw Exception('missing TLV 3 for naddr'); - } - if (tlv[3]![0].length != 4) { - throw Exception('TLV 3 should be 4 bytes'); - } - return { - 'type': 'naddr', - 'data': { - 'identifier': utf8.decode(tlv[0]![0]), - 'pubkey': HexUtil.encode(tlv[2]![0]), - 'kind': int.parse(HexUtil.encode(tlv[3]![0]), radix: 16), - 'relays': tlv[1] != null - ? tlv[1]!.map((d) => utf8.decode(d)).toList() - : [], - } - }; - - case 'nsec': - case 'npub': - case 'note': - return {'type': prefix, 'data': HexUtil.encode(data)}; - default: - throw Exception('unknown prefix $prefix'); - } - } -} - -class Bip340Util { - static String getPublicKey( - String privateKey, - ) => - bip340.getPublicKey( - privateKey, - ); - - static String sign(String privateKey, String id, String aux) => bip340.sign( - privateKey, - id, - aux, - ); - - static bool verify(String publicKey, String id, String signature) => - bip340.verify(publicKey, id, signature); -} - -class HexUtil { - static String encode(List bytes) { - return hex.encode(bytes); - } - - static List decode(String str) { - return hex.decode(str); - } - - static String generate64RandomHexChars() { - final random = Random.secure(); - final randomBytes = List.generate(32, (i) => random.nextInt(256)); - return encode(randomBytes); - } -} diff --git a/lib/widgets/main_screen/main_screen_widget.dart b/lib/widgets/main_screen/main_screen_widget.dart index 784b128..6aae8f9 100644 --- a/lib/widgets/main_screen/main_screen_widget.dart +++ b/lib/widgets/main_screen/main_screen_widget.dart @@ -30,7 +30,7 @@ class _MainScreenWidgetState extends State { body: IndexedStack( index: _selectedTap, children: [ - HomeScreen(), + // HomeScreen(), MessageScreen(), ProfileScreen(), ], diff --git a/lib/widgets/profile_screen/profile_screen.dart b/lib/widgets/profile_screen/profile_screen.dart index b7bb181..9b5fbc5 100644 --- a/lib/widgets/profile_screen/profile_screen.dart +++ b/lib/widgets/profile_screen/profile_screen.dart @@ -1,4 +1,3 @@ -import 'package:drifter/models/nip19/nip19_api.dart'; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:drifter/models/keys.dart'; @@ -26,7 +25,7 @@ class ProfileScreenState extends State { TextEditingController publicKeyInput = TextEditingController(); // final keyGenerator = KeyApi(); - final nip19 = Nip19(); + // final nip19 = Nip19(); @override void initState() { @@ -37,18 +36,21 @@ class ProfileScreenState extends State { Future generateNewKeys() async { final newPrivateKey = await Nostr.instance.keysService.generatePrivateKey(); // final newPrivateKey = keyGenerator.generatePrivateKey(); - final nsec = nip19.nsecEncode(newPrivateKey); - final nsecDecoded = nip19.decode(nsec); - assert(nsecDecoded['type'] == 'nsec'); - assert(nsecDecoded['data'] == newPrivateKey); + final nsec = + Nostr.instance.keysService.encodePrivateKeyToNsec(newPrivateKey); + final nsecDecoded = + Nostr.instance.keysService.decodeNsecKeyToPrivateKey(nsec); + // assert(nsecDecoded['type'] == 'nsec'); + // assert(nsecDecoded['data'] == newPrivateKey); final newPublicKey = await Nostr.instance.keysService .derivePublicKey(privateKey: newPrivateKey); // final newPublicKey = keyGenerator.getPublicKey(newPrivateKey); - final npub = nip19.npubEncode(newPublicKey); - final npubDecoded = nip19.decode(npub); - assert(npubDecoded['type'] == 'npub'); - assert(npubDecoded['data'] == newPublicKey); + final npub = Nostr.instance.keysService.encodePublicKeyToNpub(newPublicKey); + final npubDecoded = + Nostr.instance.keysService.decodeNpubKeyToPublicKey(npub); + // assert(npubDecoded['type'] == 'npub'); + // assert(npubDecoded['data'] == newPublicKey); return await _addKeyToStorage(newPrivateKey, newPublicKey, nsec, npub); } @@ -149,8 +151,10 @@ class ProfileScreenState extends State { AppColors.mainDarkBlue)), onPressed: () { keysExistDialog( - nip19.npubEncode(Keys.publicKey), - nip19.nsecEncode(Keys.privateKey), + Nostr.instance.keysService + .encodePublicKeyToNpub(Keys.publicKey), + Nostr.instance.keysService + .encodePrivateKeyToNsec(Keys.privateKey), ); }, child: Text(