Skip to content

Commit

Permalink
finalize rsa tests, use new rsa module all around
Browse files Browse the repository at this point in the history
  • Loading branch information
samantehrani committed Jan 18, 2025
1 parent a8eaaac commit 999d330
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 20 deletions.
5 changes: 4 additions & 1 deletion src/common/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +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";
import { KeyType, PrivateKey, PublicKey, fromJWK } from "./lib/crypto/keys";

export interface Config {
api: ApiConfig;
Expand Down Expand Up @@ -108,6 +108,9 @@ export default class Arweave {
}
transaction.owner = await pk.identifier()
.then(id => ArweaveUtils.bufferTob64Url(id));
if (pk.type === KeyType.EC_SECP256K1) {
transaction.owner = "";
}
}

if (attributes.last_tx == undefined) {
Expand Down
23 changes: 17 additions & 6 deletions src/common/lib/crypto/keys/rsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { KeyType, PublicKey, PrivateKey, getInitializationOptions, getSigningPar
import { b64UrlToBuffer, bufferTob64Url } from "../../utils";

export class RSAPrivateKey extends PrivateKey {
static usages: Array<KeyUsage> = ["sign"];
static usages: Array<KeyUsage> = ["sign", "verify"];
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<RSAPrivateKey> {
if (modulusLength !== undefined) {
if (modulusLength < 32 * 8 || modulusLength > 512 * 8) {
Expand All @@ -23,7 +23,7 @@ export class RSAPrivateKey extends PrivateKey {
}

static async deserialize({driver = crypto.subtle, format, keyData, type}: {driver?: SubtleCrypto, format: "jwk" | "raw" | "pkcs8" | "spki", keyData: JsonWebKey | Uint8Array, type: KeyType}): Promise<RSAPrivateKey> {
const key = await driver.importKey(format as any, keyData as any, getInitializationOptions(type), true, RSAPrivateKey.usages);
const key = await driver.importKey(format as any, keyData as any, getInitializationOptions(type), true, ["sign"]);
return new RSAPrivateKey({driver, type, key});
}

Expand Down Expand Up @@ -79,7 +79,11 @@ export class RSAPrivateKey extends PrivateKey {
public async serialize({format}: SerializationParams): Promise<JsonWebKey | Uint8Array> {
switch (format) {
case "jwk":
return this.driver.exportKey("jwk", this.key);
let jwk = await this.driver.exportKey("jwk", this.key);
delete jwk.ext;
delete jwk.key_ops;
delete jwk.alg;
return jwk;
default:
throw new Error(`Format ${format} no suppoerted`);
}
Expand All @@ -100,15 +104,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<RSAPublicKey> {
let k: JsonWebKey;
if (format === "raw") {
keyData = {
k = {
kty: "RSA",
e: "AQAB",
n: bufferTob64Url(keyData as Uint8Array),
key_ops: RSAPublicKey.usages
};
format = "jwk";
} else {
k = {...keyData as JsonWebKey, key_ops: RSAPublicKey.usages};
}
const key = await driver.importKey(format as any, keyData as any, getInitializationOptions(type), true, RSAPublicKey.usages);
const key = await driver.importKey(format as any, k as any, getInitializationOptions(type), true, RSAPublicKey.usages);
return new RSAPublicKey({driver, type, key});
}

Expand All @@ -118,7 +126,7 @@ export class RSAPublicKey extends PublicKey {
let result = false;
for (let s of [0, 32, maxSaltSize(this.key)]) {
if (result = await this.driver.verify(
{name: "RSA-PSS", saltLength: s},
{...getSigningParameters(this.type) as RsaPssParams, saltLength: s},
this.key,
signature,
payload
Expand All @@ -139,6 +147,9 @@ export class RSAPublicKey extends PublicKey {
const jwk = await this.driver.exportKey("jwk", this.key);
switch(format) {
case "jwk":
delete jwk.key_ops;
delete jwk.ext;
delete jwk.alg;
return jwk;
case "raw":
return b64UrlToBuffer(jwk.n!);
Expand Down
2 changes: 0 additions & 2 deletions src/common/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,10 @@ export default class Transaction
tag.get("value", { decode: true, string: false }),
]);
let hashList: Array<any> = [ArweaveUtils.stringToBuffer(this.format.toString())];

// ECDSA should not have owner field in the signature
if (this.owner !== "") {
hashList.push(this.get("owner", { decode: true, string: false }));
}

hashList = [...hashList,
this.get("target", { decode: true, string: false }),
ArweaveUtils.stringToBuffer(this.quantity),
Expand Down
14 changes: 11 additions & 3 deletions src/common/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import * as ArweaveUtils from "./lib/utils";
import "arconnect";
import { fromJWK } from "./lib/crypto/keys";

export interface KeyGenerationParams<T extends KeyType = KeyType> {
type: T
}

export default class Wallets {
private api: Api;
Expand Down Expand Up @@ -43,7 +46,10 @@ export default class Wallets {
});
}

public async generateWallet({type = KeyType.RSA_65537}: {type: KeyType} = {type: KeyType.RSA_65537}): Promise<PrivateKey> {
// public async generateKey({type}: SerializationParams<"jwk">): Promise<JsonWebKey>;
public async generateKey({type}: KeyGenerationParams<KeyType.EC_SECP256K1>): Promise<SECP256k1PrivateKey>;
public async generateKey({type}: KeyGenerationParams<KeyType.RSA_65537>): Promise<RSAPrivateKey>;
public async generateKey({type = KeyType.RSA_65537}: KeyGenerationParams): Promise<PrivateKey> {
switch(type) {
case KeyType.RSA_65537:
return RSAPrivateKey.new();
Expand All @@ -54,8 +60,10 @@ export default class Wallets {
}
}

public generate() {
return this.crypto.generateJWK();
public async generate() {
return this.generateKey({type: KeyType.RSA_65537})
.then(k => k.serialize({format: "jwk"}))
.then(jwk => jwk as JWKInterface);
}

public async jwkToAddress(
Expand Down
67 changes: 59 additions & 8 deletions test/keys.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { readFileSync } from "fs";
import * as chai from "chai";

import { SECP256k1PrivateKey, SECP256k1PublicKey } from "../src/common/lib/crypto/keys";
import { arweaveInstance } from "./_arweave";
import { RSAPrivateKey, RSAPublicKey, SECP256k1PrivateKey, SECP256k1PublicKey } from "../src/common/lib/crypto/keys";
import { KeyType, KeyTypeByte } from "../src/common/lib/crypto/keys/interface";

const expect = chai.expect;
const arweave = arweaveInstance();

describe("Crypto: EllipticCurve secp256k1", function () {
it("test de/serialization sign/verify", async function() {
const privKeyA = await SECP256k1PrivateKey.new();
const privKeyA = await arweave.wallets.generateKey({type: KeyType.EC_SECP256K1});
const privKeyAJWK = await privKeyA.serialize({format: "jwk"});
const privKeyAA = await SECP256k1PrivateKey.deserialize({format: "jwk", keyData: privKeyAJWK});
expect(privKeyA.type).to.be.equal(KeyType.EC_SECP256K1);
Expand Down Expand Up @@ -121,11 +122,61 @@ describe("Crypto: EllipticCurve secp256k1", function () {
});
});

describe("Crypto: RSA 64437", function () {
describe("Crypto: RSA 65537", function () {
this.timeout(20000);
it("De/Serialization Sign/Verify", async function() {
throw new Error('todo!')
});
it("Erlang Crypto Compatibility", async function() {
throw new Error('todo!')
const privKeyA = await arweave.wallets.generateKey({type: KeyType.RSA_65537});
const privKeyAJWK = await privKeyA.serialize({format: "jwk"});
const privKeyAA = await RSAPrivateKey.deserialize({format: "jwk", keyData: privKeyAJWK, type: KeyType.RSA_65537});
expect(privKeyA.type).to.be.equal(KeyType.RSA_65537);
expect(privKeyAJWK).to.include.keys(
"kty",
"n",
"e",
"d",
"p",
"q",
"dp",
"dq",
"qi"
);
expect(privKeyAJWK.kty).to.equal('RSA');
expect(privKeyAJWK.e).to.equal("AQAB");
expect(privKeyAJWK).to.deep.equal(await privKeyAA.serialize({format: "jwk"}));

const pubKeyA = await privKeyA.public();

const pubKeyAJWK = await pubKeyA.serialize({format: "jwk"});

const pubKeyAAJWK = await RSAPublicKey.deserialize({format: "jwk", keyData: pubKeyAJWK, type: KeyType.RSA_65537});

expect(pubKeyA.type).to.equal(KeyType.RSA_65537);
expect(privKeyAJWK).to.include.keys(
"kty",
"n",
"e"
);
expect(pubKeyAJWK).to.not.have.property('d');
expect(privKeyAJWK.kty).to.equal('RSA');
expect(privKeyAJWK.e).to.equal("AQAB");
expect(pubKeyAJWK.n).to.equal(privKeyAJWK.n);
expect(pubKeyAJWK).to.deep.equal(await pubKeyAAJWK.serialize({format: "jwk"}));

let payload = new Uint8Array(Math.random() * 1000);
payload = crypto.getRandomValues(payload);
const sigA = await privKeyA.sign({payload});
expect(sigA.length).be.equal(512);
const sigB = await (await RSAPrivateKey.new()).sign({payload});
expect(sigB.length).length.be.equal(512);
expect(sigB).not.to.be.equal(sigA);

expect(await pubKeyA.verify({payload, signature: sigA})).true;
expect(await (await RSAPublicKey.deserialize({format: "jwk", keyData: pubKeyAJWK, type: KeyType.RSA_65537})).verify({payload, signature: sigA})).true;
expect(await pubKeyA.verify({payload, signature: sigB})).false;

const raw = await pubKeyA.serialize({format: "raw"});
expect(raw.byteLength).to.equal(512);
const identifier = await pubKeyA.identifier();
expect(identifier.byteLength).to.equal(512);
});
});

0 comments on commit 999d330

Please sign in to comment.