Skip to content

Commit

Permalink
Add Third Party Caveat to macaroon demo
Browse files Browse the repository at this point in the history
  • Loading branch information
guggero committed Apr 21, 2019
1 parent 7c1de4a commit 2ad8fed
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 30 deletions.
14 changes: 10 additions & 4 deletions libs/js/bitcoin.js
Original file line number Diff line number Diff line change
Expand Up @@ -42374,16 +42374,22 @@ const ByteBuffer = class ByteBuffer {
return this._buf.subarray(0, this._length);
}
/**
* Grow the internal buffer so that it's at least as but as minCap.
* Grow the internal buffer so that it's at least as big as minCap.
* @param {int} minCap The minimum new capacity.
*/
_grow(minCap) {
if (minCap <= this._capacity) {
const capacity = this._buf.length;
if (minCap <= capacity) {
return;
}
// TODO could use more intelligent logic to grow more slowly on large buffers.
const fiftyPercent = Math.ceil(capacity * 1.5);
const doubleCap = this._buf.length * 2;
const newCap = minCap > doubleCap ? minCap : doubleCap;
let newCap = minCap > doubleCap ? minCap : doubleCap;
// If the new capacity is only 1 to 9 bytes, try if increasing
// the buffer by 50% is enough.
if (minCap - capacity < 10 && minCap < fiftyPercent) {
newCap = fiftyPercent;
}
const newContent = new Uint8Array(newCap);
newContent.set(this._buf.subarray(0, this._length));
this._buf = newContent;
Expand Down
109 changes: 98 additions & 11 deletions libs/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -3229,7 +3229,25 @@ angular.module('app').run(['$templateCache', function($templateCache) {
" <div class=\"panel-collapse collapse\" ng-class=\"{in: vm.showExplanation}\">\n" +
" <div class=\"panel-body\">\n" +
" Macaroons are Cookies with Contextual Caveats for Decentralized Authorization in the Cloud.<br/><br/>\n" +
" They are used, for example, in the <em>lnd</em> implementation of the Lightning Network.\n" +
" They are used, for example, in the <em>lnd</em> implementation of the Lightning Network.<br/>\n" +
" A <strong>Caveat</strong> (or First Party Caveat) is a condition that is either added by the issuer of the\n" +
" macaroon or the user of the caveat. Because of the used cryptographic one-way function (HMAC), conditions can be added\n" +
" by anyone holding the macaroon, but nobody can remove any condition.<br/>\n" +
" That way, a user can further restrict the access rights of a macaroon that she obtained (for example, add a condition that\n" +
" the macaroon is only valid for the next 3 seconds while transmitting it over the internet and therefore restricting\n" +
" a potential eavesdropper's chance of using a stolen macaroon).<br/>\n" +
" The issuer of the macaroon (who is the holder of the private root key) can verify a signature even if further caveats have\n" +
" been added.<br/><br/>\n" +
" <strong>Third Party Caveats</strong> are conditions that have to be met by a third party. For example, a node operator wants\n" +
" to give all users of her website limited access to her LND node. She would then set up the LND node and the website with a\n" +
" <em>Shared Key</em>. The LND node would only issue macaroons that have a Third Party Caveat added for the website.<br/>\n" +
" This basically tells the macaroon validator that &quot;this macaroon is only valid if the user can also present a discharge macaroon\n" +
" from the service <code>website</code>&quot;.<br/>\n" +
" A user that is logged in to the website would then get a discharge macaroon that basically states &quot;I have been authorized by the\n" +
" service <code>website</code>&quot; and can prove that cryptographically.<br/>\n" +
" When the user wants to connect to the LND node and use its functionality, she would present both macaroons to the node that can\n" +
" then verify they both are valid, bound to each other and meet all conditions.\n" +
"\n" +
"\n" +
" <h3>Sources, tools and other useful information:</h3>\n" +
" <ul>\n" +
Expand All @@ -3252,12 +3270,12 @@ angular.module('app').run(['$templateCache', function($templateCache) {
" class=\"form-control\"\n" +
" ng-model=\"vm.rootKey\"\n" +
" ng-change=\"vm.newMacaroon()\"\n" +
" ng-class=\"{'well-error': vm.error}\">\n" +
" ng-class=\"{'well-error': vm.error2}\">\n" +
" <span class=\"input-group-addon\" ng-if=\"!vm.error2\">&lt;-- paste hex</span>\n" +
" <span class=\"input-group-addon well-error\" ng-if=\"vm.error2\"> {{vm.error2}}</span>\n" +
" <span class=\"input-group-btn\">\n" +
" <button class=\"btn btn-primary\" ng-click=\"vm.randomRootKey()\">Randomize</button>\n" +
" </span>\n" +
" <button class=\"btn btn-primary\" ng-click=\"vm.randomRootKey()\">Randomize</button>\n" +
" </span>\n" +
" </div>\n" +
" </div>\n" +
"\n" +
Expand Down Expand Up @@ -3289,17 +3307,60 @@ angular.module('app').run(['$templateCache', function($templateCache) {
" </div>\n" +
" <div class=\"form-group\">\n" +
" <div class=\"col-lg-offset-3 col-sm-9 input-group\">\n" +
" <div class=\"input-group-btn\">\n" +
" <div class=\"input-group\" style=\"width: 100%;\">\n" +
" <button class=\"btn btn-primary\" ng-click=\"vm.addCaveat()\">Add caveat</button>\n" +
" <button class=\"btn btn-secondary pull-right\" ng-if=\"!vm.thirdPartyMac\" ng-click=\"vm.addThirdPartyCaveat()\">\n" +
" Add third party caveat\n" +
" </button>\n" +
" <button class=\"btn btn-secondary pull-right\" ng-if=\"vm.thirdPartyMac\" ng-click=\"vm.removeThirdPartyCaveat()\">\n" +
" Remove third party caveat\n" +
" </button>\n" +
" </div>\n" +
" </div>\n" +
" </div>\n" +
"\n" +
" <!-- third party caveat -->\n" +
" <div class=\"form-group\" ng-if=\"vm.thirdPartyMac\">\n" +
" <label class=\"col-sm-3 control-label\">Third Party Caveat</label>\n" +
" <div class=\"col-sm-9 input-group\">\n" +
" <div class=\"input-group\">\n" +
" <div class=\"input-group-addon\">Shared Root key (hex):</div>\n" +
" <input class=\"form-control\"\n" +
" ng-model=\"vm.thirdPartyMac.rootKey\"\n" +
" ng-change=\"vm.newMacaroon()\"\n" +
" ng-class=\"{'well-error': vm.error4}\">\n" +
" <span class=\"input-group-addon\" ng-if=\"!vm.error4\">&lt;-- paste hex</span>\n" +
" <span class=\"input-group-addon well-error\" ng-if=\"vm.error4\"> {{vm.error4}}</span>\n" +
" <span class=\"input-group-btn\">\n" +
" <button class=\"btn btn-primary\" ng-click=\"vm.randomTpmRootKey()\">Randomize</button>\n" +
" </span>\n" +
" </div>\n" +
" <div class=\"input-group\">\n" +
" <div class=\"input-group-addon\">Identifier:</div>\n" +
" <input class=\"form-control\" ng-model=\"vm.thirdPartyMac.identifier\" ng-change=\"vm.newMacaroon()\">\n" +
" </div>\n" +
" <div class=\"input-group\">\n" +
" <div class=\"input-group-addon\">Location:</div>\n" +
" <input class=\"form-control\" ng-model=\"vm.thirdPartyMac.location\" ng-change=\"vm.newMacaroon()\">\n" +
" </div>\n" +
" </div>\n" +
" </div>\n" +
"\n" +
" <!-- discharge macaroon -->\n" +
" <div class=\"form-group\" ng-if=\"vm.thirdPartyMac\">\n" +
" <label class=\"col-sm-3 control-label\">Discharge macaroon from <br/>Third Party:</label>\n" +
" <div class=\"col-sm-9 input-group\">\n" +
" <input class=\"form-control\" value=\"{{ vm.serializeMacaroon(vm.thirdPartyMac.macaroon, false) }}\" ng-readonly=\"true\">\n" +
" </div>\n" +
" </div>\n" +
"\n" +
" <div class=\"form-group\">\n" +
" <label class=\"col-sm-3 control-label\" for=\"json\">JSON:</label>\n" +
" <div class=\"col-sm-9 input-group\">\n" +
" <textarea id=\"json2\" rows=\"10\" ng-readonly=\"true\"\n" +
" class=\"form-control\">{{ vm.serializeMacaroon(vm.macaroon2, vm.showJson) }}</textarea><br/>\n" +
" <textarea id=\"json2\" rows=\"10\" ng-readonly=\"true\" class=\"form-control\">{{\n" +
" vm.serializeMacaroon(vm.macaroon2, vm.showJson)\n" +
" }}</textarea>\n" +
" <br/>\n" +
" <input type=\"checkbox\" ng-model=\"vm.showJson\"> Show as JSON\n" +
" </div>\n" +
" </div>\n" +
Expand All @@ -3311,8 +3372,8 @@ angular.module('app').run(['$templateCache', function($templateCache) {
" <form class=\"form-horizontal\">\n" +
"\n" +
" <div class=\"form-group\">\n" +
" <label class=\"col-sm-3 control-label\" for=\"hash\">Hex serialized macaroon:</label>\n" +
" <div class=\"col-sm-9 input-group\">\n" +
" <label class=\"col-sm-4 control-label\" for=\"hash\">Hex serialized macaroon:</label>\n" +
" <div class=\"col-sm-8 input-group\">\n" +
" <input id=\"hash\"\n" +
" class=\"form-control\"\n" +
" ng-model=\"vm.encodedMacaroon\"\n" +
Expand All @@ -3324,12 +3385,38 @@ angular.module('app').run(['$templateCache', function($templateCache) {
" </div>\n" +
"\n" +
" <div class=\"form-group\">\n" +
" <label class=\"col-sm-3 control-label\" for=\"json\">Decoded as JSON:</label>\n" +
" <div class=\"col-sm-9 input-group\">\n" +
" <label class=\"col-sm-4 control-label\" for=\"json\">Decoded as JSON:</label>\n" +
" <div class=\"col-sm-8 input-group\">\n" +
" <textarea id=\"json\" rows=\"30\" ng-readonly=\"true\" class=\"form-control\">{{ vm.serializeMacaroon(vm.macaroon, true) }}</textarea>\n" +
" <input type=\"checkbox\" ng-model=\"vm.tryDecodingId\"> Try to decode identifier\n" +
" </div>\n" +
" </div>\n" +
"\n" +
" <!-- verify against root key -->\n" +
" <div class=\"form-group\">\n" +
" <label class=\"col-sm-4 control-label\" for=\"verificationRootKey\">Verify signature with root key:</label>\n" +
" <div class=\"col-sm-8 input-group\">\n" +
" <input id=\"verificationRootKey\"\n" +
" class=\"form-control\"\n" +
" ng-model=\"vm.verificationRootKey\"\n" +
" ng-change=\"vm.verifyMacaroon()\"\n" +
" ng-class=\"{'well-error': vm.error3, 'well-success': vm.valid}\">\n" +
" <span class=\"input-group-addon\" ng-if=\"!vm.error3\">&lt;-- paste hex</span>\n" +
" <span class=\"input-group-addon well-error\" ng-if=\"vm.error3\"> {{vm.error3}}</span>\n" +
" </div>\n" +
" </div>\n" +
"\n" +
" <!-- verify against root key -->\n" +
" <div class=\"form-group\">\n" +
" <label class=\"col-sm-4 control-label\" for=\"discharge\">Discharge macaroon<br/>(for Third Party Caveat verification):</label>\n" +
" <div class=\"col-sm-8 input-group\">\n" +
" <input id=\"discharge\"\n" +
" class=\"form-control\"\n" +
" ng-model=\"vm.verificationDischarge\"\n" +
" ng-change=\"vm.verifyMacaroon()\">\n" +
" <span class=\"input-group-addon\">&lt;-- paste hex</span>\n" +
" </div>\n" +
" </div>\n" +
" </form>\n" +
"</div>\n"
);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"grunt-angular-templates": "^1.1.0",
"grunt-cli": "^1.3.2",
"js-sha3": "^0.6.1",
"macaroon": "^3.0.0",
"macaroon": "guggero/js-macaroon",
"merkle-lib": "^2.0.10",
"pbkdf2": "^3.0.13",
"pushdata-bitcoin": "^1.0.1",
Expand Down
109 changes: 98 additions & 11 deletions pages/macaroon/macaroon.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,25 @@ <h4 class="panel-title">
<div class="panel-collapse collapse" ng-class="{in: vm.showExplanation}">
<div class="panel-body">
Macaroons are Cookies with Contextual Caveats for Decentralized Authorization in the Cloud.<br/><br/>
They are used, for example, in the <em>lnd</em> implementation of the Lightning Network.
They are used, for example, in the <em>lnd</em> implementation of the Lightning Network.<br/>
A <strong>Caveat</strong> (or First Party Caveat) is a condition that is either added by the issuer of the
macaroon or the user of the caveat. Because of the used cryptographic one-way function (HMAC), conditions can be added
by anyone holding the macaroon, but nobody can remove any condition.<br/>
That way, a user can further restrict the access rights of a macaroon that she obtained (for example, add a condition that
the macaroon is only valid for the next 3 seconds while transmitting it over the internet and therefore restricting
a potential eavesdropper's chance of using a stolen macaroon).<br/>
The issuer of the macaroon (who is the holder of the private root key) can verify a signature even if further caveats have
been added.<br/><br/>
<strong>Third Party Caveats</strong> are conditions that have to be met by a third party. For example, a node operator wants
to give all users of her website limited access to her LND node. She would then set up the LND node and the website with a
<em>Shared Key</em>. The LND node would only issue macaroons that have a Third Party Caveat added for the website.<br/>
This basically tells the macaroon validator that &quot;this macaroon is only valid if the user can also present a discharge macaroon
from the service <code>website</code>&quot;.<br/>
A user that is logged in to the website would then get a discharge macaroon that basically states &quot;I have been authorized by the
service <code>website</code>&quot; and can prove that cryptographically.<br/>
When the user wants to connect to the LND node and use its functionality, she would present both macaroons to the node that can
then verify they both are valid, bound to each other and meet all conditions.


<h3>Sources, tools and other useful information:</h3>
<ul>
Expand All @@ -32,12 +50,12 @@ <h4>Create macaroon</h4>
class="form-control"
ng-model="vm.rootKey"
ng-change="vm.newMacaroon()"
ng-class="{'well-error': vm.error}">
ng-class="{'well-error': vm.error2}">
<span class="input-group-addon" ng-if="!vm.error2">&lt;-- paste hex</span>
<span class="input-group-addon well-error" ng-if="vm.error2"> {{vm.error2}}</span>
<span class="input-group-btn">
<button class="btn btn-primary" ng-click="vm.randomRootKey()">Randomize</button>
</span>
<button class="btn btn-primary" ng-click="vm.randomRootKey()">Randomize</button>
</span>
</div>
</div>

Expand Down Expand Up @@ -69,17 +87,60 @@ <h4>Create macaroon</h4>
</div>
<div class="form-group">
<div class="col-lg-offset-3 col-sm-9 input-group">
<div class="input-group-btn">
<div class="input-group" style="width: 100%;">
<button class="btn btn-primary" ng-click="vm.addCaveat()">Add caveat</button>
<button class="btn btn-secondary pull-right" ng-if="!vm.thirdPartyMac" ng-click="vm.addThirdPartyCaveat()">
Add third party caveat
</button>
<button class="btn btn-secondary pull-right" ng-if="vm.thirdPartyMac" ng-click="vm.removeThirdPartyCaveat()">
Remove third party caveat
</button>
</div>
</div>
</div>

<!-- third party caveat -->
<div class="form-group" ng-if="vm.thirdPartyMac">
<label class="col-sm-3 control-label">Third Party Caveat</label>
<div class="col-sm-9 input-group">
<div class="input-group">
<div class="input-group-addon">Shared Root key (hex):</div>
<input class="form-control"
ng-model="vm.thirdPartyMac.rootKey"
ng-change="vm.newMacaroon()"
ng-class="{'well-error': vm.error4}">
<span class="input-group-addon" ng-if="!vm.error4">&lt;-- paste hex</span>
<span class="input-group-addon well-error" ng-if="vm.error4"> {{vm.error4}}</span>
<span class="input-group-btn">
<button class="btn btn-primary" ng-click="vm.randomTpmRootKey()">Randomize</button>
</span>
</div>
<div class="input-group">
<div class="input-group-addon">Identifier:</div>
<input class="form-control" ng-model="vm.thirdPartyMac.identifier" ng-change="vm.newMacaroon()">
</div>
<div class="input-group">
<div class="input-group-addon">Location:</div>
<input class="form-control" ng-model="vm.thirdPartyMac.location" ng-change="vm.newMacaroon()">
</div>
</div>
</div>

<!-- discharge macaroon -->
<div class="form-group" ng-if="vm.thirdPartyMac">
<label class="col-sm-3 control-label">Discharge macaroon from <br/>Third Party:</label>
<div class="col-sm-9 input-group">
<input class="form-control" value="{{ vm.serializeMacaroon(vm.thirdPartyMac.macaroon, false) }}" ng-readonly="true">
</div>
</div>

<div class="form-group">
<label class="col-sm-3 control-label" for="json">JSON:</label>
<div class="col-sm-9 input-group">
<textarea id="json2" rows="10" ng-readonly="true"
class="form-control">{{ vm.serializeMacaroon(vm.macaroon2, vm.showJson) }}</textarea><br/>
<textarea id="json2" rows="10" ng-readonly="true" class="form-control">{{
vm.serializeMacaroon(vm.macaroon2, vm.showJson)
}}</textarea>
<br/>
<input type="checkbox" ng-model="vm.showJson"> Show as JSON
</div>
</div>
Expand All @@ -91,8 +152,8 @@ <h4>Decode macaroon</h4>
<form class="form-horizontal">

<div class="form-group">
<label class="col-sm-3 control-label" for="hash">Hex serialized macaroon:</label>
<div class="col-sm-9 input-group">
<label class="col-sm-4 control-label" for="hash">Hex serialized macaroon:</label>
<div class="col-sm-8 input-group">
<input id="hash"
class="form-control"
ng-model="vm.encodedMacaroon"
Expand All @@ -104,11 +165,37 @@ <h4>Decode macaroon</h4>
</div>

<div class="form-group">
<label class="col-sm-3 control-label" for="json">Decoded as JSON:</label>
<div class="col-sm-9 input-group">
<label class="col-sm-4 control-label" for="json">Decoded as JSON:</label>
<div class="col-sm-8 input-group">
<textarea id="json" rows="30" ng-readonly="true" class="form-control">{{ vm.serializeMacaroon(vm.macaroon, true) }}</textarea>
<input type="checkbox" ng-model="vm.tryDecodingId"> Try to decode identifier
</div>
</div>

<!-- verify against root key -->
<div class="form-group">
<label class="col-sm-4 control-label" for="verificationRootKey">Verify signature with root key:</label>
<div class="col-sm-8 input-group">
<input id="verificationRootKey"
class="form-control"
ng-model="vm.verificationRootKey"
ng-change="vm.verifyMacaroon()"
ng-class="{'well-error': vm.error3, 'well-success': vm.valid}">
<span class="input-group-addon" ng-if="!vm.error3">&lt;-- paste hex</span>
<span class="input-group-addon well-error" ng-if="vm.error3"> {{vm.error3}}</span>
</div>
</div>

<!-- verify against root key -->
<div class="form-group">
<label class="col-sm-4 control-label" for="discharge">Discharge macaroon<br/>(for Third Party Caveat verification):</label>
<div class="col-sm-8 input-group">
<input id="discharge"
class="form-control"
ng-model="vm.verificationDischarge"
ng-change="vm.verifyMacaroon()">
<span class="input-group-addon">&lt;-- paste hex</span>
</div>
</div>
</form>
</div>
Loading

0 comments on commit 2ad8fed

Please sign in to comment.