import { AuthorAddress, Base32String, ShareAddress,} from "../util/doc-types.ts";import { AuthorKeypair, ICrypto, ShareKeypair } from "./crypto-types.ts";import { isErr, ValidationError } from "../util/errors.ts";
import { randomId } from "../util/misc.ts";import { base32BytesToString, base32StringToBytes } from "./base32.ts";import { decodeKeypairToBytes, isAuthorKeypair } from "./keypair.ts";import { assembleAuthorAddress, assembleShareAddress, checkAuthorIsValid, checkShareIsValid, parseAuthorOrShareAddress,} from "../core-validators/addresses.ts";
import { GlobalCryptoDriver } from "./global-crypto-driver.ts";
import { Logger } from "../util/log.ts";let logger = new Logger("crypto", "cyan");
export const Crypto: ICrypto = class { static async sha256base32( input: string | Uint8Array, ): Promise<Base32String> { const b32 = await GlobalCryptoDriver.sha256(input);
return base32BytesToString(b32); }
static updatableSha256() { return GlobalCryptoDriver.updatableSha256(); }
static async generateAuthorKeypair( shortname: string, ): Promise<AuthorKeypair | ValidationError> { logger.debug(`generateAuthorKeypair("${shortname}")`); const keypairBytes = await GlobalCryptoDriver .generateKeypairBytes(); const keypairFormatted = { address: assembleAuthorAddress( shortname, base32BytesToString(keypairBytes.pubkey), ), secret: base32BytesToString(keypairBytes.secret), }; const err = checkAuthorIsValid(keypairFormatted.address); if (isErr(err)) return err; return keypairFormatted; }
static async generateShareKeypair( name: string, ): Promise<ShareKeypair | ValidationError> { logger.debug(`generateAuthorKeypair("${name}")`); const keypairBytes = await GlobalCryptoDriver .generateKeypairBytes(); const keypairFormatted = { shareAddress: assembleShareAddress( name, base32BytesToString(keypairBytes.pubkey), ), secret: base32BytesToString(keypairBytes.secret), }; const err = checkShareIsValid(keypairFormatted.shareAddress); if (isErr(err)) return err; return keypairFormatted; }
static async sign( keypair: AuthorKeypair | ShareKeypair, msg: string | Uint8Array, ): Promise<Base32String | ValidationError> { logger.debug(`sign`); try { const keypairBytes = decodeKeypairToBytes(keypair); if (isErr(keypairBytes)) return keypairBytes;
const signed = await GlobalCryptoDriver.sign(keypairBytes, msg);
return base32BytesToString(signed); } catch (err) { return new ValidationError( "unexpected error while signing: " + err.message, ); } }
static verify( address: AuthorAddress | ShareAddress, sig: Base32String, msg: string | Uint8Array, ): Promise<boolean> { logger.debug(`verify`); try { const parsed = parseAuthorOrShareAddress(address);
if (isErr(parsed)) return Promise.resolve(false); return GlobalCryptoDriver.verify( base32StringToBytes(parsed.pubkey), base32StringToBytes(sig), msg, ); } catch { return Promise.resolve(false); } }
static async checkKeypairIsValid( keypair: AuthorKeypair | ShareKeypair, ): Promise<true | ValidationError> { logger.debug(`checkAuthorKeypairIsValid`);
const address = isAuthorKeypair(keypair) ? keypair.address : keypair.shareAddress;
try { if ( typeof address !== "string" || typeof keypair.secret !== "string" ) { return new ValidationError( "address and secret must be strings", ); } const parseErr = parseAuthorOrShareAddress(address); if (isErr(parseErr)) return parseErr;
const msg = "a test message to sign. " + randomId(); const sig = await this.sign(keypair, msg); if (isErr(sig)) return sig;
const isValid = await this.verify(address, sig, msg); if (isValid === false) { return new ValidationError("pubkey does not match secret"); }
return true; } catch (err) { return new ValidationError( "unexpected error in checkAuthorKeypairIsValid: " + err.message, ); } }};