UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

283 lines 9.76 kB
import { __decorate, __metadata } from "tslib"; import { Field, Bool, Group, Scalar } from '../wrapped.js'; import { hashWithPrefix } from './poseidon.js'; import { deriveNonce, Signature as SignatureBigint, signaturePrefix, } from '../../../mina-signer/src/signature.js'; import { PrivateKey as PrivateKeyBigint, PublicKey as PublicKeyBigint, } from '../../../mina-signer/src/curve-bigint.js'; import { toConstantField } from '../field.js'; import { CircuitValue, prop } from '../types/circuit-value.js'; // external API export { PrivateKey, PublicKey, Signature }; /** * A signing key. You can generate one via {@link PrivateKey.random}. */ class PrivateKey extends CircuitValue { constructor(s) { super(s); } /** * Generate a random private key. * * You can obtain the associated public key via {@link toPublicKey}. * And generate signatures via {@link Signature.create}. * * Note: This uses node or browser built-in APIs to obtain cryptographically strong randomness, * and can be safely used to generate a real private key. * * @returns a new {@link PrivateKey}. */ static random() { return new PrivateKey(Scalar.random()); } /** * Create a random keypair `{ privateKey: PrivateKey, publicKey: PublicKey }`. * * Note: This uses node or browser built-in APIs to obtain cryptographically strong randomness, * and can be safely used to generate a real keypair. */ static randomKeypair() { let privateKey = PrivateKey.random(); return { privateKey, publicKey: privateKey.toPublicKey() }; } /** * Deserializes a list of bits into a {@link PrivateKey}. * * @param bs a list of {@link Bool}. * @returns a {@link PrivateKey}. */ static fromBits(bs) { return new PrivateKey(Scalar.fromBits(bs)); } /** * Convert this {@link PrivateKey} to a bigint */ toBigInt() { return this.s.toBigInt(); } /** * Create a {@link PrivateKey} from a bigint * * **Warning**: Private keys should be sampled from secure randomness with sufficient entropy. * Be careful that you don't use this method to create private keys that were sampled insecurely. */ static fromBigInt(sk) { return new PrivateKey(Scalar.from(sk)); } /** * Derives the associated public key. * * @returns a {@link PublicKey}. */ toPublicKey() { return PublicKey.fromPrivateKey(this); } /** * Decodes a base58 string into a {@link PrivateKey}. * * @returns a {@link PrivateKey}. */ static fromBase58(privateKeyBase58) { let scalar = PrivateKeyBigint.fromBase58(privateKeyBase58); return new PrivateKey(Scalar.from(scalar)); } /** * Encodes a {@link PrivateKey} into a base58 string. * @returns a base58 encoded string */ toBase58() { return PrivateKey.toBase58(this); } // static version, to operate on non-class versions of this type /** * Static method to encode a {@link PrivateKey} into a base58 string. * @returns a base58 encoded string */ static toBase58(privateKey) { return PrivateKeyBigint.toBase58(privateKey.s.toBigInt()); } static toValue(v) { return v.toBigInt(); } static fromValue(v) { if (v instanceof PrivateKey) return v; return PrivateKey.fromBigInt(v); } } __decorate([ prop, __metadata("design:type", Scalar) ], PrivateKey.prototype, "s", void 0); // TODO: this doesn't have a non-default check method yet. does it need one? /** * A public key, which is also an address on the Mina network. * You can derive a {@link PublicKey} directly from a {@link PrivateKey}. */ class PublicKey extends CircuitValue { /** * Returns the {@link Group} representation of this {@link PublicKey}. * @returns A {@link Group} */ toGroup() { // compute y from elliptic curve equation y^2 = x^3 + 5 let { x, isOdd } = this; let y = x.square().mul(x).add(5).sqrt(); // negate y if its parity is different from the public key's let sameParity = y.isOdd().equals(isOdd).toField(); let sign = sameParity.mul(2).sub(1); // (2*sameParity - 1) == 1 if same parity, -1 if different parity y = y.mul(sign); return new Group({ x, y }); } /** * Creates a {@link PublicKey} from a {@link Group} element. * @returns a {@link PublicKey}. */ static fromGroup({ x, y }) { return PublicKey.fromObject({ x, isOdd: y.isOdd() }); } /** * Derives a {@link PublicKey} from a {@link PrivateKey}. * @returns a {@link PublicKey}. */ static fromPrivateKey({ s }) { return PublicKey.fromGroup(Group.generator.scale(s)); } /** * Creates a {@link PublicKey} from a JSON structure element. * @returns a {@link PublicKey}. */ static from(g) { return PublicKey.fromObject({ x: Field.from(g.x), isOdd: Bool(g.isOdd) }); } /** * Creates an empty {@link PublicKey}. * @returns an empty {@link PublicKey} */ static empty() { return PublicKey.from({ x: 0n, isOdd: false }); } /** * Checks if a {@link PublicKey} is empty. * @returns a {@link Bool} */ isEmpty() { // there are no curve points with x === 0 return this.x.equals(0); } /** * Decodes a base58 encoded {@link PublicKey} into a {@link PublicKey}. * @returns a {@link PublicKey} */ static fromBase58(publicKeyBase58) { let { x, isOdd } = PublicKeyBigint.fromBase58(publicKeyBase58); return PublicKey.from({ x: Field(x), isOdd: Bool(!!isOdd) }); } /** * Encodes a {@link PublicKey} in base58 format. * @returns a base58 encoded {@link PublicKey} */ toBase58() { return PublicKey.toBase58(this); } /** * Static method to encode a {@link PublicKey} into base58 format. * @returns a base58 encoded {@link PublicKey} */ static toBase58({ x, isOdd }) { x = toConstantField(x, 'toBase58', 'pk', 'public key'); return PublicKeyBigint.toBase58({ x: x.toBigInt(), isOdd: isOdd.toBoolean(), }); } /** * Serializes a {@link PublicKey} into its JSON representation. * @returns a JSON string */ static toJSON(publicKey) { return publicKey.toBase58(); } /** * Deserializes a JSON string into a {@link PublicKey}. * @returns a JSON string */ static fromJSON(publicKey) { return PublicKey.fromBase58(publicKey); } static toValue({ x, isOdd }) { return { x: x.toBigInt(), isOdd: isOdd.toBoolean() }; } static fromValue({ x, isOdd }) { return PublicKey.from({ x: Field.from(x), isOdd: Bool(isOdd) }); } } __decorate([ prop, __metadata("design:type", Field) ], PublicKey.prototype, "x", void 0); __decorate([ prop, __metadata("design:type", Bool) ], PublicKey.prototype, "isOdd", void 0); /** * A Schnorr {@link Signature} over the Pasta Curves. */ class Signature extends CircuitValue { /** * Signs a message using a {@link PrivateKey}. * @returns a {@link Signature} */ static create(privKey, msg) { let publicKey = PublicKey.fromPrivateKey(privKey).toGroup(); let d = privKey.s; // we chose an arbitrary prefix for the signature // there's no consequences in practice and the signatures can be used with any network // if there needs to be a custom nonce, include it in the message itself let kPrime = Scalar.from(deriveNonce({ fields: msg.map((f) => f.toBigInt()) }, { x: publicKey.x.toBigInt(), y: publicKey.y.toBigInt() }, d.toBigInt(), 'devnet')); let { x: r, y: ry } = Group.generator.scale(kPrime); let k = ry.isOdd().toBoolean() ? kPrime.neg() : kPrime; let h = hashWithPrefix(signaturePrefix('devnet'), msg.concat([publicKey.x, publicKey.y, r])); let e = Scalar.fromField(h); let s = e.mul(d).add(k); return new Signature(r, s); } /** * Verifies the {@link Signature} using a message and the corresponding {@link PublicKey}. * @returns a {@link Bool} */ verify(publicKey, msg) { let point = publicKey.toGroup(); // we chose an arbitrary prefix for the signature // there's no consequences in practice and the signatures can be used with any network // if there needs to be a custom nonce, include it in the message itself let h = hashWithPrefix(signaturePrefix('devnet'), msg.concat([point.x, point.y, this.r])); let r = point.scale(h).neg().add(Group.generator.scale(this.s)); return r.x.equals(this.r).and(r.y.isEven()); } /** * Decodes a base58 encoded signature into a {@link Signature}. */ static fromBase58(signatureBase58) { let { r, s } = SignatureBigint.fromBase58(signatureBase58); return Signature.fromObject({ r: Field(r), s: Scalar.from(s) }); } /** * Encodes a {@link Signature} in base58 format. */ toBase58() { let r = this.r.toBigInt(); let s = this.s.toBigInt(); return SignatureBigint.toBase58({ r, s }); } static fromValue({ r, s }) { return Signature.fromObject({ r: Field.from(r), s: Scalar.from(s) }); } } __decorate([ prop, __metadata("design:type", Field) ], Signature.prototype, "r", void 0); __decorate([ prop, __metadata("design:type", Scalar) ], Signature.prototype, "s", void 0); //# sourceMappingURL=signature.js.map