From c25c4e782229d982054d34b910f2626fd5d6e34a Mon Sep 17 00:00:00 2001 From: Saam Tehrani Date: Fri, 17 Jan 2025 17:42:08 -0700 Subject: [PATCH] finalize rsa tests, use new rsa module all around --- src/common/common.ts | 5 +- src/common/lib/crypto/keys/rsa.ts | 23 ++++-- src/common/lib/crypto/keys/secp256k1.ts | 13 ++-- src/common/lib/transaction.ts | 2 - src/common/wallets.ts | 16 +++- test/fixtures/RustCrypto-k256/msg.bin | 1 - test/fixtures/RustCrypto-k256/pk.json | 1 - test/fixtures/RustCrypto-k256/sig.bin | Bin 64 -> 0 bytes test/fixtures/RustCrypto-k256/sk.json | 1 - test/fixtures/RustCrypto-k256/src.rs | 53 ------------- test/fixtures/erlang/bad/msg.bin | Bin 2048 -> 0 bytes test/fixtures/erlang/bad/pk.json | 1 - test/fixtures/erlang/bad/sig.bin | Bin 64 -> 0 bytes test/fixtures/erlang/bad/sk.json | 1 - test/fixtures/erlang/msg.bin | Bin 2048 -> 13 bytes test/fixtures/erlang/pk.json | 2 +- test/fixtures/erlang/sig.bin | Bin 64 -> 65 bytes test/fixtures/erlang/sk.json | 2 +- test/fixtures/erlang/src.erl | 69 ----------------- test/keys.ts | 98 +++++++++++++++--------- 20 files changed, 103 insertions(+), 185 deletions(-) delete mode 100644 test/fixtures/RustCrypto-k256/msg.bin delete mode 100644 test/fixtures/RustCrypto-k256/pk.json delete mode 100644 test/fixtures/RustCrypto-k256/sig.bin delete mode 100644 test/fixtures/RustCrypto-k256/sk.json delete mode 100644 test/fixtures/RustCrypto-k256/src.rs delete mode 100644 test/fixtures/erlang/bad/msg.bin delete mode 100644 test/fixtures/erlang/bad/pk.json delete mode 100644 test/fixtures/erlang/bad/sig.bin delete mode 100644 test/fixtures/erlang/bad/sk.json delete mode 100644 test/fixtures/erlang/src.erl diff --git a/src/common/common.ts b/src/common/common.ts index 8e2c47d..2a57a2c 100644 --- a/src/common/common.ts +++ b/src/common/common.ts @@ -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; @@ -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) { diff --git a/src/common/lib/crypto/keys/rsa.ts b/src/common/lib/crypto/keys/rsa.ts index ce6e7b9..da14435 100644 --- a/src/common/lib/crypto/keys/rsa.ts +++ b/src/common/lib/crypto/keys/rsa.ts @@ -2,7 +2,7 @@ import { KeyType, PublicKey, PrivateKey, getInitializationOptions, getSigningPar import { b64UrlToBuffer, bufferTob64Url } from "../../utils"; export class RSAPrivateKey extends PrivateKey { - static usages: Array = ["sign"]; + static usages: Array = ["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 { if (modulusLength !== undefined) { if (modulusLength < 32 * 8 || modulusLength > 512 * 8) { @@ -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 { - 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}); } @@ -79,7 +79,11 @@ export class RSAPrivateKey extends PrivateKey { public async serialize({format}: SerializationParams): Promise { 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`); } @@ -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 { + 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}); } @@ -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 @@ -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!); diff --git a/src/common/lib/crypto/keys/secp256k1.ts b/src/common/lib/crypto/keys/secp256k1.ts index 6e9b6b2..363eeb3 100644 --- a/src/common/lib/crypto/keys/secp256k1.ts +++ b/src/common/lib/crypto/keys/secp256k1.ts @@ -80,7 +80,7 @@ export class SECP256k1PrivateKey extends PrivateKey { if (isDigest == false) { digest = new Uint8Array(await crypto.subtle.digest("SHA-256", payload)); } else if (digest.byteLength !== ByteLens.MSG_HASH) { - Promise.reject(`Invalid digest size ${digest.byteLength}`); + return Promise.reject(`Invalid digest size ${digest.byteLength}`); } const[signature, recovery]: SignatureAndRecovery = this.driver.sign(this.key, digest); const recovarableSignature = new Uint8Array(ByteLens.ECDSA_SIG_RECOVERABLE); @@ -132,13 +132,13 @@ export class SECP256k1PublicKey extends PublicKey { driver = (await ENGINE); } if (signature.byteLength !== ByteLens.ECDSA_SIG_RECOVERABLE) { - Promise.reject(`Invaldi signature length $${signature.byteLength} needs to by ${ByteLens.ECDSA_SIG_RECOVERABLE}`); + return Promise.reject(`Invaldi signature length ${signature.byteLength} needs to by ${ByteLens.ECDSA_SIG_RECOVERABLE}`); } let digest = payload; if (isDigest === false) { digest = new Uint8Array(await crypto.subtle.digest("SHA-256", payload)); } else if (digest.byteLength !== ByteLens.MSG_HASH) { - Promise.reject(`Invalid digest size ${digest.byteLength}`); + return Promise.reject(`Invalid digest size ${digest.byteLength}`); } const compactSignature = signature.slice(0, ByteLens.ECDSA_SIG_COMPACT); const recoveryId = signature[signature.byteLength - 1]; @@ -163,14 +163,14 @@ export class SECP256k1PublicKey extends PublicKey { public async verify({payload, signature, isDigest = false}: VerifyingParams): Promise { if (signature.byteLength !== ByteLens.ECDSA_SIG_RECOVERABLE) { - Promise.reject(`Invaldi signature length $${signature.byteLength} needs to by ${ByteLens.ECDSA_SIG_RECOVERABLE}`); + return Promise.reject(`Invaldi signature length ${signature.byteLength} needs to by ${ByteLens.ECDSA_SIG_RECOVERABLE}`); } const compactSignature = signature.slice(0, ByteLens.ECDSA_SIG_COMPACT); let digest = payload; if (isDigest === false) { digest = new Uint8Array(await crypto.subtle.digest("SHA-256", payload)); } else if (digest.byteLength !== ByteLens.MSG_HASH) { - Promise.reject(`Invalid digest size ${digest.byteLength}`); + return Promise.reject(`Invalid digest size ${digest.byteLength}`); } return this.driver.verify(compactSignature, digest, this.key); } @@ -203,7 +203,6 @@ export class SECP256k1PublicKey extends PublicKey { } public async identifier(): Promise { - const raw = await this.serialize({format: "raw"}); - return new Uint8Array([KeyTypeByte[KeyType.EC_SECP256K1], ...raw, 0]); + return new Uint8Array([...await this.serialize({format: "raw"})]); } } diff --git a/src/common/lib/transaction.ts b/src/common/lib/transaction.ts index d9438e5..9787ba3 100644 --- a/src/common/lib/transaction.ts +++ b/src/common/lib/transaction.ts @@ -253,12 +253,10 @@ export default class Transaction tag.get("value", { decode: true, string: false }), ]); let hashList: Array = [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), diff --git a/src/common/wallets.ts b/src/common/wallets.ts index 50665d4..496ddd2 100644 --- a/src/common/wallets.ts +++ b/src/common/wallets.ts @@ -6,6 +6,9 @@ import * as ArweaveUtils from "./lib/utils"; import "arconnect"; import { fromJWK } from "./lib/crypto/keys"; +export interface KeyGenerationParams { + type: T +} export default class Wallets { private api: Api; @@ -43,7 +46,10 @@ export default class Wallets { }); } - public async generateWallet({type = KeyType.RSA_65537}: {type: KeyType} = {type: KeyType.RSA_65537}): Promise { + // public async generateKey({type}: SerializationParams<"jwk">): Promise; + public async generateKey({type}: KeyGenerationParams): Promise; + public async generateKey({type}: KeyGenerationParams): Promise; + public async generateKey({type = KeyType.RSA_65537}: KeyGenerationParams): Promise { switch(type) { case KeyType.RSA_65537: return RSAPrivateKey.new(); @@ -54,12 +60,14 @@ 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( - jwk?: JWKInterface | "use_wallet" + jwk?: JWKInterface | "use_wallet" | PrivateKey | PublicKey ): Promise { return this.getAddress(jwk); } diff --git a/test/fixtures/RustCrypto-k256/msg.bin b/test/fixtures/RustCrypto-k256/msg.bin deleted file mode 100644 index 336732e..0000000 --- a/test/fixtures/RustCrypto-k256/msg.bin +++ /dev/null @@ -1 +0,0 @@ -l±TëÙ,gD ͘€<€)×iœ-}©Ô÷ •<Ê° \ No newline at end of file diff --git a/test/fixtures/RustCrypto-k256/pk.json b/test/fixtures/RustCrypto-k256/pk.json deleted file mode 100644 index 7ddf0f5..0000000 --- a/test/fixtures/RustCrypto-k256/pk.json +++ /dev/null @@ -1 +0,0 @@ -{"kty":"EC","crv":"secp256k1","x":"C2frqVbstVWOjiaRlOOTLW45mx8B1fqAsYiN90K1Rb8","y":"C3gFS4ahukAft_j8tzVuLDMilC4qk3Uz66FSKsyOWLE"} \ No newline at end of file diff --git a/test/fixtures/RustCrypto-k256/sig.bin b/test/fixtures/RustCrypto-k256/sig.bin deleted file mode 100644 index e028faa1840a95b90ed234b22ec43747747317b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64 zcmV-G0KfnC=>Q<&groYoC5U?Iw=GdsOxM-u-jl*jf?mTx&ic$gM_WSNg|$<14`}n1 WR}I9dxa;aqvxHBM>*KL;6_G=p+#|yP diff --git a/test/fixtures/RustCrypto-k256/sk.json b/test/fixtures/RustCrypto-k256/sk.json deleted file mode 100644 index a414666..0000000 --- a/test/fixtures/RustCrypto-k256/sk.json +++ /dev/null @@ -1 +0,0 @@ -{"kty":"EC","crv":"secp256k1","x":"C2frqVbstVWOjiaRlOOTLW45mx8B1fqAsYiN90K1Rb8","y":"C3gFS4ahukAft_j8tzVuLDMilC4qk3Uz66FSKsyOWLE","d":"mR1VX4cR1ZBD4L-vh5e_AL_ay3khAEyg6txU6ppoAGc"} \ No newline at end of file diff --git a/test/fixtures/RustCrypto-k256/src.rs b/test/fixtures/RustCrypto-k256/src.rs deleted file mode 100644 index b9a6929..0000000 --- a/test/fixtures/RustCrypto-k256/src.rs +++ /dev/null @@ -1,53 +0,0 @@ -use k256::ecdsa::{VerifyingKey, signature::Verifier, SigningKey, Signature, signature::Signer}; -use k256::sha2::Digest; -use rand_core::{OsRng, RngCore}; // requires 'getrandom' feature -use std::fs::File; -use std::io::{Write, Read}; -use serde::{Serialize, Deserialize}; - -#[derive(Serialize, Deserialize)] -struct JwkKey { - kty: String, - crv: String, - x: String, - y: String, - d: Option, -} -use base64::{engine::general_purpose, Engine as _}; -fn main() { - let message_size = 2048; - let mut rng = OsRng; - let mut message = vec![0u8; message_size]; - rng.fill_bytes(&mut message); - let engine = &general_purpose::URL_SAFE_NO_PAD; - - let mut file = File::create("msg.bin").unwrap(); - file.write_all(&message).unwrap(); - let digest = k256::sha2::Sha256::digest(&message); - - let signing_key = SigningKey::random(&mut OsRng); - let sk_jwk = JwkKey { - kty: "EC".to_string(), - crv: "secp256k1".to_string(), - x: engine.encode(signing_key.verifying_key().to_encoded_point(false).x().unwrap()), - y: engine.encode(signing_key.verifying_key().to_encoded_point(false).y().unwrap()), - d: Some(engine.encode(signing_key.to_bytes())), - }; - let sk_file = File::create("sk.json").unwrap(); - serde_json::to_writer(sk_file, &sk_jwk).unwrap(); - let signature: Signature = signing_key.sign(&digest); - let mut file = File::create("sig.bin").unwrap(); - file.write_all(&signature.to_bytes()).unwrap(); - - let verifying_key = VerifyingKey::from(&signing_key); - let pk_jwk = JwkKey { - kty: "EC".to_string(), - crv: "secp256k1".to_string(), - x: engine.encode(signing_key.verifying_key().to_encoded_point(false).x().unwrap()), - y: engine.encode(signing_key.verifying_key().to_encoded_point(false).y().unwrap()), - d: None - }; - let pk_file = File::create("pk.json").unwrap(); - serde_json::to_writer(pk_file, &pk_jwk).unwrap(); - assert!(verifying_key.verify(&digest, &signature).is_ok()); -} diff --git a/test/fixtures/erlang/bad/msg.bin b/test/fixtures/erlang/bad/msg.bin deleted file mode 100644 index 3d3f3589db13449955abc2251a46763c18cbcb4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2048 zcmV+b2>zf z4v;V4QW1z$lGNFBHVKnV{EzFAUv9cev96mbho+rU*Q4Ahlm(im3@XBJtR^Br>9VIDO%|!l3Llkm;`YHH1+z#JuH>gnpPy zWqpGr_K+xp(-4-!q7!$5Vjl(8d8vIVt*zJ(l|ffD04Z?h%fb77>)vK>VcJrUcmPed z_Q|2$m8BHv-`WXe7hSA!honL>}G*h-$EIUHyEs)c;@bsVHl@z$}*n5ONv4c!xs zNE+e#tK>V)G|W%i+S*pb)3XDS92v|xnYU7D<0QsecHOWh@In8|9aL73-FvF-?PBhd zDA_GTi%N9mnCxq5$3wejpJ=T$tg`gi^cKxudY=`$-A62{3^{`H_DB3#wQZUd>1}O- zTjRmG?WI03MxPi68S-F236ifag;nq_YFL5?Ow4)0 z6U9a*+*XO!dz3PL2{G8H9SuyBz7cjA<4wm;+irUsd|1u2)V|1M_k9(^VhibX0*fc! zLHQV-*}QFCq$)7~>9+4v)m~F-4yoyEhco!~joQ|H7DlGyJ@?z}CA|frm)ogCZV{oZ z6G^a2+c%N>-WT%Mj;*8mkdOAeAWOHqZi4^~IZ%oIwS*t+9q4}k2XJy>Fb?(K0y^~i zN9RRr`@4xAD%WnbIDv1)^&xK!b7Jo2D^J>H-&YfA{mFkl|9);8+S$E!vPirX^Nvk-XVeHYWdVu3wTZ zXMQwmI$11-WQ&d3KYrOYr}h_*@aFzY;+%uoyktGA%MW9zevVn&NvRg{B9*a%|F&855J7gCan~2(R9}ze1u`%K&&3+rMa%wVaESz z3%>WHZ9ZHpI7+9^GyUx6z_R*N5VW8c`qGWQp=&oD(4DN6aI_Y4jGmXS(T&fsTDS(6 zEcqLw$#6@&=#3)H0p<9M-sOKItO6#TO@Ud z=W9Iv;BbSVRr*PgtU5FWRNe~`8;HBkUNPLc*e@H4p^uk4N zH*uojYJV+0I_yL-?t**tYJ|Cp7ywRc+-cVg4kSrZP(ZnvKv63M@uppA*bRuh%s+@qw$Rze~d6;%yYWR8YdAOo!xnG?P&F9o@E=^ zz-iQMY*h{FJ}qOjlfS0rxXci2V!Mf)u*7!&et=dkW(|~F&bUjCb23jGK#2qEq3nSo zukqxq7K8T}>%^pD8NJ=&e?GFaQZVg&23auqJbprw1(v^?9SHpiM*U}49CfpEgZb|_RbK|8wrCTpvv{5OYiOlGoiTk)1%KlSO={GzCeWi);EMx9t04XW;yt* za(OmE#zls-I@J^}X@Xv{M8it1L3l}a7;N=lZrnT%*y{XM+Xw`)r8Q(J%Bg1hS{7-J?7J+_9r8YH!Uaa}5tp}-c@ z-H@OZ zzO(OaV7{yZ4iq$Z*n0(huEyB%{Bnzz-0zw`OH3O~e1TaIW&S_@Uz71W&0JY+b?BCB z3+^GomH$O}X~P?C4@(juodMPCg!WEka(`jAjJEm9JC4haYWYj)QD$!Xh)A6n^V_Gn zj^i1N(zTqckym(UoJmPQLl}v{egt;;o?gCO!mOSSrDtBzyu&hVyCdM#+q=G2IA%4x e3&%o=jbwW9EZ)9I|3AG(VMJINUyVL+txg}<;shfA diff --git a/test/fixtures/erlang/bad/pk.json b/test/fixtures/erlang/bad/pk.json deleted file mode 100644 index fc2597e..0000000 --- a/test/fixtures/erlang/bad/pk.json +++ /dev/null @@ -1 +0,0 @@ -{"crv":"secp256k1","kty":"EC","x":"UST_lMlB9UOra_QOgq1mkJOBs76z8T57tchRG8_fMWA","y":"EmEDrx2vWCI-R1x1esQ6CZjCedTIn8ylGn3cV4QMrBE"} \ No newline at end of file diff --git a/test/fixtures/erlang/bad/sig.bin b/test/fixtures/erlang/bad/sig.bin deleted file mode 100644 index 30a674951a554d4535917075afa44b97b3920d99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64 zcmV-G0Kflm^awma0u1Yaa&ZxJEU$rns%9Ph!G=Nr{vi463ch=s;>*eZZ`>Ut*StL@ WSmXsj8}RaCP?Dc@W+stUTuG7BP#&rP diff --git a/test/fixtures/erlang/bad/sk.json b/test/fixtures/erlang/bad/sk.json deleted file mode 100644 index d1eb8a5..0000000 --- a/test/fixtures/erlang/bad/sk.json +++ /dev/null @@ -1 +0,0 @@ -{"crv":"secp256k1","d":"26kqNRRqYQFVjRCvY0fknAxoW6f7LGvFOa83RZ6FH8g","kty":"EC"} \ No newline at end of file diff --git a/test/fixtures/erlang/msg.bin b/test/fixtures/erlang/msg.bin index 1bf4949c838605fd405da1d01b45a2c9d0ee7989..6e3b12cbbb2512c8258d0068403f43b0c9c0f1f1 100644 GIT binary patch literal 13 UcmZ=M&CRV;@J%f)PE1b)03rzl@&Et; literal 2048 zcmV+b2> z{Z(rN&9l!>j#4E-56~dgtAYSSCnG9^DTRYTzY5ah(7yMkrEIZw6f}iN&!L9)yZa$V zMSRsUm)Nh2hL`#3)NT#H*m2sZpZ=;QU!HIWrbCfn!%)cz=D+Po?m1oAFBT~{`K1eb z@Xbd?p;s5oDdy#jfXHq`8^@?XJVZgb9)&U5HL6YG>@Nv2C_6!J0LmGYjRcR-yHnRF zXGrbj#e`K;2MlFuk>z^=W!-XLr+SltsVzC#FP#SLgq}O&#k5W!QCS8Re$l%y<%6F& zf#E8srm#6Y$c)L9EJoV`aG(9OaJ;=m%wY*rI~H-vqh+o3gFn0cJySOlz0P5` zb_*EqH@}Ls5n5)Mlv`$lgBhjD{TSeDdxb!s^WgjCLtNIIXliyF8Oqs|wlx5PH} z*2;D%IIoWZLor01|p2UA7nk z<=gPm^YjO$*RMa1fut&d!^gblJkdEku$9tt1yW6WZ5#s^;TGVzcFy~>pDQ>=a=wJblj(T7ad#=M-t zj$Y@MAXlD`pM&%X%Tm5ruR%x&ntY=|7hUZQ*qQk+qtLYTY4<>$w>&O<9ixW0jajHO z$1|I;^tnjZ+nNPNYbT*cY?P^I^0^RLa2;WTVr4u!k~pZdZf2XyAVQfbC86r9&F?Kt z7&F>=#B|5}+9EM~bNqo7S>t5=0Tkt@{K<41ja-)=L`)!>#<%PH-*%NffV11~Dn!XI zHgd7E_3@*fHGX7*JTO;lXN@3O@18zvQ+bPy>!fE;GPjP?nTQ+wE4RU%owWcUVgg;` zhIG7239fFO#t@)v{{|3+qJZ0rY}wWX$C#3IL69|-5b~10@ao&8aM-#_D}NBC2^TwQ zEAKg#@XVM`tZc?_Erb8;4?@$?40g^T8MFswD-C;2QV&s`-j3JWT9Em@bq!z=cVPuu;3WZxBe#vvLn7dux2iSUe;;-v*<@43U} z*2UZ8prXFq{{gr}F};zwFH?~#KWt({unUqWgy!i!)^G@uC-nA1GRV?)!7%|WwPyi~!qDJK(3@kZBx7D2_2XyI9zTQaeg2z(14 zYo7#xvyjoLJba$|ZOPvBb$q=oUJf+)HQawoEmU1y7;UG2dINr2uK7VZ^Ijc(JksH7 z;9Q2Cz`=$8R<%at@E?(X2x4i3l`mnyN*0upeld{zW&8_F_k>;5l7eT+s!;C&l(Gv8 zm$Y;(P&?WXm$Ri5d|^{bsT*i*2{;V6LQL!8zn|LOnf_(!{6qej_W^d^gDF(){bWhJ zBsgxrpcHObaC`wg7&W3xHe>Oae-T;$_7-%HitF`$c}Y& zhd8|0i1U2K#nXjnoe3^8!p!`H5;VN7hHV-;Sv_zx@A2Ggt+3Q+smFJZWme>3BN^*( z0K<`!z6(NDomoJeO)%o?J+>pFW*mM{J|%|%PlIbRy3&oFDQUZ9EGqZ zt}XKF`(shKy)fwO`kc%wHYW8IT#gwwQW3_D$G2`d!89_TX#?$wjr=60m@SK$(`klt zK;bs%qp~wV2)j8)O~a`hRwIPsWzs6z9EgX5W0-k{K>yKh+bb<#AXBZZ?hy_bfB%*T6BZi)d+q!3PC%l|^Oh_8N?qq?K8OW@^1UHEQ<0kGT%*ip2xaezf%ehe5?n zvZ=Y0@PyT_qUU}m9>XQh7@dA)0$}+jW_X~bxR8joLtaa) z!jZ#K)vE6insbRBABqbG@tL%+nNu@tP(HZewG?yb}8i31ZvjH!P7<8TQ(vTRV30sY>#b1udAW-ZYZ*H+_A7rr&)A3eAMm+!g?{e$ XXfG)T;R9f0Ow^{~aC&_Wc^abtEZrY% literal 64 zcmV-G0Kfk&aAIz%mD~9MB)jJcaoHzWUgJu}jf0ap?ytSdwMrcwWPvT+CQ#C>rz1Vd WLqSQl|Ie2whxy7%_>6CvmfVgN?IDH$ diff --git a/test/fixtures/erlang/sk.json b/test/fixtures/erlang/sk.json index 30e6689..e9926b4 100644 --- a/test/fixtures/erlang/sk.json +++ b/test/fixtures/erlang/sk.json @@ -1 +1 @@ -{"crv":"secp256k1","d":"ehs4HqZTA6zO_DojYYTVIKtAXMIoTwqxoebybJFv9zU","kty":"EC"} \ No newline at end of file +{"kty":"EC","crv":"secp256k1","d":"j0qcQaZZv-YmxsnUlfUtbgvcmpYlCQFjfA6T9An25qQ","x":"l_Kr2lNxtAekWHlQ1nfMaHwKRuP_Gzd9_4RFHmFejgA","y":"XWTfbtSexI6qSYA8zQjJeFA6bQc6KP4eiiqpqhX7rUo"} diff --git a/test/fixtures/erlang/src.erl b/test/fixtures/erlang/src.erl deleted file mode 100644 index 41adc3b..0000000 --- a/test/fixtures/erlang/src.erl +++ /dev/null @@ -1,69 +0,0 @@ --module(ecdsa_secp256k1). --export([create_fixtures/0]). --include_lib("public_key/include/public_key.hrl"). --define(SigUpperBound, binary:decode_unsigned(<<16#7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0:256>>)). --define(SigDiv, binary:decode_unsigned(<<16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141:256>>)). - -create_fixtures() -> - {PrivateKey, PublicKey} = generate_key_secp256k1(), - write_jwk(PrivateKey), - Msg = crypto:strong_rand_bytes(2048), - file:write_file("./fixtures/msg.bin", Msg), - Signature_DER = sign(Msg, PrivateKey), - file:write_file("./fixtures/sig.der", Signature_DER), - write_raw_sig(Signature_DER), - ECPoint = #'ECPoint'{point=PublicKey}, - PK = {ECPoint, {namedCurve, secp256k1}}, - public_key:verify(Msg, sha256, Signature_DER, PK). - -sign(Msg, PrivKey) -> - Sig = public_key:sign( - Msg, - sha256, - PrivKey - ), - correct_low_s(Sig). - -write_raw_sig(Der) -> - {_, R, S} = public_key:der_decode('ECDSA-Sig-Value', Der), - RBin = int_to_bin(R), - SBin = int_to_bin(S), - file:write_file("./fixtures/sig.bin", <>). - -write_jwk({_, _, PrivBytes, _, PubBytes, _}) -> - <<4:8, XY/binary>> = PubBytes, - <> = XY, - JX = base64url:encode(X), - JY = base64url:encode(Y), - JD = base64url:encode(PrivBytes), - PrivateJWK = jsx:encode(#{<<"kty">> => <<"EC">>, <<"crv">> => <<"secp256k1">>, <<"d">> => JD}), - PublicJWK = jsx:encode(#{<<"kty">> => <<"EC">>, <<"crv">> => <<"secp256k1">>, <<"x">> => JX, <<"y">> => JY}), - file:write_file("./fixtures/pk.json", PublicJWK), - file:write_file("./fixtures/sk.json", PrivateJWK). - -generate_key_secp256k1() -> - PrivateKey = public_key:generate_key({namedCurve, secp256k1}), - {_, _, _, _, PubBytes, _} = PrivateKey, - {PrivateKey, PubBytes}. - -int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []); -int_to_bin(X) -> int_to_bin_pos(X, []). - -int_to_bin_pos(0,Ds=[_|_]) -> - list_to_binary(Ds); -int_to_bin_pos(X,Ds) -> - int_to_bin_pos(X bsr 8, [(X band 255)|Ds]). - -int_to_bin_neg(-1, Ds=[MSB|_]) when MSB >= 16#80 -> - list_to_binary(Ds); -int_to_bin_neg(X,Ds) -> - int_to_bin_neg(X bsr 8, [(X band 255)|Ds]). - -correct_low_s(Sig) -> - {_, R, S} = public_key:der_decode('ECDSA-Sig-Value', Sig), - case S =< ?SigUpperBound of - true -> - Sig; - false -> - public_key:der_encode('ECDSA-Sig-Value', #'ECDSA-Sig-Value'{ r = R, s = ?SigDiv - S }) - end. diff --git a/test/keys.ts b/test/keys.ts index 6732729..4b4ff83 100644 --- a/test/keys.ts +++ b/test/keys.ts @@ -1,14 +1,16 @@ 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"; +import { sign } from "crypto"; 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); @@ -58,16 +60,7 @@ describe("Crypto: EllipticCurve secp256k1", function () { const raw = await pubKeyA.serialize({format: "raw"}); expect(raw.byteLength).to.equal(33); const identifier = await pubKeyA.identifier(); - expect(identifier.byteLength).to.equal(35); - expect(identifier[0]).to.equal(KeyTypeByte[KeyType.EC_SECP256K1]); - // @ts-ignore - if (pubKeyA.key[pubKeyA.key.byteLength-1] % 2 === 0) { - expect(identifier[1]).to.equal(2); - } else { - expect(identifier[1]).to.equal(3); - } - expect(identifier.slice(2, 34)).to.deep.equal(raw.slice(1, 33)); - expect(identifier[34]).to.deep.equal(0); + expect(identifier.byteLength).to.equal(33); // recover pk from sig & data const recoveredPubKeyA = await SECP256k1PublicKey.recover({payload, isDigest: false, signature: sigA }); @@ -83,6 +76,7 @@ describe("Crypto: EllipticCurve secp256k1", function () { expect(await inValidSigPubKeyA.serialize({format: "raw"})).to.not.deep.equal(raw); } }); + it("Erlang Crypto Compatibility", async function() { const path = "./test/fixtures/erlang"; const sk = JSON.parse(readFileSync(`${path}/sk.json`).toString()) as JsonWebKey; @@ -98,34 +92,66 @@ describe("Crypto: EllipticCurve secp256k1", function () { const payload = readFileSync(`${path}/msg.bin`); const sig = new Uint8Array(readFileSync(`${path}/sig.bin`)); - expect(await (await privKey.public()).verify({payload, signature: sig})).true + expect(await (await privKey.public()).verify({payload, signature: sig})).true; + expect(sig).to.deep.equal(await privKey.sign({payload, isDigest: false})); }); +}); - it("RustCrypto-k256 Compatibility", async function() { - const path = "./test/fixtures/RustCrypto-k256"; - const sk = JSON.parse(readFileSync(`${path}/sk.json`).toString()) as JsonWebKey; - const pk = JSON.parse(readFileSync(`${path}/pk.json`).toString()) as JsonWebKey; +describe("Crypto: RSA 65537", function () { + this.timeout(20000); + it("De/Serialization Sign/Verify", async function() { + 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 privKey = await SECP256k1PrivateKey.deserialize({format: "jwk", keyData: sk}); - const pubKey = await SECP256k1PublicKey.deserialize({format: "jwk", keyData: pk}); + const pubKeyA = await privKeyA.public(); - expect(((await privKey.serialize({format: "jwk"})))['d']).to.deep.equal(sk['d']); - expect(((await pubKey.serialize({format: "jwk"})))['x']).to.deep.equal(pk['x']); - expect(((await pubKey.serialize({format: "jwk"})))['y']).to.deep.equal(pk['y']); - expect(((await (await privKey.public()).serialize({format: "jwk"})))['x']).to.deep.equal(pk['x']); - expect(((await (await privKey.public()).serialize({format: "jwk"})))['y']).to.deep.equal(pk['y']); + const pubKeyAJWK = await pubKeyA.serialize({format: "jwk"}); - const payload = readFileSync(`${path}/msg.bin`); - const sig = new Uint8Array(readFileSync(`${path}/sig.bin`)); - expect(await (await privKey.public()).verify({payload, signature: sig})).true - }); -}); + const pubKeyAAJWK = await RSAPublicKey.deserialize({format: "jwk", keyData: pubKeyAJWK, type: KeyType.RSA_65537}); -describe("Crypto: RSA 64437", function () { - it("De/Serialization Sign/Verify", async function() { - throw new Error('todo!') - }); - it("Erlang Crypto Compatibility", async function() { - throw new Error('todo!') + 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); }); });