@cloudflare/blindrsa-ts
Advanced tools
Comparing version 0.1.2 to 0.2.0
@@ -9,8 +9,12 @@ export declare enum PrepareType { | ||
}; | ||
export interface BlindRSAParams { | ||
name: string; | ||
hash: string; | ||
saltLength: number; | ||
prepareType: PrepareType; | ||
} | ||
export declare class BlindRSA { | ||
private readonly hash; | ||
private readonly saltLength; | ||
private readonly prepareType; | ||
readonly params: BlindRSAParams; | ||
private static readonly NAME; | ||
constructor(hash: string, saltLength: number, prepareType: PrepareType); | ||
constructor(params: BlindRSAParams); | ||
toString(): string; | ||
@@ -22,5 +26,6 @@ prepare(msg: Uint8Array): Uint8Array; | ||
finalize(publicKey: CryptoKey, msg: Uint8Array, blindSig: Uint8Array, inv: Uint8Array): Promise<Uint8Array>; | ||
generateKey(algorithm: Pick<RsaHashedKeyGenParams, 'modulusLength' | 'publicExponent'>, extractable: boolean, keyUsages: readonly KeyUsage[]): Promise<CryptoKeyPair>; | ||
verify(publicKey: CryptoKey, signature: ArrayBuffer, message: ArrayBuffer): Promise<boolean>; | ||
static generateKey(algorithm: Pick<RsaHashedKeyGenParams, 'modulusLength' | 'publicExponent' | 'hash'>): Promise<CryptoKeyPair>; | ||
generateKey(algorithm: Pick<RsaHashedKeyGenParams, 'modulusLength' | 'publicExponent'>): Promise<CryptoKeyPair>; | ||
verify(publicKey: CryptoKey, signature: Uint8Array, message: Uint8Array): Promise<boolean>; | ||
} | ||
//# sourceMappingURL=blindrsa.d.ts.map |
// Copyright (c) 2023 Cloudflare, Inc. | ||
// Licensed under the Apache-2.0 license found in the LICENSE file or at https://opensource.org/licenses/Apache-2.0 | ||
import sjcl from './sjcl/index.js'; | ||
import { assertNever, emsa_pss_encode, i2osp, is_coprime, joinAll, os2ip, random_integer_uniform, rsasp1, rsavp1, } from './util.js'; | ||
import sjcl from './sjcl/index.js'; | ||
export var PrepareType; | ||
@@ -11,7 +11,5 @@ (function (PrepareType) { | ||
export class BlindRSA { | ||
constructor(hash, saltLength, prepareType) { | ||
this.hash = hash; | ||
this.saltLength = saltLength; | ||
this.prepareType = prepareType; | ||
switch (this.prepareType) { | ||
constructor(params) { | ||
this.params = params; | ||
switch (params.prepareType) { | ||
case PrepareType.Deterministic: | ||
@@ -21,10 +19,13 @@ case PrepareType.Randomized: | ||
default: | ||
assertNever('PrepareType', prepareType); | ||
assertNever('PrepareType', params.prepareType); | ||
} | ||
} | ||
toString() { | ||
return `RSABSSA-${this.hash.replace('-', '')}-PSS${this.saltLength === 0 ? 'ZERO' : ''}-${PrepareType[this.prepareType]}`; | ||
const hash = this.params.hash.replace('-', ''); | ||
const pssType = 'PSS' + (this.params.saltLength === 0 ? 'ZERO' : ''); | ||
const prepare = PrepareType[this.params.prepareType]; | ||
return `RSABSSA-${hash}-${pssType}-${prepare}`; | ||
} | ||
prepare(msg) { | ||
const msg_prefix_len = this.prepareType; | ||
const msg_prefix_len = this.params.prepareType; | ||
const msg_prefix = crypto.getRandomValues(new Uint8Array(msg_prefix_len)); | ||
@@ -45,4 +46,4 @@ return joinAll([msg_prefix, msg]); | ||
const hash = hashFn.name; | ||
if (hash.toLowerCase() !== this.hash.toLowerCase()) { | ||
throw new Error(`hash is not ${this.hash}`); | ||
if (hash.toLowerCase() !== this.params.hash.toLowerCase()) { | ||
throw new Error(`hash is not ${this.params.hash}`); | ||
} | ||
@@ -63,3 +64,3 @@ const jwkKey = await crypto.subtle.exportKey('jwk', key); | ||
// 2. If EMSA-PSS-ENCODE raises an error, raise the error and stop | ||
const opts = { sLen: this.saltLength, hash }; | ||
const opts = { sLen: this.params.saltLength, hash }; | ||
const encoded_msg = await emsa_pss_encode(msg, modulusLength - 1, opts); | ||
@@ -145,3 +146,3 @@ // 3. m = bytes_to_int(encoded_msg) | ||
// raise "invalid signature" and stop | ||
const algorithm = { name: BlindRSA.NAME, saltLength: this.saltLength }; | ||
const algorithm = { name: BlindRSA.NAME, saltLength: this.params.saltLength }; | ||
if (!(await crypto.subtle.verify(algorithm, publicKey, sig, msg))) { | ||
@@ -152,7 +153,13 @@ throw new Error('invalid signature'); | ||
} | ||
generateKey(algorithm, extractable, keyUsages) { | ||
return crypto.subtle.generateKey({ ...algorithm, name: BlindRSA.NAME, hash: this.hash }, extractable, keyUsages); | ||
static generateKey(algorithm) { | ||
return crypto.subtle.generateKey({ ...algorithm, name: BlindRSA.NAME }, true, [ | ||
'sign', | ||
'verify', | ||
]); | ||
} | ||
generateKey(algorithm) { | ||
return BlindRSA.generateKey({ ...algorithm, hash: this.params.hash }); | ||
} | ||
verify(publicKey, signature, message) { | ||
return crypto.subtle.verify({ name: BlindRSA.NAME, saltLength: this.saltLength }, publicKey, signature, message); | ||
return crypto.subtle.verify({ name: BlindRSA.NAME, saltLength: this.params.saltLength }, publicKey, signature, message); | ||
} | ||
@@ -159,0 +166,0 @@ } |
@@ -1,4 +0,7 @@ | ||
import { BlindRSA } from './blindrsa.js'; | ||
export declare const SUITES: { | ||
import { BlindRSA, type BlindRSAParams } from './blindrsa.js'; | ||
export { BlindRSA, type BlindRSAParams }; | ||
export declare const Params: Record<string, BlindRSAParams>; | ||
export declare const RSABSSA: { | ||
readonly SHA384: { | ||
readonly generateKey: (algorithm: Pick<RsaHashedKeyGenParams, 'modulusLength' | 'publicExponent'>) => Promise<CryptoKeyPair>; | ||
readonly PSS: { | ||
@@ -15,3 +18,2 @@ readonly Randomized: () => BlindRSA; | ||
export declare function getSuiteByName(name: string): BlindRSA; | ||
export { BlindRSA }; | ||
//# sourceMappingURL=index.d.ts.map |
// Copyright (c) 2023 Cloudflare, Inc. | ||
// Licensed under the Apache-2.0 license found in the LICENSE file or at https://opensource.org/licenses/Apache-2.0 | ||
// Blind RSA draft 14 | ||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures-14 | ||
// RSA Blind Signature Protocol | ||
// | ||
// The RFC-9474 specifies (1) a protocol for computing RSA blind signatures | ||
// using RSA-PSS encoding and (2) a family of variants for this protocol, | ||
// denoted RSABSSA (RSA Blind Signature with Appendix). | ||
// | ||
// In order to facilitate deployment, it is defined in such a way that the | ||
// resulting (unblinded) signature can be verified with a standard RSA-PSS | ||
// library. | ||
// | ||
// RFC9474: https://www.rfc-editor.org/info/rfc9474 | ||
import { BlindRSA, PrepareType } from './blindrsa.js'; | ||
export const SUITES = { | ||
export { BlindRSA }; | ||
// Params allows to instantiate the RSABSSA protocol using BlindRSA class | ||
// with one of the approved variants. | ||
export const Params = { | ||
RSABSSA_SHA384_PSS_Randomized: { | ||
name: 'RSABSSA-SHA384-PSS-Randomized', | ||
hash: 'SHA-384', | ||
saltLength: 48, | ||
prepareType: PrepareType.Randomized, | ||
}, | ||
RSABSSA_SHA384_PSS_Deterministic: { | ||
name: 'RSABSSA-SHA384-PSS-Deterministic', | ||
hash: 'SHA-384', | ||
saltLength: 48, | ||
prepareType: PrepareType.Deterministic, | ||
}, | ||
RSABSSA_SHA384_PSSZERO_Randomized: { | ||
name: 'RSABSSA-SHA384-PSSZERO-Randomized', | ||
hash: 'SHA-384', | ||
saltLength: 0, | ||
prepareType: PrepareType.Randomized, | ||
}, | ||
RSABSSA_SHA384_PSSZERO_Deterministic: { | ||
name: 'RSABSSA-SHA384-PSSZERO-Deterministic', | ||
hash: 'SHA-384', | ||
saltLength: 0, | ||
prepareType: PrepareType.Deterministic, | ||
}, | ||
}; | ||
// RSABSSA is used to access the variants of the protocol. | ||
export const RSABSSA = { | ||
SHA384: { | ||
generateKey: (algorithm) => BlindRSA.generateKey({ ...algorithm, hash: 'SHA-384' }), | ||
PSS: { | ||
Randomized: () => new BlindRSA('SHA-384', 48, PrepareType.Randomized), | ||
Deterministic: () => new BlindRSA('SHA-384', 48, PrepareType.Deterministic), | ||
Randomized: () => new BlindRSA(Params.RSABSSA_SHA384_PSS_Randomized), | ||
Deterministic: () => new BlindRSA(Params.RSABSSA_SHA384_PSS_Deterministic), | ||
}, | ||
PSSZero: { | ||
Randomized: () => new BlindRSA('SHA-384', 0, PrepareType.Randomized), | ||
Deterministic: () => new BlindRSA('SHA-384', 0, PrepareType.Deterministic), | ||
Randomized: () => new BlindRSA(Params.RSABSSA_SHA384_PSSZERO_Randomized), | ||
Deterministic: () => new BlindRSA(Params.RSABSSA_SHA384_PSSZERO_Deterministic), | ||
}, | ||
@@ -19,18 +59,9 @@ }, | ||
export function getSuiteByName(name) { | ||
const lstSuites = [ | ||
SUITES.SHA384.PSS.Randomized, | ||
SUITES.SHA384.PSSZero.Randomized, | ||
SUITES.SHA384.PSS.Deterministic, | ||
SUITES.SHA384.PSSZero.Deterministic, | ||
]; | ||
const nameLowerCaee = name.toLowerCase(); | ||
for (const suite of lstSuites) { | ||
const ss = suite(); | ||
if (nameLowerCaee === ss.toString().toLowerCase()) { | ||
return ss; | ||
for (const suiteParams of Object.values(Params)) { | ||
if (name.toLowerCase() === suiteParams.name.toLowerCase()) { | ||
return new BlindRSA(suiteParams); | ||
} | ||
} | ||
throw new Error('wrong suite name'); | ||
throw new Error(`wrong suite name: ${name}`); | ||
} | ||
export { BlindRSA }; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@cloudflare/blindrsa-ts", | ||
"version": "0.1.2", | ||
"description": "blindrsa-ts: A TypeScript Library for the Blind RSA Signature Protocol", | ||
"version": "0.2.0", | ||
"description": "blindrsa-ts: A TypeScript Library for the RSA Blind Signature Protocol", | ||
"author": "Armando Faz <armfazh@cloudflare.com>", | ||
@@ -21,2 +21,3 @@ "maintainers": [ | ||
"blindrsa", | ||
"signature", | ||
"crypto", | ||
@@ -43,18 +44,18 @@ "cryptography" | ||
"devDependencies": { | ||
"@types/benchmark": "2.1.2", | ||
"@types/jest": "29.5.3", | ||
"@typescript-eslint/eslint-plugin": "5.60.0", | ||
"@typescript-eslint/parser": "5.60.0", | ||
"@types/benchmark": "2.1.3", | ||
"@types/jest": "29.5.5", | ||
"@typescript-eslint/eslint-plugin": "6.7.4", | ||
"@typescript-eslint/parser": "6.7.4", | ||
"benchmark": "2.1.4", | ||
"eslint": "8.44.0", | ||
"eslint-config-prettier": "8.8.0", | ||
"eslint-plugin-jest": "27.2.2", | ||
"eslint": "8.51.0", | ||
"eslint-config-prettier": "9.0.0", | ||
"eslint-plugin-jest": "27.4.2", | ||
"eslint-plugin-jest-formatting": "3.1.0", | ||
"eslint-plugin-prettier": "5.0.0", | ||
"eslint-plugin-security": "1.7.1", | ||
"jest": "29.6.1", | ||
"prettier": "3.0.0", | ||
"rimraf": "5.0.1", | ||
"typescript": "5.1.6" | ||
"jest": "29.7.0", | ||
"prettier": "3.0.3", | ||
"rimraf": "5.0.5", | ||
"typescript": "5.2.2" | ||
} | ||
} |
@@ -5,7 +5,101 @@ [![NPM](https://img.shields.io/npm/v/@cloudflare/blindrsa-ts?style=plastic)](https://www.npmjs.com/package/@cloudflare/blindrsa-ts) [![NPM](https://img.shields.io/npm/l/@cloudflare/blindrsa-ts?style=plastic)](LICENSE.txt) | ||
# blindrsa-ts: A TypeScript Library for Blind RSA Signature protocol. | ||
# blindrsa-ts: A TypeScript Library for the RSA Blind Signature Protocol | ||
**Specification:** Compliant with IETF [draft-irtf-cfrg-rsa-blind-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/) and tests vectors match with [v14](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures-14). | ||
**Specification:** Library is compliant with the [RFC-9474](https://www.rfc-editor.org/info/rfc9474) document by IETF/IRTF and matches the provided [tests vectors](https://www.rfc-editor.org/rfc/rfc9474.html#appendix-A). | ||
### Protocol | ||
The RSA Blind Signature Protocol is a two-party protocol between a Client and Server where they interact to compute | ||
`sig = Sign(sk, input_msg)` | ||
where `input_msg = Prepare(msg)` is a prepared version of the private message `msg` provided by the Client, and `sk` is the private signing key provided by the Server. | ||
```js | ||
Client(pk, msg) Server(sk, pk) | ||
----------------------------------------------------- | ||
input_msg = Prepare(msg) | ||
blinded_msg, inv = Blind(pk, input_msg) | ||
blinded_msg | ||
----------> | ||
blind_sig = BlindSign(sk, blinded_msg) | ||
blind_sig | ||
<---------- | ||
sig = Finalize(pk, input_msg, blind_sig, inv) | ||
``` | ||
### Usage | ||
#### Variants Supported | ||
This package supports the four variants specified in [RFC9474](https://www.rfc-editor.org/info/rfc9474). Consult [Section 5](https://www.rfc-editor.org/rfc/rfc9474.html#section-5) of the document for the proper usage of each variant in an application. | ||
```ts | ||
import { RSABSSA } from "@cloudflare/blindrsa-ts" | ||
const variants = [ | ||
RSABSSA.SHA384.PSS.Randomized, | ||
RSABSSA.SHA384.PSSZero.Randomized, | ||
RSABSSA.SHA384.PSS.Deterministic, | ||
RSABSSA.SHA384.PSSZero.Deterministic, | ||
] | ||
``` | ||
#### Setup | ||
Once a Blind-RSA variant was chosen, start by generating the server's keypair. Both the key length and the public exponent can be specified. | ||
```ts | ||
const suite = RSABSSA.SHA384.PSS.Randomized(); | ||
const { privateKey, publicKey } = await suite.generateKey({ | ||
publicExponent: Uint8Array.from([1, 0, 1]), | ||
modulusLength: 2048, | ||
}); | ||
``` | ||
Server distributes its public key to clients. | ||
#### Step 1 | ||
The client prepares arbitrary input to be blindly-signed by the server. The `blind` method generates a blinded message and an inverse object that later will be used during the finalization step. | ||
```ts | ||
const msgString = 'Alice and Bob'; | ||
const message = new TextEncoder().encode(msgString); | ||
const preparedMsg = suite.prepare(message); | ||
const { blindedMsg, inv } = await suite.blind(publicKey, preparedMsg); | ||
``` | ||
The client sends only the blinded message to the server. | ||
#### Step 2 | ||
Once the server received the blinded message, it responds to the client with a blind signature. | ||
```ts | ||
const blindSignature = await suite.blindSign(privateKey, blindedMsg); | ||
``` | ||
The server sends the blinded signature to the client. | ||
#### Step 3 | ||
The client produces the final signature using blinded signature received from the server together with the inverse object generated at the first step. | ||
```ts | ||
const signature = await suite.finalize(publicKey, preparedMsg, blindSignature, inv); | ||
``` | ||
Thus, the client obtains a pair `(preparedMsg, signature)` which can be verified for validity. | ||
#### Step 4 | ||
Anyone with access to the server's public key can verify the signature on top of the `preparedMsg`. | ||
```ts | ||
const isValid = await suite.verify(publicKey, signature, preparedMsg); // true | ||
``` | ||
### Development | ||
@@ -12,0 +106,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
142040
3188
128