npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nodeless-cose-js

v0.1.0

Published

Runtime-agnostic JavaScript COSE implementation

Readme

nodeless-cose-js

Runtime-agnostic JavaScript implementation of COSE (CBOR Object Signing and Encryption)

A fork of cose-js designed to work seamlessly across multiple JavaScript runtimes:

  • Node.js - Full native support
  • React Native - Babel-transpiled, no Node.js dependencies
  • Web Browsers - Pure JavaScript, Web Crypto API compatible

Features

  • 🔒 Secure - Uses @noble/curves (audited, actively maintained)
  • 🌐 Cross-platform - No Node.js-specific dependencies
  • 🎯 Modern - ES6+, async/await, low-S signatures (RFC 6979 + BIP 62)
  • Tested - 29 tests passing in Node.js, 16 in React Native/Babel
  • 🔄 Interoperable - Compatible with other COSE implementations

Current Status: Phase 1 - Sign1 Operations

Phase 1 focuses exclusively on Sign1 (single-signer) messages:

  • ✅ Create Sign1 messages
  • ✅ Verify Sign1 messages
  • ✅ ES256, ES384, ES512 algorithms
  • ✅ External AAD support
  • ✅ Protected and unprotected headers

Future phases will add Mac, Mac0, Encrypt, Encrypt0, and multi-signature support.

Install

npm install nodeless-cose-js --save

Usage

Create and Sign a Message (Sign1)

const cose = require('nodeless-cose-js');
const { p256 } = require('@noble/curves/nist.js');

// Generate a new private key (or load from storage)
const privateKey = p256.utils.randomPrivateKey();  // 32 bytes
// OR from hex: const privateKey = Buffer.from('your-private-key-hex', 'hex');

// Your message to sign
const plaintext = 'Important message!';

// Headers (protected and/or unprotected)
const headers = {
  p: { alg: 'ES256' },  // Protected: algorithm
  u: { kid: '11' }       // Unprotected: key ID
};

// Signer with private key
const signer = {
  key: privateKey  // Uint8Array or Buffer (32 bytes for ES256)
};

// Create signed message
const signedMessage = await cose.sign.create(headers, plaintext, signer);

console.log('COSE Sign1 message:', signedMessage.toString('hex'));

Verify a Signed Message

const cose = require('nodeless-cose-js');
const { p256 } = require('@noble/curves/nist.js');

// COSE Sign1 message (received from somewhere)
const signedMessage = Buffer.from('d28443a10126a10442313172496d706f7274616e74206d657373616765215840...', 'hex');

// Get the public key (derived from private key, or received from sender)
const privateKey = Buffer.from('6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c19', 'hex');
const publicKey = p256.getPublicKey(privateKey);  // 33 bytes (compressed) or 65 bytes (uncompressed)

// Verifier with public key
const verifier = {
  key: publicKey  // Uint8Array or Buffer
};

// Verify and extract payload
const payload = await cose.sign.verify(signedMessage, verifier);

console.log('Verified message:', payload.toString('utf8'));
// Output: Important message!

Sign with External AAD (Additional Authenticated Data)

const cose = require('nodeless-cose-js');
const { p256 } = require('@noble/curves/nist.js');

const privateKey = p256.utils.randomPrivateKey();
const plaintext = 'Secret data';
const headers = {
  p: { alg: 'ES256' },
  u: { kid: 'key-1' }
};

const signer = {
  key: privateKey,
  externalAAD: Buffer.from('context-specific-data', 'utf8')  // External AAD
};

const signedMessage = await cose.sign.create(headers, plaintext, signer);

Verify with External AAD

const publicKey = p256.getPublicKey(privateKey);

const verifier = {
  key: publicKey,
  externalAAD: Buffer.from('context-specific-data', 'utf8')  // Must match!
};

const payload = await cose.sign.verify(signedMessage, verifier);

Working with Only Unprotected Headers

// No protected headers
const headers = {
  u: { 
    alg: 'ES256',
    kid: 'my-key-id'
  }
};

const signedMessage = await cose.sign.create(headers, plaintext, signer);

ES384 and ES512 Algorithms

const { p384, p521 } = require('@noble/curves/nist.js');

// ES384 (P-384 curve, SHA-384)
const privateKey384 = p384.utils.randomPrivateKey();  // 48 bytes
const publicKey384 = p384.getPublicKey(privateKey384);

const headers384 = { p: { alg: 'ES384' } };
const signer384 = { key: privateKey384 };

// ES512 (P-521 curve, SHA-512)  
const privateKey512 = p521.utils.randomPrivateKey();  // 66 bytes
const publicKey512 = p521.getPublicKey(privateKey512);

const headers512 = { p: { alg: 'ES512' } };
const signer512 = { key: privateKey512 };

API Reference

cose.sign.create(headers, payload, signer, [options])

Creates a signed COSE Sign1 message.

Parameters:

  • headers (Object):
    • p (Object, optional): Protected headers (e.g., { alg: 'ES256' })
    • u (Object, optional): Unprotected headers (e.g., { kid: '11' })
  • payload (Buffer|String): The message to sign
  • signer (Object):
    • key (Uint8Array|Buffer): Private key (32 bytes for ES256, 48 for ES384, 66 for ES512)
    • externalAAD (Buffer, optional): External additional authenticated data
  • options (Object, optional):
    • excludetag (Boolean): If true, omits the CBOR tag (default: false)

Returns: Promise - The COSE Sign1 message

cose.sign.verify(message, verifier, [options])

Verifies a COSE Sign1 message and returns the payload.

Parameters:

  • message (Buffer): The COSE Sign1 message to verify
  • verifier (Object):
    • key (Uint8Array|Buffer): Public key in compressed (33/49/67 bytes) or uncompressed (65/97/133 bytes) format
    • externalAAD (Buffer, optional): External AAD (must match signing)
  • options (Object, optional):
    • defaultType (Number): Default CBOR tag type if untagged

Returns: Promise - The verified payload

Throws: Error if signature is invalid

Security Features

Low-S Signatures (BIP 62)

This implementation uses low-S normalization for ECDSA signatures, following modern cryptographic best practices:

  • What it is: For each signature (r, s), we ensure s < n/2 where n is the curve order
  • Why it matters: Prevents signature malleability attacks
  • Compatibility: Used by Bitcoin (BIP 62), Ethereum, and modern Web3 standards
  • COSE compliant: RFC 9053 doesn't mandate low-S or high-S - both are valid

RFC 6979 Deterministic ECDSA

All signatures are deterministic (same message + key = same signature), eliminating nonce-related vulnerabilities.

Testing

Node.js Tests

npm test
# 29 tests passed (Sign1 + interop)

React Native / Babel Tests

npm run test:babel  
# 16 tests passed (Sign1 only)

Interoperability Tests

Included tests verify compatibility with the elliptic library (used by original cose-js):

  • ✅ Our signatures verify with elliptic
  • ✅ We use RFC 6979 deterministic k generation
  • ✅ We apply low-S normalization (security improvement)

Supported Algorithms (Phase 1)

| Algorithm | Curve | Hash | Key Size | Status | |-----------|-------|------|----------|--------| | ES256 | P-256 | SHA-256 | 32 bytes | ✅ Supported | | ES384 | P-384 | SHA-384 | 48 bytes | ✅ Supported | | ES512 | P-521 | SHA-512 | 66 bytes | ✅ Supported |

Note: RSA algorithms (RS256, PS256, etc.) are not supported in Phase 1.

Dependencies

Runtime Dependencies

Why These Libraries?

  • @noble/*: Actively maintained, audited, pure JavaScript, no Node.js dependencies
  • cbor-x: Faster and more compatible than cbor, works in all runtimes
  • No elliptic: Has known security issues, community moving away from it

Differences from cose-js

  1. Runtime-agnostic - Works in Node.js AND React Native
  2. Modern crypto - Uses @noble/curves instead of elliptic
  3. Low-S signatures - Enhanced security (BIP 62 compliant)
  4. Better CBOR - Uses cbor-x for performance and compatibility
  5. ⚠️ Phase 1 only - Currently supports Sign1 only (Mac, Encrypt coming later)

Roadmap

  • [x] Phase 1: Sign1 operations (complete!)
  • [ ] Phase 2: Mac0/Mac operations
  • [ ] Phase 3: Encrypt0/Encrypt operations
  • [ ] Phase 4: Key derivation (HKDF)
  • [ ] Phase 5: Performance optimization

License

Apache-2.0 (same as original cose-js)

Credits