Skip to main content

SAG-ts

Installation​

Install the SAG-ts library using npm or yarn:

npm install @cypher-laboratory/alicesring-sag
yarn add @cypher-laboratory/alicesring-sag

Import Required Classes​

import { RingSignature, Curve, CurveName, Point } from '@cypher-laboratory/alicesring-sag';

Initialize the Curve​

Choose the elliptic curve that matches the keys you're using. Common choices are SECP256K1 for Bitcoin/Ethereum keys or ED25519.

const curve = new Curve(CurveName.SECP256K1);

Create the Ring of Public Keys​

Convert each public key into a Point object.

// Example public keys in compressed hexadecimal format
// ex: 0x02 + x-coordinate (32 bytes) for SECP256K1
const publicKeys = [
'030066ba293cc22d0eadbe494e9bd4d6d05c3e09d74dff0e991075de74b2359678',
'0316d7da70ba247a6a40bb310187e8789b80c45fa6dc0061abb8ced49cbe7f887f', '0221869ca3ae33be3a7327e9a0272203afa72c52a5460ceb9f4a50930531bd926a',
// ... other public keys
];

// Convert to Point objects
const ring: Point[] = publicKeys.map(compressed => Point.deserialize(compressed));

Or

// Example public keys in hexadecimal format
const publicKeyHexes = [
{ x: 'x-coordinate-hex-1', y: 'y-coordinate-hex-1' },
{ x: 'x-coordinate-hex-2', y: 'y-coordinate-hex-2' },
// ... other public keys
];

// Convert to Point objects
const ring: Point[] = publicKeyHexes.map(pk => new Point(
curve,
[BigInt('0x' + pk.x), BigInt('0x' + pk.y)]
));

Prepare Your Private Key and Message​

const signerPrivateKey = BigInt('0x...'); // Your private key as a bigint
const message = `Hello, Alice's Ring!`;

Sign the Message​

const signature = RingSignature.sign(
ring,
signerPrivateKey,
message,
curve
);

Verify the Signature​

const isValid = signature.verify();
console.log('Is the signature valid?', isValid);

Serialize and Deserialize the Signature​

Serialize to JSON:

const jsonString = signature.toJsonString();

Deserialize from JSON:

const importedSignature = RingSignature.fromJson(jsonString);

Serialize to Base64:

const base64String = signature.toBase64();

Deserialize from Base64:

const importedSignature = RingSignature.fromBase64(base64String);

Usage Example​

import { RingSignature, Curve, CurveName, Point } from '@cypher-laboratory/alicesring-sag';

// Initialize curve
const curve = new Curve(CurveName.SECP256K1);

// Create ring
const ring: Point[] = [
new Point(curve, [BigInt('0x1...'), BigInt('0x2...')]),
new Point(curve, [BigInt('0x3...'), BigInt('0x4...')]),
// ... add more public keys
];

// Your private key
const signerPrivateKey = BigInt('0x5...');

// Message to sign
const message = 'Confidential message';

// Sign the message
const signature = RingSignature.sign(
ring,
signerPrivateKey,
message,
curve
);

// Verify the signature
if (signature.verify()) {
console.log('Signature is valid.');
} else {
console.log('Signature is invalid.');
}

// Serialize the signature
const signatureJson = signature.toJsonString();

// Deserialize and verify again
const importedSignature = RingSignature.fromJson(signatureJson);
console.log('Is the imported signature valid?', importedSignature.verify());

Understanding the Key Classes​

Point Class​

The Point class represents a point on an elliptic curve.

Constructor:

const point = new Point(curve, [xCoordinate, yCoordinate]);
  • Parameters:
    • curve: An instance of the Curve class.
    • [xCoordinate, yCoordinate]: The coordinates of the point as bigints.

Methods:

  • mult(scalar: bigint): Point: Multiplies the point by a scalar.
  • add(point: Point): Point: Adds another point to the current point.
  • equals(point: Point): boolean: Checks if two points are equal.
  • negate(): Point: Returns the negation of the point.
  • serialize(): string: Serializes the point to a compressed hex string.
  • static deserialize(compressed: string): Point: Deserializes a point from a compressed hex string.
  • toEthAddress(): string: Converts the point to an Ethereum address (only for SECP256K1).

Example:

// Create a point
const point = new Point(curve, [BigInt('0x...'), BigInt('0x...')]);

// Multiply the point
const multipliedPoint = point.mult(BigInt(2));

// Serialize and deserialize
const serialized = point.serialize();
const deserializedPoint = Point.deserialize(serialized);

Curve Class​

The Curve class represents the elliptic curve.

Constructor:

const curve = new Curve(CurveName.SECP256K1);

Properties:

  • name: The name of the curve (CurveName.SECP256K1 or CurveName.ED25519).
  • N: The order of the curve.
  • G: The generator point as [x, y].
  • P: The field size.

Methods:

  • GtoPoint(): Point: Returns the generator point as a Point instance.
  • isOnCurve(point: Point | [bigint, bigint]): boolean: Checks if a point is on the curve.

Example:

const curve = new Curve(CurveName.SECP256K1);

// Get the generator point
const G = curve.GtoPoint();

// Check if a point is on the curve
const isValid = curve.isOnCurve(point);

RingSignature Class​

The RingSignature class handles the creation and verification of ring signatures.

Methods:

  • static sign(ring: Point[], signerPrivateKey: bigint, message: string, curve: Curve): RingSignature: Signs a message.
  • verify(): boolean: Verifies the signature.
  • toJsonString(): string: Serializes the signature to a JSON string.
  • static fromJson(json: string | object): RingSignature: Deserializes a signature from JSON.
  • toBase64(): string: Serializes the signature to a Base64 string.
  • static fromBase64(base64: string): RingSignature: Deserializes a signature from Base64.

Example:

// Sign a message
const signature = RingSignature.sign(ring, signerPrivateKey, message, curve);

// Verify the signature
const isValid = signature.verify();

// Serialize and deserialize
const json = signature.toJsonString();
const signatureFromJson = RingSignature.fromJson(json);

Detailed Implementation​

Point Class​

The Point class represents a point on an elliptic curve. It includes methods for point addition, scalar multiplication, serialization, deserialization, and validation.

Constructor:

constructor(curve: Curve, coordinates: [bigint, bigint], safeMode = true)
  • Parameters:
    • curve: An instance of the Curve class.
    • coordinates: A tuple [x, y] representing the point's coordinates.
    • safeMode: A boolean indicating whether to check if the point is on the curve.

Key Methods:

  • mult(scalar: bigint): Point: Multiplies the point by a scalar.
  • add(point: Point): Point: Adds another point to this point.
  • negate(): Point: Negates the point.
  • equals(point: Point): boolean: Checks if this point is equal to another.
  • serialize(): string: Serializes the point to a compressed hex string.
  • static deserialize(compressed: string): Point: Deserializes a point from a compressed hex string.
  • toEthAddress(): string: Converts the point to an Ethereum address (for SECP256K1 curve).
  • checkLowOrder(): boolean: Checks if the point is of low order (relevant for ED25519 curve).

Curve Class​

The Curve class encapsulates the properties of an elliptic curve.

Constructor:

constructor(curve: CurveName)
  • Parameter:
    • curve: The name of the curve (CurveName.SECP256K1 or CurveName.ED25519).

Key Properties:

  • name: The name of the curve.
  • N: The order of the curve.
  • G: The generator point [x, y].
  • P: The prime number defining the field.

Key Methods:

  • GtoPoint(): Point: Returns the generator point as a Point instance.
  • isOnCurve(point: Point | [bigint, bigint]): boolean: Checks if a point is on the curve.
  • equals(curve: Curve): boolean: Checks if this curve is equal to another.

Example:

const curve = new Curve(CurveName.SECP256K1);
const G = curve.GtoPoint();

Note: For a complete explanation of SAG and LSAG differences and usages, refer to the Ring Signatures documentation.

Sponsors​

We would like to thank the XRPL Foundation (https://xrpl.org/) for their support and funding, which have allowed this audited library to be developed for the benefit of many.

Contribution Guidelines​

We welcome contributions to improve and extend the functionality of the SAG-ts library. Feel free to open issues, suggest enhancements, or submit pull requests on our GitHub repository.


Feel free to explore the other implementations and tools in the Alice's Ring project:

  • LSAG Typescript: TypeScript implementation of Linkable Spontaneous Anonymous Group (LSAG) signatures.
  • Metamask-snap: A Metamask snap to create and verify ring signatures easily.
  • SAG EVM Verifier: EVM verifier for SAG ring signature written in Solidity.
  • Rust Verifier: Rust verifier for SAG and LSAG ring signatures.

For further reading and a deeper understanding of the cryptographic principles involved, refer to the Zero to Monero document (p.36), which served as a reference for this implementation.


Happy coding! If you have any questions or need assistance, don't hesitate to reach out to the community.