Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signMessage and verifyMessage functions #794

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ const errors = require('./common').errors
const generateAddress =
require('./offline/generate-address').generateAddressAPI
const computeLedgerHash = require('./offline/ledgerhash')
const signMessage =
require('./offline/sign-message')
const verifyMessage =
require('./offline/verify-message')
const signPaymentChannelClaim =
require('./offline/sign-payment-channel-claim')
const verifyPaymentChannelClaim =
Expand Down Expand Up @@ -152,6 +156,8 @@ _.assign(RippleAPI.prototype, {

generateAddress,
computeLedgerHash,
signMessage,
verifyMessage,
signPaymentChannelClaim,
verifyPaymentChannelClaim,
errors
Expand Down
4 changes: 3 additions & 1 deletion src/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ module.exports = {
utils.convertKeysFromSnakeCaseToCamelCase,
iso8601ToRippleTime: utils.iso8601ToRippleTime,
rippleTimeToISO8601: utils.rippleTimeToISO8601,
isValidSecret: utils.isValidSecret
isValidSecret: utils.isValidSecret,
stringToUtf8ByteArray: utils.stringToUtf8ByteArray,
bytesToHex: utils.bytesToHex
}
5 changes: 5 additions & 0 deletions src/common/schema-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function loadSchemas() {
require('./schemas/objects/max-adjustment.json'),
require('./schemas/objects/memo.json'),
require('./schemas/objects/memos.json'),
require('./schemas/objects/message.json'),
require('./schemas/objects/public-key.json'),
require('./schemas/objects/uint32.json'),
require('./schemas/objects/value.json'),
Expand Down Expand Up @@ -77,6 +78,8 @@ function loadSchemas() {
require('./schemas/output/get-transaction.json'),
require('./schemas/output/get-transactions.json'),
require('./schemas/output/get-trustlines.json'),
require('./schemas/output/sign-message.json'),
require('./schemas/output/verify-message.json'),
require('./schemas/output/sign-payment-channel-claim.json'),
require('./schemas/output/verify-payment-channel-claim.json'),
require('./schemas/input/get-balances.json'),
Expand Down Expand Up @@ -107,6 +110,8 @@ function loadSchemas() {
require('./schemas/input/sign.json'),
require('./schemas/input/submit.json'),
require('./schemas/input/generate-address.json'),
require('./schemas/input/sign-message.json'),
require('./schemas/input/verify-message.json'),
require('./schemas/input/sign-payment-channel-claim.json'),
require('./schemas/input/verify-payment-channel-claim.json'),
require('./schemas/input/combine.json')
Expand Down
17 changes: 17 additions & 0 deletions src/common/schemas/input/sign-message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "signMessageParameters",
"type": "object",
"properties": {
"message": {
"$ref": "message",
"description": "Message text."
},
"privateKey": {
"$ref": "publicKey",
"description": "The private key to sign the payment channel claim."
}
},
"additionalProperties": false,
"required": ["message", "privateKey"]
}
21 changes: 21 additions & 0 deletions src/common/schemas/input/verify-message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "verifyMessageParameters",
"type": "object",
"properties": {
"message": {
"$ref": "message",
"description": "Message text."
},
"signature": {
"$ref": "signature",
"description": "Signature of this message."
},
"publicKey": {
"$ref": "publicKey",
"description": "Public key of the message's sender"
}
},
"additionalProperties": false,
"required": ["message", "signature", "publicKey"]
}
7 changes: 7 additions & 0 deletions src/common/schemas/objects/message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "message",
"description": "Message objects represent arbitrary data that can be signed",
"type": "string",
"additionalProperties": false
}
7 changes: 7 additions & 0 deletions src/common/schemas/output/sign-message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "signMessage",
"type": "string",
"$ref": "signature",
"additionalProperties": false
}
6 changes: 6 additions & 0 deletions src/common/schemas/output/verify-message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "verifyMessage",
"type": "boolean",
"additionalProperties": false
}
44 changes: 43 additions & 1 deletion src/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,46 @@ function iso8601ToRippleTime(iso8601: string): number {
return unixToRippleTimestamp(Date.parse(iso8601))
}

/**
* Converts a JS string to a UTF-8 "byte" array.
* From {@link https://git.io/v52py|Google's common JavaScript library}.
* @param {string} str 16-bit unicode string.
* @return {!Array<number>} UTF-8 byte array.
*/
function stringToUtf8ByteArray(str) {
var out = [], p = 0;
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c < 128) {
out[p++] = c;
} else if (c < 2048) {
out[p++] = (c >> 6) | 192;
out[p++] = (c & 63) | 128;
} else if (
((c & 0xFC00) == 0xD800) && (i + 1) < str.length &&
((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
// Surrogate Pair
c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
out[p++] = (c >> 18) | 240;
out[p++] = ((c >> 12) & 63) | 128;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
} else {
out[p++] = (c >> 12) | 224;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
}
}
return out;
}

function bytesToHex(a) {
return a.map(function(byteValue) {
const hex = byteValue.toString(16).toUpperCase();
return hex.length > 1 ? hex : '0' + hex;
}).join('');
}

module.exports = {
dropsToXrp,
xrpToDrops,
Expand All @@ -89,5 +129,7 @@ module.exports = {
removeUndefined,
rippleTimeToISO8601,
iso8601ToRippleTime,
isValidSecret
isValidSecret,
stringToUtf8ByteArray,
bytesToHex
}
4 changes: 4 additions & 0 deletions src/common/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ module.exports = {
submit: _.partial(schemaValidate, 'submitParameters'),
computeLedgerHash: _.partial(schemaValidate, 'computeLedgerHashParameters'),
generateAddress: _.partial(schemaValidate, 'generateAddressParameters'),
signMessage: _.partial(schemaValidate,
'signMessageParameters'),
verifyMessage: _.partial(schemaValidate,
'verifyMessageParameters'),
signPaymentChannelClaim: _.partial(schemaValidate,
'signPaymentChannelClaimParameters'),
verifyPaymentChannelClaim: _.partial(schemaValidate,
Expand Down
16 changes: 16 additions & 0 deletions src/offline/sign-message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* @flow */
'use strict' // eslint-disable-line strict
const common = require('../common')
const keypairs = require('ripple-keypairs')
const binary = require('ripple-binary-codec')
const {validate, stringToUtf8ByteArray, bytesToHex} = common

function signMessage(message: string,
privateKey: string
): string {
validate.signMessage({message, privateKey})

return keypairs.sign(bytesToHex(stringToUtf8ByteArray(message)), privateKey)
}

module.exports = signMessage
16 changes: 16 additions & 0 deletions src/offline/verify-message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* @flow */
'use strict' // eslint-disable-line strict
const common = require('../common')
const keypairs = require('ripple-keypairs')
const binary = require('ripple-binary-codec')
const {validate, stringToUtf8ByteArray, bytesToHex} = common

function verifyMessage(message: string,
signature: string, publicKey: string
): string {
validate.verifyMessage({message, signature, publicKey})

return keypairs.verify(bytesToHex(stringToUtf8ByteArray(message)), signature, publicKey)
}

module.exports = verifyMessage
29 changes: 29 additions & 0 deletions test/api-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,35 @@ describe('RippleAPI', function() {
});
});

it('signMessage', function() {
const privateKey =
'AC051121672EA8DC119D53AFBD0221607BE6B6B61A720BAB41C144B47E8782B3';
const result = this.api.signMessage(
requests.signMessage.message,
privateKey);
checkResult(responses.signMessage,
'signMessage', result)
});

it('verifyMessage', function() {
const publicKey =
'02DCAF8A5B6FBBC3582B87F326176C402B466EEE65FCC23189FC09F0834A78CE3C';
const result = this.api.verifyMessage(
requests.signMessage.message,
responses.signMessage, publicKey);
checkResult(true, 'verifyMessage', result)
});

it('verifyMessage - invalid', function() {
const publicKey =
'02BB1EE6437B0EA8D5DB68EF4FC7CA77F919764A11649ED49EE9C5A0950BA8BF04';
const result = this.api.verifyMessage(
requests.signMessage.message,
responses.signMessage, publicKey);
checkResult(false,
'verifyMessage', result)
});

it('signPaymentChannelClaim', function() {
const privateKey =
'ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A';
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/requests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ module.exports = {
escrow: require('./sign-escrow.json'),
signAs: require('./sign-as')
},
signMessage: require('./sign-message'),
signPaymentChannelClaim: require('./sign-payment-channel-claim'),
getPaths: {
normal: require('./getpaths/normal'),
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/requests/sign-message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"message": "This is a test message for the signMessage and verifyMessage tests."
}
1 change: 1 addition & 0 deletions test/fixtures/responses/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ module.exports = {
escrow: require('./sign-escrow.json'),
signAs: require('./sign-as')
},
signMessage: require('./sign-message'),
signPaymentChannelClaim: require('./sign-payment-channel-claim'),
combine: {
single: require('./combine.json')
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/responses/sign-message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"3045022100E6D5B31AC54D3964DD016D5BC448CEF1D1643E7CB0190E6B289EBEAC8F92887802201A4EFD3B21C1792698D85770593B3503DD88C8BC513E19803F4AAA0951CEADB1"