@stricahq/bip32ed25519
Version:
Pure javascript implementation of Bip32Ed25519, used for Cardano blockchain key pair.
135 lines (134 loc) • 5.41 kB
JavaScript
"use strict";
/**
* Copyright 2021 Ashish Prajapati
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* *** Includes code to override elliptic.js implementation for ed25519 ***
*
* LICENSE
*
* This software is licensed under the MIT License.
*
* Copyright Fedor Indutny, 2014.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
const hash = require("hash.js");
const elliptic = require("elliptic");
const utils = elliptic.utils;
const parseBytes = utils.parseBytes;
const KeyPair = require("./key");
const Signature = require("./signature");
function EDDSA() {
if (!(this instanceof EDDSA))
return new EDDSA();
const curve = elliptic.curves.ed25519.curve;
this.curve = curve;
this.g = curve.g;
this.g.precompute(curve.n.bitLength() + 1);
this.pointClass = curve.point().constructor;
this.encodingLength = Math.ceil(curve.n.bitLength() / 8);
this.hash = hash.sha512;
}
module.exports = EDDSA;
/**
* @param {Array|String} message - message bytes
* @param {Array|String|KeyPair} secret - secret bytes or a keypair
* @returns {Signature} - signature
*/
EDDSA.prototype.signExtended = function sign(message, secret) {
message = parseBytes(message);
const key = this.keyFromSecret(secret);
const r = this.hashInt(key.messagePrefix(), message);
const R = this.g.mul(r);
const Rencoded = this.encodePoint(R);
const s_ = this.hashInt(Rencoded, key.pubBytes(), message).mul(key.kl());
const S = r.add(s_).umod(this.curve.n);
return this.makeSignature({ R, S, Rencoded });
};
/**
* @param {Array} message - message bytes
* @param {Array|String|Signature} sig - sig bytes
* @param {Array|String|Point|KeyPair} pub - public key
* @returns {Boolean} - true if public key matches sig of message
*/
EDDSA.prototype.verify = function verify(message, sig, pub) {
message = parseBytes(message);
sig = this.makeSignature(sig);
const key = this.keyFromPublic(pub);
const h = this.hashInt(sig.Rencoded(), key.pubBytes(), message);
const SG = this.g.mul(sig.S());
const RplusAh = sig.R().add(key.pub().mul(h));
return RplusAh.eq(SG);
};
EDDSA.prototype.hashInt = function hashInt() {
const hash = this.hash();
for (let i = 0; i < arguments.length; i++)
hash.update(arguments[i]);
return utils.intFromLE(hash.digest()).umod(this.curve.n);
};
EDDSA.prototype.keyFromPublic = function keyFromPublic(pub) {
return KeyPair.fromPublic(this, utils.parseBytes(pub));
};
EDDSA.prototype.keyFromSecret = function keyFromSecret(secret) {
return KeyPair.fromSecret(this, utils.parseBytes(secret));
};
EDDSA.prototype.makeSignature = function makeSignature(sig) {
if (sig instanceof Signature)
return sig;
return new Signature(this, sig);
};
/**
* * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03#section-5.2
*
* EDDSA defines methods for encoding and decoding points and integers. These are
* helper convenience methods, that pass along to utility functions implied
* parameters.
*
*/
EDDSA.prototype.encodePoint = function encodePoint(point) {
const enc = point.getY().toArray("le", this.encodingLength);
enc[this.encodingLength - 1] |= point.getX().isOdd() ? 0x80 : 0;
return enc;
};
EDDSA.prototype.decodePoint = function decodePoint(bytes) {
bytes = utils.parseBytes(bytes);
const lastIx = bytes.length - 1;
const normed = bytes.slice(0, lastIx).concat(bytes[lastIx] & ~0x80);
const xIsOdd = (bytes[lastIx] & 0x80) !== 0;
const y = utils.intFromLE(normed);
return this.curve.pointFromY(y, xIsOdd);
};
EDDSA.prototype.encodeInt = function encodeInt(num) {
return num.toArray("le", this.encodingLength);
};
EDDSA.prototype.decodeInt = function decodeInt(bytes) {
return utils.intFromLE(bytes);
};
EDDSA.prototype.isPoint = function isPoint(val) {
return val instanceof this.pointClass;
};