bsv-bap 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -0
- package/dist/MasterID.d.ts +9 -1
- package/dist/MemberID.d.ts +12 -1
- package/dist/api.d.ts +6 -6
- package/dist/apiTypes.d.ts +2 -2
- package/dist/index.d.ts +27 -1
- package/dist/index.modern.js +5 -5
- package/dist/index.modern.js.map +9 -9
- package/dist/index.module.js +5 -5
- package/dist/index.module.js.map +9 -9
- package/dist/poa.d.ts +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
@@ -31,6 +31,7 @@ NOTE: All examples in this document use fake identity keys, addresses and signat
|
|
31
31
|
- [Revoking an attestation](#revoking-an-attestation)
|
32
32
|
- [BAP on the BSV Metanet - PROVISIONAL](#bap-on-the-bsv-metanet---provisional)
|
33
33
|
- [BAP w3c DID - PROVISIONAL](#bap-w3c-did---provisional)
|
34
|
+
- [Bitcoin Backup Compatible Export Methods](#bitcoin-backup-compatible-export-methods)
|
34
35
|
- [Extending the protocol](#extending-the-protocol)
|
35
36
|
|
36
37
|
# TODO
|
@@ -46,6 +47,15 @@ The design goals:
|
|
46
47
|
3. Allow for rotation of signing keys without having to change the existing attestations
|
47
48
|
4. Allow for creation of an infinite amount of identities, but still allow for proving of attested attributes between the identities
|
48
49
|
|
50
|
+
## Library Features
|
51
|
+
|
52
|
+
This JavaScript/TypeScript implementation of BAP includes:
|
53
|
+
- Full protocol implementation for creating and managing identities
|
54
|
+
- Hierarchical Deterministic (HD) key derivation
|
55
|
+
- ECIES encryption/decryption for secure data transmission
|
56
|
+
- Bitcoin-backup compatible export methods for secure identity storage
|
57
|
+
- Comprehensive API for attestations, delegations, and identity management
|
58
|
+
|
49
59
|
# Protocol
|
50
60
|
|
51
61
|
The protocol is defined using the [Bitcom](https://bitcom.bitdb.network/) convention. The signing is done using the [AUTHOR IDENTITY Protocol](https://github.com/BitcoinFiles/AUTHOR_IDENTITY_PROTOCOL).
|
@@ -814,6 +824,136 @@ When keys are rotated to a new signing key the new key can be added to the DID a
|
|
814
824
|
}
|
815
825
|
```
|
816
826
|
|
827
|
+
# Bitcoin Backup Compatible Export Methods
|
828
|
+
|
829
|
+
The BAP library provides export methods that are compatible with the [bitcoin-backup](https://github.com/yourusername/bitcoin-backup) package format. These methods allow you to export master and member identities in a format that can be directly used with bitcoin-backup for secure storage.
|
830
|
+
|
831
|
+
## Exporting Master Identity for Backup
|
832
|
+
|
833
|
+
To export a master identity (BAP instance) in bitcoin-backup compatible format:
|
834
|
+
|
835
|
+
```javascript
|
836
|
+
const bap = new BAP('xprv...');
|
837
|
+
|
838
|
+
// Export for backup with optional parameters
|
839
|
+
const masterBackup = bap.exportForBackup(
|
840
|
+
'My Identity', // optional label
|
841
|
+
undefined, // optional xprv override
|
842
|
+
'word list...' // optional mnemonic
|
843
|
+
);
|
844
|
+
|
845
|
+
console.log(masterBackup);
|
846
|
+
// {
|
847
|
+
// ids: '...encrypted string...',
|
848
|
+
// xprv: 'xprv...',
|
849
|
+
// mnemonic: 'word list...',
|
850
|
+
// label: 'My Identity',
|
851
|
+
// createdAt: '2024-01-20T10:30:00.000Z'
|
852
|
+
// }
|
853
|
+
```
|
854
|
+
|
855
|
+
The exported object contains:
|
856
|
+
- `ids`: Encrypted string containing all identity information
|
857
|
+
- `xprv`: The HD private key (extended private key)
|
858
|
+
- `mnemonic`: BIP39 mnemonic phrase (if provided)
|
859
|
+
- `label`: Optional descriptive label
|
860
|
+
- `createdAt`: ISO timestamp of when the export was created
|
861
|
+
|
862
|
+
## Exporting Member Identity for Backup
|
863
|
+
|
864
|
+
To export a member identity in bitcoin-backup compatible format:
|
865
|
+
|
866
|
+
```javascript
|
867
|
+
// Export member from BAP instance
|
868
|
+
const memberBackup = bap.exportMemberForBackup(
|
869
|
+
'alice@example.com' // optional label
|
870
|
+
);
|
871
|
+
|
872
|
+
console.log(memberBackup);
|
873
|
+
// {
|
874
|
+
// wif: 'L1...',
|
875
|
+
// id: '...encrypted data...',
|
876
|
+
// label: 'alice@example.com',
|
877
|
+
// createdAt: '2024-01-20T10:30:00.000Z'
|
878
|
+
// }
|
879
|
+
|
880
|
+
// Or export directly from MemberID instance
|
881
|
+
const memberID = new MemberID('L1...');
|
882
|
+
const memberBackup = memberID.exportForBackup('Member Label');
|
883
|
+
```
|
884
|
+
|
885
|
+
The exported object contains:
|
886
|
+
- `wif`: Wallet Import Format private key
|
887
|
+
- `id`: Encrypted member identity data
|
888
|
+
- `label`: Optional descriptive label
|
889
|
+
- `createdAt`: ISO timestamp of when the export was created
|
890
|
+
|
891
|
+
## Integration with bitcoin-backup
|
892
|
+
|
893
|
+
These export methods are designed to work seamlessly with the bitcoin-backup package:
|
894
|
+
|
895
|
+
```javascript
|
896
|
+
import { BAP } from 'bsv-bap';
|
897
|
+
import { BackupService } from 'bitcoin-backup';
|
898
|
+
|
899
|
+
// Initialize services
|
900
|
+
const bap = new BAP('xprv...');
|
901
|
+
const backupService = new BackupService();
|
902
|
+
|
903
|
+
// Export and encrypt master backup
|
904
|
+
const masterBackup = bap.exportForBackup('Primary Identity');
|
905
|
+
const encryptedMaster = await backupService.encryptBapMaster(
|
906
|
+
masterBackup,
|
907
|
+
'password123'
|
908
|
+
);
|
909
|
+
|
910
|
+
// Export and encrypt member backup
|
911
|
+
const memberBackup = bap.exportMemberForBackup('Work Identity');
|
912
|
+
const encryptedMember = await backupService.encryptBapMember(
|
913
|
+
memberBackup,
|
914
|
+
'password123'
|
915
|
+
);
|
916
|
+
|
917
|
+
// Store encrypted backups securely
|
918
|
+
await storage.save('master-backup.json', encryptedMaster);
|
919
|
+
await storage.save('member-backup.json', encryptedMember);
|
920
|
+
```
|
921
|
+
|
922
|
+
## Restoring from Backup
|
923
|
+
|
924
|
+
To restore identities from bitcoin-backup format:
|
925
|
+
|
926
|
+
```javascript
|
927
|
+
import { BAP, MemberID } from 'bsv-bap';
|
928
|
+
import { BackupService } from 'bitcoin-backup';
|
929
|
+
|
930
|
+
const backupService = new BackupService();
|
931
|
+
|
932
|
+
// Restore master identity
|
933
|
+
const encryptedMaster = await storage.load('master-backup.json');
|
934
|
+
const masterData = await backupService.decryptBapMaster(
|
935
|
+
encryptedMaster,
|
936
|
+
'password123'
|
937
|
+
);
|
938
|
+
const bap = BAP.import(masterData.xprv, masterData.ids);
|
939
|
+
|
940
|
+
// Restore member identity
|
941
|
+
const encryptedMember = await storage.load('member-backup.json');
|
942
|
+
const memberData = await backupService.decryptBapMember(
|
943
|
+
encryptedMember,
|
944
|
+
'password123'
|
945
|
+
);
|
946
|
+
const memberID = new MemberID(memberData.wif);
|
947
|
+
```
|
948
|
+
|
949
|
+
## Security Considerations
|
950
|
+
|
951
|
+
- Always encrypt backups before storing them
|
952
|
+
- Use strong passwords for encryption
|
953
|
+
- Store encrypted backups in secure locations
|
954
|
+
- Never share unencrypted private keys or mnemonics
|
955
|
+
- Consider using hardware security modules for production systems
|
956
|
+
|
817
957
|
# Extending the protocol
|
818
958
|
|
819
959
|
The protocol could be extended for other use cases, by introducing new keywords (next to ATTEST, REVOKE, ID AND ALIAS) or introducing other `urn:bap:...` schemes.
|
package/dist/MasterID.d.ts
CHANGED
@@ -3,7 +3,7 @@ import type { HD } from "@bsv/sdk";
|
|
3
3
|
import { type APIFetcher } from "./api";
|
4
4
|
import type { GetAttestationResponse, GetSigningKeysResponse } from "./apiTypes";
|
5
5
|
import type { Identity, IdentityAttributes, OldIdentity, MemberIdentity } from "./interface";
|
6
|
-
import { MemberID } from
|
6
|
+
import { MemberID } from "./MemberID";
|
7
7
|
import { BaseClass } from "./BaseClass";
|
8
8
|
/**
|
9
9
|
* MasterID class
|
@@ -233,5 +233,13 @@ declare class MasterID extends BaseClass {
|
|
233
233
|
export(): Identity;
|
234
234
|
exportMemberBackup(): MemberIdentity;
|
235
235
|
newId(): MemberID;
|
236
|
+
/**
|
237
|
+
* Export member data in bitcoin-backup compatible format
|
238
|
+
* @returns Object with wif and encrypted member data
|
239
|
+
*/
|
240
|
+
exportMember(): {
|
241
|
+
wif: string;
|
242
|
+
encryptedData: string;
|
243
|
+
};
|
236
244
|
}
|
237
245
|
export { MasterID };
|
package/dist/MemberID.d.ts
CHANGED
@@ -12,7 +12,7 @@ export declare class MemberID extends BaseClass {
|
|
12
12
|
address: string;
|
13
13
|
signature: string;
|
14
14
|
};
|
15
|
-
signOpReturnWithAIP(opReturn: number[][]
|
15
|
+
signOpReturnWithAIP(opReturn: number[][]): number[][];
|
16
16
|
getPublicKey(): string;
|
17
17
|
import(identity: MemberIdentity): void;
|
18
18
|
static fromMemberIdentity(identity: MemberIdentity): MemberID;
|
@@ -33,4 +33,15 @@ export declare class MemberID extends BaseClass {
|
|
33
33
|
* Get the public key for encrypting data for this identity
|
34
34
|
*/
|
35
35
|
getEncryptionPublicKey(): string;
|
36
|
+
/**
|
37
|
+
* Export member data in bitcoin-backup compatible format
|
38
|
+
* @param label Optional user-defined label
|
39
|
+
* @returns BapMemberBackup compatible object
|
40
|
+
*/
|
41
|
+
exportForBackup(label?: string): {
|
42
|
+
wif: string;
|
43
|
+
id: string;
|
44
|
+
label?: string;
|
45
|
+
createdAt: string;
|
46
|
+
};
|
36
47
|
}
|
package/dist/api.d.ts
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
/**
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
* Helper function to get attestation from a BAP API server
|
3
|
+
*
|
4
|
+
* @param apiUrl
|
5
|
+
* @param apiData
|
6
|
+
* @returns {Promise<any>}
|
7
|
+
*/
|
8
8
|
export declare const getApiData: <T>(apiUrl: string, apiData: unknown, server: string, token: string) => Promise<T>;
|
9
9
|
export type APIFetcher = <T>(url: string, data: unknown) => Promise<T>;
|
10
10
|
export declare const apiFetcher: (host: string, token: string) => APIFetcher;
|
package/dist/apiTypes.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
import type { Organization, Person, WithContext } from
|
1
|
+
import type { Organization, Person, WithContext } from "schema-dts";
|
2
2
|
export interface APIResponse<T> {
|
3
|
-
status:
|
3
|
+
status: "success" | "error";
|
4
4
|
result?: T;
|
5
5
|
message?: string;
|
6
6
|
}
|
package/dist/index.d.ts
CHANGED
@@ -238,6 +238,32 @@ export declare class BAP {
|
|
238
238
|
* @param attestationHash
|
239
239
|
*/
|
240
240
|
getAttestationsForHash(attestationHash: string): Promise<GetAttestationResponse>;
|
241
|
+
/**
|
242
|
+
* Export master BAP data in bitcoin-backup compatible format
|
243
|
+
* @param label Optional user-defined label
|
244
|
+
* @param xprv Extended private key (if not provided, the HDPrivateKey will be used)
|
245
|
+
* @param mnemonic BIP39 mnemonic phrase (optional)
|
246
|
+
* @returns BapMasterBackup compatible object
|
247
|
+
*/
|
248
|
+
exportForBackup(label?: string, xprv?: string, mnemonic?: string): {
|
249
|
+
ids: string;
|
250
|
+
xprv: string;
|
251
|
+
mnemonic: string;
|
252
|
+
label?: string;
|
253
|
+
createdAt: string;
|
254
|
+
};
|
255
|
+
/**
|
256
|
+
* Export a specific member ID in bitcoin-backup compatible format
|
257
|
+
* @param idKey The key of the identity to export
|
258
|
+
* @param label Optional user-defined label
|
259
|
+
* @returns BapMemberBackup compatible object
|
260
|
+
*/
|
261
|
+
exportMemberForBackup(idKey: string, label?: string): {
|
262
|
+
wif: string;
|
263
|
+
id: string;
|
264
|
+
label?: string;
|
265
|
+
createdAt: string;
|
266
|
+
};
|
241
267
|
}
|
242
268
|
export { MasterID, MemberID };
|
243
|
-
export type { Attestation, Identity, MemberIdentity, IdentityAttributes, PathPrefix };
|
269
|
+
export type { Attestation, Identity, MemberIdentity, IdentityAttributes, PathPrefix, };
|
package/dist/index.modern.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
// @bun
|
2
|
-
import{BSM as
|
2
|
+
import{BSM as g,BigNumber as Lj,ECIES as wj,HD as Yj,OP as K,Signature as Gj}from"@bsv/sdk";import{Utils as Fj}from"@bsv/sdk";var s=async(j,$,J,q)=>{let z=`${J}${j}`;return(await fetch(z,{method:"post",headers:{"Content-type":"application/json; charset=utf-8",token:q,format:"json"},body:JSON.stringify($)})).json()},x=(j,$)=>async(J,q)=>{return s(J,q,j,$)};import{Utils as r}from"@bsv/sdk";var{toHex:A,toArray:m}=r,X="1BAPSuaPnfGnSBM3GLV9yhxUdYe4vGbdMT",c=A(m(X)),M="15PciHG22SNLQJXMoSUaWVi7WSqc7hCfva",kj=A(m(M)),N="https://api.sigmaidentity.com/v1",F=2147483647,f="m/424150'/0'/0'",w=`m/424150'/${F}'/${F}'`;import{BSM as _,Utils as Qj,ECIES as Zj,Hash as U,PublicKey as E,BigNumber as h}from"@bsv/sdk";import{randomBytes as i}from"crypto";var Y={getRandomString(j=32){return i(j).toString("hex")},getSigningPathFromHex(j,$=!0){let J="m",q=j.match(/.{1,8}/g);if(!q)throw new Error("Invalid hex string");let z=2147483647;for(let Q of q){let W=Number(`0x${Q}`);if(W>z)W-=z;J+=`/${W}${$?"'":""}`}return J},getNextIdentityPath(j){let $=j.split("/"),J=$[$.length-2],q=!1;if(J.match("'"))q=!0;let z=(Number(J.replace(/[^0-9]/g,""))+1).toString();return $[$.length-2]=z+(q?"'":""),$[$.length-1]=`0${q?"'":""}`,$.join("/")},getNextPath(j){let $=j.split("/"),J=$[$.length-1],q=!1;if(J.match("'"))q=!0;let z=(Number(J.replace(/[^0-9]/g,""))+1).toString();return $[$.length-1]=z+(q?"'":""),$.join("/")}};import{BSM as H,Utils as Bj,PrivateKey as S,BigNumber as qj}from"@bsv/sdk";import{ECIES as a,Utils as e,OP as l,PublicKey as p}from"@bsv/sdk";var{toArray:C,toUTF8:t,toBase64:jj}=e,{electrumDecrypt:$j,electrumEncrypt:Jj}=a;class V{identityAttributes={};encrypt(j,$){let{privKey:J,pubKey:q}=this.getEncryptionKey(),z=$?p.fromString($):q;return jj(Jj(C(j),z,J))}decrypt(j,$){let{privKey:J}=this.getEncryptionKey(),q=void 0;if($)q=p.fromString($);return t($j(C(j,"base64"),J,q))}signOpReturnWithAIP(j,$){let J=this.getAIPMessageBuffer(j),{address:q,signature:z}=this.signMessage(J.flat(),$);return this.formatAIPOutput(J,q,z)}getAttributes(){return this.identityAttributes}getAttribute(j){if(this.identityAttributes[j])return this.identityAttributes[j];return null}setAttribute(j,$){if(!$)return;if(this.identityAttributes[j])this.updateExistingAttribute(j,$);else this.createNewAttribute(j,$)}unsetAttribute(j){delete this.identityAttributes[j]}addAttribute(j,$,J=""){let q=J;if(!J)q=Y.getRandomString();this.identityAttributes[j]={value:$,nonce:q}}getAttributeUrns(){let j="";for(let $ in this.identityAttributes){let J=this.getAttributeUrn($);if(J)j+=`${J}
|
3
3
|
`}return j}getAttributeUrn(j){let $=this.identityAttributes[j];if($)return`urn:bap:id:${j}:${$.value}:${$.nonce}`;return null}parseStringUrns(j){let $={},J=j.replace(/^\s+/g,"").replace(/\r/gm,"").split(`
|
4
|
-
`);for(let q of J){let Q=q.replace(/^\s+/g,"").replace(/\s+$/g,"").split(":");if(Q[0]==="urn"&&Q[1]==="bap"&&Q[2]==="id"&&Q[3]&&Q[4]&&Q[5])$[Q[3]]={value:Q[4],nonce:Q[5]}}return $}parseAttributes(j){if(typeof j==="string")return this.parseStringUrns(j);for(let $ in j)if(!j[$].value||!j[$].nonce)throw new Error("Invalid identity attribute");return j||{}}updateExistingAttribute(j,$){if(typeof $==="string"){this.identityAttributes[j].value=$;return}if(this.identityAttributes[j].value=$.value||"",$.nonce)this.identityAttributes[j].nonce=$.nonce}createNewAttribute(j,$){if(typeof $==="string"){this.addAttribute(j,$);return}this.addAttribute(j,$.value||"",$.nonce)}getAIPMessageBuffer(j,$){let J=j.findIndex((z)=>z[0]===
|
5
|
-
`);for(let q of J){let Q=q.replace(/^\s+/g,"").replace(/\s+$/g,"").split(":");if(Q[0]==="urn"&&Q[1]==="bap"&&Q[2]==="id"&&Q[3]&&Q[4]&&Q[5])$[Q[3]]={value:Q[4],nonce:Q[5]}}return $}getIdentityKey(){return this.identityKey}set rootPath(j){if(this.#j){let $=j;if(j.split("/").length<5)$=`${
|
6
|
-
`,J),this.importOldIds(J);return}if(typeof J!=="object")throw new Error("decrypted, but found unrecognized identities format");this.importIds(J,!1)}importOldIds(j){for(let $ of j){let J=new
|
4
|
+
`);for(let q of J){let Q=q.replace(/^\s+/g,"").replace(/\s+$/g,"").split(":");if(Q[0]==="urn"&&Q[1]==="bap"&&Q[2]==="id"&&Q[3]&&Q[4]&&Q[5])$[Q[3]]={value:Q[4],nonce:Q[5]}}return $}parseAttributes(j){if(typeof j==="string")return this.parseStringUrns(j);for(let $ in j)if(!j[$].value||!j[$].nonce)throw new Error("Invalid identity attribute");return j||{}}updateExistingAttribute(j,$){if(typeof $==="string"){this.identityAttributes[j].value=$;return}if(this.identityAttributes[j].value=$.value||"",$.nonce)this.identityAttributes[j].nonce=$.nonce}createNewAttribute(j,$){if(typeof $==="string"){this.addAttribute(j,$);return}this.addAttribute(j,$.value||"",$.nonce)}getAIPMessageBuffer(j,$){let J=j.findIndex((z)=>z[0]===l.OP_RETURN),q=[];if(J===-1)q.push([l.OP_RETURN]),J=0;if($)for(let z of $)q.push(j[J+z]);else for(let z of j)q.push(z);return q}formatAIPOutput(j,$,J){let q=[C("|"),C(M),C("BITCOIN_ECDSA"),C($),C(J,"base64")];return[...j,...q]}}var{magicHash:zj}=H;class O extends V{key;idName;description;address;identityKey;constructor(j,$={}){super();this.key=j,this.address=j.toAddress(),this.idName="Member ID 1",this.description="",this.identityKey="",this.identityAttributes=this.parseAttributes($)}signMessage(j,$){let J=this.key,q=J.toAddress(),z=H.sign(j,J,"raw"),Q=new qj(zj(j)),W=z.CalculateRecoveryFactor(J.toPublicKey(),Q),L=H.sign(j,J,"raw").toCompact(W,!0,"base64");return{address:q,signature:L}}signOpReturnWithAIP(j){let $=this.getAIPMessageBuffer(j),{address:J,signature:q}=this.signMessage($.flat());return this.formatAIPOutput($,J,q)}getPublicKey(){return this.key.toPublicKey().toString()}import(j){this.idName=j.name,this.description=j.description,this.key=S.fromWif(j.derivedPrivateKey),this.address=this.key.toAddress(),this.identityAttributes=j.identityAttributes||{},this.identityKey=j.identityKey}static fromMemberIdentity(j){let $=new O(S.fromWif(j.derivedPrivateKey));return $.import(j),$}static fromBackup(j){let $=new O(S.fromWif(j.wif)),J=JSON.parse($.decrypt(j.id));return $.import(J),$}export(){return{name:this.idName,description:this.description,derivedPrivateKey:this.key.toWif(),address:this.address,identityAttributes:this.getAttributes(),identityKey:this.identityKey}}getEncryptionKey(){return{privKey:this.key.deriveChild(this.key.toPublicKey(),w),pubKey:this.key.deriveChild(this.key.toPublicKey(),w).toPublicKey()}}getEncryptionPublicKey(){let{pubKey:j}=this.getEncryptionKey();return j.toString()}exportForBackup(j){let $=this.export(),J=this.encrypt(JSON.stringify($));return{wif:this.key.toWif(),id:J,...j&&{label:j},createdAt:new Date().toISOString()}}}var{toArray:G,toHex:k,toBase58:Wj,toUTF8:b,toBase64:D}=Qj,{electrumDecrypt:P,electrumEncrypt:I}=Zj,{magicHash:o}=_;class R extends V{#j;#J=N;#Q="";#$;#q;#z;#Z;idName;description;rootAddress;identityKey;identityAttributes;getApiData;constructor(j,$={},J=""){super();if(J){let Q=k(U.sha256(J,"utf8")),W=Y.getSigningPathFromHex(Q);this.#j=j.derive(W)}else this.#j=j;this.#Z=J,this.idName="ID 1",this.description="",this.#$=`${f}/0/0/0`,this.#q=`${f}/0/0/0`,this.#z=`${f}/0/0/1`;let q=this.#j.derive(this.#$);this.rootAddress=q.privKey.toPublicKey().toAddress(),this.identityKey=this.deriveIdentityKey(this.rootAddress);let z={...$};this.identityAttributes=this.parseAttributes(z),this.getApiData=x(this.#J,this.#Q)}set BAP_SERVER(j){this.#J=j}get BAP_SERVER(){return this.#J}set BAP_TOKEN(j){this.#Q=j}get BAP_TOKEN(){return this.#Q}deriveIdentityKey(j){let $=k(U.sha256(j,"utf8"));return Wj(U.ripemd160($,"hex"))}parseAttributes(j){if(typeof j==="string")return this.parseStringUrns(j);for(let $ in j)if(!j[$].value||!j[$].nonce)throw new Error("Invalid identity attribute");return j||{}}parseStringUrns(j){let $={},J=j.replace(/^\s+/g,"").replace(/\r/gm,"").split(`
|
5
|
+
`);for(let q of J){let Q=q.replace(/^\s+/g,"").replace(/\s+$/g,"").split(":");if(Q[0]==="urn"&&Q[1]==="bap"&&Q[2]==="id"&&Q[3]&&Q[4]&&Q[5])$[Q[3]]={value:Q[4],nonce:Q[5]}}return $}getIdentityKey(){return this.identityKey}set rootPath(j){if(this.#j){let $=j;if(j.split("/").length<5)$=`${f}${j}`;if(!this.validatePath($))throw new Error(`invalid signing path given ${$}`);this.#$=$;let J=this.#j.derive($);this.rootAddress=J.pubKey.toAddress(),this.identityKey=this.deriveIdentityKey(this.rootAddress),this.#q=$,this.#z=$}}get rootPath(){return this.#$}getRootPath(){return this.#$}set currentPath(j){let $=j;if(j.split("/").length<5)$=`${f}${j}`;if(!this.validatePath($))throw new Error("invalid signing path given");this.#q=this.#z,this.#z=$}get currentPath(){return this.#z}get previousPath(){return this.#q}get idSeed(){return this.#Z}incrementPath(){this.currentPath=Y.getNextPath(this.currentPath)}validatePath(j){if(j.match(/\/[0-9]{1,10}'?\/[0-9]{1,10}'?\/[0-9]{1,10}'?\/[0-9]{1,10}'?\/[0-9]{1,10}'?\/[0-9]{1,10}'?/)){let $=j.split("/");if($.length===7&&Number($[1].replace("'",""))<=F&&Number($[2].replace("'",""))<=F&&Number($[3].replace("'",""))<=F&&Number($[4].replace("'",""))<=F&&Number($[5].replace("'",""))<=F&&Number($[6].replace("'",""))<=F)return!0}return!1}getInitialIdTransaction(){return this.getIdTransaction(this.#$)}getIdTransaction(j=""){if(this.#z===this.#$)throw new Error("Current path equals rootPath. ID was probably not initialized properly");let $=[G(X),G("ID"),G(this.identityKey),G(this.getCurrentAddress())];return this.signOpReturnWithAIP($,j||this.#q)}getAddress(j){return this.#j.derive(j).privKey.toPublicKey().toAddress()}getCurrentAddress(){return this.getAddress(this.#z)}getEncryptionKey(){let $=this.#j.derive(this.#$).derive(w).privKey;return{privKey:$,pubKey:$.toPublicKey()}}getEncryptionKeyType42(){let j=this.#j.derive(this.#$),$=j.privKey.deriveChild(j.toPublic().pubKey,w);return{privKey:$,pubKey:$.toPublicKey()}}getEncryptionPublicKey(){let{pubKey:j}=this.getEncryptionKey();return j.toString()}getEncryptionPublicKeyWithSeed(j){return this.getEncryptionPrivateKeyWithSeed(j).toPublicKey().toString()}encrypt(j,$){let z=this.#j.derive(this.#$).derive(w).privKey.toPublicKey(),Q=$?E.fromString($):z;return D(I(G(j),Q,null))}decrypt(j,$){let q=this.#j.derive(this.#$).derive(w).privKey,z=void 0;if($)z=E.fromString($);return b(P(G(j,"base64"),q,z))}encryptWithSeed(j,$,J){let q=this.getEncryptionPrivateKeyWithSeed($),z=q.toPublicKey(),Q=J?E.fromString(J):z;return D(I(G(j),Q,q))}decryptWithSeed(j,$,J){let q=this.getEncryptionPrivateKeyWithSeed($),z=void 0;if(J)z=E.fromString(J);return b(P(G(j,"base64"),q,z))}getEncryptionPrivateKeyWithSeed(j){let $=k(U.sha256(j,"utf8")),J=Y.getSigningPathFromHex($);return this.#j.derive(this.#$).derive(J).privKey}getAttestation(j){let $=U.sha256(j,"utf8");return`bap:attest:${k($)}:${this.getIdentityKey()}`}getAttestationHash(j){let $=this.getAttributeUrn(j);if(!$)return null;let J=this.getAttestation($),q=U.sha256(J,"utf8");return k(q)}signMessage(j,$){let J=$||this.#z,q=this.#j.derive(J).privKey,z=q.toAddress(),Q=_.sign(j,q,"raw"),W=new h(o(j)),L=Q.CalculateRecoveryFactor(q.toPublicKey(),W),v=_.sign(j,q,"raw").toCompact(L,!0,"base64");return{address:z,signature:v}}signMessageWithSeed(j,$){let J=k(U.sha256($,"utf8")),q=Y.getSigningPathFromHex(J),Q=this.#j.derive(this.#$).derive(q),W=Q.privKey.toPublicKey().toAddress(),L=G(j,"utf8"),v=_.sign(L,Q.privKey,"raw"),u=new h(o(L)),n=v.CalculateRecoveryFactor(Q.privKey.toPublicKey(),u),d=_.sign(L,Q.privKey,"raw").toCompact(n,!0,"base64");return{address:W,signature:d}}signOpReturnWithAIP(j,$=""){let J=this.getAIPMessageBuffer(j),{address:q,signature:z}=this.signMessage(J.flat(),$);return this.formatAIPOutput(j,q,z)}async getIdSigningKeys(){let j=await this.getApiData("/signing-keys",{idKey:this.identityKey});return console.log("getIdSigningKeys",j),j}async getAttributeAttestations(j){let $=this.getAttestationHash(j),J=await this.getApiData("/attestation/get",{hash:$});return console.log("getAttestations",j,$,J),J}import(j){this.idName=j.name,this.description=j.description||"",this.identityKey=j.identityKey,this.#$=j.rootPath,this.rootAddress=j.rootAddress,this.#q=j.previousPath,this.#z=j.currentPath,this.#Z=("idSeed"in j?j.idSeed:"")||"",this.identityAttributes=this.parseAttributes(j.identityAttributes)}export(){return{name:this.idName,description:this.description,identityKey:this.identityKey,rootPath:this.#$,rootAddress:this.rootAddress,previousPath:this.#q,currentPath:this.#z,idSeed:this.#Z,identityAttributes:this.getAttributes(),lastIdPath:""}}exportMemberBackup(){let j=this.#j.derive(this.#z).privKey;return{name:this.idName,description:this.description,derivedPrivateKey:j.toWif(),address:j.toPublicKey().toAddress(),identityAttributes:this.getAttributes(),identityKey:this.identityKey}}newId(){this.incrementPath();let j=this.#j.derive(this.#z).privKey;return new O(j)}exportMember(){let j=this.exportMemberBackup(),$=this.#j.derive(this.#z).privKey,J=D(I(G(JSON.stringify(j)),$.toPublicKey()));return{wif:j.derivedPrivateKey,encryptedData:J}}}var{toArray:Z,toUTF8:T,toBase64:y,toHex:B}=Fj,{electrumEncrypt:Xj,electrumDecrypt:Cj}=wj;class Uj{#j;#J={};#Q=N;#$="";#q="";getApiData;constructor(j,$="",J=""){if(!j)throw new Error("No HDPrivateKey given");if(this.#j=Yj.fromString(j),$)this.#$=$;if(J)this.#Q=J;this.getApiData=x(this.#Q,this.#$)}get lastIdPath(){return this.#q}getPublicKey(j=""){if(j)return this.#j.derive(j).pubKey.toString();return this.#j.pubKey.toString()}getHdPublicKey(j=""){if(j)return this.#j.derive(j).toPublic().toString();return this.#j.toPublic().toString()}set BAP_SERVER(j){this.#Q=j;for(let $ in this.#J)this.#J[$].BAP_SERVER=j}get BAP_SERVER(){return this.#Q}set BAP_TOKEN(j){this.#$=j;for(let $ in this.#J)this.#J[$].BAP_TOKEN=j}get BAP_TOKEN(){return this.#$}checkIdBelongs(j){if(this.#j.derive(j.rootPath).pubKey.toAddress()!==j.rootAddress)throw new Error("ID does not belong to this private key");return!0}listIds(){return Object.keys(this.#J)}newId(j,$={},J=""){let q;if(!j)q=this.getNextValidPath();else q=j;let z=new R(this.#j,$,J);z.BAP_SERVER=this.#Q,z.BAP_TOKEN=this.#$,z.rootPath=q,z.currentPath=Y.getNextPath(q);let Q=z.getIdentityKey();return this.#J[Q]=z,this.#q=q,this.#J[Q]}removeId(j){delete this.#J[j]}getNextValidPath(){if(this.#q)return Y.getNextIdentityPath(this.#q);return`/0'/${Object.keys(this.#J).length}'/0'`}getId(j){return this.#J[j]||null}setId(j){this.checkIdBelongs(j),this.#J[j.getIdentityKey()]=j}importIds(j,$=!0){if($&&typeof j==="string"){this.importEncryptedIds(j);return}let J=j;if(!J.lastIdPath)throw new Error("ID cannot be imported as it is not complete");if(!J.ids)throw new Error(`ID data is not in the correct format: ${j}`);let q=j.lastIdPath;for(let z of J.ids){if(!z.identityKey||!z.identityAttributes||!z.rootAddress)throw new Error("ID cannot be imported as it is not complete");let Q=new R(this.#j,{},z.idSeed);if(Q.BAP_SERVER=this.#Q,Q.BAP_TOKEN=this.#$,Q.import(z),q==="")q=Q.currentPath;this.checkIdBelongs(Q),this.#J[Q.getIdentityKey()]=Q}this.#q=q}importEncryptedIds(j){let $=this.decrypt(j),J=JSON.parse($);if(Array.isArray(J)){console.log(`Importing old format:
|
6
|
+
`,J),this.importOldIds(J);return}if(typeof J!=="object")throw new Error("decrypted, but found unrecognized identities format");this.importIds(J,!1)}importOldIds(j){for(let $ of j){let J=new R(this.#j,{},$.idSeed??"");J.BAP_SERVER=this.#Q,J.BAP_TOKEN=this.#$,J.import($),this.checkIdBelongs(J),this.#J[J.getIdentityKey()]=J,this.#q=J.currentPath}}exportIds(j,$=!0){let J={lastIdPath:this.#q,ids:[]},q=j||Object.keys(this.#J);for(let z of q){if(!this.#J[z])throw new Error(`Identity ${z} not found`);J.ids.push(this.#J[z].export())}if($)return this.encrypt(JSON.stringify(J));return J}exportId(j,$=!0){let J={lastIdPath:this.#q,ids:[]};if(J.ids.push(this.#J[j].export()),$)return this.encrypt(JSON.stringify(J));return J}encrypt(j){let $=this.#j.derive(w);return y(Xj(Z(j),$.pubKey,null))}decrypt(j){let $=this.#j.derive(w);return T(Cj(Z(j,"base64"),$.privKey))}signAttestationWithAIP(j,$,J=0,q=""){let z=this.getId($);if(!z)throw new Error("Could not find identity to attest with");let Q=this.getAttestationBuffer(j,J,q),{address:W,signature:L}=z.signMessage(Q);return this.createAttestationTransaction(j,J,W,L,q)}verifyAttestationWithAIP(j){if(!j.every((q)=>Array.isArray(q))||j[0][0]!==K.OP_RETURN||B(j[1])!==c)throw new Error("Not a valid BAP transaction");let $=B(j[7])==="44415441"?5:0,J={type:T(j[2]),hash:B(j[3]),sequence:T(j[4]),signingProtocol:T(j[7+$]),signingAddress:T(j[8+$]),signature:y(j[9+$])};if($&&j[3]===j[8])J.data=B(j[9]);console.log({attestation:J});try{let q=[];for(let z=0;z<6+$;z++)q.push(j[z]);J.verified=this.verifySignature(q.flat(),J.signingAddress,J.signature)}catch(q){J.verified=!1}return J}createAttestationTransaction(j,$,J,q,z=""){let Q=[[K.OP_RETURN],Z(X),Z("ATTEST"),Z(j),Z(`${$}`),Z("|")];if(z)Q.push(Z(X),Z("DATA"),Z(j),Z(z),Z("|"));return Q.push(Z(M),Z("BITCOIN_ECDSA"),Z(J),Z(q,"base64")),console.log({elements:Q}),Q}getAttestationBuffer(j,$=0,J=""){let q=[[K.OP_RETURN],Z(X),Z("ATTEST"),Z(j),Z(`${$}`),Z("|")];if(J)q.push(Z(X),Z("DATA"),Z(j),Z(J),Z("|"));return q.flat()}verifySignature(j,$,J){let q;if(Array.isArray(j))q=j;else if(Buffer.isBuffer(j))q=[...j];else q=Z(j,"utf8");let z=Gj.fromCompact(J,"base64"),Q;for(let W=0;W<4;W++)try{if(Q=z.RecoverPublicKey(W,new Lj(g.magicHash(q))),g.verify(q,z,Q)&&Q.toAddress()===$)return!0}catch(L){}return!1}async verifyChallengeSignature(j,$,J,q){if(!this.verifySignature(J,$,q))return!1;try{let Q=await this.getApiData("/attestation/valid",{idKey:j,address:$,challenge:J,signature:q});if(Q?.status==="success"&&Q?.result?.valid===!0)return!0;return!1}catch(Q){return console.error("API call failed:",Q),!1}}async isValidAttestationTransaction(j){if(this.verifyAttestationWithAIP(j))return this.getApiData("/attestation/valid",{tx:j});return!1}async getIdentityFromAddress(j){return this.getApiData("/identity/from-address",{address:j})}async getIdentity(j){return this.getApiData("/identity/get",{idKey:j})}async getAttestationsForHash(j){return this.getApiData("/attestations",{hash:j})}exportForBackup(j,$,J){return{ids:this.exportIds(),xprv:$||this.#j.toString(),mnemonic:J||"",...j&&{label:j},createdAt:new Date().toISOString()}}exportMemberForBackup(j,$){let J=this.#J[j];if(!J)throw new Error(`Identity ${j} not found`);let q=J.exportMember();return{wif:q.wif,id:q.encryptedData,...$&&{label:$},createdAt:new Date().toISOString()}}}export{O as MemberID,R as MasterID,Uj as BAP};
|
7
7
|
|
8
|
-
//# debugId=
|
8
|
+
//# debugId=1F3C2BE0F86889F964756E2164756E21
|