Skip to content

Add JSON-LD verification via BTC public key #442

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

Merged
merged 3 commits into from
Jan 26, 2017
Merged
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
25 changes: 17 additions & 8 deletions playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -251,17 +251,26 @@ <h3>
</textarea>
</div>

<div id="privatekey-secp256k1-div" class="span6">
<h3>Bitcoin Private Key (secp256k1)</h3>
<textarea id="privatekey-secp256k1" class="compressed process span6 codemirror-input"
placeholder="Enter your Bitcoin (secp256k1) private key here..." rows="3">L4mEi7eEdTNNFQEWaa7JhUKAbtHdVvByGAqvpJKC53mfiqunjBjw</textarea>
<div id="privatekey-koblitz-div" class="span6">
<h3>Bitcoin (ECDSA Koblitz) Private Key for Signing</h3>
<textarea id="privatekey-koblitz" class="compressed process span6 codemirror-input"
placeholder="Enter your Bitcoin (Koblitz) private key here..." rows="3">L4mEi7eEdTNNFQEWaa7JhUKAbtHdVvByGAqvpJKC53mfiqunjBjw</textarea>

<h3>Bitcoin (ECDSA Koblitz) Public Key for Verification</h3>
<textarea id="publickey-koblitz" class="compressed process span6 codemirror-input"
placeholder="Enter your Bitcoin (Koblitz) public key here..." rows="3">1LGpGhGK8whX23ZNdxrgtjKrek9rP4xWER</textarea>

<div class="koblitz-verification"></div>
</div>


</div>
</div>

<div id="markup-errors" class="hide alert alert-error"></div>
<div id="param-errors" class="hide alert alert-error"></div>
<div id="validation-errors" class="hide alert alert-error"></div>
<div id="validation-message" class="hide alert alert-success"></div>
<div id="using-context-map" class="hide alert alert-note">
<p>NOTE: A remote context that is not known to be fully working yet was detected in your input.
If you wish, you can use an alternative context created by the JSON-LD community to
Expand Down Expand Up @@ -328,7 +337,7 @@ <h3>Bitcoin Private Key (secp256k1)</h3>
</a>
</li>
<li>
<a id="tab-signed-secp256k1" href="#pane-signed-secp256k1" data-toggle="tab" name="tab-signed-secp256k1">
<a id="tab-signed-koblitz" href="#pane-signed-koblitz" data-toggle="tab" name="tab-signed-koblitz">
<i class="icon-pencil"></i>
<span>Signed with Bitcoin</span>
</a>
Expand Down Expand Up @@ -357,8 +366,8 @@ <h3>Bitcoin Private Key (secp256k1)</h3>
<div id="pane-signed-rsa" class="tab-pane">
<textarea id="signed-rsa" class="codemirror-output"></textarea>
</div>
<div id="pane-signed-secp256k1" class="tab-pane">
<textarea id="signed-secp256k1" class="codemirror-output"></textarea>
<div id="pane-signed-koblitz" class="tab-pane">
<textarea id="signed-koblitz" class="codemirror-output"></textarea>
</div>
</div><!-- /.tab-content -->
</div>
Expand Down Expand Up @@ -406,7 +415,7 @@ <h3>Bitcoin Private Key (secp256k1)</h3>
$('#context-div').hide();
$('#frame-div').hide();
$('#privatekey-rsa-div').hide();
$('#privatekey-secp256k1-div').hide();
$('#privatekey-koblitz-div').hide();
$('#markup,#context,#frame').bind('keyup', function() {
$('.btn-group > .btn').each(function () {
$(this).removeClass('active');
Expand Down
44 changes: 26 additions & 18 deletions playground/jsonld-signatures.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ var libs = {};

api.SECURITY_CONTEXT_URL = 'https://w3id.org/security/v1';
api.SUPPORTED_ALGORITHMS = [
'BitcoinSignature2016',
'EcdsaKoblitzSignature2016',
'GraphSignature2012',
'LinkedDataSignature2015'
];
Expand Down Expand Up @@ -144,11 +144,12 @@ api.sign = function(input, options, callback) {

if(api.SUPPORTED_ALGORITHMS.indexOf(algorithm) === -1) {
return callback(new Error(
'[jsig.sign] options.algorithm must be one of: ' +
'[jsigs.sign] Unsupported algorithm "' + algorithm + '"; ' +
'options.algorithm must be one of: ' +
JSON.stringify(api.SUPPORTED_ALGORITHMS)));
}

if(algorithm === 'BitcoinSignature2016') {
if(algorithm === 'EcdsaKoblitzSignature2016') {
if(typeof privateKeyWif !== 'string') {
return callback(new TypeError(
'[jsig.sign] options.privateKeyWif must be a base 58 formatted string.'));
Expand Down Expand Up @@ -297,10 +298,12 @@ api.verify = function(input, options, callback) {
return callback(new Error('[jsigs.verify] No signature found.'));
}
var algorithm = jsonld.getValues(signature, 'type')[0] || '';
algorithm = algorithm.replace(/^.+:/, ''); // strip off any namespace to compare to known algorithm names
if(api.SUPPORTED_ALGORITHMS.indexOf(algorithm) === -1) {
return callback(new Error(
'[jsigs.verify] Unsupported signature algorithm; supported ' +
'algorithms are: ' + JSON.stringify(api.SUPPORTED_ALGORITHMS)));
'[jsigs.verify] Unsupported signature algorithm "' + algorithm + '"; ' +
'supported algorithms are: ' +
JSON.stringify(api.SUPPORTED_ALGORITHMS)));
}
return _verify(algorithm, input, options, callback);
});
Expand Down Expand Up @@ -678,13 +681,14 @@ function _verify(algorithm, input, options, callback) {
* @param callback(err, signature) called once the operation completes.
*/
var _createSignature = function(input, options, callback) {
if(options.algorithm === 'BitcoinSignature2016') {
var signature, privateKey;

if(options.algorithm === 'EcdsaKoblitzSignature2016') {
// works same in any environment
var signature;
try {
var bitcoreMessage = api.use('bitcoreMessage');
var bitcore = bitcoreMessage.Bitcore;
var privateKey = bitcore.PrivateKey.fromWIF(options.privateKeyWif);
privateKey = bitcore.PrivateKey.fromWIF(options.privateKeyWif);
var message = bitcoreMessage(_getDataToHash(input, options));
signature = message.sign(privateKey);
} catch(err) {
Expand All @@ -695,7 +699,6 @@ var _createSignature = function(input, options, callback) {

if(_nodejs) {
// optimize using node libraries
var signature;
try {
var crypto = api.use('crypto');
var signer = crypto.createSign('RSA-SHA256');
Expand All @@ -708,10 +711,9 @@ var _createSignature = function(input, options, callback) {
}

// browser or other environment
var signature;
try {
var forge = api.use('forge');
var privateKey = forge.pki.privateKeyFromPem(options.privateKeyPem);
privateKey = forge.pki.privateKeyFromPem(options.privateKeyPem);
var md = forge.md.sha256.create();
md.update(_getDataToHash(input, options), 'utf8');
signature = forge.util.encode64(privateKey.sign(md));
Expand All @@ -736,20 +738,26 @@ var _createSignature = function(input, options, callback) {
* @param callback(err, valid) called once the operation completes.
*/
var _verifySignature = function(input, signature, options, callback) {
if(options.algorithm === 'BitcoinSignature2016') {
var verified;

if(options.algorithm === 'EcdsaKoblitzSignature2016') {
// works same in any environment
var bitcoreMessage = api.use('bitcoreMessage');
var message = bitcoreMessage(_getDataToHash(input, options));
var verified = message.verify(options.publicKeyWif, signature);
return callback(null, verified);
try {
var bitcoreMessage = api.use('bitcoreMessage');
var message = bitcoreMessage(_getDataToHash(input, options));
verified = message.verify(options.publicKeyWif, signature);
return callback(null, verified);
} catch (err) {
return callback(err);
}
}

if(_nodejs) {
// optimize using node libraries
var crypto = api.use('crypto');
var verifier = crypto.createVerify('RSA-SHA256');
verifier.update(_getDataToHash(input, options), 'utf8');
var verified = verifier.verify(options.publicKeyPem, signature, 'base64');
verified = verifier.verify(options.publicKeyPem, signature, 'base64');
return callback(null, verified);
}

Expand All @@ -758,7 +766,7 @@ var _verifySignature = function(input, signature, options, callback) {
var publicKey = forge.pki.publicKeyFromPem(options.publicKeyPem);
var md = forge.md.sha256.create();
md.update(_getDataToHash(input, options), 'utf8');
var verified = publicKey.verify(
verified = publicKey.verify(
md.digest().bytes(), forge.util.decode64(signature));
callback(null, verified);
};
Expand Down
4 changes: 4 additions & 0 deletions playground/playground.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@
#markup-container, #output-container {
margin-top: 1em;
}

#privatekey-koblitz-div .CodeMirror {
height: 100px;
}
70 changes: 56 additions & 14 deletions playground/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,8 @@
var editor;

// don't use JSON-LD for PEM data
if(key === 'privatekey-rsa' || key === 'privatekey-secp256k1') {
if(key === 'privatekey-rsa' || key === 'privatekey-koblitz' ||
key === 'publickey-koblitz') {
editor = CodeMirror.fromTextArea(node, {
matchBrackets: true,
autoCloseBrackets: true,
Expand Down Expand Up @@ -739,10 +740,10 @@
var id = playground.activeTab = evt.target.id;

if(['tab-compacted', 'tab-flattened', 'tab-framed',
'tab-signed-rsa', 'tab-signed-secp256k1', ].indexOf(id) > -1) {
'tab-signed-rsa', 'tab-signed-koblitz', ].indexOf(id) > -1) {
// these options require more UI inputs, so compress UI space
$('#markup-div').removeClass('span12').addClass('span6');
$('#frame-div, #privatekey-rsa-div, #privatekey-secp256k1-div, ' +
$('#frame-div, #privatekey-rsa-div, #privatekey-koblitz-div, ' +
'#context-div').hide();
if(id === 'tab-compacted' || id === 'tab-flattened') {
$('#param-type').html('JSON-LD Context');
Expand All @@ -753,15 +754,15 @@
} else if(id === 'tab-signed-rsa') {
$('#param-type').html('PEM-encoded Private Key');
$('#privatekey-rsa-div').show();
} else if(id === 'tab-signed-secp256k1') {
} else if(id === 'tab-signed-koblitz') {
$('#param-type').html('Base 58 Encoded Private Key');
$('#privatekey-secp256k1-div').show();
$('#privatekey-koblitz-div').show();
}
}
else {
// else no input textarea required
$('#context-div, #frame-div, #privatekey-rsa-div, ' +
'#privatekey-secp256k1-div').hide();
'#privatekey-koblitz-div').hide();
$('#markup-div').removeClass('span6').addClass('span12');
$('#param-type').html('');
}
Expand Down Expand Up @@ -838,11 +839,11 @@
creator: 'https://example.com/jdoe/keys/1'
});
}
else if(playground.activeTab === 'tab-signed-secp256k1') {
else if(playground.activeTab === 'tab-signed-koblitz') {
options.format = 'application/ld+json';

var jsigs = window.jsigs;
var pkey = playground.editors['privatekey-secp256k1'].getValue();
var privateKey = playground.editors['privatekey-koblitz'].getValue().trim();

// add security context to input
if(!('@context' in input)) {
Expand All @@ -854,11 +855,49 @@
[input['@context'], 'https://w3id.org/security/v1'];
}

promise = jsigs.promises.sign(input, {
privateKeyWif: pkey,
algorithm: 'BitcoinSignature2016',
domain: 'example.com',
creator: 'public-key:' + new bitcoreMessage.Bitcore.PrivateKey(pkey).toPublicKey()
var signed = null;
var publicKeyFromPrivateKey;
try {
publicKeyFromPrivateKey = new bitcoreMessage.Bitcore.PrivateKey(privateKey).toPublicKey().toAddress('livenet');
} catch (e) {
promise = Promise.reject(e)
}

promise = promise || jsigs.promises.sign(input, {
privateKeyWif: privateKey,
algorithm: 'EcdsaKoblitzSignature2016',
creator: 'ecdsa-koblitz-pubkey:' + publicKeyFromPrivateKey
}).then( function(_signed) {
signed = _signed;
var publicKey = playground.editors['publickey-koblitz'].getValue().trim();
var verificationKey = {
'@context': jsigs.SECURITY_CONTEXT_URL,
id: 'ecdsa-koblitz-pubkey:' + publicKey,
type: 'CryptographicKey',
publicKeyWif: publicKey
};
var publicKeyBtcOwner = {
'@context': jsigs.SECURITY_CONTEXT_URL,
publicKey: [verificationKey]
};
return jsigs.promises.verify(signed, {
publicKey: verificationKey,
publicKeyOwner: publicKeyBtcOwner
})
}).then( function(verification) {
if (verification) {
$('#validation-message')
.text('Signature validated successfully using this public key')
.show();
} else {
$('#validation-errors')
.text('Signature was NOT validated using this public key')
.show();
}
return signed;
}).catch( function(e) {
console.error(e.stack)
throw(e)
});
}
else {
Expand All @@ -882,6 +921,8 @@
playground.process = playground._process = function(){
$('#markup-errors').hide().empty();
$('#param-errors').hide().empty();
$('#validation-errors').hide().empty();
$('#validation-message').hide().empty();
$('#processing-errors').hide().empty();
$('#using-context-map').hide();
$('#using-context-map table tbody').empty();
Expand Down Expand Up @@ -1202,7 +1243,8 @@
hasData = true;
editor.setValue(playground.humanize(data[key]));
}else{
if(key !== 'privatekey-rsa' && key !== 'privatekey-secp256k1') {
if(key !== 'privatekey-rsa' && key !== 'privatekey-koblitz' &&
key !== 'publickey-koblitz') {
editor.setValue("{}");
}
}
Expand Down