| 'use strict'; |
| |
| var hash = require('hash.js'); |
| var curves = require('../curves'); |
| var utils = require('../utils'); |
| var assert = utils.assert; |
| var parseBytes = utils.parseBytes; |
| var KeyPair = require('./key'); |
| var Signature = require('./signature'); |
| |
| function EDDSA(curve) { |
| assert(curve === 'ed25519', 'only tested with ed25519 so far'); |
| |
| if (!(this instanceof EDDSA)) |
| return new EDDSA(curve); |
| |
| var curve = curves[curve].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.sign = function sign(message, secret) { |
| message = parseBytes(message); |
| var key = this.keyFromSecret(secret); |
| var r = this.hashInt(key.messagePrefix(), message); |
| var R = this.g.mul(r); |
| var Rencoded = this.encodePoint(R); |
| var s_ = this.hashInt(Rencoded, key.pubBytes(), message) |
| .mul(key.priv()); |
| var S = r.add(s_).umod(this.curve.n); |
| return this.makeSignature({ R: R, S: S, Rencoded: 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); |
| var key = this.keyFromPublic(pub); |
| var h = this.hashInt(sig.Rencoded(), key.pubBytes(), message); |
| var SG = this.g.mul(sig.S()); |
| var RplusAh = sig.R().add(key.pub().mul(h)); |
| return RplusAh.eq(SG); |
| }; |
| |
| EDDSA.prototype.hashInt = function hashInt() { |
| var hash = this.hash(); |
| for (var 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, pub); |
| }; |
| |
| EDDSA.prototype.keyFromSecret = function keyFromSecret(secret) { |
| return KeyPair.fromSecret(this, 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) { |
| var 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); |
| |
| var lastIx = bytes.length - 1; |
| var normed = bytes.slice(0, lastIx).concat(bytes[lastIx] & ~0x80); |
| var xIsOdd = (bytes[lastIx] & 0x80) !== 0; |
| |
| var 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; |
| }; |