diff --git a/packages/sdk38/src/secp256k1wallet.ts b/packages/sdk38/src/secp256k1wallet.ts index 3e48a07c..45c26924 100644 --- a/packages/sdk38/src/secp256k1wallet.ts +++ b/packages/sdk38/src/secp256k1wallet.ts @@ -9,9 +9,8 @@ import { Slip10RawIndex, stringToPath, xchacha20NonceLength, - Xchacha20poly1305Ietf, } from "@cosmjs/crypto"; -import { fromBase64, fromHex, fromUtf8, toBase64, toHex, toUtf8 } from "@cosmjs/encoding"; +import { fromBase64, fromUtf8, toBase64, toHex, toUtf8 } from "@cosmjs/encoding"; import { assert, isNonNullObject } from "@cosmjs/utils"; import { rawSecp256k1PubkeyToAddress } from "./address"; @@ -19,12 +18,16 @@ import { encodeSecp256k1Signature } from "./signature"; import { StdSignature } from "./types"; import { AccountData, + decrypt, + encrypt, + EncryptionConfiguration, executeKdf, KdfConfiguration, makeCosmoshubPath, OfflineSigner, prehash, PrehashType, + supportedAlgorithms, } from "./wallet"; const serializationTypeV1 = "secp256k1wallet-v1"; @@ -42,8 +45,6 @@ const basicPasswordHashingOptions: KdfConfiguration = { }, }; -const algorithmIdXchacha20poly1305Ietf = "xchacha20poly1305-ietf"; - /** * This interface describes a JSON object holding the encrypted wallet and the meta data. * All fields in here must be JSON types. @@ -54,14 +55,7 @@ export interface Secp256k1WalletSerialization { /** Information about the key derivation function (i.e. password to encryption key) */ readonly kdf: KdfConfiguration; /** Information about the symmetric encryption */ - readonly encryption: { - /** - * An algorithm identifier, such as "xchacha20poly1305-ietf". - */ - readonly algorithm: string; - /** A map of algorithm-specific parameters */ - readonly params: Record; - }; + readonly encryption: EncryptionConfiguration; /** An instance of Secp256k1WalletData, which is stringified, encrypted and base64 encoded. */ readonly data: string; } @@ -179,11 +173,10 @@ export class Secp256k1Wallet implements OfflineSigner { const untypedRoot: any = root; switch (untypedRoot.type) { case serializationTypeV1: { - const nonce = fromHex(untypedRoot.encryption.params.nonce); - const decryptedBytes = await Xchacha20poly1305Ietf.decrypt( + const decryptedBytes = await decrypt( fromBase64(untypedRoot.data), encryptionKey, - nonce, + untypedRoot.encryption, ); const decryptedDocument = JSON.parse(fromUtf8(decryptedBytes)); const { mnemonic, accounts } = decryptedDocument; @@ -299,18 +292,17 @@ export class Secp256k1Wallet implements OfflineSigner { ), }; const dataToEncryptRaw = toUtf8(JSON.stringify(dataToEncrypt)); - const nonce = Random.getBytes(xchacha20NonceLength); - const encryptedData = await Xchacha20poly1305Ietf.encrypt(dataToEncryptRaw, encryptionKey, nonce); + + const encryptionConfiguration: EncryptionConfiguration = { + algorithm: supportedAlgorithms.xchacha20poly1305Ietf, + params: { nonce: toHex(Random.getBytes(xchacha20NonceLength)) }, + }; + const encryptedData = await encrypt(dataToEncryptRaw, encryptionKey, encryptionConfiguration); const out: Secp256k1WalletSerialization = { type: serializationTypeV1, kdf: kdfConfiguration, - encryption: { - algorithm: algorithmIdXchacha20poly1305Ietf, - params: { - nonce: toHex(nonce), - }, - }, + encryption: encryptionConfiguration, data: toBase64(encryptedData), }; return JSON.stringify(out); diff --git a/packages/sdk38/src/wallet.ts b/packages/sdk38/src/wallet.ts index 7378636b..ff5496a7 100644 --- a/packages/sdk38/src/wallet.ts +++ b/packages/sdk38/src/wallet.ts @@ -1,5 +1,12 @@ -import { Argon2id, Argon2idOptions, Sha256, Sha512, Slip10RawIndex } from "@cosmjs/crypto"; -import { toAscii } from "@cosmjs/encoding"; +import { + Argon2id, + Argon2idOptions, + Sha256, + Sha512, + Slip10RawIndex, + Xchacha20poly1305Ietf, +} from "@cosmjs/crypto"; +import { fromHex, toAscii } from "@cosmjs/encoding"; import { assert } from "@cosmjs/utils"; import { StdSignature } from "./types"; @@ -84,3 +91,50 @@ export async function executeKdf(password: string, configuration: KdfConfigurati throw new Error("Unsupported KDF algorithm"); } } + +/** + * Configuration how to encrypt data or how data was encrypted. + * This is stored as part of the wallet serialization and must only contain JSON types. + */ +export interface EncryptionConfiguration { + /** + * An algorithm identifier, such as "xchacha20poly1305-ietf". + */ + readonly algorithm: string; + /** A map of algorithm-specific parameters */ + readonly params: Record; +} + +export const supportedAlgorithms = { + xchacha20poly1305Ietf: "xchacha20poly1305-ietf", +}; + +export async function encrypt( + plaintext: Uint8Array, + encryptionKey: Uint8Array, + config: EncryptionConfiguration, +): Promise { + switch (config.algorithm) { + case supportedAlgorithms.xchacha20poly1305Ietf: { + const nonce = fromHex((config.params as any).nonce); + return Xchacha20poly1305Ietf.encrypt(plaintext, encryptionKey, nonce); + } + default: + throw new Error(`Unsupported encryption algorithm: '${config.algorithm}'`); + } +} + +export async function decrypt( + ciphertext: Uint8Array, + encryptionKey: Uint8Array, + config: EncryptionConfiguration, +): Promise { + switch (config.algorithm) { + case supportedAlgorithms.xchacha20poly1305Ietf: { + const nonce = fromHex((config.params as any).nonce); + return Xchacha20poly1305Ietf.decrypt(ciphertext, encryptionKey, nonce); + } + default: + throw new Error(`Unsupported encryption algorithm: '${config.algorithm}'`); + } +} diff --git a/packages/sdk38/types/secp256k1wallet.d.ts b/packages/sdk38/types/secp256k1wallet.d.ts index 32cffb3f..af84a682 100644 --- a/packages/sdk38/types/secp256k1wallet.d.ts +++ b/packages/sdk38/types/secp256k1wallet.d.ts @@ -1,6 +1,6 @@ import { Slip10RawIndex } from "@cosmjs/crypto"; import { StdSignature } from "./types"; -import { AccountData, KdfConfiguration, OfflineSigner, PrehashType } from "./wallet"; +import { AccountData, EncryptionConfiguration, KdfConfiguration, OfflineSigner, PrehashType } from "./wallet"; /** * This interface describes a JSON object holding the encrypted wallet and the meta data. * All fields in here must be JSON types. @@ -11,14 +11,7 @@ export interface Secp256k1WalletSerialization { /** Information about the key derivation function (i.e. password to encryption key) */ readonly kdf: KdfConfiguration; /** Information about the symmetric encryption */ - readonly encryption: { - /** - * An algorithm identifier, such as "xchacha20poly1305-ietf". - */ - readonly algorithm: string; - /** A map of algorithm-specific parameters */ - readonly params: Record; - }; + readonly encryption: EncryptionConfiguration; /** An instance of Secp256k1WalletData, which is stringified, encrypted and base64 encoded. */ readonly data: string; } diff --git a/packages/sdk38/types/wallet.d.ts b/packages/sdk38/types/wallet.d.ts index 8075062c..f2edb92d 100644 --- a/packages/sdk38/types/wallet.d.ts +++ b/packages/sdk38/types/wallet.d.ts @@ -38,3 +38,28 @@ export interface KdfConfiguration { readonly params: Record; } export declare function executeKdf(password: string, configuration: KdfConfiguration): Promise; +/** + * Configuration how to encrypt data or how data was encrypted. + * This is stored as part of the wallet serialization and must only contain JSON types. + */ +export interface EncryptionConfiguration { + /** + * An algorithm identifier, such as "xchacha20poly1305-ietf". + */ + readonly algorithm: string; + /** A map of algorithm-specific parameters */ + readonly params: Record; +} +export declare const supportedAlgorithms: { + xchacha20poly1305Ietf: string; +}; +export declare function encrypt( + plaintext: Uint8Array, + encryptionKey: Uint8Array, + config: EncryptionConfiguration, +): Promise; +export declare function decrypt( + ciphertext: Uint8Array, + encryptionKey: Uint8Array, + config: EncryptionConfiguration, +): Promise;