From 6eddcca14cf3c4ee8b0861ac8fc66dcef9cdb541 Mon Sep 17 00:00:00 2001 From: Saam Tehrani Date: Fri, 3 Jan 2025 00:21:02 +0000 Subject: [PATCH] update interfaces to support new keys/backward compatible --- src/common/common.ts | 17 ++++-- src/common/lib/crypto/crypto-interface.ts | 3 +- src/common/lib/crypto/keys/ec.ts | 6 +-- src/common/lib/crypto/keys/index.ts | 9 ++-- src/common/lib/crypto/keys/interface.ts | 7 +-- src/common/lib/crypto/keys/rsa.ts | 53 +++++++++++++++---- src/common/lib/crypto/keys/secp256k1.ts | 58 +++++++++++++++------ src/common/lib/crypto/keys/utils.ts | 32 ++++++++++++ src/common/lib/crypto/node-driver.ts | 60 ++++++++-------------- src/common/transactions.ts | 51 +++++++++--------- src/common/wallets.ts | 30 +++++++---- test/fixtures/RustCrypto-k256/msg.bin | Bin 2048 -> 32 bytes test/keys.ts | 16 +++--- 13 files changed, 218 insertions(+), 124 deletions(-) create mode 100644 src/common/lib/crypto/keys/utils.ts diff --git a/src/common/common.ts b/src/common/common.ts index 0f0f6854..8e2c47d7 100644 --- a/src/common/common.ts +++ b/src/common/common.ts @@ -11,6 +11,7 @@ import * as ArweaveUtils from "./lib/utils"; import Silo from "./silo"; import Chunks from "./chunks"; import Blocks from "./blocks"; +import { PrivateKey, PublicKey, fromJWK } from "./lib/crypto/keys"; export interface Config { api: ApiConfig; @@ -83,7 +84,7 @@ export default class Arweave { public async createTransaction( attributes: Partial, - jwk?: JWKInterface | "use_wallet" + keyData?: JWKInterface | "use_wallet" | PrivateKey | PublicKey ): Promise { const transaction: Partial = {}; @@ -95,10 +96,18 @@ export default class Arweave { ); } - if (attributes.owner == undefined) { - if (jwk && jwk !== "use_wallet") { - transaction.owner = jwk.n; + if (attributes.owner == undefined && keyData && keyData !== "use_wallet") { + let pk: PublicKey; + if (keyData instanceof PrivateKey){ + pk = await keyData.public() + } else if (keyData instanceof PublicKey) { + pk = keyData; + } else { + pk = await fromJWK(keyData as JsonWebKey) + .then(sk => sk.public()); } + transaction.owner = await pk.identifier() + .then(id => ArweaveUtils.bufferTob64Url(id)); } if (attributes.last_tx == undefined) { diff --git a/src/common/lib/crypto/crypto-interface.ts b/src/common/lib/crypto/crypto-interface.ts index 29dcc117..df8eb00c 100644 --- a/src/common/lib/crypto/crypto-interface.ts +++ b/src/common/lib/crypto/crypto-interface.ts @@ -1,4 +1,5 @@ import { JWKInterface } from "../wallet"; +import { PrivateKey } from "./keys"; export interface SignatureOptions { saltLength?: number; @@ -8,7 +9,7 @@ export default interface CryptoInterface { generateJWK(): Promise; sign( - jwk: JWKInterface, + jwk: JWKInterface | PrivateKey, data: Uint8Array, options?: SignatureOptions ): Promise; diff --git a/src/common/lib/crypto/keys/ec.ts b/src/common/lib/crypto/keys/ec.ts index 6ab733be..d4159007 100644 --- a/src/common/lib/crypto/keys/ec.ts +++ b/src/common/lib/crypto/keys/ec.ts @@ -1,4 +1,4 @@ -import { KeyType, KeyTypeByte, PublicKey, PrivateKey, getInitializationOptions, getSigningParameters, SerializationParams } from "./interface"; +import { KeyType, KeyTypeByte, PublicKey, PrivateKey, getInitializationOptions, getSigningParameters, SerializationParams, SigningParams, VerifyingParams } from "./interface"; export class EllipticCurvePrivateKey extends PrivateKey { static usages: Array = ["sign", "verify"]; @@ -38,7 +38,7 @@ export class EllipticCurvePrivateKey extends PrivateKey { } } - public async sign({payload}: {payload: Uint8Array}): Promise { + public async sign({payload}: SigningParams): Promise { return new Uint8Array(await this.driver.sign( getSigningParameters(this.type), this.key, @@ -88,7 +88,7 @@ export class EllipticCurvePublicKey extends PublicKey { this.key = key; } - public async verify({payload, signature}: {payload: Uint8Array, signature: Uint8Array}): Promise { + public async verify({payload, signature}: VerifyingParams): Promise { switch(this.type) { case KeyType.ED_25519: return this.driver.verify( diff --git a/src/common/lib/crypto/keys/index.ts b/src/common/lib/crypto/keys/index.ts index f714a4ac..983df7bc 100644 --- a/src/common/lib/crypto/keys/index.ts +++ b/src/common/lib/crypto/keys/index.ts @@ -1,4 +1,5 @@ -export { RSAPrivateKey, RSAPublicKey } from './rsa'; -export { EllipticCurvePrivateKey, EllipticCurvePublicKey } from './ec'; -export { SECP256k1PrivateKey, SECP256k1PublicKey } from './secp256k1'; -export { KeyType, PrivateKey, PublicKey } from './interface'; +export { RSAPrivateKey, RSAPublicKey } from "./rsa"; +export { EllipticCurvePrivateKey, EllipticCurvePublicKey } from "./ec"; +export { SECP256k1PrivateKey, SECP256k1PublicKey } from "./secp256k1"; +export { KeyType, PrivateKey, PublicKey } from "./interface"; +export { fromJWK, fromIdentifier } from "./utils"; diff --git a/src/common/lib/crypto/keys/interface.ts b/src/common/lib/crypto/keys/interface.ts index dd16a641..af6ed97d 100644 --- a/src/common/lib/crypto/keys/interface.ts +++ b/src/common/lib/crypto/keys/interface.ts @@ -1,5 +1,3 @@ -import { Secp256k1 } from "@solar-republic/wasm-secp256k1"; - export enum KeyType { RSA_65537 = "rsa_65537", EC_SECP256K1 = "ec_secp256k1", @@ -7,10 +5,10 @@ export enum KeyType { }; export const KeyTypeByte = { - [KeyType.RSA_65537]: null, [KeyType.EC_SECP256K1]: 2, [KeyType.ED_25519]: 3 } +export const KeyTypeBytesReverse: Map = new Map(Object.entries(KeyTypeByte).map(([key, value]) => ([value, key]))); export type Format = "jwk" | "raw"; @@ -20,15 +18,18 @@ export interface SerializationParams { export interface SigningParams { payload: Uint8Array; + is_digest?: boolean; } export interface VerifyingParams { payload: Uint8Array; signature: Uint8Array; + is_digest?: boolean; } export interface EncryptionParams { secret: Uint8Array; + } export interface DecryptionParams { diff --git a/src/common/lib/crypto/keys/rsa.ts b/src/common/lib/crypto/keys/rsa.ts index 51e6b863..ce6e7b96 100644 --- a/src/common/lib/crypto/keys/rsa.ts +++ b/src/common/lib/crypto/keys/rsa.ts @@ -1,7 +1,8 @@ -import { KeyType, PublicKey, PrivateKey, getInitializationOptions, getSigningParameters, SerializationParams } from "./interface"; +import { KeyType, PublicKey, PrivateKey, getInitializationOptions, getSigningParameters, SerializationParams, VerifyingParams, SigningParams } from "./interface"; +import { b64UrlToBuffer, bufferTob64Url } from "../../utils"; export class RSAPrivateKey extends PrivateKey { - static usages: Array = ["sign", "verify"]; + static usages: Array = ["sign"]; static async new({driver = crypto.subtle, type = KeyType.RSA_65537, modulusLength}: {driver?: SubtleCrypto, type?: KeyType, modulusLength?: number} = {driver: crypto.subtle, type: KeyType.RSA_65537}): Promise { if (modulusLength !== undefined) { if (modulusLength < 32 * 8 || modulusLength > 512 * 8) { @@ -47,9 +48,13 @@ export class RSAPrivateKey extends PrivateKey { } - public async sign({payload}: {payload: Uint8Array}): Promise { + public async sign({payload}: SigningParams, saltLength?: number): Promise { + let signingOptions = getSigningParameters(this.type) as RsaPssParams; + if (saltLength !== undefined) { + signingOptions = {...signingOptions, saltLength}; + } return new Uint8Array(await this.driver.sign( - getSigningParameters(this.type), + signingOptions, this.key, payload )); @@ -59,8 +64,14 @@ export class RSAPrivateKey extends PrivateKey { if (this.publicKey !== null) { return this.publicKey; } - let keyData = await this.driver.exportKey("spki", this.key); - this.publicKey = await RSAPublicKey.deserialize({driver: this.driver, format: "spki", keyData, type: this.type}); + let keyData = await this.driver.exportKey("jwk", this.key); + delete keyData.d; + delete keyData.dp; + delete keyData.dq; + delete keyData.q; + delete keyData.qi; + delete keyData.key_ops; + this.publicKey = await RSAPublicKey.deserialize({driver: this.driver, format: "jwk", keyData, type: this.type}); return this.publicKey; } @@ -89,11 +100,19 @@ export class RSAPublicKey extends PublicKey { } static async deserialize({driver = crypto.subtle, format, keyData, type}: {driver?: SubtleCrypto, format: "jwk" | "raw" | "pkcs8" | "spki", keyData: JsonWebKey | ArrayBuffer, type: KeyType}): Promise { + if (format === "raw") { + keyData = { + kty: "RSA", + e: "AQAB", + n: bufferTob64Url(keyData as Uint8Array), + }; + format = "jwk"; + } const key = await driver.importKey(format as any, keyData as any, getInitializationOptions(type), true, RSAPublicKey.usages); return new RSAPublicKey({driver, type, key}); } - public async verify({payload, signature}: {payload: Uint8Array, signature: Uint8Array}): Promise { + public async verify({payload, signature}: VerifyingParams): Promise { switch(this.type) { case KeyType.RSA_65537: { let result = false; @@ -114,12 +133,24 @@ export class RSAPublicKey extends PublicKey { } } - public async identifier(): Promise { - return await this.serialize({format: "raw"}) as Uint8Array; + public async serialize({format}: SerializationParams<"jwk">): Promise; + public async serialize({format}: SerializationParams<"raw">): Promise; + public async serialize({format}: SerializationParams): Promise { + const jwk = await this.driver.exportKey("jwk", this.key); + switch(format) { + case "jwk": + return jwk; + case "raw": + return b64UrlToBuffer(jwk.n!); + default: + throw new Error(`Unsupported format ${format}`); + } } - public async serialize(params: SerializationParams): Promise { - throw new Error("not implemented"); + + public async identifier(): Promise { + return this.serialize({format: "raw"}); } + } const maxSaltSize = (key: CryptoKey): number => { diff --git a/src/common/lib/crypto/keys/secp256k1.ts b/src/common/lib/crypto/keys/secp256k1.ts index 6bfd61ee..cbb3e6fa 100644 --- a/src/common/lib/crypto/keys/secp256k1.ts +++ b/src/common/lib/crypto/keys/secp256k1.ts @@ -1,5 +1,5 @@ import { Secp256k1, initWasmSecp256k1 } from "@solar-republic/wasm-secp256k1"; -import { KeyType, KeyTypeByte, PrivateKey, PublicKey, SerializationParams } from "./interface"; +import { KeyType, KeyTypeByte, PrivateKey, PublicKey, SerializationParams, SigningParams, VerifyingParams } from "./interface"; import { bufferTob64Url, b64UrlToBuffer } from "../../utils"; // TODO: build wasm module and internalize the dependency @@ -69,14 +69,28 @@ export class SECP256k1PrivateKey extends PrivateKey { } } - public async sign({payload}: {payload: Uint8Array}): Promise { - const[signature, _recovery] = this.driver.sign(this.key, payload); + public async sign({payload, is_digest = false}: SigningParams): Promise { + let digest = payload; + if (is_digest == false) { + digest = new Uint8Array(await crypto.subtle.digest("SHA-256", payload)); + } + const[signature, _recovery] = this.driver.sign(this.key, digest); return signature; } } export class SECP256k1PublicKey extends PublicKey { static usages: Array = ["verify"]; + static async fromIdentifier({identifier}: {identifier: Uint8Array}): Promise { + if (identifier[0] !== KeyTypeByte[KeyType.EC_SECP256K1]) { + throw new Error("Invalid prefix"); + } + if (identifier.byteLength !== 35) { + throw new Error("Invalid identifier length"); + } + const rawCompressed = identifier.slice(1, 34); + return SECP256k1PublicKey.deserialize({format: "raw", keyData: rawCompressed}); + } static async deserialize({driver = null, format, keyData}: {driver?: Secp256k1 | null, format: "jwk" | "raw", keyData: JsonWebKey | Uint8Array}): Promise { if (driver === null) { driver = (await ENGINE); @@ -88,14 +102,14 @@ export class SECP256k1PublicKey extends PublicKey { } const x = b64UrlToBuffer(k.x!); const y = b64UrlToBuffer(k.y!); - if (x.length !== 32 || y.length !== 32) { - throw new Error(`Invalid secp256k1 PublicKey coordinate size: X: ${x.length}, Y: ${y.length}`); + if (x.byteLength !== 32 || y.byteLength !== 32) { + throw new Error(`Invalid secp256k1 PublicKey coordinate size: X: ${x.byteLength}, Y: ${y.byteLength}`); } return new SECP256k1PublicKey({driver, key: Uint8Array.from([0x04, ...x, ...y])}); } else { const key = keyData as Uint8Array; - if (key.length !== 65) { - throw new Error(`Invalid secp256k1 PublicKey size ${key.length}`); + if (!([33, 65].includes(key.byteLength))) { + throw new Error(`Invalid secp256k1 PublicKey size ${key.byteLength}`); } return new SECP256k1PublicKey({driver, key}); } @@ -104,16 +118,28 @@ export class SECP256k1PublicKey extends PublicKey { private readonly driver: Secp256k1; private readonly key: Uint8Array; constructor({driver, key}: {driver: Secp256k1, key: Uint8Array}) { - if (key.length !== 65 || key[0] !== 0x04) { - throw new Error('Only uncompressed format accepted for initialization!'); + if (key.byteLength === 65) { + if (key[0] !== 0x04) { + throw new Error('Unaccepted uncompressed format prefix!'); + } + } else if(key.byteLength === 33) { + if (![0x02, 0x03].includes(key[0])) { + throw new Error('Unaccepted compressed format prefix!'); + } + } else { + throw new Error(`Incorrect public key size: ${key.byteLength}`); } super({type: KeyType.EC_SECP256K1}); this.driver = driver; this.key = key; } - public async verify({payload, signature}: {payload: Uint8Array, signature: Uint8Array}): Promise { - return this.driver.verify(signature, payload, this.key); + public async verify({payload, signature, is_digest = false}: VerifyingParams): Promise { + let digest = payload; + if (is_digest === false) { + digest = new Uint8Array(await crypto.subtle.digest("SHA-256", payload)); + } + return this.driver.verify(signature, digest, this.key); } public async serialize({format}: SerializationParams<"jwk">): Promise; @@ -130,14 +156,14 @@ export class SECP256k1PublicKey extends PublicKey { case "raw": const x = this.key.slice(1, 33); const y = this.key.slice(33); - const raw_compressed = new Uint8Array(33); + const rawCompressed = new Uint8Array(33); if ((y[31] & 1) === 1) { - raw_compressed[0] = 3; + rawCompressed[0] = 3; } else { - raw_compressed[0] = 2; + rawCompressed[0] = 2; } - raw_compressed.set(x, 1); - return raw_compressed; + rawCompressed.set(x, 1); + return rawCompressed; default: throw new Error(`Unsupported format ${format}`); } diff --git a/src/common/lib/crypto/keys/utils.ts b/src/common/lib/crypto/keys/utils.ts new file mode 100644 index 00000000..af9fe233 --- /dev/null +++ b/src/common/lib/crypto/keys/utils.ts @@ -0,0 +1,32 @@ +import { EllipticCurvePrivateKey } from "./ec"; +import { KeyType, PrivateKey, PublicKey, KeyTypeBytesReverse } from "./interface"; +import { RSAPrivateKey, RSAPublicKey } from "./rsa"; +import { SECP256k1PrivateKey, SECP256k1PublicKey } from "./secp256k1" + +export const fromJWK = async (keyData: JsonWebKey): Promise => { + const format = "jwk"; + switch(keyData.kty) { + case "EC": + return SECP256k1PrivateKey.deserialize({format, keyData}); + case "OKP": + return EllipticCurvePrivateKey.deserialize({format, keyData, type: KeyType.ED_25519}); + case "RSA": + return RSAPrivateKey.deserialize({format, keyData, type: KeyType.RSA_65537}); + default: + throw new Error(`Unsupported kty ${keyData.kty}`); + } +} + +export const fromIdentifier = async ({identifier}: {identifier: Uint8Array}): Promise => { + if (identifier.byteLength % 2 == 0) { + return RSAPublicKey.deserialize({format: "raw", keyData: identifier, type: KeyType.RSA_65537}); + } + const keyTypeByte = identifier[0]; + switch (KeyTypeBytesReverse.get(keyTypeByte)) { + case "ec_secp256k1": + return SECP256k1PublicKey.fromIdentifier({identifier}) + default: + throw new Error(`Unknown KeyType byte prefix ${keyTypeByte}`); + } + +}; diff --git a/src/common/lib/crypto/node-driver.ts b/src/common/lib/crypto/node-driver.ts index 547a4db8..fa4f6a82 100644 --- a/src/common/lib/crypto/node-driver.ts +++ b/src/common/lib/crypto/node-driver.ts @@ -1,5 +1,7 @@ +import { b64UrlToBuffer } from "../../lib/utils"; import { JWKInterface } from "../wallet"; import CryptoInterface, { SignatureOptions } from "./crypto-interface"; +import { PrivateKey, RSAPrivateKey, fromIdentifier } from "./keys"; import { pemTojwk, jwkTopem } from "./pem"; import * as crypto from "crypto"; @@ -39,10 +41,15 @@ export default class NodeCryptoDriver implements CryptoInterface { } public sign( - jwk: object, + jwk: object | PrivateKey, data: Uint8Array, { saltLength }: SignatureOptions = {} ): Promise { + if (jwk instanceof RSAPrivateKey) { + return jwk.sign({payload: data}, saltLength); + } else if (jwk instanceof PrivateKey) { + return jwk.sign({payload: data}); + } return new Promise((resolve, reject) => { resolve( crypto @@ -57,48 +64,25 @@ export default class NodeCryptoDriver implements CryptoInterface { }); } - public verify( - publicModulus: string, + public async verify( + owner: string, data: Uint8Array, signature: Uint8Array ): Promise { - return new Promise((resolve, reject) => { - const publicJwk = { - kty: "RSA", - e: "AQAB", - n: publicModulus, + const identifier = b64UrlToBuffer(owner); + const pk = await fromIdentifier({identifier}); + const result = await pk.verify({payload: data, signature}); + if (!result) { + const details = { + asymmetricKeyType: pk.type }; - - const pem = this.jwkToPem(publicJwk); //? - const keyObject = crypto.createPublicKey({ - key: pem, - format: "pem", - }); - - const verify = crypto.createVerify(this.hashAlgorithm); - verify.update(data); - const verifyResult = verify.verify( - { - key: keyObject, - padding: crypto.constants.RSA_PKCS1_PSS_PADDING, - }, - signature + console.warn( + "Transaction Verification Failed! \n" + + `Details: ${JSON.stringify(details, null, 2)} \n` + + "N.B. ArweaveJS is only guaranteed to verify txs created using ArweaveJS." ); - - if (!verifyResult) { - const details = { - asymmetricKeyType: keyObject.asymmetricKeyType, - modulusLength: keyObject.asymmetricKeyDetails?.modulusLength, - }; - console.warn( - "Transaction Verification Failed! \n" + - `Details: ${JSON.stringify(details, null, 2)} \n` + - "N.B. ArweaveJS is only guaranteed to verify txs created using ArweaveJS." - ); - } - - resolve(verifyResult); - }); + } + return result; } public hash( diff --git a/src/common/transactions.ts b/src/common/transactions.ts index c565b977..78ebc2d1 100644 --- a/src/common/transactions.ts +++ b/src/common/transactions.ts @@ -14,7 +14,7 @@ import { } from "./lib/transaction-uploader"; import Chunks from "./chunks"; import "arconnect"; -import { allowedNodeEnvironmentFlags } from "process"; +import { PrivateKey, fromJWK } from "./lib/crypto/keys"; export interface TransactionConfirmedData { block_indep_hash: string; @@ -191,36 +191,19 @@ export default class Transactions { public async sign( transaction: Transaction, - jwk?: JWKInterface | "use_wallet", //"use_wallet" for backwards compatibility only + jwk?: JWKInterface | "use_wallet" | PrivateKey, //"use_wallet" for backwards compatibility only options?: SignatureOptions ): Promise { /** Non-exhaustive (only checks key names), but previously no jwk checking was done */ const isJwk = (obj: object): boolean => { - let valid = true; - ["n", "e", "d", "p", "q", "dp", "dq", "qi"].map( - (key) => !(key in obj) && (valid = false) - ); - return valid; + return "kty" in obj; }; const validJwk = typeof jwk === "object" && isJwk(jwk); const externalWallet = typeof arweaveWallet === "object"; - - if (!validJwk && !externalWallet) { + if (!validJwk && !externalWallet && !(jwk instanceof PrivateKey)) { throw new Error( - `No valid JWK or external wallet found to sign transaction.` + `No valid JWK or external wallet or PrivateKey found to sign transaction.` ); - } else if (validJwk) { - transaction.setOwner(jwk.n); - - let dataToSign = await transaction.getSignatureData(); - let rawSignature = await this.crypto.sign(jwk, dataToSign, options); - let id = await this.crypto.hash(rawSignature); - - transaction.setSignature({ - id: ArweaveUtils.bufferTob64Url(id), - owner: jwk.n, - signature: ArweaveUtils.bufferTob64Url(rawSignature), - }); } else if (externalWallet) { try { const existingPermissions = await arweaveWallet.getPermissions(); @@ -241,14 +224,31 @@ export default class Transactions { signature: signedTransaction.signature, }); } else { - //can't get here, but for sanity we'll throw an error. - throw new Error(`An error occurred while signing. Check wallet is valid`); + let sk: PrivateKey; + if (jwk instanceof PrivateKey){ + sk = jwk; + } else { + sk = await fromJWK(jwk as JsonWebKey); + } + const owner = await sk.public() + .then(pk => pk.identifier()) + .then(id => ArweaveUtils.bufferTob64Url(id)); + transaction.setOwner(owner); + + let dataToSign = await transaction.getSignatureData(); + let rawSignature = await this.crypto.sign(sk, dataToSign, options); + let id = await this.crypto.hash(rawSignature); + + transaction.setSignature({ + id: ArweaveUtils.bufferTob64Url(id), + owner: owner, + signature: ArweaveUtils.bufferTob64Url(rawSignature), + }); } } public async verify(transaction: Transaction): Promise { const signaturePayload = await transaction.getSignatureData(); - /** * The transaction ID should be a SHA-256 hash of the raw signature bytes, so this needs * to be recalculated from the signature and checked against the transaction ID. @@ -257,7 +257,6 @@ export default class Transactions { decode: true, string: false, }); - const expectedId = ArweaveUtils.bufferTob64Url( await this.crypto.hash(rawSignature) ); diff --git a/src/common/wallets.ts b/src/common/wallets.ts index 3564915d..a498a954 100644 --- a/src/common/wallets.ts +++ b/src/common/wallets.ts @@ -1,9 +1,10 @@ import Api from "./lib/api"; import CryptoInterface from "./lib/crypto/crypto-interface"; -import { KeyType, PrivateKey, RSAPrivateKey, EllipticCurvePrivateKey, SECP256k1PrivateKey } from "./lib/crypto/keys"; +import { KeyType, PrivateKey, PublicKey, RSAPrivateKey, EllipticCurvePrivateKey, SECP256k1PrivateKey } from "./lib/crypto/keys"; import { JWKInterface } from "./lib/wallet"; import * as ArweaveUtils from "./lib/utils"; import "arconnect"; +import { fromJWK } from "./lib/crypto/keys"; export default class Wallets { @@ -62,15 +63,11 @@ export default class Wallets { public async jwkToAddress( jwk?: JWKInterface | "use_wallet" ): Promise { - if (!jwk || jwk === "use_wallet") { - return this.getAddress(); - } else { - return this.getAddress(jwk); - } + return this.getAddress(jwk); } - public async getAddress(jwk?: JWKInterface | "use_wallet"): Promise { - if (!jwk || jwk === "use_wallet") { + public async getAddress(keyData?: JWKInterface | "use_wallet" | PrivateKey | PublicKey): Promise { + if (!keyData || keyData === "use_wallet") { try { // @ts-ignore await arweaveWallet.connect(["ACCESS_ADDRESS"]); @@ -81,13 +78,24 @@ export default class Wallets { // @ts-ignore return arweaveWallet.getActiveAddress(); } else { - return this.ownerToAddress(jwk.n); + let pk: PublicKey; + if (keyData instanceof PrivateKey){ + pk = await keyData.public() + } else if (keyData instanceof PublicKey) { + pk = keyData; + } else { + pk = await fromJWK(keyData) + .then(sk => sk.public()); + } + return pk.identifier() + .then(identifier => this.crypto.hash(identifier)) + .then(address => ArweaveUtils.bufferTob64Url(address)); } } - public async ownerToAddress(owner: string): Promise { + public async ownerToAddress(identifier: string): Promise { return ArweaveUtils.bufferTob64Url( - await this.crypto.hash(ArweaveUtils.b64UrlToBuffer(owner)) + await this.crypto.hash(ArweaveUtils.b64UrlToBuffer(identifier)) ); } } diff --git a/test/fixtures/RustCrypto-k256/msg.bin b/test/fixtures/RustCrypto-k256/msg.bin index 5f67f3a4f159a811f4b6a8bae16c5c0ce5cf3374..336732ef7ea503169e4dc1c47593f48d6f0729fb 100644 GIT binary patch literal 32 ocmd1V7{d1YrcSzx!dccC4K@v$*E8qnvemA<@||a@&8ZE70O@BAY5)KL literal 2048 zcmV+b2>b4jK~(-HsAnTcFc;ihJwo%f&CN=ywPy zB^Ya|BoLANHdd2c^s-;%C8NjkTfL zDu>30+E+?h#b<;YJhb^<&FKU+QF2Xzr^mG?TcSiAy17{30EtwY(D zhO;=bBwA}SK*=0h8ri_IN@lC}kdXOHIH&@c9dQ!)BSp#AaRJ4W4$@F=(mAZNj`;i1 zHV1IO;M*_}A1D#I+|)%cG-JCZK{DE2_RKz774mbUc&F?VW~#6On(<%lOmXI36CDNE(MoVArR^3`09uhvTgTInH~9S!tz9S@S_^n_BG z78$7$%w`rQqF|dKXcN5cz_hj9qTVUF0OLvQV!IA(-Z$-LWOCkrC|f^~E%{cG;`88? zP!QTh4qjx)>e4kC7|+Q5mG-4MUaNoh1UCr^{5BMpu3WRbOfmA0>Q(zPWL4hJSj0nX zieq&ftI3>s-fY4Ar*Eek^v%cV1T+v_l@UqP zh^fC5-qb9<#31Lnqg(DQTTLX@*GHBnl-ju8XWr;OE+>;+60Ya!r5xkw-Z|sE;WB0Y zm{ak`RVFDKMvb;^#MO?&$iDqHYrNP}rX{L92~CMudJKt?Z=i%5`b{!R9qk+#d`8pX zIIog^Bq;BS8AIc76|A3pV6g6wa|=S*9ty@zK%+J(#zRr3+p-zFtXQJB(nX$Wh!#tg zsi470`R9z5ZK=Q-p|lZ&*vc+vfVxi6WK5liu%pb9u0u8?niSx&oj%!be=xNiAU|gt zfS*?0I>wv-AT5$|jOJ!72DsPP#{nyCY9;qgKtkmOJIB;W(y0n~VfFyR^&;{etYHTw zcjtaWZk6_*cPI*UGO#c52qF#%5w2+{FOoC`;Sagi0V(+rg*__;(>vI~#jq+QEG8_5 z_h1?O6=P0k!K`ekJPKwSI6o2?7jVVIKuUJn72kDoHa%|pxIvl2&546F<4swa#Ys$y z0@#j2v2ENUfj(~q()>hD$~FZ}sSyKb9w1DgIExyy6Y*aTedi>rn)<`$8b_vdRjG#1 zQ5i!04qh>+DQO68HQ|XZQCvIm^zxi+q)K1*sq4P};(R2)8yFmZuI0!&1w9a-W}W*+80sMNk~m5S^>bpwx7J zMD#!InU=85rN@hZ-YUIXf1lGG!x#iL?Bhm3)$5OuSb0d$c{K({B4^+SbAG`w}U(hl<9 z5#QQk#4K;m`2j@O*Ls_Wz)8Rt`UF79jc%XB%x6$4=z&(y#Xui`*U^TT5tOK{!qyVs z8vZ7kaLZ8+M;6`qCNR*xXAn-RJ`6X04)}!ALu5sBFH@c|Ryw3H`&#d??DFG`wkxql zzonBIbz4P_kawWEV4mZGs<7NtU|)dF#KME$#uymiNen#o#x0h|IodzXb}hQa z2YL`dwHs_Xj58}z;xFv<3PWThm^(KDchQB0N-FqLVwrJ9;fn%J$yJ`ZsZvRMbeSw0 z?6u1X5iv=U(HRbh-q-@-)W|e5h1z4_PgTE(U1Zi+)-xK=RetrYMctm16Vr(M*J7O# zEko{eDyE(zl^SzA8Pijb>UywlMv4N}-lY5}2BYw*+3?@-4%e!i~~C9p?epSE>fIk6$d#fU0EO@96T1R@)sinTy57}H5F0doFQ#T3a{~K z7cI_zk0tq-m(*v(NW(y}C#=CkzI6<}6`qTS