You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			186 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			186 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			TypeScript
		
	
| import { getSodiumRenderer } from '../../crypto';
 | |
| import { StringUtils, UserUtils } from '../../utils';
 | |
| import { fromHexToArray, fromUInt8ArrayToBase64 } from '../../utils/String';
 | |
| import { GetNetworkTime } from './getNetworkTime';
 | |
| 
 | |
| export type SnodeSignatureResult = {
 | |
|   timestamp: number;
 | |
|   // sig_timestamp: number;
 | |
|   signature: string;
 | |
|   pubkey_ed25519: string;
 | |
|   pubkey: string; // this is the x25519 key of the pubkey we are doing the request to (ourself for our swarm usually)
 | |
| };
 | |
| 
 | |
| async function getSnodeSignatureByHashesParams({
 | |
|   messages,
 | |
|   method,
 | |
|   pubkey,
 | |
| }: {
 | |
|   pubkey: string;
 | |
|   messages: Array<string>;
 | |
|   method: 'delete';
 | |
| }): Promise<
 | |
|   Pick<SnodeSignatureResult, 'pubkey_ed25519' | 'signature' | 'pubkey'> & {
 | |
|     messages: Array<string>;
 | |
|   }
 | |
| > {
 | |
|   const ourEd25519Key = await UserUtils.getUserED25519KeyPair();
 | |
| 
 | |
|   if (!ourEd25519Key) {
 | |
|     const err = `getSnodeSignatureParams "${method}": User has no getUserED25519KeyPair()`;
 | |
|     window.log.warn(err);
 | |
|     throw new Error(err);
 | |
|   }
 | |
|   const edKeyPrivBytes = fromHexToArray(ourEd25519Key?.privKey);
 | |
|   const verificationData = StringUtils.encode(`${method}${messages.join('')}`, 'utf8');
 | |
|   const message = new Uint8Array(verificationData);
 | |
| 
 | |
|   const sodium = await getSodiumRenderer();
 | |
|   try {
 | |
|     const signature = sodium.crypto_sign_detached(message, edKeyPrivBytes);
 | |
|     const signatureBase64 = fromUInt8ArrayToBase64(signature);
 | |
| 
 | |
|     return {
 | |
|       signature: signatureBase64,
 | |
|       pubkey_ed25519: ourEd25519Key.pubKey,
 | |
|       pubkey,
 | |
|       messages,
 | |
|     };
 | |
|   } catch (e) {
 | |
|     window.log.warn('getSnodeSignatureParams failed with: ', e.message);
 | |
|     throw e;
 | |
|   }
 | |
| }
 | |
| 
 | |
| async function getSnodeSignatureParams(params: {
 | |
|   pubkey: string;
 | |
|   namespace: number | null | 'all'; // 'all' can be used to clear all namespaces (during account deletion)
 | |
|   method: 'retrieve' | 'store' | 'delete_all';
 | |
| }): Promise<SnodeSignatureResult> {
 | |
|   const ourEd25519Key = await UserUtils.getUserED25519KeyPair();
 | |
| 
 | |
|   if (!ourEd25519Key) {
 | |
|     const err = `getSnodeSignatureParams "${params.method}": User has no getUserED25519KeyPair()`;
 | |
|     window.log.warn(err);
 | |
|     throw new Error(err);
 | |
|   }
 | |
|   const namespace = params.namespace || 0;
 | |
|   const edKeyPrivBytes = fromHexToArray(ourEd25519Key?.privKey);
 | |
| 
 | |
|   const signatureTimestamp = GetNetworkTime.getNowWithNetworkOffset();
 | |
| 
 | |
|   const withoutNamespace = `${params.method}${signatureTimestamp}`;
 | |
|   const withNamespace = `${params.method}${namespace}${signatureTimestamp}`;
 | |
|   const verificationData =
 | |
|     namespace === 0
 | |
|       ? StringUtils.encode(withoutNamespace, 'utf8')
 | |
|       : StringUtils.encode(withNamespace, 'utf8');
 | |
| 
 | |
|   const message = new Uint8Array(verificationData);
 | |
| 
 | |
|   const sodium = await getSodiumRenderer();
 | |
|   try {
 | |
|     const signature = sodium.crypto_sign_detached(message, edKeyPrivBytes);
 | |
|     const signatureBase64 = fromUInt8ArrayToBase64(signature);
 | |
| 
 | |
|     return {
 | |
|       // sig_timestamp: signatureTimestamp,
 | |
|       timestamp: signatureTimestamp,
 | |
|       signature: signatureBase64,
 | |
|       pubkey_ed25519: ourEd25519Key.pubKey,
 | |
|       pubkey: params.pubkey,
 | |
|     };
 | |
|   } catch (e) {
 | |
|     window.log.warn('getSnodeSignatureParams failed with: ', e.message);
 | |
|     throw e;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * NOTE if shortenOrExtend is an empty string it means we want to hardcode the expiry to a TTL value, otherwise it's to shorten or extend the TTL
 | |
|  */
 | |
| async function generateUpdateExpirySignature({
 | |
|   shortenOrExtend,
 | |
|   timestamp,
 | |
|   messageHashes,
 | |
| }: {
 | |
|   shortenOrExtend: 'extend' | 'shorten' | '';
 | |
|   timestamp: number;
 | |
|   messageHashes: Array<string>;
 | |
| }): Promise<{ signature: string; pubkey_ed25519: string } | null> {
 | |
|   const ourEd25519Key = await UserUtils.getUserED25519KeyPair();
 | |
| 
 | |
|   if (!ourEd25519Key) {
 | |
|     const err = 'getSnodeSignatureParams "expiry": User has no getUserED25519KeyPair()';
 | |
|     window.log.warn(err);
 | |
|     throw new Error(err);
 | |
|   }
 | |
| 
 | |
|   const edKeyPrivBytes = fromHexToArray(ourEd25519Key?.privKey);
 | |
| 
 | |
|   // ("expire" || ShortenOrExtend || expiry || messages[0] || ... || messages[N])
 | |
|   const verificationString = `expire${shortenOrExtend}${timestamp}${messageHashes.join('')}`;
 | |
|   const verificationData = StringUtils.encode(verificationString, 'utf8');
 | |
|   const message = new Uint8Array(verificationData);
 | |
| 
 | |
|   const sodium = await getSodiumRenderer();
 | |
|   try {
 | |
|     const signature = sodium.crypto_sign_detached(message, edKeyPrivBytes);
 | |
|     const signatureBase64 = fromUInt8ArrayToBase64(signature);
 | |
| 
 | |
|     return {
 | |
|       signature: signatureBase64,
 | |
|       pubkey_ed25519: ourEd25519Key.pubKey,
 | |
|     };
 | |
|   } catch (e) {
 | |
|     window.log.warn('getSnodeSignatureParams "expiry" failed with: ', e.message);
 | |
|     return null;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * NOTE if shortenOrExtend is an empty string it means we want to hardcode the expiry to a TTL value, otherwise it's to shorten or extend the TTL
 | |
|  */
 | |
| async function generateGetExpiriesSignature({
 | |
|   timestamp,
 | |
|   messageHashes,
 | |
| }: {
 | |
|   timestamp: number;
 | |
|   messageHashes: Array<string>;
 | |
| }): Promise<{ signature: string; pubkey_ed25519: string } | null> {
 | |
|   const ourEd25519Key = await UserUtils.getUserED25519KeyPair();
 | |
|   if (!ourEd25519Key) {
 | |
|     const err = 'getSnodeSignatureParams "get_expiries": User has no getUserED25519KeyPair()';
 | |
|     window.log.warn(err);
 | |
|     throw new Error(err);
 | |
|   }
 | |
| 
 | |
|   const edKeyPrivBytes = fromHexToArray(ourEd25519Key?.privKey);
 | |
| 
 | |
|   // ("get_expiries" || timestamp || messages[0] || ... || messages[N])
 | |
|   const verificationString = `get_expiries${timestamp}${messageHashes.join('')}`;
 | |
|   const verificationData = StringUtils.encode(verificationString, 'utf8');
 | |
|   const message = new Uint8Array(verificationData);
 | |
| 
 | |
|   const sodium = await getSodiumRenderer();
 | |
|   try {
 | |
|     const signature = sodium.crypto_sign_detached(message, edKeyPrivBytes);
 | |
|     const signatureBase64 = fromUInt8ArrayToBase64(signature);
 | |
| 
 | |
|     return {
 | |
|       signature: signatureBase64,
 | |
|       pubkey_ed25519: ourEd25519Key.pubKey,
 | |
|     };
 | |
|   } catch (e) {
 | |
|     window.log.warn('generateSignature "get_expiries" failed with: ', e.message);
 | |
|     return null;
 | |
|   }
 | |
| }
 | |
| 
 | |
| export const SnodeSignature = {
 | |
|   getSnodeSignatureParams,
 | |
|   getSnodeSignatureByHashesParams,
 | |
|   generateUpdateExpirySignature,
 | |
|   generateGetExpiriesSignature,
 | |
| };
 |