o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
283 lines • 9.76 kB
JavaScript
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