diff --git a/types/node-jose/index.d.ts b/types/node-jose/index.d.ts new file mode 100644 index 0000000000..dc4caaeca5 --- /dev/null +++ b/types/node-jose/index.d.ts @@ -0,0 +1,335 @@ +// Type definitions for node-jose 1.1 +// Project: https://github.com/cisco/node-jose +// Definitions by: Nadun Indunil +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 3.3 + +/// + +export function canYouSee(ks: JWK.Key | JWK.KeyStore, opts: object): JWS.Verifier; + +export namespace JWA { + interface DecryptEncryptOptions { + aad?: Buffer; + adata?: Buffer; + iv?: Buffer; + tag?: Buffer; // Not used in encrypt + mac?: Buffer; // Not used in encrypt + epu?: Buffer; // encryption party info + epv?: Buffer; // encryption party info + kdata?: Buffer; + epk?: Buffer; // ephemeral pub key used in ec + enc?: string; // algorithm to use in ec + alg?: string; // variation of enc, probably oversight in lib code + apu?: Buffer; // agreement party info used in ec + apv?: Buffer; // agreement party info used in ec + p2s?: Buffer; // used in pbes + p2c?: number; // used in pbes + } + + interface DeriveOptions { + length?: number; // key length + otherInfo?: Buffer; // info used in concatkdf + public?: Buffer; // public key used in ecdh + hash?: Buffer; // hash used in ecdh + salt?: Buffer; // salt value used in hkdf + info?: Buffer; // app identifier info used in hkdf + } + + interface EncryptReturn { + data: Buffer; // The cipher text + tag?: Buffer; // The tag used in some algorithms + } + + interface SignReturn { + data: Buffer; // the data passed into the sign function + mac: Buffer; // the signature for `data` + } + + interface SignVerifyOptions { + loose?: boolean; + } + + interface VerifyReturn { + data: Buffer; // the data passed into the verify function + mac: Buffer; // the signature for `data` + valid: boolean; // whether the signature matches the data + } + + function decrypt( + alg: string, + key: string | Buffer, + cdata: string | Buffer, + props?: DecryptEncryptOptions + ): Promise; + + function derive(alg: string, key: string | Buffer, props?: DeriveOptions): Promise; + + function digest(alg: string, data: string | Buffer, props?: any): Promise; + + function encrypt( + alg: string, + key: string | Buffer, + pdata: string | Buffer, + props?: DecryptEncryptOptions + ): Promise; + + function sign( + alg: string, + key: string | Buffer, + pdata: string | Buffer, + props: SignVerifyOptions + ): Promise; + + function verify( + alg: string, + key: string | Buffer, + pdata: string | Buffer, + mac: string | Buffer, + props: SignVerifyOptions + ): Promise; +} + +export namespace JWE { + function createEncrypt(keys: JWK.Key | JWK.Key[]): Encryptor; + function createEncrypt( + options: { + format?: 'compact' | 'flattened'; + zip?: boolean; + fields?: object; + }, + key: JWK.Key + ): Encryptor; + + function createDecrypt(key: JWK.Key | JWK.KeyStore, opts?: any): Decryptor; + + interface Encryptor { + update(input: any): this; + final(): Promise; + } + + interface Decryptor { + decrypt(input: string): Promise; + } + + interface DecryptResult { + /** + * an array of the member names from the "protected" member + */ + protected: string[]; + /** + * the decrypted content (alternate) + */ + plaintext: Buffer; + } +} + +export namespace JWK { + const MODE_DECRYPT: string; + + const MODE_ENCRYPT: string; + + const MODE_SIGN: string; + + const MODE_UNWRAP: string; + + const MODE_VERIFY: string; + + const MODE_WRAP: string; + + function asKey( + key: string | Buffer | object | RawKey, + form?: 'json' | 'private' | 'pkcs8' | 'public' | 'spki' | 'pkix' | 'x509' | 'pem' + ): Promise; + /** + * To import a JWK-set as a keystore + */ + function asKeyStore(ks: object | string): Promise; + + function createKey(kty: any, size: any, props: any): Promise; + /** + * To create an empty keystore + */ + function createKeyStore(): KeyStore; + + function isKey(input: any): input is Key; + + function isKeyStore(input: any): input is KeyStore; + + type KeyUse = 'sig' | 'enc' | 'desc'; + + interface JWEEncryptor { + update(input: any): this; + final(): Promise; + } + + interface RawKey { + alg: string; + kty: string; + use: KeyUse; + + // e and n make up the public key + e: string; + n: string; + } + + interface KeyStoreGetFilter { + kty?: string; + use?: KeyUse; + alg?: string; + } + + interface KeyStoreGetOptions extends KeyStoreGetFilter { + kid: string; + } + + interface KeyStore { + /** + * To export the public keys of a keystore as a JWK-set + */ + toJSON(exportPrivateKeys?: boolean): object; + /** + * To retrieve a key from a keystore + */ + get(kid: string, filter?: KeyStoreGetFilter): RawKey; + get(options: KeyStoreGetOptions): RawKey; + all(options?: Partial): RawKey[]; + add(key: RawKey): Promise; + /** + * @param key + * String serialization of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER + * Buffer of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER + * @param form + * is either a: + * - "json" for a JSON stringified JWK + * - "private" for a DER encoded 'raw' private key + * - "pkcs8" for a DER encoded (unencrypted!) PKCS8 private key + * - "public" for a DER encoded SPKI public key (alternate to 'spki') + * - "spki" for a DER encoded SPKI public key + * - "pkix" for a DER encoded PKIX X.509 certificate + * - "x509" for a DER encoded PKIX X.509 certificate + * - "pem" for a PEM encoded of PKCS8 / SPKI / PKIX + */ + add( + key: string | Buffer | Key | object, + form?: 'json' | 'private' | 'pkcs8' | 'public' | 'spki' | 'pkix' | 'x509' | 'pem' + ): Promise; + + generate(kty: string, size?: string | number, props?: any): Promise; + + remove(key: Key): void; + } + + interface Key { + keystore: KeyStore; + length: number; + kty: string; + kid: string; + use: KeyUse; + alg: string; + + toPEM(isPrivate?: boolean): string; + toJSON(isPrivate?: boolean, excluded?: string[]): object; + thumbprint(hash?: string): Promise; + } +} + +export namespace JWS { + function createSign(keys: JWK.Key | JWK.Key[]): Signer; + function createSign( + options: { + format?: 'compact' | 'flattened'; + alg?: string; + compact?: boolean; + fields?: object; + }, + key: JWK.Key | JWK.Key[] + ): Signer; + + /** + * Using a keystore. + */ + function createVerify( + input?: string | JWK.Key | JWK.KeyStore | object, + opts?: { allowEmbeddedKey?: boolean; algorithms?: string[]; handlers?: any } + ): Verifier; + + interface CreateSignResult { + signResult: object; + } + + interface Signer { + update(input: Buffer | string, encoding?: string): this; + final(): Promise; + } + + interface BaseResult { + /** + * the combined 'protected' and 'unprotected' header members + */ + header: object; + /** + * the signed content + */ + payload: Buffer; + /** + * The key used to verify the signature + */ + key: JWK.Key; + protected: string[]; + } + + interface VerificationResult extends BaseResult { + /** + * the verified signature + */ + signature: Buffer | string; + } + + interface Verifier { + verify(input: string, opts?: { allowEmbeddedKey?: boolean }): Promise; + } + + interface Exp { + complete(jws: any): any; + } + + interface VerifyOptions { + allowEmbeddedKey?: boolean; + algorithms?: string[]; + handlers: { exp: boolean | Exp }; + } +} + +export function parse(input: Buffer | string | object): parse.ParseReturn; + +export namespace parse { + interface ParseReturn { + type: 'JWS' | 'JWE'; + format: 'compact' | 'json'; + input: Buffer | string | object; + header: object; + perform: (ks: JWK.KeyStore) => Promise | Promise; + } + function compact(input: Buffer | string | object): ParseReturn; + + function json(input: Buffer | string | object): ParseReturn; +} + +export namespace util { + function asBuffer(input: string | Buffer, encoding?: string): Buffer; + + function randomBytes(len: number): Buffer; + + namespace base64url { + function decode(base64url: string): string; + + function encode(buffer: string | Buffer, encoding?: string): string; + } + + namespace utf8 { + function decode(input: string): string; + + function encode(input: string): string; + } +} diff --git a/types/node-jose/node-jose-tests.ts b/types/node-jose/node-jose-tests.ts new file mode 100644 index 0000000000..dbeaccc9dc --- /dev/null +++ b/types/node-jose/node-jose-tests.ts @@ -0,0 +1,276 @@ +import * as jose from 'node-jose'; + +const keystore = jose.JWK.createKeyStore(); +const output = keystore.toJSON(); +keystore.toJSON(true); + +jose.JWK.asKeyStore('input').then(result => {}); + +let key = keystore.get('kid'); + +key = keystore.get('kid', { kty: 'RSA' }); + +// ... and by 'use' +key = keystore.get('kid', { use: 'enc' }); + +// ... and by 'alg' +key = keystore.get('kid', { alg: 'RSA-OAEP' }); + +// ... and by 'kty' and 'use' +key = keystore.get('kid', { kty: 'RSA', use: 'enc' }); + +// same as above, but with a single {props} argument +key = keystore.get({ kid: 'kid', kty: 'RSA', use: 'enc' }); + +let everything = keystore.all(); + +// filter by 'kid' +everything = keystore.all({ kid: 'kid' }); + +// filter by 'kty' +everything = keystore.all({ kty: 'RSA' }); + +// filter by 'use' +everything = keystore.all({ use: 'enc' }); + +// filter by 'alg' +everything = keystore.all({ alg: 'RSA-OAEP' }); + +// filter by 'kid' + 'kty' + 'alg' +everything = keystore.all({ kid: 'kid', kty: 'RSA', alg: 'RSA-OAEP' }); + +keystore.add('input').then(result => {}); + +keystore.add('input', 'json').then(result => { + // {result} is a jose.JWK.Key +}); + +keystore.generate('oct', 256).then(result => { + // {result} is a jose.JWK.Key +}); + +// ... with properties +const props = { + kid: 'gBdaS-G8RLax2qgObTD94w', + alg: 'A256GCM', + use: 'enc' +}; + +let key2: jose.JWK.Key; +keystore.generate('oct', 256, props).then(result => { + key2 = result; + keystore.remove(key2); + + jose.JWK.asKey(key2).then(result => {}); + + jose.JWK.asKey('input', 'json').then(result => {}); +}); + +jose.JWK.createKey('oct', 256, { alg: 'A256GCM' }).then(result => { + const output4 = result.toJSON(true); + result.thumbprint('hash').then(print => {}); + + const key = result; + + jose.JWS.createSign(key) + .update('input') + .final() + .then(result => { + // {result} is a JSON object -- JWS using the JSON General Serialization + }); + + jose.JWS.createSign({ format: 'flattened' }, key) + .update('input') + .final() + .then(result => { + // {result} is a JSON object -- JWS using the JSON Flattened Serialization + }); + + jose.JWS.createSign({ format: 'compact' }, key) + .update('input') + .final() + .then(result => { + // {result} is a String -- JWS using the Compact Serialization + }); + + jose.JWS.createSign({ alg: 'PS256' }, key) + .update('input') + .final() + .then(result => { + // .... + }); + + jose.JWS.createSign({ fields: { cty: 'jwk+json' } }, key) + .update('input') + .final() + .then(result => { + // .... + }); + + jose.JWS.createSign(key) + .update('input', 'utf8') + .final() + .then(result => { + // .... + }); + + let opts = { + algorithms: ['PS*'] + }; + jose.JWS.createVerify(key, opts) + .verify('input') + .then(result => { + // ... + }); + + opts = { + algorithms: ['*', '!HS*'] + }; + jose.JWS.createVerify(key, opts) + .verify('input') + .then(result => { + // ... + }); + + const opts2 = { + handlers: { + exp: true + } + }; + + jose.JWS.createVerify(key, opts2) + .verify('input') + .then(result => { + // ... + }); + + jose.JWE.createEncrypt(key) + .update('input') + .final() + .then(result => { + // {result} is a JSON Object -- JWE using the JSON General Serialization + }); + + jose.JWE.createEncrypt({ format: 'compact' }, key) + .update('input') + .final() + .then(result => { + // {result} is a String -- JWE using the Compact Serialization + }); + + jose.JWE.createEncrypt({ format: 'flattened' }, key) + .update('input') + .final() + .then(result => { + // {result} is a JSON Object -- JWE using the JSON Flattened Serialization + }); + + jose.JWE.createEncrypt({ zip: true }, key) + .update('input') + .final() + .then(result => { + // .... + }); + + jose.JWE.createEncrypt({ fields: { cty: 'jwk+json' } }, key) + .update('input') + .final() + .then(result => { + // .... + }); + + jose.JWE.createEncrypt([key, key]) + .update('input') + .final() + .then(result => { + // .... + }); + + jose.JWE.createDecrypt(key) + .decrypt('input') + .then(result => { + // .... + }); + + const opts3 = { + algorithms: ['dir', 'A*GCM'] + }; + jose.JWE.createDecrypt(key, opts3) + .decrypt('input') + .then(result => { + // ... + }); + + const opts4 = { + algorithms: ['*', '!RSA*'] + }; + jose.JWS.createVerify(key, opts4) + .verify('input') + .then(result => { + // ... + }); + + const opts5 = { + handlers: { + exp: true + } + }; + jose.JWE.createDecrypt(key, opts5) + .decrypt('input') + .then(result => { + // ... + }); +}); + +jose.JWS.createVerify(keystore) + .verify('input') + .then(result => { + // {result} is a Object with: + // * header: the combined 'protected' and 'unprotected' header members + // * payload: Buffer of the signed content + // * signature: Buffer of the verified signature + // * key: The key used to verify the signature + }); + +// {key} can be: +// * jose.JWK.Key +// * JSON object representing a JWK +jose.JWS.createVerify(key) + .verify('input') + .then(result => { + // ... + }); + +jose.JWS.createVerify() + .verify('input', { allowEmbeddedKey: true }) + .then(result => { + // ... + }); + +const verifier = jose.JWS.createVerify({ allowEmbeddedKey: true }); + +verifier.verify('input').then(result => { + // ... +}); + +jose.JWE.createDecrypt(keystore) + .decrypt('input') + .then(result => { + // {result} is a Object with: + // * header: the combined 'protected' and 'unprotected' header members + // * protected: an array of the member names from the "protected" member + // * key: Key used to decrypt + // * payload: Buffer of the decrypted content + // * plaintext: Buffer of the decrypted content (alternate) + }); + +jose.util.asBuffer('input'); + +jose.util.base64url.encode('input'); +jose.util.base64url.encode('input', 'utf8'); + +jose.util.base64url.encode('input'); + +jose.util.base64url.decode('input'); + +jose.util.randomBytes(32); diff --git a/types/node-jose/tsconfig.json b/types/node-jose/tsconfig.json new file mode 100644 index 0000000000..c01c2b1fb7 --- /dev/null +++ b/types/node-jose/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "node-jose-tests.ts" + ] +} \ No newline at end of file diff --git a/types/node-jose/tslint.json b/types/node-jose/tslint.json new file mode 100644 index 0000000000..e60c15844f --- /dev/null +++ b/types/node-jose/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "dtslint/dt.json" +} \ No newline at end of file